From 72f6e7d3dfdb092ebedb9be021533d234b2ec0a0 Mon Sep 17 00:00:00 2001 From: Thibault Duplessis Date: Mon, 27 Feb 2012 00:19:32 +0100 Subject: [PATCH] Refactor to introduce Move --- chess/src/main/scala/model/Actor.scala | 88 +++++++++++++++--------- chess/src/main/scala/model/Game.scala | 11 ++- chess/src/main/scala/model/Move.scala | 16 +++++ chess/src/main/scala/model/package.scala | 3 - 4 files changed, 80 insertions(+), 38 deletions(-) create mode 100644 chess/src/main/scala/model/Move.scala diff --git a/chess/src/main/scala/model/Actor.scala b/chess/src/main/scala/model/Actor.scala index b7d27bb2ef..527f582b0b 100644 --- a/chess/src/main/scala/model/Actor.scala +++ b/chess/src/main/scala/model/Actor.scala @@ -7,7 +7,7 @@ import scala.math.{ min, max } case class Actor(piece: Piece, pos: Pos, board: Board) { - lazy val implications: Implications = kingSafety(piece.role match { + lazy val moves: List[Move] = kingSafety(piece.role match { case Bishop ⇒ longRange(Bishop.dirs) @@ -22,45 +22,47 @@ case class Actor(piece: Piece, pos: Pos, board: Board) { side ← Side.kingRookSide(kingPos, pos) if history canCastle color on side } yield history.withoutCastle(color, side)) map { nh ⇒ - longRange(Rook.dirs) mapValues (_ withHistory nh) + longRange(Rook.dirs) map (_ withHistory nh) } getOrElse longRange(Rook.dirs) case Pawn ⇒ pawnDir(pos) map { next ⇒ val fwd = Some(next) filterNot board.occupations - def capture(horizontal: Direction): Option[Implication] = for { + def capture(horizontal: Direction): Option[Move] = for { p ← horizontal(next); if enemies(p); b ← board.taking(pos, p) - } yield (p, b) - def enpassant(horizontal: Direction): Option[Implication] = for { + } yield move(p, b, Some(p)) + def enpassant(horizontal: Direction): Option[Move] = for { victimPos ← horizontal(pos); if pos.y == color.passablePawnY victim ← board(victimPos); if victim == !color - Pawn targetPos ← horizontal(next) victimFrom ← pawnDir(victimPos) flatMap pawnDir if history.lastMove == Some(victimFrom, victimPos) b ← board.taking(pos, targetPos, Some(victimPos)) - } yield (targetPos, b) - def forward(p: Pos) = - if (pos.y == color.promotablePawnY) board.promote(pos, p) - else board.move(pos, p) + } yield move(targetPos, b, enpassant = true) + def forward(p: Pos): Option[Move] = + if (pos.y == color.promotablePawnY) + board.promote(pos, p) map { b ⇒ move(p, b, promotion = Some(Queen)) } + else + board.move(pos, p) map { b ⇒ move(p, b) } List( for { p ← fwd - b ← forward(p) - } yield (p, b), + m ← forward(p) + } yield m, for { p ← fwd; if pos.y == color.unmovedPawnY p2 ← pawnDir(p); if !(board occupations p2) b ← board.move(pos, p2) - } yield (p2, b), + } yield move(p2, b), capture(_.left), capture(_.right), enpassant(_.left), enpassant(_.right) - ).flatten toMap - } getOrElse Map.empty + ).flatten + } getOrElse Nil }) - lazy val moves: Set[Pos] = implications.keySet + lazy val destinations: List[Pos] = moves map (_.dest) def color = piece.color def is(c: Color) = c == piece.color @@ -76,20 +78,20 @@ case class Actor(piece: Piece, pos: Pos, board: Board) { case role ⇒ (role.dirs map { d ⇒ d(pos) }).flatten toSet } - private def kingSafety(implications: Implications): Implications = - implications filterNot { - case (p, b) ⇒ b actorsOf !color exists { enemy ⇒ - b kingPosOf color map (enemy threatens _) getOrElse false + private def kingSafety(ms: List[Move]): List[Move] = + ms filterNot { m ⇒ + m.after actorsOf !color exists { enemy ⇒ + m.after kingPosOf color map (enemy threatens _) getOrElse false } } - private def castle: Implications = { + private def castle: List[Move] = { lazy val enemyThreats = (board actorsOf !color).toSet flatMap { actor: Actor ⇒ actor.threats } - def on(side: Side): Option[Implication] = for { + def on(side: Side): Option[Move] = for { kingPos ← board kingPosOf color if history canCastle color on side tripToRook = side.tripToRook(kingPos, board) @@ -102,38 +104,39 @@ case class Actor(piece: Piece, pos: Pos, board: Board) { b1 ← board take rookPos b2 ← b1.move(kingPos, newKingPos) b3 ← b2.place(color.rook, newRookPos) - } yield (newKingPos, b3 updateHistory (_ withoutCastles color)) + b4 = b3 updateHistory (_ withoutCastles color) + } yield move(newKingPos, b4, castle = true) - List(on(KingSide), on(QueenSide)).flatten toMap + List(on(KingSide), on(QueenSide)).flatten } - private def preventsCastle(implications: Implications) = + private def preventsCastle(ms: List[Move]) = if (history.canCastle(color).any) { val newHistory = history withoutCastles color - implications mapValues (_ withHistory newHistory) + ms map (_ withHistory newHistory) } - else implications + else ms - private def shortRange(dirs: Directions): Implications = { + private def shortRange(dirs: Directions): List[Move] = (dirs map { _(pos) }).flatten filterNot friends map { to ⇒ - (if (enemies(to)) board.taking(pos, to) else board.move(pos, to)) map (to -> _) + if (enemies(to)) board.taking(pos, to) map { move(to, _, Some(to)) } + else board.move(pos, to) map { move(to, _) } } flatten - } toMap - private def longRange(dirs: Directions): Implications = { + private def longRange(dirs: Directions): List[Move] = { - def forward(p: Pos, dir: Direction): List[Implication] = dir(p) match { + def forward(p: Pos, dir: Direction): List[Move] = dir(p) match { case None ⇒ Nil case Some(next) if friends(next) ⇒ Nil case Some(next) if enemies(next) ⇒ board.taking(pos, next) map { b ⇒ - (next, b) + move(next, b, Some(pos)) } toList case Some(next) ⇒ board.move(pos, next) map { b ⇒ - (next, b) :: forward(next, dir) + move(next, b) :: forward(next, dir) } getOrElse Nil } - (dirs flatMap { dir ⇒ forward(pos, dir) }) toMap + dirs flatMap { dir ⇒ forward(pos, dir) } } private def longRangePoss(dirs: Directions): List[Pos] = { @@ -148,6 +151,23 @@ case class Actor(piece: Piece, pos: Pos, board: Board) { dirs flatMap { dir ⇒ forward(pos, dir) } } + private def move( + dest: Pos, + after: Board, + capture: Option[Pos] = None, + castle: Boolean = false, + promotion: Option[PromotableRole] = None, + enpassant: Boolean = false) = Move( + piece = piece, + orig = pos, + dest = dest, + before = board, + after = after, + capture = capture, + castle = castle, + promotion = promotion, + enpassant = enpassant) + private def history = board.history private def friends = board occupation color private def enemies = board occupation !color diff --git a/chess/src/main/scala/model/Game.scala b/chess/src/main/scala/model/Game.scala index dd258ec06c..1813cc58e1 100644 --- a/chess/src/main/scala/model/Game.scala +++ b/chess/src/main/scala/model/Game.scala @@ -3,10 +3,19 @@ package model case class Game( board: Board, - player: Color) { + player: Color, + pgnMoves: List[String] = Nil) { def this() = this(Board.empty, White) + //def playMoves(moves: (Pos, Pos)*): Valid[Game] = + //moves.foldLeft(success(this): Valid[Situation]) { (sit, move) ⇒ + //sit flatMap { s ⇒ s.playMove(move._1, move._2) } + //} + + //def playMove(from: Pos, to: Pos, promotion: PromotableRole = Queen): Valid[Game] = { + //} + val players = List(White, Black) def situation = board as player diff --git a/chess/src/main/scala/model/Move.scala b/chess/src/main/scala/model/Move.scala new file mode 100644 index 0000000000..6ef924f876 --- /dev/null +++ b/chess/src/main/scala/model/Move.scala @@ -0,0 +1,16 @@ +package lila.chess +package model + +case class Move( + piece: Piece, + orig: Pos, + dest: Pos, + before: Board, + after: Board, + capture: Option[Pos], + promotion: Option[PromotableRole], + castle: Boolean, + enpassant: Boolean) { + + def withHistory(h: History) = copy(after = after withHistory h) +} diff --git a/chess/src/main/scala/model/package.scala b/chess/src/main/scala/model/package.scala index ced1c44aa8..e7434c1283 100644 --- a/chess/src/main/scala/model/package.scala +++ b/chess/src/main/scala/model/package.scala @@ -4,7 +4,4 @@ package object model { type Direction = Pos => Option[Pos] type Directions = List[Direction] - - type Implication = (Pos, Board) - type Implications = Map[Pos, Board] }