Skip to content

Commit 45f0f12

Browse files
author
Matt Tucker
committed
Fix Oware Isometric test - be able to load FEN when final stones have been scored
1 parent c363e8e commit 45f0f12

File tree

4 files changed

+74
-46
lines changed

4 files changed

+74
-46
lines changed

src/main/scala/samurai/Api.scala

+11-5
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,8 @@ object Api {
5151
private class OwarePosition(
5252
position: OwareGame,
5353
ply: Int = 0,
54-
fromFen: Option[FEN] = None
54+
fromFen: Option[FEN] = None,
55+
forceEnd: Boolean = false
5556
) extends Position {
5657
// TODO: yes, this is an abuse of scala. We could get an
5758
// exception here, but I'm not sure how to work around that
@@ -170,7 +171,8 @@ object Api {
170171
lazy val gameResult: GameResult =
171172
GameResult.resultFromInt(position.outcome(), gameEnd)
172173

173-
lazy val gameEnd: Boolean = position.hasEnded() && (!position.isRepetition() || isRepetition)
174+
lazy val gameEnd: Boolean =
175+
forceEnd || (position.hasEnded() && (!position.isRepetition() || isRepetition))
174176

175177
lazy val gameOutcome: Int = position.outcome()
176178
lazy val isRepetition: Boolean = position.isRepetition() && !allSeedsOnSameSide
@@ -212,13 +214,17 @@ object Api {
212214
new OwarePosition(game, fen.ply.getOrElse(0), Some(fen))
213215
}
214216

215-
def positionFromVariantNameAndFEN(variantName: String, fenString: String): Position = {
217+
def positionFromVariantNameAndFEN(
218+
variantName: String,
219+
fenString: String,
220+
forceEnd: Boolean = false
221+
): Position = {
216222
val game = new OwareGame()
217223
val fen = FEN(fenString)
218224
game.setBoard(owareBoardFromFen(fenString))
219225
variantName.toLowerCase() match {
220-
case "oware" => new OwarePosition(game, fen.ply.getOrElse(0), Some(fen))
221-
case _ => new OwarePosition(new OwareGame(), fen.ply.getOrElse(0), Some(fen))
226+
case "oware" => new OwarePosition(game, fen.ply.getOrElse(0), Some(fen), forceEnd)
227+
case _ => new OwarePosition(new OwareGame(), fen.ply.getOrElse(0), Some(fen), forceEnd)
222228
}
223229
}
224230

src/main/scala/samurai/format/FEN.scala

+21-5
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,10 @@ final case class FEN(value: String) extends AnyVal {
2525
private def intFromFen(index: Int): Option[Int] =
2626
value.split(' ').lift(index).flatMap(_.toIntOption)
2727

28+
private def boardStr = value.split(' ')(0)
29+
2830
def owareStoneArray: Array[Int] =
29-
(
30-
value.split(' ')(0).split('/')(1).split(',')
31-
++
32-
value.split(' ')(0).split('/')(0).split(',').reverse
33-
)
31+
(boardStr.split('/')(1).split(',') ++ boardStr.split('/')(0).split(',').reverse)
3432
.map(c =>
3533
c.toString() match {
3634
case x if 1 to 6 map (_.toString) contains x => Array.fill(x.toInt)(0)
@@ -40,7 +38,25 @@ final case class FEN(value: String) extends AnyVal {
4038
.flatten
4139
.toArray
4240

41+
def stonesOwnedByPlayer(player: Player) = {
42+
val stonesByPlayer = owareStoneArray.splitAt(6)
43+
player.fold(stonesByPlayer._1, stonesByPlayer._2).sum
44+
}
45+
4346
def initial = value == Forsyth.initial.value
47+
48+
private def rawPlayerScore(player: Player) =
49+
player.fold(player1Score, player2Score) - stonesOwnedByPlayer(player)
50+
51+
def finalStonesScored = player1Score + player2Score == 48
52+
53+
def withoutFinalStonesScored =
54+
if (finalStonesScored)
55+
FEN(
56+
s"${boardStr} ${rawPlayerScore(Player.P1)} ${rawPlayerScore(Player.P2)} ${value.split(' ').drop(3).mkString(" ")}"
57+
)
58+
else this
59+
4460
}
4561

4662
object FEN {

src/main/scala/samurai/format/Forsyth.scala

+5-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,11 @@ object Forsyth {
1414
val initial = FEN("4S,4S,4S,4S,4S,4S/4S,4S,4S,4S,4S,4S 0 0 S 1")
1515

1616
def <<@(variant: Variant, fen: FEN): Option[Situation] = {
17-
val apiPosition = Api.positionFromVariantNameAndFEN(variant.name, fen.value)
17+
val apiPosition = Api.positionFromVariantNameAndFEN(
18+
variant.name,
19+
fen.withoutFinalStonesScored.value,
20+
fen.finalStonesScored
21+
)
1822
Some(
1923
Situation(
2024
Board(

src/main/scala/samurai/variant/Variant.scala

+37-35
Original file line numberDiff line numberDiff line change
@@ -52,44 +52,46 @@ abstract class Variant private[variant] (
5252
def isValidPromotion(@nowarn promotion: Option[PromotableRole]): Boolean = true
5353

5454
def validMoves(situation: Situation): Map[Pos, List[Move]] =
55-
situation.board.apiPosition.legalMoves
56-
.map { move =>
57-
val numSeeds = situation.board.apiPosition.fen.owareStoneArray(move)
58-
(
59-
move,
60-
Pos(move),
61-
Pos(
62-
(numSeeds + move + (numSeeds - 1) / 11) % 12
63-
) // dest = seeds + move position + skipping own house
64-
)
65-
}
66-
.map {
67-
case (move, Some(orig), Some(dest)) => {
68-
val uciMove = s"${orig.key}${dest.key}"
69-
val previousMoves = situation.board.uciMoves
70-
val newPosition = situation.board.apiPosition.makeMovesWithPrevious(List(move), previousMoves)
55+
if (situation.status.isEmpty)
56+
situation.board.apiPosition.legalMoves
57+
.map { move =>
58+
val numSeeds = situation.board.apiPosition.fen.owareStoneArray(move)
7159
(
72-
orig,
73-
Move(
74-
piece = situation.board.pieces(orig)._1,
75-
orig = orig,
76-
dest = dest,
77-
situationBefore = situation,
78-
after = situation.board.copy(
79-
pieces = newPosition.pieceMap,
80-
uciMoves = situation.board.uciMoves :+ uciMove,
81-
position = newPosition.some
82-
),
83-
autoEndTurn = true,
84-
capture = None,
85-
promotion = None
86-
)
60+
move,
61+
Pos(move),
62+
Pos(
63+
(numSeeds + move + (numSeeds - 1) / 11) % 12
64+
) // dest = seeds + move position + skipping own house
8765
)
8866
}
89-
case (_, orig, dest) => sys.error(s"Invalid position from uci: ${orig}${dest}")
90-
}
91-
.groupBy(_._1)
92-
.map { case (k, v) => (k, v.toList.map(_._2)) }
67+
.map {
68+
case (move, Some(orig), Some(dest)) => {
69+
val uciMove = s"${orig.key}${dest.key}"
70+
val previousMoves = situation.board.uciMoves
71+
val newPosition = situation.board.apiPosition.makeMovesWithPrevious(List(move), previousMoves)
72+
(
73+
orig,
74+
Move(
75+
piece = situation.board.pieces(orig)._1,
76+
orig = orig,
77+
dest = dest,
78+
situationBefore = situation,
79+
after = situation.board.copy(
80+
pieces = newPosition.pieceMap,
81+
uciMoves = situation.board.uciMoves :+ uciMove,
82+
position = newPosition.some
83+
),
84+
autoEndTurn = true,
85+
capture = None,
86+
promotion = None
87+
)
88+
)
89+
}
90+
case (_, orig, dest) => sys.error(s"Invalid position from uci: ${orig}${dest}")
91+
}
92+
.groupBy(_._1)
93+
.map { case (k, v) => (k, v.toList.map(_._2)) }
94+
else Map.empty
9395

9496
def move(
9597
situation: Situation,

0 commit comments

Comments
 (0)