-
-
Notifications
You must be signed in to change notification settings - Fork 7
/
Copy pathForsyth.scala
113 lines (94 loc) · 3.4 KB
/
Forsyth.scala
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
package strategygames.togyzkumalak.format
import cats.implicits._
import strategygames.{ Player, Score }
import strategygames.togyzkumalak._
import strategygames.togyzkumalak.variant.Variant
/** Transform a game to standard Forsyth Edwards Notation
* http://en.wikipedia.org/wiki/Forsyth%E2%80%93Edwards_Notation
*/
object Forsyth {
val initial = FEN("9S,9S,9S,9S,9S,9S,9S,9S,9S/9S,9S,9S,9S,9S,9S,9S,9S,9S 0 0 S 1")
def <<@(variant: Variant, fen: FEN): Option[Situation] = {
Some(
Situation(
Board(
pieces = fen.pieces,
history = History(),
variant = variant
),
fen.value.split(' ')(3) match {
case "S" => P1
case "N" => P2
case _ => sys.error("Invalid player in fen")
}
).withHistory(
History(
score = Score(fen.player1Score, fen.player2Score),
// seems like we might be using History.halfMoveClock incorrectly
halfMoveClock = fen.fullMove.getOrElse(0)
)
)
)
}
def <<(fen: FEN): Option[Situation] = <<@(Variant.default, fen)
case class SituationPlus(situation: Situation, fullTurnCount: Int) {
def turnCount = fullTurnCount * 2 - situation.player.fold(2, 1)
// when we get a multiaction variant we should set this
def plies = turnCount
}
def <<<@(variant: Variant, fen: FEN): Option[SituationPlus] =
<<@(variant, fen) map { sit =>
SituationPlus(
// not doing half move clock history like we do in chess
sit,
fen.value.split(' ').last.toIntOption.map(_ max 1 min 500) | 1
)
}
def <<<(fen: FEN): Option[SituationPlus] = <<<@(Variant.default, fen)
def >>(situation: Situation): FEN = >>(SituationPlus(situation, 1))
def >>(parsed: SituationPlus): FEN =
parsed match {
case SituationPlus(situation, _) =>
>>(Game(situation, plies = parsed.plies, turnCount = parsed.turnCount))
}
def >>(game: Game): FEN = {
val boardFen = boardPart(game.situation.board)
val scoreStr = game.situation.board.history.score.fenStr
val player = game.situation.player.fold('S', 'N')
val moves = game.situation.board.history.halfMoveClock
FEN(s"${boardFen} ${scoreStr} ${player} ${moves}")
}
def exportBoard(board: Board): String = {
val boardFen = boardPart(board)
val scoreStr = board.history.score.fenStr
s"${boardFen} ${scoreStr}"
}
def boardPart(board: Board): String = {
val fen = new scala.collection.mutable.StringBuilder(70)
var empty = 0
for (y <- Rank.allReversed) {
empty = 0
val files = File.allByWidth(board.variant.boardSize.width)
for (x <- files) {
board(x, y) match {
case None => empty = empty + 1
case Some((piece, count)) =>
if (empty > 0) {
fen.append(s"${empty},")
empty = 0
}
if (piece.role == Role.defaultRole)
fen.append(s"${count}${piece.forsyth.toString.toUpperCase()},")
else fen.append(s"${piece.forsyth},")
}
}
if (empty > 0) fen.append(s"${empty},")
fen.append('/')
}
fen.toString.replace(",/", "/").dropRight(1)
}
def boardAndPlayer(situation: Situation): String =
boardAndPlayer(situation.board, situation.player)
def boardAndPlayer(board: Board, turnPlayer: Player): String =
s"${exportBoard(board)} ${turnPlayer.letter}"
}