Skip to content

Commit ece31e5

Browse files
chore: refactor side moves
1 parent 14b016e commit ece31e5

File tree

2 files changed

+72
-26
lines changed

2 files changed

+72
-26
lines changed

src/main/scala/abalone/Pos.scala

+19-9
Original file line numberDiff line numberDiff line change
@@ -192,15 +192,6 @@ case class Pos private (index: Int) extends AnyVal {
192192
def upRight: Option[Pos] = Pos.at(file.index + 1, rank.index + 1)
193193

194194
def neighbours: List[Option[Pos]] = List(left, upLeft, upRight, right, downRight, downLeft)
195-
def sideMovesDirsFromDir(dir: Option[String]): (Option[Pos], Option[Pos]) = dir match {
196-
case Some("left") => (this.downLeft, this.upLeft)
197-
case Some("upLeft") => (this.left, this.upRight)
198-
case Some("upRight") => (this.upLeft, this.right)
199-
case Some("right") => (this.upRight, this.downRight)
200-
case Some("downRight") => (this.right, this.downLeft)
201-
case Some("downLeft") => (this.downRight, this.left)
202-
case _ => (None, None)
203-
}
204195

205196
// NOTE - *neighbourhood
206197
// these below only work for neighbour pos but that's probably fine as in Abalone we only move to (potentially extended) neighbourhood
@@ -213,6 +204,15 @@ case class Pos private (index: Int) extends AnyVal {
213204
case Some("downLeft") => this.downLeft
214205
case _ => None
215206
}
207+
def dir(dir: String): Option[Pos] = dir match {
208+
case "left" => this.left
209+
case "upLeft" => this.upLeft
210+
case "upRight" => this.upRight
211+
case "right" => this.right
212+
case "downRight" => this.downRight
213+
case "downLeft" => this.downLeft
214+
case _ => None
215+
}
216216

217217
def dir(pos: Pos): Option[String] =
218218
(pos.file.index - this.file.index, pos.rank.index - this.rank.index) match {
@@ -318,6 +318,16 @@ object Pos {
318318
if (isInHexagon(x + File.all.size * y)) Some(new Pos(x + File.all.size * y))
319319
else None
320320

321+
def sideMovesDirsFromDir(dir: Option[String]): List[String] = dir match {
322+
case Some("left") => List("downLeft", "upLeft")
323+
case Some("upLeft") => List("left", "upRight")
324+
case Some("upRight") => List("upLeft", "right")
325+
case Some("right") => List("upRight", "downRight")
326+
case Some("downRight") => List("right", "downLeft")
327+
case Some("downLeft") => List("downRight", "left")
328+
case _ => List()
329+
}
330+
321331
def fromKey(key: String): Option[Pos] = allKeys get key
322332

323333
def piotr(c: Char): Option[Pos] = allPiotrs get c

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

+53-17
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ abstract class Variant private[variant] (
9696
Map(pos ->
9797
pos.neighbours.flatten
9898
.filterNot(situation.board.pieces.contains(_))
99-
.map(landingSquare =>
99+
.map(landingSquare =>
100100
Move(piece, pos, landingSquare, situation, boardAfter(situation, pos, landingSquare), false)
101101
)
102102
)
@@ -105,26 +105,37 @@ abstract class Variant private[variant] (
105105
def validMovesOf2And3(situation: Situation): Map[Pos, List[(String, Move)]] = {
106106
val activePlayerPieces = situation.board.piecesOf(situation.player)
107107
val opponentPieces = situation.board.piecesOf(!situation.player)
108-
108+
109109
def createMove(orig: Pos, dest: Pos, category: String): (String, Move) =
110110
(category, Move(Piece(situation.player, Role.defaultRole), orig, dest, situation, boardAfter(situation, orig, dest), true, if (category == "pushout") Some(dest) else None))
111111

112112
def generateSideMoves(lineOfMarbles: List[Pos], direction: Option[String]): List[(String, Move)] = {
113-
// "left" is related to the direction
114-
def canLeftSideMove(pos: Pos): Boolean =
115-
pos.sideMovesDirsFromDir(direction)._1.fold(false)(p => situation.board.isEmptySquare(Some(p)))
116-
def canRightSideMove(pos: Pos): Boolean =
117-
pos.sideMovesDirsFromDir(direction)._2.fold(false)(p => situation.board.isEmptySquare(Some(p)))
113+
def canMoveTowards(pos: Pos, dir: String): Boolean = {
114+
situation.board.isEmptySquare(pos.dir(dir))
115+
}
116+
117+
val possibleSideMovesDirections = Pos.sideMovesDirsFromDir(direction)
118+
def possibleSideMoves: List[Option[(Pos, Pos)]] = possibleSideMovesDirections.map(
119+
dir =>
120+
if (lineOfMarbles.flatMap(
121+
(pos) => Some(canMoveTowards(pos, dir))
122+
).contains(false)) None
123+
else List(
124+
lineOfMarbles.headOption,
125+
lineOfMarbles.reverse.headOption.flatMap(_.dir(dir))
126+
) match {
127+
case List(Some(head), Some(tail)) => Some( (head, tail) )
128+
case _ => None
129+
}
130+
)
118131

119132
List(
120133
if (lineOfMarbles.size == 3) generateSideMoves(lineOfMarbles.dropRight(1), direction)
121134
else None,
122-
if (!lineOfMarbles.map(canLeftSideMove).contains(false))
123-
Some(createMove(lineOfMarbles(0), lineOfMarbles.last.sideMovesDirsFromDir(direction)._1.get, "side"))
124-
else None,
125-
if (!lineOfMarbles.map(canRightSideMove).contains(false))
126-
Some(createMove(lineOfMarbles(0), lineOfMarbles.last.sideMovesDirsFromDir(direction)._2.get, "side"))
127-
else None,
135+
possibleSideMoves.flatMap {
136+
case Some((orig, dest)) => Some(createMove(orig, dest, "side"))
137+
case _ => None
138+
}
128139
).flatten
129140
}
130141

@@ -183,11 +194,11 @@ abstract class Variant private[variant] (
183194
}.flatMap {
184195
case (pos, neighbour) =>
185196
generateMovesForNeighbours(pos, neighbour)
186-
}
187-
).view.toList
197+
}).view.toList
188198
}
189199
}
190200

201+
// @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)
191202
def boardAfter(situation: Situation, orig: Pos, dest: Pos): Board = {
192203
// 1. move pieces and get the score
193204
val (pieces, capture) = piecesAfterAction(situation.board.pieces, situation.player, orig, dest)
@@ -210,9 +221,34 @@ abstract class Variant private[variant] (
210221
def piecesAfterAction(pieces: PieceMap, player: Player, @nowarn orig: Pos, dest: Pos): (PieceMap, Boolean) = {
211222
var capture = false
212223

213-
if (pieces.contains(dest)) { // push
214-
// compute direction between orig and dest to push the opponent marble
224+
/*
225+
226+
* * * * *
227+
* a A B C 1
228+
* * * * * 2 *
229+
How to move pieces based on orig and dest :
230+
1. identify the type of move :
231+
- push : dest contains a marble (orig=C, dest=a)
232+
- line move : orig meets dest thanks to the direction we passed as extra parameter
233+
- side move : NOT a push or a side move
234+
235+
2. play the move
236+
- line move :
237+
- move from orig to dest
238+
- push :
239+
- dest contains a marble
240+
- we know the direction :
241+
- apply the direction from dest until there is an empty square or being off the board
242+
- in case of being off the board, increment the score (capture = true)
243+
- do the line move (orig to dest)
244+
- side move :
245+
- 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 ?
246+
247+
in case of a side move, orig=A and dest=1, how do we determine how to move A and B ?
248+
249+
*/
215250

251+
if (pieces.contains(dest)) { // push
216252
// 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)
217253

218254
// then move the 2 marbles :

0 commit comments

Comments
 (0)