Skip to content

Commit

Permalink
type safety for pgn move time, using scalalib.model.Seconds
Browse files Browse the repository at this point in the history
  • Loading branch information
ornicar committed Dec 31, 2024
1 parent 55ce6ab commit 75b5a36
Show file tree
Hide file tree
Showing 3 changed files with 16 additions and 12 deletions.
13 changes: 7 additions & 6 deletions core/src/main/scala/format/pgn/Pgn.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package format.pgn

import cats.syntax.all.*
import monocle.syntax.all.*
import scalalib.model.Seconds

type PgnTree = Node[Move]

Expand Down Expand Up @@ -56,17 +57,17 @@ case class Move(
glyphs: Glyphs = Glyphs.empty,
opening: Option[String] = None,
result: Option[String] = None,
secondsLeft: Option[Int] = None, // %clk clock in seconds for the move player, after the move
moveTime: Option[Int] = None, // %emt estimated move time in seconds
timeLeft: Option[Seconds] = None, // %clk clock in seconds for the move player, after the move
moveTime: Option[Seconds] = None, // %emt estimated move time in seconds
variationComments: List[Comment] = Nil
):

private def clockString: Option[String] = List(
secondsLeft.map(seconds => "[%clk " + Move.formatPgnSeconds(seconds) + "]"),
timeLeft.map(seconds => "[%clk " + Move.formatPgnSeconds(seconds) + "]"),
moveTime.map(seconds => "[%emt " + Move.formatPgnSeconds(seconds) + "]")
).flatten.some.filter(_.nonEmpty).map(_.mkString(" "))

def hasComment = comments.nonEmpty || secondsLeft.isDefined || moveTime.isDefined
def hasComment = comments.nonEmpty || timeLeft.isDefined || moveTime.isDefined

private def nonEmpty = hasComment || opening.isDefined || result.isDefined

Expand Down Expand Up @@ -99,6 +100,6 @@ object Move:
private def noDoubleLineBreak(txt: String) =
noDoubleLineBreakRegex.replaceAllIn(txt, "\n")

def formatPgnSeconds(t: Int): String =
val d = java.time.Duration.ofSeconds(t)
def formatPgnSeconds(t: Seconds): String =
val d = java.time.Duration.ofSeconds(t.value)
f"${d.toHours}:${d.toMinutesPart}%02d:${d.toSecondsPart}%02d"
3 changes: 2 additions & 1 deletion test-kit/src/main/scala/chess/ChessTreeArbitraries.scala
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package chess
import cats.syntax.all.*
import chess.format.pgn.{ Comment, Glyphs, InitialComments, Move as PgnMove, Pgn, Tags }
import org.scalacheck.Gen
import scalalib.model.Seconds

case class GameTree[A](init: Situation, ply: Ply, tree: Option[Node[A]])
case class WithMove[A](move: Move, data: A)
Expand Down Expand Up @@ -88,7 +89,7 @@ object ChessTreeArbitraries:
comments <- genComments(5)
glyphs <- Gen.someOf(Glyphs.all).map(xs => Glyphs.fromList(xs.toList))
clock <- Gen.posNum[Int]
yield WithMove(move, PgnMove(move.san, comments, glyphs, secondsLeft = clock.some))
yield WithMove(move, PgnMove(move.san, comments, glyphs, timeLeft = Seconds(clock).some))

def genNode[A: Generator](value: A, variations: List[A] = Nil): Gen[Node[A]] =
value.next.flatMap: nextSeeds =>
Expand Down
12 changes: 7 additions & 5 deletions test-kit/src/test/scala/format/pgn/TimeFormatTest.scala
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,17 @@ package chess
package format.pgn

import scalalib.time.*
import scalalib.model.Seconds

class TimeFormatTest extends ChessTest:

test("format seconds"):
assertEquals(Move.formatPgnSeconds(0), "0:00:00")
assertEquals(Move.formatPgnSeconds(9), "0:00:09")
assertEquals(Move.formatPgnSeconds(60), "0:01:00")
assertEquals(Move.formatPgnSeconds(79835), "22:10:35")
assertEquals(Move.formatPgnSeconds(979835), "272:10:35")
def f(s: Int) = Move.formatPgnSeconds(Seconds(s))
assertEquals(f(0), "0:00:00")
assertEquals(f(9), "0:00:09")
assertEquals(f(60), "0:01:00")
assertEquals(f(79835), "22:10:35")
assertEquals(f(979835), "272:10:35")

test("format PGN tags"):
assertEquals(Tag.UTCDate.format.format(millisToDateTime(1680424483730L)), "2023.04.02")
Expand Down

0 comments on commit 75b5a36

Please sign in to comment.