@@ -54,28 +54,38 @@ abstract class Variant private[variant] (
54
54
55
55
/*
56
56
In Abalone there are 3 kinds of moves.
57
- Let's use the top 2 rows of a board to illustrate (numbers are empty squares, Z is an opponent marble)
57
+ Let's use the top 3 rows of a board to illustrate (capital letters are empty squares, 1 is an opponent marble)
58
58
59
- Z a b c 7
60
- 1 2 3 4 5 6
59
+ A B C D E
60
+ 1 a b c G H
61
+ I J K L M N O
61
62
62
63
- line moves are marbles moving in line to an empty square :
63
- 'a' to '7' would move three marbles to the right. This is the same as if 'a' jumped over 'b' and 'c' to land on '7'.
64
- 'c' to '7' would move one marble to the right.
64
+ 'a' to 'G' would move three marbles to the right.
65
+ This is the same as if 'a' jumped over 'b' and 'c' to land on 'G'.
66
+ 'c' to 'G' would move one marble to the right.
65
67
- pushes are line moves targeting a square hosting an opponent marble
66
- They will be processed as two line moves (one jump per player).
67
- 'c' to 'Z' and 'b' to 'Z' are pushes to the left
68
+ They will be processed later on as two line moves (one jump per player).
69
+ 'c' to '1' and 'b' to '1' are pushes to the left.
70
+ In case we play a move from 'c' to '1', c will land on 1 and 1 will be removed from the board.
71
+ When a piece is pushed off the board, the Move is created with an extra parameter.
68
72
- side moves can only be described starting from the right origin (figure out the longest diagonal) :
69
- 'a' to '5' is a downRight side move of three marbles.
70
- 'c' to '2' is a downLeft side move of three marbles.
71
- 'a' to '4' is a downRight side move of two marbles (only 'a' and 'b' would move).
72
- 'c' to '3' is a downLeft side move of two marbles (only 'c' and 'b' would move)
73
- 'a' to '3' or 'c' to '4' are line moves of a single marble
73
+ 'a' to 'M' is a downRight side move of three marbles.
74
+ 'c' to 'J' is a downLeft side move of three marbles.
75
+ 'a' to 'L' is a downRight side move of two marbles (only 'a' and 'b' would move).
76
+ 'c' to 'K' is a downLeft side move of two marbles (only 'c' and 'b' would move)
77
+ 'a' to 'K' or 'c' to 'L' are line moves of a single marble
78
+
79
+ For moves of 2 marbles or more, once you get the direction of the line,
80
+ you can easily generate the side moves as being the one before and the one after,
81
+ following a rotation :
82
+ \ /
83
+ - o -
84
+ / \
85
+ e.g. if you are moving upRight the side moves to consider are "upLeft" and "right".
74
86
75
- we want to have :
76
87
1. moves of 1 marble
77
- 2. moves of 2 marbles, using moves of 1
78
- 3. moves of 3 marbles, using moves of 2
88
+ 2. generate any possible pair of marbles and use it to generate moves of 2 and 3 marbles
79
89
then merge these as valid moves.
80
90
*/
81
91
def validMoves (situation : Situation ): Map [Pos , List [Move ]] = {
@@ -91,155 +101,81 @@ abstract class Variant private[variant] (
91
101
}
92
102
}
93
103
94
- def validMovesOf1 (situation : Situation ): Map [Pos , List [Move ]] =
95
- situation.board.piecesOf(situation.player).flatMap {
104
+ // @TODO: move this into the validMoves method and make it become private as it's used by validMovesOf1 AND validMovesOf2
105
+ def turnPieces (s : Situation ): PieceMap = s.board.piecesOf(s.player)
106
+
107
+ def validMovesOf1 (s : Situation ): Map [Pos , List [Move ]] =
108
+ this .turnPieces(s).flatMap {
96
109
case ((pos, piece)) =>
97
110
Map (pos ->
98
111
pos.neighbours.flatten
99
- .filterNot(situation .board.pieces.contains(_))
112
+ .filterNot(s .board.pieces.contains(_))
100
113
.map(landingSquare =>
101
- Move (piece, pos, landingSquare, situation , boardAfter(situation , pos, landingSquare), false )
114
+ Move (piece, pos, landingSquare, s , boardAfter(s , pos, landingSquare), false )
102
115
)
103
116
)
104
117
}.toMap
105
118
106
- def validMovesOf2 (situation : Situation , validMoves1 : Map [Pos , List [Move ]]): Map [Pos , List [(String , Move )]] =
107
- situation.board.piecesOf(situation.player).flatMap {
108
- case ((pos, piece)) => {
109
- Map (
110
- pos -> pos.neighbours.flatMap {
111
- case Some (neighbour) if (situation.board.piecesOf(situation.player).contains(neighbour)) => {
112
- neighbour.neighbours.flatMap {
113
- case Some (neighbourOfNeighbour)
114
- if (situation.board.isEmptySquare(Some (neighbourOfNeighbour))
115
- && (pos.isInLine(neighbour, neighbourOfNeighbour) || situation.board.isEmptySquare(pos.dir(neighbour.dir(neighbourOfNeighbour))))
116
- && ! validMoves1.get(pos).toList.flatMap(_.map(_.toUci)).exists(m => m.toString == s " Move( ${pos}, ${neighbourOfNeighbour},None) " )) =>
117
- Some ( (" nopush" , Move (piece, pos, neighbourOfNeighbour, situation, boardAfter(situation, neighbour, neighbourOfNeighbour), false )) )
118
- case Some (neighbourOfNeighbour)
119
- if (pos.isInLine(neighbour, neighbourOfNeighbour) && situation.board.piecesOf(! situation.player).contains(neighbourOfNeighbour)) =>
120
- if (neighbourOfNeighbour.dir(pos.dir(neighbour)) == None )
121
- Some ( (" pushout" , Move (piece, pos, neighbourOfNeighbour, situation, boardAfter(situation, neighbour, neighbourOfNeighbour), false , neighbour.dir(pos.dir(neighbour)))) )
122
- else
123
- if (situation.board.isEmptySquare(neighbourOfNeighbour.dir(pos.dir(neighbour))))
124
- Some ( (" push" , Move (piece, pos, neighbourOfNeighbour, situation, boardAfter(situation, neighbour, neighbourOfNeighbour), false )) )
119
+ def validMovesOf2 (s : Situation ): Map [Pos , List [(String , Move )]] = {
120
+ def generateMove (orig : Pos , dest : Pos , category : String ) = category match {
121
+ case " pushout" => Some ( (category, Move (Piece (s.player, Role .defaultRole), orig, dest, s, boardAfter(s, orig, dest), true , Some (dest))) )
122
+ case _ => Some ( (category, Move (Piece (s.player, Role .defaultRole), orig, dest, s, boardAfter(s, orig, dest), true )) )
123
+ }
124
+
125
+ def generateSideMovesOf2 (pos : Pos , neighbour : Pos , direction : Option [String ]): List [(String , Move )] = List (
126
+ if (
127
+ pos.sideMovesDirsFromDir(direction)._1 != None &&
128
+ s.board.isEmptySquare(pos.sideMovesDirsFromDir(direction)._1) &&
129
+ neighbour.sideMovesDirsFromDir(direction)._1 != None &&
130
+ s.board.isEmptySquare(neighbour.sideMovesDirsFromDir(direction)._1)
131
+ )
132
+ generateMove(pos, neighbour.sideMovesDirsFromDir(direction)._1.get, " side" )
133
+ else
134
+ None ,
135
+ if (
136
+ pos.sideMovesDirsFromDir(direction)._2 != None &&
137
+ s.board.isEmptySquare(pos.sideMovesDirsFromDir(direction)._2) &&
138
+ neighbour.sideMovesDirsFromDir(direction)._2 != None &&
139
+ s.board.isEmptySquare(neighbour.sideMovesDirsFromDir(direction)._2)
140
+ )
141
+ generateMove(pos, neighbour.sideMovesDirsFromDir(direction)._2.get, " side" )
142
+ else
143
+ None
144
+ ).flatten
145
+
146
+ this .turnPieces(s).map {
147
+ case ( (pos, _) ) => pos ->
148
+ pos.neighbours.flatMap {
149
+ case Some (neighbour) if (s.board.piecesOf(s.player).contains(neighbour)) => Some ( (neighbour, pos.dir(neighbour)) )
150
+ case _ => None
151
+ }
152
+ }.flatMap {
153
+ case (pos, neighbourAndDir) => Map ( pos ->
154
+ neighbourAndDir.flatMap {
155
+ case (neighbour, direction) =>
156
+ neighbour.dir(direction) match {
157
+ case Some (neighbourOfNeighbour) =>
158
+ List (
159
+ if (s.board.isEmptySquare(Some (neighbourOfNeighbour)))
160
+ generateMove(pos, neighbourOfNeighbour, " line" )
161
+ else None ,
162
+ if (s.board.piecesOf(! s.player).contains(neighbourOfNeighbour))
163
+ if (neighbourOfNeighbour.dir(direction) == None )
164
+ generateMove(pos, neighbourOfNeighbour, " pushout" )
165
+ else if (s.board.isEmptySquare(neighbourOfNeighbour.dir(direction)))
166
+ generateMove(pos, neighbourOfNeighbour, " push" )
125
167
else None
126
- case _ => None
168
+ else None , // here, adding else if (s.board.piecesOf(s.player).contains(neighbourOfNeighbour)), we could generate moves of 2 marbles
169
+ generateSideMovesOf2(pos, neighbour, direction)
170
+ ).flatten
171
+ case None => {
172
+ generateSideMovesOf2(pos, neighbour, direction)
173
+ }
127
174
}
128
175
}
129
- case _ => None
130
- }
131
- )
132
- }
133
- }.toMap
134
-
135
- // def validMovesOf3(situation: Situation, @nowarn validMoves2: Map[Pos, List[(String, Move)]]): Map[Pos, List[(String, Move)]] =
136
- // situation.board.piecesOf(situation.player).flatMap {
137
- // case ((pos, piece)) => {
138
- // Map(
139
- // pos -> pos.neighbours.flatMap {
140
- // case Some(neighbour)
141
- // if(situation.board.piecesOf(situation.player).contains(neighbour)
142
- // && (neighbour.dir(pos.dir(neighbour).getOrElse("")) != None)
143
- // && situation.board.piecesOf(situation.player).contains((neighbour.dir(pos.dir(neighbour).getOrElse("")).get))
144
- // ) => { // in case we could potentially have a 3rd marble...
145
- // // if (neighbour.dir(pos.dir(neighbour)).getOrElse("") != None) {
146
- // val thirdMarblePos = neighbour.dir(pos.dir(neighbour).getOrElse("")).get
147
- // thirdMarblePos.neighbours.flatMap {
148
- // case Some(neighbourOf3rdMarble)
149
- // if (
150
- // !situation.board.pieces.contains(neighbourOf3rdMarble)
151
-
152
- // // && !validMoves2.contains()
153
- // ) => { // line or side move, but we need to remove the 2 marbles moves
154
- // // if (neighbour)
155
- // Some(
156
- // (
157
- // "lineOrSide",
158
- // Move(
159
- // situation.board.pieces.getOrElse(
160
- // pos,
161
- // Piece(!piece.player, piece.role)
162
- // ),
163
- // pos,
164
- // neighbourOf3rdMarble,
165
- // situation,
166
- // situation.board.variant.boardAfter(situation, neighbour, neighbourOf3rdMarble),
167
- // false
168
- // )
169
- // )
170
- // )
171
- // }
172
- // case Some(neighbourOf3rdMarble) if (situation.board.piecesOf(!situation.player).contains(neighbourOf3rdMarble)) => { // push
173
- // Some(
174
- // (
175
- // "push",
176
- // Move(
177
- // situation.board.pieces.getOrElse(
178
- // pos,
179
- // Piece(!piece.player, piece.role)
180
- // ),
181
- // pos,
182
- // neighbourOf3rdMarble,
183
- // situation,
184
- // situation.board.variant.boardAfter(situation, neighbour, neighbourOf3rdMarble),
185
- // false
186
- // )
187
- // )
188
- // )
189
- // }
190
- // case _ => None
191
- // // }
192
- // }
193
- // }
194
- // case _ => None
195
- // }
196
- // )
197
- // }
198
- // }
199
-
200
-
201
- // def validMovesOf3(situation: Situation, validMoves2: Map[Pos, List[Move]], validMoves1: Map[Pos, List[Move]]): Map[Pos, List[Move]] =
202
- // situation.board.piecesOf(situation.player).flatMap {
203
- // case ((pos, piece)) => {
204
- // Map(
205
- // pos -> pos.neighbours.flatMap {
206
- // case Some(neighbour) if(situation.board.piecesOf(situation.player).contains(neighbour)) => {
207
- // neighbour.neighbours.flatMap {
208
- // case Some(neighbourOfNeighbour) => {
209
- // if (
210
- // !situation.board.pieces.contains(neighbourOfNeighbour)
211
- // && pos.dir(neighbour.dir(neighbourOfNeighbour).getOrElse("")) != None
212
- // && (
213
- // (
214
- // !situation.board.piecesOf(!situation.player).contains(pos.dir(neighbour.dir(neighbourOfNeighbour).getOrElse("")).getOrElse(pos))
215
- // && !situation.board.piecesOf(situation.player).contains(pos.dir(neighbour.dir(neighbourOfNeighbour).getOrElse("")).getOrElse(pos))
216
- // )
217
- // || pos.dir(neighbour) == neighbour.dir(neighbourOfNeighbour) // line moves
218
- // )
219
- // && !(validMoves1.get(pos).get.contains(
220
- // Move(
221
- // situation.board.pieces.getOrElse(
222
- // pos,
223
- // Piece(
224
- // !piece.player, piece.role)
225
- // ),
226
- // pos,
227
- // neighbourOfNeighbour,
228
- // situation,
229
- // situation.board.variant.boardAfter(situation, neighbour, neighbourOfNeighbour),
230
- // false
231
- // )))
232
- // ) {
233
- // Some(move)
234
- // }
235
- // }
236
- // }
237
- // }
238
- // }
239
- // )
240
- // }
241
- // }
242
-
176
+ )
177
+ }.toMap
178
+ }
243
179
244
180
def validSideMoves (@ nowarn situation : Situation ): Map [Pos , List [Move ]] = {
245
181
Map ()
0 commit comments