Skip to content

Commit d4742c9

Browse files
author
Matt Tucker
committed
Alter Go fen to store pass count and pass isometric tests
1 parent 98936cf commit d4742c9

12 files changed

+128
-90
lines changed

src/main/scala/go/Api.scala

+14-12
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ object Api {
5252
def setBoard(goBoard: GoBoard): Unit
5353
def deepCopy: Position
5454

55+
val turn: String
5556
val initialFen: FEN
5657
val fen: FEN
5758
val pieceMap: PieceMap
@@ -177,20 +178,21 @@ object Api {
177178

178179
def goDiagram: String = position.toBoard.toDiagram
179180

181+
val turn =
182+
if (position.turn() == 1) "b"
183+
else "w" // cant trust engine fen - not sure why but it always returns 'b'
184+
180185
def fenString: String = {
181186
val splitDiagram = goDiagram.split(' ')
182187
val board = splitDiagram.lift(0).getOrElse(" board fen error ").replace("X", "S").replace("O", "s")
183-
val turn =
184-
if (position.turn() == 1) "b"
185-
else "w" // cant trust engine fen - not sure why but it always returns 'b'
186-
val ko = splitDiagram.lift(2).getOrElse("-").toString()
188+
val pocket = "[SSSSSSSSSSssssssssss]"
189+
val ko = splitDiagram.lift(2).getOrElse("-").toString()
187190
// TODO: generating the score is slow.
188-
val p1FenScore = (p1Score * 10).toInt
189-
val p2FenScore = (p2Score * 10).toInt
190-
val fenKomi = (komi * 10).toInt
191-
val fullMoveStr = (ply / 2 + 1).toString()
192-
val pocket = "[SSSSSSSSSSssssssssss]"
193-
return s"${board}${pocket} ${turn} ${ko} ${p1FenScore} ${p2FenScore} 0 0 ${fenKomi} ${fullMoveStr}"
191+
val p1FenScore = (p1Score * 10).toInt
192+
val p2FenScore = (p2Score * 10).toInt
193+
val fenKomi = (komi * 10).toInt
194+
val fullMoveStr = (ply / 2 + 1).toString()
195+
return s"${board}${pocket} ${turn} ${ko} ${p1FenScore} ${p2FenScore} 0 0 ${fenKomi} 0 ${fullMoveStr}"
194196
}
195197

196198
def toPosition = position.toBoard().position()
@@ -354,8 +356,8 @@ object Api {
354356
case _ => sys.error(s"not given a go variant name: ${variantKey}")
355357
}
356358

357-
val fenRegex =
358-
"([0-9Ss]?){1,19}(/([0-9Ss]?){1,19}){8,18}\\[[Ss]+\\] [w|b] - [0-9]+ [0-9]+ [0-9]+ [0-9]+ [0-9]+ [0-9]+"
359+
private val fenRegex =
360+
"([0-9Ss]?){1,19}(/([0-9Ss]?){1,19}){8,18}\\[[Ss]+\\] [w|b] - [0-9]+ [0-9]+ [0-9]+ [0-9]+ [0-9]+ [0-3] [0-9]+"
359361
def validateFEN(fenString: String): Boolean =
360362
Try(goBoardFromFen(FEN(fenString))).isSuccess && fenString.matches(fenRegex)
361363

src/main/scala/go/format/FEN.scala

+6-3
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,18 @@ final case class FEN(value: String) extends AnyVal {
2020

2121
def player2Captures: Int = intFromFen(6).getOrElse(0)
2222

23-
def fullMove: Option[Int] = intFromFen(8)
23+
def komi: Double = intFromFen(7).getOrElse(0) / 10.0
24+
25+
// Consecutive Pass count. Capped at 2, 3 is reserved for end game (after "ss:")
26+
def fenPassCount: Int = intFromFen(8).getOrElse(0)
27+
28+
def fullMove: Option[Int] = intFromFen(9)
2429

2530
def ply: Option[Int] =
2631
fullMove map { fm =>
2732
fm * 2 - (if (player.exists(_.p1)) 2 else 1)
2833
}
2934

30-
def komi: Double = intFromFen(7).getOrElse(0) / 10.0
31-
3235
def handicap: Option[Int] = if (fullMove == Some(1)) Some(board.count(_ == 'S')) else None
3336

3437
def engineFen: String =

src/main/scala/go/format/Forsyth.scala

+41-6
Original file line numberDiff line numberDiff line change
@@ -13,21 +13,24 @@ import strategygames.go.variant.Variant
1313
object Forsyth {
1414

1515
val initial = FEN(
16-
"19/19/19/19/19/19/19/19/19/19/19/19/19/19/19/19/19/19/19[SSSSSSSSSSssssssssss] b - 0 75 0 0 75 1"
16+
"19/19/19/19/19/19/19/19/19/19/19/19/19/19/19/19/19/19/19[SSSSSSSSSSssssssssss] b - 0 75 0 0 75 0 1"
1717
)
1818

1919
def <<@(variant: Variant, fen: FEN): Option[Situation] = {
2020
val apiPosition = Api.positionFromVariantNameAndFEN(variant.key, fen.value)
21-
val fenParts = fen.value.split(' ').toList
22-
val p1Captures = fenParts.lift(5).flatMap(_.toIntOption).getOrElse(0)
23-
val p2Captures = fenParts.lift(6).flatMap(_.toIntOption).getOrElse(1)
2421
Some(
2522
Situation(
2623
Board(
2724
pieces = apiPosition.pieceMap,
28-
history = History().copy(captures=Score(p1Captures, p2Captures)),
25+
history = History(captures = Score(fen.player1Captures, fen.player2Captures)),
2926
variant = variant,
3027
pocketData = apiPosition.pocketData,
28+
uciMoves = fen.fenPassCount match {
29+
case 0 => List()
30+
case 1 => List("pass")
31+
case 2 => List("pass", "pass")
32+
case 3 => List("ss:")
33+
},
3134
position = apiPosition.some
3235
),
3336
fen.value.split(' ')(1) match {
@@ -52,7 +55,6 @@ object Forsyth {
5255
def <<<@(variant: Variant, fen: FEN): Option[SituationPlus] =
5356
<<@(variant, fen) map { sit =>
5457
SituationPlus(
55-
// not doing half move clock history like we do in chess
5658
sit,
5759
fen.value.split(' ').last.toIntOption.map(_ max 1 min 500) | 1
5860
)
@@ -77,8 +79,41 @@ object Forsyth {
7779
FEN(
7880
board.apiPosition.fen.value
7981
.split(" ")
82+
// Update player if we have a last action of select squares that the API doesnt know about
83+
.updated(
84+
1,
85+
board.history.lastTurn.headOption match {
86+
case Some(_: Uci.SelectSquares) if board.apiPosition.turn == "w" => "b"
87+
case Some(_: Uci.SelectSquares) if board.apiPosition.turn == "b" => "w"
88+
case _ => board.apiPosition.turn
89+
}
90+
)
91+
// Update captures
8092
.updated(5, board.history.captures.p1.toString)
8193
.updated(6, board.history.captures.p2.toString)
94+
// Update current consecutive Pass count. Use 3 to represent end of game
95+
.updated(
96+
8,
97+
(board.uciMoves.reverse.headOption match {
98+
case Some(uci) if uci.startsWith("ss:") => 3
99+
case Some(uci) if uci == "pass" => {
100+
board.uciMoves.reverse.drop(1).headOption match {
101+
case Some(uci) if uci == "pass" => 2
102+
case _ => 1
103+
}
104+
}
105+
case _ => 0
106+
}).toString
107+
)
108+
// Update fullTurnCount if we have a last action of select squares that the API doesnt know about
109+
.updated(
110+
9,
111+
board.history.lastTurn.headOption match {
112+
case Some(_: Uci.SelectSquares) if board.apiPosition.turn == "w" =>
113+
board.apiPosition.fen.value.split(" ")(9) + 1
114+
case _ => board.apiPosition.fen.value.split(" ")(9)
115+
}
116+
)
82117
.mkString(" ")
83118
)
84119

src/main/scala/go/opening/EcopeningDB.scala

+3-3
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,8 @@ object EcopeningDB {
1414
}
1515

1616
lazy val allByEco: Map[ECO, Ecopening] = Map(
17-
"A00" -> new Ecopening("A00", "go9x9", "Go Start Pos", "Go Start Pos", "", "9/9/9/9/9/9/9/9/9[SSSSSSSSSSssssssssss] b - 0 55 0 0 55 1", ""),
18-
"B00" -> new Ecopening("B00", "go13x13", "Go Start Pos", "Go Start Pos", "", "13/13/13/13/13/13/13/13/13/13/13/13/13[SSSSSSSSSSssssssssss] b - 0 75 0 0 75 1", ""),
19-
"C00" -> new Ecopening("C00", "go19x19", "Go Start Pos", "Go Start Pos", "", "19/19/19/19/19/19/19/19/19/19/19/19/19/19/19/19/19/19/19[SSSSSSSSSSssssssssss] b - 0 75 0 0 75 1", "")
17+
"A00" -> new Ecopening("A00", "go9x9", "Go Start Pos", "Go Start Pos", "", "9/9/9/9/9/9/9/9/9[SSSSSSSSSSssssssssss] b - 0 55 0 0 55 0 1", ""),
18+
"B00" -> new Ecopening("B00", "go13x13", "Go Start Pos", "Go Start Pos", "", "13/13/13/13/13/13/13/13/13/13/13/13/13[SSSSSSSSSSssssssssss] b - 0 75 0 0 75 0 1", ""),
19+
"C00" -> new Ecopening("C00", "go19x19", "Go Start Pos", "Go Start Pos", "", "19/19/19/19/19/19/19/19/19/19/19/19/19/19/19/19/19/19/19[SSSSSSSSSSssssssssss] b - 0 75 0 0 75 0 1", "")
2020
)
2121
}

src/main/scala/go/variant/Go13x13.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ case object Go13x13
2020

2121
// cache this rather than checking with the API everytime
2222
override def initialFen =
23-
format.FEN("13/13/13/13/13/13/13/13/13/13/13/13/13[SSSSSSSSSSssssssssss] b - 0 75 0 0 75 1")
23+
format.FEN("13/13/13/13/13/13/13/13/13/13/13/13/13[SSSSSSSSSSssssssssss] b - 0 75 0 0 75 0 1")
2424

2525
override def boardFenFromHandicap(handicap: Int): String = {
2626
handicap match {

src/main/scala/go/variant/Go19x19.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ case object Go19x19
2323
// cache this rather than checking with the API everytime
2424
override def initialFen =
2525
format.FEN(
26-
"19/19/19/19/19/19/19/19/19/19/19/19/19/19/19/19/19/19/19[SSSSSSSSSSssssssssss] b - 0 75 0 0 75 1"
26+
"19/19/19/19/19/19/19/19/19/19/19/19/19/19/19/19/19/19/19[SSSSSSSSSSssssssssss] b - 0 75 0 0 75 0 1"
2727
)
2828

2929
override def boardFenFromHandicap(handicap: Int): String = {

src/main/scala/go/variant/Go9x9.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ case object Go9x9
2020

2121
// cache this rather than checking with the API everytime
2222
override def initialFen =
23-
format.FEN("9/9/9/9/9/9/9/9/9[SSSSSSSSSSssssssssss] b - 0 55 0 0 55 1")
23+
format.FEN("9/9/9/9/9/9/9/9/9[SSSSSSSSSSssssssssss] b - 0 55 0 0 55 0 1")
2424

2525
override def komi: Double = 5.5
2626

src/test/scala/chess/ChessTest.scala

+1-2
Original file line numberDiff line numberDiff line change
@@ -215,9 +215,8 @@ trait ChessTest extends Specification with ValidatedMatchers {
215215
// TODO: add in end game conditions, etc.
216216
}
217217

218-
def turnsAreEqual(g1: StratGame, gameData: GameFenIsometryData) = {
218+
def turnsAreEqual(g1: StratGame, gameData: GameFenIsometryData) =
219219
gameData.turnCount must_== g1.turnCount
220-
}
221220

222221
def _testEveryMoveLoadFenIsometry(
223222
lib: GameLogic,

src/test/scala/format/sgf/DumperTest.scala

+5-5
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ class DumperTest extends Specification with ValidatedMatchers {
4747
val initialFen = Some(
4848
FEN(
4949
Go19x19,
50-
"19/19/19/15S3/19/19/19/19/19/19/19/19/19/19/19/3S11S3/19/19/19[SSSSSSSSSSssssssssss] w - 0 75 0 0 75 1"
50+
"19/19/19/15S3/19/19/19/19/19/19/19/19/19/19/19/3S11S3/19/19/19[SSSSSSSSSSssssssssss] w - 0 75 0 0 75 0 1"
5151
)
5252
)
5353
val output = ";B[dp]\n;B[pd]\n;B[pp]\n;W[bs]\n;B[cr]\n;W[dq]\n;B[ep]"
@@ -61,7 +61,7 @@ class DumperTest extends Specification with ValidatedMatchers {
6161
val initialFen = Some(
6262
FEN(
6363
Go13x13,
64-
"13/13/13/3S5S3/13/13/3S2S2S3/13/13/3S5S3/13/13/13[SSSSSSSSSSssssssssss] w - 0 75 0 0 75 1"
64+
"13/13/13/3S5S3/13/13/3S2S2S3/13/13/3S5S3/13/13/13[SSSSSSSSSSssssssssss] w - 0 75 0 0 75 0 1"
6565
)
6666
)
6767
val output = ";B[dd]\n;B[dg]\n;B[dj]\n;B[gg]\n;B[jd]\n;B[jg]\n;B[jj]\n;W[bm]\n;B[cl]\n;W[dk]\n;B[ej]"
@@ -75,7 +75,7 @@ class DumperTest extends Specification with ValidatedMatchers {
7575
val initialFen = Some(
7676
FEN(
7777
Go13x13,
78-
"13/13/13/13/13/13/13/13/13/13/13/13/13[SSSSSSSSSSssssssssss] b - 0 40 0 0 40 1"
78+
"13/13/13/13/13/13/13/13/13/13/13/13/13[SSSSSSSSSSssssssssss] b - 0 40 0 0 40 0 1"
7979
)
8080
)
8181
val output = ";B[bm]\n;W[cl]\n;B[dk]\n;W[ej]"
@@ -103,7 +103,7 @@ class DumperTest extends Specification with ValidatedMatchers {
103103
"Go9x9 with initial fen handicap" should {
104104
"have an sgf output with just additioanl moves" in {
105105
val initialFen =
106-
Some(FEN(Go9x9, "9/9/2S3S2/9/2S3S2/9/2S3S2/9/9[SSSSSSSSSSssssssssss] w - 0 865 0 0 55 1"))
106+
Some(FEN(Go9x9, "9/9/2S3S2/9/2S3S2/9/2S3S2/9/9[SSSSSSSSSSssssssssss] w - 0 865 0 0 55 0 1"))
107107
val output = ";B[cc]\n;B[ce]\n;B[cg]\n;B[gc]\n;B[ge]\n;B[gg]"
108108
val actionStrs: ActionStrs = Vector()
109109

@@ -114,7 +114,7 @@ class DumperTest extends Specification with ValidatedMatchers {
114114
"Go9x9 with initial fen handicap" should {
115115
"have an sgf output with additionl moves" in {
116116
val initialFen =
117-
Some(FEN(Go9x9, "9/9/2S3S2/9/2S3S2/9/2S3S2/9/9[SSSSSSSSSSssssssssss] w - 0 865 0 0 55 1"))
117+
Some(FEN(Go9x9, "9/9/2S3S2/9/2S3S2/9/2S3S2/9/9[SSSSSSSSSSssssssssss] w - 0 865 0 0 55 0 1"))
118118
val output = ";B[cc]\n;B[ce]\n;B[cg]\n;B[gc]\n;B[ge]\n;B[gg]\n;W[bi]\n;B[ch]\n;W[]\n;B[dc]"
119119
val actionStrs: ActionStrs = Vector(Vector("s@b1"), Vector("s@c2"), Vector("pass"), Vector("s@d7"))
120120
Dumper.apply(Go9x9, actionStrs, initialFen) must_== output

0 commit comments

Comments
 (0)