style: apply spotless formatting
Build & Test (NowChessSystems) TeamCity build finished

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Janis Eccarius
2026-06-21 12:39:10 +02:00
parent 36a0b26c65
commit be941ff414
3 changed files with 25 additions and 22 deletions
@@ -9,17 +9,17 @@ import org.apache.spark.sql.functions as F
* *
* Every batch job consumes the same five-column shape: * Every batch job consumes the same five-column shape:
* - white_id, black_id : player identifiers * - white_id, black_id : player identifiers
* - result : one of "white", "black", "draw" * - result : one of "white", "black", "draw"
* - move_count : number of plies * - move_count : number of plies
* - pgn : full PGN ("[Event …]…\n\n1. e4 …"), header and movetext separated by a blank line * - pgn : full PGN ("[Event …]…\n\n1. e4 …"), header and movetext separated by a blank line
* *
* Two backends, selected by the `NOWCHESS_PGN_PATH` environment variable: * Two backends, selected by the `NOWCHESS_PGN_PATH` environment variable:
* - unset → PostgreSQL `game_records` table (production) * - unset → PostgreSQL `game_records` table (production)
* - set → a Lichess PGN dump file/URL (demo). Point it at a `lichess_db_standard_rated_*.pgn[.zst]` * - set → a Lichess PGN dump file/URL (demo). Point it at a `lichess_db_standard_rated_*.pgn[.zst]` to drive every
* to drive every batch job from real Lichess games. * batch job from real Lichess games.
* *
* Lichess parsing uses only Spark SQL string functions — no UDFs — so Catalyst can push predicates, * Lichess parsing uses only Spark SQL string functions — no UDFs — so Catalyst can push predicates, matching the
* matching the no-UDF approach already used in OpeningBookJob. * no-UDF approach already used in OpeningBookJob.
*/ */
object GameSource: object GameSource:
@@ -48,16 +48,16 @@ object GameSource:
/** Parses a Lichess PGN dump into the normalised game shape. /** Parses a Lichess PGN dump into the normalised game shape.
* *
* `path` may be: * `path` may be:
* - an http(s)/ftp URL — fetched once via SparkContext.addFile and distributed to executors, then read * - an http(s)/ftp URL — fetched once via SparkContext.addFile and distributed to executors, then read from the
* from the local replica (no S3/PVC needed; handy for a staging demo) * local replica (no S3/PVC needed; handy for a staging demo)
* - any Hadoop-readable path (file://, hdfs://, s3a://, …) * - any Hadoop-readable path (file://, hdfs://, s3a://, …)
* *
* `.zst` dumps (Lichess' native format) are decompressed in-process via zstd-jni; `.gz`/`.bz2` are * `.zst` dumps (Lichess' native format) are decompressed in-process via zstd-jni; `.gz`/`.bz2` are handled by
* handled by Spark's text reader codecs. * Spark's text reader codecs.
* *
* Records are split on the "[Event " tag that opens every game, so each row holds one complete game * Records are split on the "[Event " tag that opens every game, so each row holds one complete game (the empty
* (the empty fragment before the first game is filtered out). Header tags are read with regexp_extract; * fragment before the first game is filtered out). Header tags are read with regexp_extract; the movetext (after the
* the movetext (after the blank line) is cleaned of clock/eval comments and move numbers to count plies. * blank line) is cleaned of clock/eval comments and move numbers to count plies.
*/ */
def fromLichessPgn(spark: SparkSession, path: String): DataFrame = def fromLichessPgn(spark: SparkSession, path: String): DataFrame =
val resolved = resolvePath(spark, path) val resolved = resolvePath(spark, path)
@@ -89,9 +89,9 @@ object GameSource:
) )
.filter((F.col("white_id") =!= "").and(F.col("black_id") =!= "")) .filter((F.col("white_id") =!= "").and(F.col("black_id") =!= ""))
/** Turns an http(s)/ftp URL into a cluster-local path by fetching it once with SparkContext.addFile, /** Turns an http(s)/ftp URL into a cluster-local path by fetching it once with SparkContext.addFile, which
* which distributes the file to every executor. `.zst` is decompressed in-process and the plain `.pgn` * distributes the file to every executor. `.zst` is decompressed in-process and the plain `.pgn` is redistributed.
* is redistributed. Non-URL paths are returned unchanged. * Non-URL paths are returned unchanged.
*/ */
private def resolvePath(spark: SparkSession, path: String): String = private def resolvePath(spark: SparkSession, path: String): String =
if !path.matches("^(https?|ftp)://.*") then path if !path.matches("^(https?|ftp)://.*") then path
@@ -343,10 +343,10 @@ class GameResource:
@Path("/{gameId}/fen-history") @Path("/{gameId}/fen-history")
@Produces(Array(MediaType.APPLICATION_JSON)) @Produces(Array(MediaType.APPLICATION_JSON))
def getFenHistory(@PathParam("gameId") gameId: String): Response = def getFenHistory(@PathParam("gameId") gameId: String): Response =
val entry = registry.get(gameId).getOrElse(throw GameNotFoundException(gameId)) val entry = registry.get(gameId).getOrElse(throw GameNotFoundException(gameId))
val engine = entry.engine val engine = entry.engine
val initial = engine.initialContext val initial = engine.initialContext
val moves = engine.context.moves val moves = engine.context.moves
val contexts = moves.scanLeft(initial)((ctx, move) => engine.ruleSet.applyMove(ctx)(move)) val contexts = moves.scanLeft(initial)((ctx, move) => engine.ruleSet.applyMove(ctx)(move))
val fens = contexts.map(ctx => ioClient.exportFen(ctx)) val fens = contexts.map(ctx => ioClient.exportFen(ctx))
ok(FenHistoryDto(fens)) ok(FenHistoryDto(fens))
@@ -71,7 +71,10 @@ class TournamentBotGamePlayer:
val id = objectMapper.readTree(response.readEntity(classOf[String])).path("id").asText() val id = objectMapper.readTree(response.readEntity(classOf[String])).path("id").asText()
response.close() response.close()
Option(id).filter(_.nonEmpty) Option(id).filter(_.nonEmpty)
else { log.warnf("Parking bot %s returned status %d", botName(difficulty), response.getStatus); response.close(); None } else {
log.warnf("Parking bot %s returned status %d", botName(difficulty), response.getStatus); response.close();
None
}
}.getOrElse(None) }.getOrElse(None)
} }