@@ -4,7 +4,7 @@ import cats.data.Validated
4
4
import cats .syntax .option ._
5
5
import scala .annotation .nowarn
6
6
7
- import strategygames .{ GameFamily , Player }
7
+ import strategygames .{ GameFamily , Player , Score }
8
8
import strategygames .abalone ._
9
9
import strategygames .abalone .format .FEN
10
10
@@ -106,34 +106,33 @@ abstract class Variant private[variant] (
106
106
val activePlayerPieces = situation.board.piecesOf(situation.player)
107
107
val opponentPieces = situation.board.piecesOf(! situation.player)
108
108
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 ))
111
111
112
112
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))
116
114
117
- def possibleSideMoves : List [(Pos , Pos )] = Pos .sideMovesDirsFromDir(direction).map(
115
+ def possibleSideMoves : List [(Pos , Pos , String )] = Pos .sideMovesDirsFromDir(direction).map(
118
116
dir =>
119
117
if (lineOfMarbles.map(
120
118
(pos) => canMoveTowards(pos, dir)
121
119
).contains(false )) None
122
120
else (
123
121
lineOfMarbles.headOption,
124
- lineOfMarbles.reverse.headOption.flatMap(_.dir(dir))
122
+ lineOfMarbles.reverse.headOption.flatMap(_.dir(dir)),
123
+ dir
125
124
) match {
126
- case (Some (head), Some (tail)) => Some ( (head, tail) )
125
+ case ( ( Some (head), Some (tail), dir) ) => Some ( (head, tail, dir ) )
127
126
case _ => None
128
127
}
129
128
).flatten
130
129
131
- List (
130
+ List (
132
131
if (lineOfMarbles.size == 3 ) generateSideMoves(lineOfMarbles.dropRight(1 ), direction) else List (),
133
132
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
+ }
137
136
).flatten
138
137
}
139
138
@@ -143,30 +142,30 @@ abstract class Variant private[variant] (
143
142
neighbour.dir(direction).toList.flatMap {
144
143
case (thirdSquareInLine) => {
145
144
if (situation.board.isEmptySquare(Some (thirdSquareInLine))) // xx.
146
- Some (createMove(pos, thirdSquareInLine, " line " ))
145
+ Some (createMove(" line " , pos, thirdSquareInLine ))
147
146
else if (opponentPieces.contains(thirdSquareInLine)) // xxo
148
147
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
150
149
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
152
151
}
153
152
case _ => None
154
153
}
155
154
else if (activePlayerPieces.contains(thirdSquareInLine)) // xxx
156
155
thirdSquareInLine.dir(direction).flatMap {
157
156
case (fourthSquareInLine) => // xxx_
158
157
if (situation.board.isEmptySquare(Some (fourthSquareInLine))) // xxx.
159
- Some (createMove(pos, fourthSquareInLine, " line " ))
158
+ Some (createMove(" line " , pos, fourthSquareInLine ))
160
159
else if (opponentPieces.contains(fourthSquareInLine)) // xxxo
161
160
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
164
163
case _ => fourthSquareInLine.dir(direction).flatMap { // xxxo?
165
164
case (fifthSquareInLine) =>
166
165
if (opponentPieces.contains(fifthSquareInLine)) // xxxoo
167
166
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
170
169
case _ => None
171
170
}
172
171
else None
@@ -193,72 +192,86 @@ abstract class Variant private[variant] (
193
192
case (pos, neighbour) =>
194
193
generateMovesForNeighbours(pos, neighbour)
195
194
}).view.toList
196
- }
195
+ }
197
196
}
198
197
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 = {
201
199
// 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)
213
209
)
214
210
)
215
211
}
216
212
217
- // @TODO: adapt
213
+ // @TODO: rewrite this code properly as it's currently full of get()
218
214
// 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 ) = {
220
216
var capture = false
217
+ var updatedPieceMap : PieceMap = Map ()
221
218
222
219
/*
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
232
227
233
228
2. play the move
234
229
- line move :
235
- - move from orig to dest
230
+ - move from orig to dest, without considering the direction
236
231
- push :
237
232
- dest contains a marble
238
- - we know the direction :
233
+ - we have the direction :
239
234
- apply the direction from dest until there is an empty square or being off the board
240
235
- in case of being off the board, increment the score (capture = true)
241
236
- do the line move (orig to dest)
242
237
- 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
247
241
*/
248
242
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
+ }
257
272
}
258
- if (player == P1 )
259
- (pieces, capture)
260
- else
261
- (pieces, capture)
273
+
274
+ (updatedPieceMap, capture)
262
275
}
263
276
264
277
def move (
@@ -275,6 +288,7 @@ abstract class Variant private[variant] (
275
288
// if a player runs out of move, the match is a draw
276
289
def stalemateIsDraw = true
277
290
291
+ // @TODO: might want to use this winner method in specialEnd method below ? code is duplicated...
278
292
def winner (situation : Situation ): Option [Player ] = {
279
293
if (situation.board.history.score.p1 == 6 ) Some (P1 )
280
294
else if (situation.board.history.score.p2 == 6 ) Some (P2 )
0 commit comments