From dbd98042ebb6af4f71e20b7b11f7853888aecc07 Mon Sep 17 00:00:00 2001
From: Allan Joseph
Date: Sat, 18 Apr 2026 17:19:13 +0530
Subject: [PATCH 1/3] export puzzle "day" field for daily puzzles
---
.gitignore | 5 +++++
src/main/scala/Puzzles.scala | 15 +++++++++++----
2 files changed, 16 insertions(+), 4 deletions(-)
diff --git a/.gitignore b/.gitignore
index 84575a9..2b0d7ad 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,8 @@
+.bloop/
+.metals/
+.vscode/
+
target/
out/
project/project
+project/metals.sbt
diff --git a/src/main/scala/Puzzles.scala b/src/main/scala/Puzzles.scala
index 883c296..78de703 100644
--- a/src/main/scala/Puzzles.scala
+++ b/src/main/scala/Puzzles.scala
@@ -7,6 +7,7 @@ import akka.util.ByteString
import chess.format.{ Fen, FullFen }
import com.typesafe.config.ConfigFactory
import java.nio.file.Paths
+import java.time.Instant
import lila.db.dsl.*
import reactivemongo.akkastream.cursorProducer
import reactivemongo.api.*
@@ -27,7 +28,8 @@ object Puzzles:
plays: Int,
themes: List[String],
openings: List[String],
- gameUrl: String
+ gameUrl: String,
+ day: Option[Instant]
)
def main(args: Array[String]): Unit =
@@ -65,6 +67,7 @@ object Puzzles:
plays <- doc.int("plays")
themes <- doc.getAsOpt[List[String]]("themes")
gameId <- doc.string("gameId")
+ day = doc.getAsOpt[Instant]("day")
openings = doc.getAsOpt[List[String]]("opening")
yield PuzzleLine(
id = id,
@@ -80,7 +83,8 @@ object Puzzles:
val hash = Fen.readPly(fen).map(_ + 1).fold("")(p => s"#$p")
s"https://lichess.org/${gameId}${if asWhite then "" else "/black"}$hash"
,
- openings = openings.getOrElse(Nil)
+ openings = openings.getOrElse(Nil),
+ day = day
)
def toCsvLine(puzzle: PuzzleLine): String =
@@ -94,7 +98,8 @@ object Puzzles:
puzzle.plays,
puzzle.themes.sorted.mkString(" "),
puzzle.gameUrl,
- puzzle.openings.mkString(" ")
+ puzzle.openings.mkString(" "),
+ puzzle.day.map(_.toEpochMilli()).getOrElse("")
).mkString(",")
def csvSink: Sink[String, Future[IOResult]] =
@@ -134,7 +139,9 @@ object Puzzles:
.map(toCsvLine)
.prepend(
Source(
- List("PuzzleId,FEN,Moves,Rating,RatingDeviation,Popularity,NbPlays,Themes,GameUrl,OpeningTags")
+ List(
+ "PuzzleId,FEN,Moves,Rating,RatingDeviation,Popularity,NbPlays,Themes,GameUrl,OpeningTags,date"
+ )
)
)
.runWith(csvSink)
From b615701de67d8b37b36e42dcfd5d201dd6f740a7 Mon Sep 17 00:00:00 2001
From: Allan Joseph
Date: Sat, 18 Apr 2026 17:22:41 +0530
Subject: [PATCH 2/3] rename column
---
src/main/scala/Puzzles.scala | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/main/scala/Puzzles.scala b/src/main/scala/Puzzles.scala
index 78de703..d9e1547 100644
--- a/src/main/scala/Puzzles.scala
+++ b/src/main/scala/Puzzles.scala
@@ -140,7 +140,7 @@ object Puzzles:
.prepend(
Source(
List(
- "PuzzleId,FEN,Moves,Rating,RatingDeviation,Popularity,NbPlays,Themes,GameUrl,OpeningTags,date"
+ "PuzzleId,FEN,Moves,Rating,RatingDeviation,Popularity,NbPlays,Themes,GameUrl,OpeningTags,DailyDate"
)
)
)
From b3aa4a6a3e10a1c0b2e8f139a89cf6da874f274f Mon Sep 17 00:00:00 2001
From: Allan Joseph
Date: Sat, 18 Apr 2026 18:05:03 +0530
Subject: [PATCH 3/3] untested: update web template
---
web/index.html.tpl | 13 ++++++++-----
1 file changed, 8 insertions(+), 5 deletions(-)
diff --git a/web/index.html.tpl b/web/index.html.tpl
index f2275f8..231c8f4 100644
--- a/web/index.html.tpl
+++ b/web/index.html.tpl
@@ -136,12 +136,12 @@
Puzzles are formatted as standard CSV. The fields are as follows:
- PuzzleId,FEN,Moves,Rating,RatingDeviation,Popularity,NbPlays,Themes,GameUrl,OpeningTags
+ PuzzleId,FEN,Moves,Rating,RatingDeviation,Popularity,NbPlays,Themes,GameUrl,OpeningTags,DailyDate
Sample
- 00sHx,q3k1nr/1pp1nQpp/3p4/1P2p3/4P3/B1PP1b2/B5PP/5K2 b k - 0 17,e8d7 a2e6 d7d8 f7f8,1760,80,83,72,mate mateIn2 middlegame short,https://lichess.org/yyznGmXs/black#34,Italian_Game Italian_Game_Classical_Variation
-00sJ9,r3r1k1/p4ppp/2p2n2/1p6/3P1qb1/2NQR3/PPB2PP1/R1B3K1 w - - 5 18,e3g3 e8e1 g1h2 e1c1 a1c1 f4h6 h2g1 h6c1,2671,105,87,325,advantage attraction fork middlegame sacrifice veryLong,https://lichess.org/gyFeQsOE#35,French_Defense French_Defense_Exchange_Variation
-00sJb,Q1b2r1k/p2np2p/5bp1/q7/5P2/4B3/PPP3PP/2KR1B1R w - - 1 17,d1d7 a5e1 d7d1 e1e3 c1b1 e3b6,2235,76,97,64,advantage fork long,https://lichess.org/kiuvTFoE#33,Sicilian_Defense Sicilian_Defense_Dragon_Variation
-00sO1,1k1r4/pp3pp1/2p1p3/4b3/P3n1P1/8/KPP2PN1/3rBR1R b - - 2 31,b8c7 e1a5 b7b6 f1d1,998,85,94,293,advantage discoveredAttack master middlegame short,https://lichess.org/vsfFkG0s/black#62,
+ 00sHx,q3k1nr/1pp1nQpp/3p4/1P2p3/4P3/B1PP1b2/B5PP/5K2 b k - 0 17,e8d7 a2e6 d7d8 f7f8,1760,80,83,72,mate mateIn2 middlegame short,https://lichess.org/yyznGmXs/black#34,Italian_Game Italian_Game_Classical_Variation,
+00sJ9,r3r1k1/p4ppp/2p2n2/1p6/3P1qb1/2NQR3/PPB2PP1/R1B3K1 w - - 5 18,e3g3 e8e1 g1h2 e1c1 a1c1 f4h6 h2g1 h6c1,2671,105,87,325,advantage attraction fork middlegame sacrifice veryLong,https://lichess.org/gyFeQsOE#35,French_Defense French_Defense_Exchange_Variation,1607774862751
+00sJb,Q1b2r1k/p2np2p/5bp1/q7/5P2/4B3/PPP3PP/2KR1B1R w - - 1 17,d1d7 a5e1 d7d1 e1e3 c1b1 e3b6,2235,76,97,64,advantage fork long,https://lichess.org/kiuvTFoE#33,Sicilian_Defense Sicilian_Defense_Dragon_Variation,
+00sO1,1k1r4/pp3pp1/2p1p3/4b3/P3n1P1/8/KPP2PN1/3rBR1R b - - 2 31,b8c7 e1a5 b7b6 f1d1,998,85,94,293,advantage discoveredAttack master middlegame short,https://lichess.org/vsfFkG0s/black#62,,
Notes
Moves are in UCI format. Use a chess library to convert them to SAN, for display.
@@ -167,6 +167,9 @@
The OpeningTags field is only set for puzzles starting before move 20.
Here's the list of possible openings.
+
+ The DailyDate field is a Unix timestamp in milliseconds. If present, it indicates the date when the puzzle was featured as a daily puzzle on lichess.org/training/daily.
+
Generating these chess puzzles
took more than 100 years of CPU time.