Skip to content

Commit 763e0db

Browse files
feat: boardAfter
1 parent d8f559b commit 763e0db

File tree

3 files changed

+209
-73
lines changed

3 files changed

+209
-73
lines changed

src/main/scala/abalone/Move.scala

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ case class Move(
1616
) extends Action(situationBefore, after, metrics) {
1717

1818
def situationAfter =
19-
Situation(finalizeAfter, if (autoEndTurn) !piece.player else piece.player)
19+
Situation(finalizeAfter, !piece.player)
2020

2121
def applyVariantEffect: Move = before.variant addVariantEffect this
2222

src/main/scala/abalone/variant/Variant.scala

+78-64
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import cats.data.Validated
44
import cats.syntax.option._
55
import scala.annotation.nowarn
66

7-
import strategygames.{ GameFamily, Player }
7+
import strategygames.{ GameFamily, Player, Score }
88
import strategygames.abalone._
99
import strategygames.abalone.format.FEN
1010

@@ -106,34 +106,33 @@ abstract class Variant private[variant] (
106106
val activePlayerPieces = situation.board.piecesOf(situation.player)
107107
val opponentPieces = situation.board.piecesOf(!situation.player)
108108

109-
def createMove(orig: Pos, dest: Pos, category: String): (String, Move) =
110-
(category, Move(Piece(situation.player, Role.defaultRole), orig, dest, situation, boardAfter(situation, orig, dest), true, if (category == "pushout") Some(dest) else None))
109+
def createMove(category: String, orig: Pos, dest: Pos, directions: List[String] = List()): (String, Move) =
110+
(category, Move(Piece(situation.player, Role.defaultRole), orig, dest, situation, boardAfter(situation, orig, dest, directions), true, if (category == "pushout") Some(dest) else None))
111111

112112
def generateSideMoves(lineOfMarbles: List[Pos], direction: Option[String]): List[(String, Move)] = {
113-
def canMoveTowards(pos: Pos, dir: String): Boolean = {
114-
situation.board.isEmptySquare(pos.dir(dir))
115-
}
113+
def canMoveTowards(pos: Pos, direction: String): Boolean = situation.board.isEmptySquare(pos.dir(direction))
116114

117-
def possibleSideMoves: List[(Pos, Pos)] = Pos.sideMovesDirsFromDir(direction).map(
115+
def possibleSideMoves: List[(Pos, Pos, String)] = Pos.sideMovesDirsFromDir(direction).map(
118116
dir =>
119117
if (lineOfMarbles.map(
120118
(pos) => canMoveTowards(pos, dir)
121119
).contains(false)) None
122120
else (
123121
lineOfMarbles.headOption,
124-
lineOfMarbles.reverse.headOption.flatMap(_.dir(dir))
122+
lineOfMarbles.reverse.headOption.flatMap(_.dir(dir)),
123+
dir
125124
) match {
126-
case (Some(head), Some(tail)) => Some( (head, tail) )
125+
case ( (Some(head), Some(tail), dir) ) => Some( (head, tail, dir) )
127126
case _ => None
128127
}
129128
).flatten
130129

131-
List(
130+
List(
132131
if (lineOfMarbles.size == 3) generateSideMoves(lineOfMarbles.dropRight(1), direction) else List(),
133132
possibleSideMoves.flatMap {
134-
case ( (orig, dest) ) => Some(createMove(orig, dest, "side"))
135-
case _ => None
136-
}
133+
case ( (orig, dest, dir) ) => Some(createMove("side", orig, dest, List(dir, direction.getOrElse("")))) // @TODO: get rid of this getOrElse. was added for quick testing purpose
134+
case _ => None
135+
}
137136
).flatten
138137
}
139138

@@ -143,30 +142,30 @@ abstract class Variant private[variant] (
143142
neighbour.dir(direction).toList.flatMap {
144143
case (thirdSquareInLine) => {
145144
if (situation.board.isEmptySquare(Some(thirdSquareInLine))) // xx.
146-
Some(createMove(pos, thirdSquareInLine, "line"))
145+
Some(createMove("line", pos, thirdSquareInLine))
147146
else if (opponentPieces.contains(thirdSquareInLine)) // xxo
148147
thirdSquareInLine.dir(direction) match { // xxo?
149-
case None => Some(createMove(pos, thirdSquareInLine, "pushout")) // xxo\
148+
case None => Some(createMove("pushout", pos, thirdSquareInLine, List(direction.getOrElse("")))) // xxo\ // @TODO: get rid of this getOrElse. was added for quick testing purpose
150149
case Some(emptySquare) if situation.board.isEmptySquare(Some(emptySquare)) => {
151-
Some(createMove(pos, thirdSquareInLine, "push")) // xxo.
150+
Some(createMove("push", pos, thirdSquareInLine, List(direction.getOrElse("")))) // xxo. // @TODO: get rid of this getOrElse. was added for quick testing purpose
152151
}
153152
case _ => None
154153
}
155154
else if (activePlayerPieces.contains(thirdSquareInLine)) // xxx
156155
thirdSquareInLine.dir(direction).flatMap {
157156
case (fourthSquareInLine) => // xxx_
158157
if (situation.board.isEmptySquare(Some(fourthSquareInLine))) // xxx.
159-
Some(createMove(pos, fourthSquareInLine, "line"))
158+
Some(createMove("line", pos, fourthSquareInLine))
160159
else if (opponentPieces.contains(fourthSquareInLine)) // xxxo
161160
fourthSquareInLine.dir(direction) match { // xxxo?
162-
case None => Some(createMove(pos, fourthSquareInLine, "pushout")) // xxxo\
163-
case Some(emptyPos) if (situation.board.isEmptySquare(Some(emptyPos))) => Some(createMove(pos, fourthSquareInLine, "push")) // xxxo.
161+
case None => Some(createMove("pushout", pos, fourthSquareInLine, List(direction.getOrElse("")))) // xxxo\ // @TODO: get rid of this getOrElse. was added for quick testing purpose
162+
case Some(emptyPos) if (situation.board.isEmptySquare(Some(emptyPos))) => Some(createMove("push", pos, fourthSquareInLine, List(direction.getOrElse("")))) // xxxo. // @TODO: get rid of this getOrElse. was added for quick testing purpose
164163
case _ => fourthSquareInLine.dir(direction).flatMap { // xxxo?
165164
case (fifthSquareInLine) =>
166165
if (opponentPieces.contains(fifthSquareInLine)) // xxxoo
167166
fifthSquareInLine.dir(direction) match {
168-
case None => Some(createMove(pos, fourthSquareInLine, "pushout")) // xxxoo\
169-
case Some(emptySquare) if situation.board.isEmptySquare(Some(emptySquare)) => Some(createMove(pos, emptySquare, "push")) // xxxoo.
167+
case None => Some(createMove("pushout", pos, fourthSquareInLine, List(direction.getOrElse("")))) // xxxoo\ // @TODO: get rid of this getOrElse. was added for quick testing purpose
168+
case Some(emptySquare) if situation.board.isEmptySquare(Some(emptySquare)) => Some(createMove("push", pos, emptySquare, List(direction.getOrElse("")))) // xxxoo. // @TODO: get rid of this getOrElse. was added for quick testing purpose
170169
case _ => None
171170
}
172171
else None
@@ -193,72 +192,86 @@ abstract class Variant private[variant] (
193192
case (pos, neighbour) =>
194193
generateMovesForNeighbours(pos, neighbour)
195194
}).view.toList
196-
}
195+
}
197196
}
198197

199-
// @TODO: would be interesting to pass the direction so we can easily differenciate a side move from a line move (orig meets dest in case of line move)
200-
def boardAfter(situation: Situation, orig: Pos, dest: Pos): Board = {
198+
def boardAfter(situation: Situation, orig: Pos, dest: Pos, directions: List[String] = List()): Board = {
201199
// 1. move pieces and get the score
202-
val (pieces, capture) = piecesAfterAction(situation.board.pieces, situation.player, orig, dest)
203-
204-
// 2. update the board
205-
situation.board.copy(
206-
pieces = pieces,
207-
history = History(
208-
situation.history.currentTurn,
209-
situation.history.currentTurn, // @TODO: fix this, understand how to create the new Uci with the move being played
210-
situation.history.positionHashes,
211-
if (capture) situation.history.score.add(situation.player) else situation.history.score,
212-
situation.history.halfMoveClock + 1
200+
val (pieces, capture) = piecesAfterAction(situation.board.pieces, orig, dest, directions)
201+
val score = Score(situation.history.score.p1, situation.history.score.p2)
202+
203+
val boardAfter = situation.board.copy(pieces = pieces)
204+
boardAfter.withHistory(
205+
situation.history.copy(
206+
// @TODO: lastTurn handled on Move.finalizeAfter to keep consistency with other gamelogics ?
207+
score = if (capture) score.add(situation.player) else score,
208+
halfMoveClock = situation.board.history.halfMoveClock + 1 // situation.player.fold(0, 1)
213209
)
214210
)
215211
}
216212

217-
// @TODO: adapt
213+
// @TODO: rewrite this code properly as it's currently full of get()
218214
// will take care of moving the marbles and determine if a capture has been made
219-
def piecesAfterAction(pieces: PieceMap, player: Player, @nowarn orig: Pos, dest: Pos): (PieceMap, Boolean) = {
215+
private def piecesAfterAction(pieces: PieceMap, orig: Pos, dest: Pos, directions: List[String]): (PieceMap, Boolean) = {
220216
var capture = false
217+
var updatedPieceMap: PieceMap = Map()
221218

222219
/*
223-
224-
* * * * *
225-
* a A B C 1
226-
* * * * * 2 *
227-
How to move pieces based on orig and dest :
228-
1. identify the type of move :
229-
- push : dest contains a marble (orig=C, dest=a)
230-
- line move : orig meets dest thanks to the direction we passed as extra parameter
231-
- side move : NOT a push or a side move
220+
How to move pieces based on orig, dest, and direction :
221+
1. identify the type of move
222+
- line move : no direction to apply (a line move is just 1 marble moving)
223+
- push : 1 direction, and dest contains a marble
224+
- side move : 2 direction:
225+
- HEAD will contain the direction to apply to move from orig to dest
226+
- TAIL will contain the direction to apply to each marble for doing effectively the side move
232227
233228
2. play the move
234229
- line move :
235-
- move from orig to dest
230+
- move from orig to dest, without considering the direction
236231
- push :
237232
- dest contains a marble
238-
- we know the direction :
233+
- we have the direction :
239234
- apply the direction from dest until there is an empty square or being off the board
240235
- in case of being off the board, increment the score (capture = true)
241236
- do the line move (orig to dest)
242237
- side move :
243-
- apply the direction to orig and dest : but still have to find the marble in the middle in case of a move of 3 marbles ?
244-
245-
in case of a side move, orig=A and dest=1, how do we determine how to move A and B ?
246-
238+
- we received both directions :s
239+
- 1st direction : we know how to move through all these marbles from orig to dest
240+
- 2nd direction : we know what direction we want to apply to all these 2 or 3 marbles
247241
*/
248242

249-
if (pieces.contains(dest)) { // push
250-
// apply that direction from orig, until we find an empty square or get out of the board (considering a max distance of 3 from dest)
251-
252-
// then move the 2 marbles :
253-
// pieces(finalDest) = pieces(dest)
254-
// pieces(dest) = pieces(orig)
255-
// pieces(orig) = None
256-
capture = true
243+
if (directions.size == 0) { // line move
244+
updatedPieceMap = pieces + (dest -> pieces(orig)) - (orig)
245+
} else if (directions.size == 1) { // push
246+
if(dest.dir(directions(0)) == None) {
247+
// __(_)o\
248+
capture = true
249+
updatedPieceMap = pieces + (dest -> pieces(orig)) - orig
250+
} else if (!pieces.contains(dest.dir(directions(0)).get)) {
251+
// __(_)o.
252+
updatedPieceMap = pieces + (dest.dir(directions(0)).get -> pieces(dest)) + (dest -> pieces(orig)) - orig
253+
} else if (dest.dir(directions(0)).get.dir(directions(0)) == None) {
254+
// ___oo\
255+
capture = true
256+
updatedPieceMap = pieces + (dest.dir(directions(0)).get -> pieces(dest)) + (dest -> pieces(orig)) - orig
257+
} else {
258+
// ___oo.
259+
updatedPieceMap = pieces + (dest.dir(directions(0)).get.dir(directions(0)).get -> pieces(dest)) + (dest -> pieces(orig)) - orig
260+
}
261+
} else { // side move
262+
// from orig, move of the second direction
263+
// then move from orig of the first direction and repeat until we have reached dest
264+
if ( orig.dir(directions(1)).get.dir(directions(0)).get.index != dest.index ) {
265+
updatedPieceMap = pieces + (orig.dir(directions(0)).get -> pieces(orig)) - orig +
266+
(orig.dir(directions(1)).get.dir(directions(0)).get -> pieces(orig.dir(directions(1)).get)) - orig.dir(directions(1)).get +
267+
(orig.dir(directions(1)).get.dir(directions(1)).get.dir(directions(0)).get -> pieces(orig.dir(directions(1)).get.dir(directions(1)).get)) - orig.dir(directions(1)).get.dir(directions(1)).get
268+
} else {
269+
updatedPieceMap = pieces + (orig.dir(directions(0)).get -> pieces(orig)) - orig +
270+
(orig.dir(directions(1)).get.dir(directions(0)).get -> pieces(orig.dir(directions(1)).get)) - orig.dir(directions(1)).get
271+
}
257272
}
258-
if (player == P1)
259-
(pieces, capture)
260-
else
261-
(pieces, capture)
273+
274+
(updatedPieceMap, capture)
262275
}
263276

264277
def move(
@@ -275,6 +288,7 @@ abstract class Variant private[variant] (
275288
// if a player runs out of move, the match is a draw
276289
def stalemateIsDraw = true
277290

291+
// @TODO: might want to use this winner method in specialEnd method below ? code is duplicated...
278292
def winner(situation: Situation): Option[Player] = {
279293
if (situation.board.history.score.p1 == 6) Some(P1)
280294
else if (situation.board.history.score.p2 == 6) Some(P2)

0 commit comments

Comments
 (0)