Don't castle if the enemy threatens part of the king trip
parent
90356308d5
commit
90946a29b7
|
@ -3,6 +3,7 @@ package model
|
|||
|
||||
import Pos.makePos
|
||||
import scalaz.Success
|
||||
import scala.math.{ min, max }
|
||||
|
||||
case class Actor(piece: Piece, pos: Pos, board: Board) {
|
||||
|
||||
|
@ -62,13 +63,15 @@ case class Actor(piece: Piece, pos: Pos, board: Board) {
|
|||
def is(c: Color) = c == piece.color
|
||||
def is(p: Piece) = p == piece
|
||||
|
||||
def threatens(to: Pos): Boolean = enemies(to) && ((piece.role match {
|
||||
def threatens(to: Pos): Boolean = enemies(to) && threats(to)
|
||||
|
||||
lazy val threats: Set[Pos] = piece.role match {
|
||||
case Pawn ⇒ pawnDir(pos) map { next ⇒
|
||||
List(next.left, next.right) flatten
|
||||
} getOrElse Nil
|
||||
case role if role.longRange ⇒ longRangePoss(role.dirs)
|
||||
case role ⇒ (role.dirs map { d ⇒ d(pos) }).flatten
|
||||
}) contains to)
|
||||
Set(next.left, next.right) flatten
|
||||
} getOrElse Set.empty
|
||||
case role if role.longRange ⇒ longRangePoss(role.dirs) toSet
|
||||
case role ⇒ (role.dirs map { d ⇒ d(pos) }).flatten toSet
|
||||
}
|
||||
|
||||
private def kingSafety(implications: Implications): Implications =
|
||||
implications filterNot {
|
||||
|
@ -78,20 +81,27 @@ case class Actor(piece: Piece, pos: Pos, board: Board) {
|
|||
}
|
||||
|
||||
private def castle: Implications = {
|
||||
def on(side: Side): Option[Implication] = for {
|
||||
kingPos ← board kingPosOf color
|
||||
if history canCastle color on side
|
||||
tripToRook = side.tripToRook(kingPos, board)
|
||||
rookPos ← tripToRook.lastOption
|
||||
if board(rookPos) == Some(color.rook)
|
||||
newKingPos ← makePos(side.castledKingX, kingPos.y)
|
||||
newRookPos ← makePos(side.castledRookX, rookPos.y)
|
||||
b1 ← board take rookPos
|
||||
b2 ← b1.move(kingPos, newKingPos)
|
||||
b3 ← b2.place(color.rook, newRookPos)
|
||||
} yield (newKingPos, b3 updateHistory (_ withoutCastles color))
|
||||
|
||||
List(on(KingSide), on(QueenSide)).flatten toMap
|
||||
if (history.canCastle(color).any) {
|
||||
val enemyThreats = board threatsOf !color
|
||||
def on(side: Side): Option[Implication] = for {
|
||||
kingPos ← board kingPosOf color
|
||||
if history canCastle color on side
|
||||
tripToRook = side.tripToRook(kingPos, board)
|
||||
rookPos ← tripToRook.lastOption
|
||||
if board(rookPos) == Some(color.rook)
|
||||
newKingPos ← makePos(side.castledKingX, kingPos.y)
|
||||
securedPoss = kingPos <-> newKingPos
|
||||
if (enemyThreats & securedPoss.toSet).isEmpty
|
||||
newRookPos ← makePos(side.castledRookX, rookPos.y)
|
||||
b1 ← board take rookPos
|
||||
b2 ← b1.move(kingPos, newKingPos)
|
||||
b3 ← b2.place(color.rook, newRookPos)
|
||||
} yield (newKingPos, b3 updateHistory (_ withoutCastles color))
|
||||
|
||||
List(on(KingSide), on(QueenSide)).flatten toMap
|
||||
}
|
||||
else Map.empty
|
||||
}
|
||||
|
||||
private def preventsCastle(implications: Implications) =
|
||||
|
|
|
@ -19,9 +19,10 @@ case class Board(pieces: Map[Pos, Piece], history: History) {
|
|||
case (pos, piece) ⇒ (pos, Actor(piece, pos, this))
|
||||
}
|
||||
|
||||
lazy val colorActors: Map[Color, Iterable[Actor]] = actors.values groupBy (_.color)
|
||||
lazy val colorActors: Map[Color, List[Actor]] =
|
||||
actors.values groupBy (_.color) mapValues (_.toList)
|
||||
|
||||
def actorsOf(c: Color) = colorActors get c getOrElse Nil
|
||||
def actorsOf(c: Color): List[Actor] = colorActors get c getOrElse Nil
|
||||
|
||||
def actorAt(at: Pos): Valid[Actor] = actors get at toSuccess ("No piece on " + at)
|
||||
|
||||
|
@ -33,6 +34,10 @@ case class Board(pieces: Map[Pos, Piece], history: History) {
|
|||
|
||||
def movesFrom(from: Pos): Valid[Set[Pos]] = actorAt(from) map (_.moves)
|
||||
|
||||
def threatsOf(c: Color): Set[Pos] = actorsOf(c).toSet flatMap { actor: Actor =>
|
||||
actor.threats
|
||||
}
|
||||
|
||||
def seq(actions: Board ⇒ Valid[Board]*): Valid[Board] =
|
||||
actions.foldLeft(success(this): Valid[Board])(_ flatMap _)
|
||||
|
||||
|
@ -96,7 +101,7 @@ case class Board(pieces: Map[Pos, Piece], history: History) {
|
|||
|
||||
def withHistory(h: History): Board = copy(history = h)
|
||||
|
||||
def updateHistory(f: History => History) = copy(history = f(history))
|
||||
def updateHistory(f: History ⇒ History) = copy(history = f(history))
|
||||
|
||||
def as(c: Color) = Situation(this, c)
|
||||
|
||||
|
|
|
@ -29,6 +29,9 @@ sealed case class Pos private (x: Int, y: Int) extends Ordered[Pos] {
|
|||
def ?<(other: Pos) = x < other.x
|
||||
def ?>(other: Pos) = x > other.x
|
||||
|
||||
def <->(other: Pos): Iterable[Pos] =
|
||||
min(x, other.x) to max(x, other.x) map { makePos(_, y) } flatten
|
||||
|
||||
def xToString = Pos xToString x
|
||||
def yToString = y.toString
|
||||
|
||||
|
|
|
@ -195,6 +195,17 @@ PPPPPPPP
|
|||
board place Black.rook at C3 flatMap (_ movesFrom E1) must bePoss(D1, D2, E2, F2)
|
||||
}
|
||||
}
|
||||
"chess 960" in {
|
||||
"far kingside" in {
|
||||
val board: Board = """BK R"""
|
||||
"rook threat" in {
|
||||
board place Black.rook at F3 flatMap (_ movesFrom B1) must bePoss(A2, B2, C2, C1)
|
||||
}
|
||||
"enemy king threat" in {
|
||||
board place Black.king at E2 flatMap (_ movesFrom B1) must bePoss(A2, B2, C2, C1)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
"threat on rook does not prevent castling" in {
|
||||
"king side" in {
|
||||
|
|
Loading…
Reference in New Issue