Fix diagonal checks for black
This commit is contained in:
parent
77bce34c8b
commit
80a9cfe827
|
@ -109,7 +109,7 @@ proc generateMoves(self: ChessBoard, location: Location): seq[Move]
|
||||||
|
|
||||||
func topLeftDiagonal(piece: Piece): Location {.inline.} = (if piece.color == White: (-1, -1) else: (1, 1))
|
func topLeftDiagonal(piece: Piece): Location {.inline.} = (if piece.color == White: (-1, -1) else: (1, 1))
|
||||||
func topRightDiagonal(piece: Piece): Location {.inline.} = (if piece.color == White: (-1, 1) else: (1, -1))
|
func topRightDiagonal(piece: Piece): Location {.inline.} = (if piece.color == White: (-1, 1) else: (1, -1))
|
||||||
func bottomLeftDiagonal(piece: Piece): Location {.inline.} = (if piece.color == White: (-1, 1) else: (1, -1))
|
func bottomLeftDiagonal(piece: Piece): Location {.inline.} = (if piece.color == White: (1, -1) else: (-1, 1))
|
||||||
func bottomRightDiagonal(piece: Piece): Location {.inline.} = (if piece.color == White: (1, 1) else: (-1, -1))
|
func bottomRightDiagonal(piece: Piece): Location {.inline.} = (if piece.color == White: (1, 1) else: (-1, -1))
|
||||||
func leftSide(piece: Piece): Location {.inline.} = (if piece.color == White: (0, -1) else: (0, 1))
|
func leftSide(piece: Piece): Location {.inline.} = (if piece.color == White: (0, -1) else: (0, 1))
|
||||||
func rightSide(piece: Piece): Location {.inline.} = (if piece.color == White: (0, 1) else: (0, -1))
|
func rightSide(piece: Piece): Location {.inline.} = (if piece.color == White: (0, 1) else: (0, -1))
|
||||||
|
@ -117,7 +117,6 @@ func topSide(piece: Piece): Location {.inline.} = (if piece.color == White: (-1,
|
||||||
func bottomSide(piece: Piece): Location {.inline.} = (if piece.color == White: (1, 0) else: (-1, 0))
|
func bottomSide(piece: Piece): Location {.inline.} = (if piece.color == White: (1, 0) else: (-1, 0))
|
||||||
func forward(piece: Piece): Location {.inline.} = (if piece.color == Black: (1, 0) else: (-1, 0))
|
func forward(piece: Piece): Location {.inline.} = (if piece.color == Black: (1, 0) else: (-1, 0))
|
||||||
func doublePush(piece: Piece): Location {.inline.} = (if piece.color == Black: (2, 0) else: (-2, 0))
|
func doublePush(piece: Piece): Location {.inline.} = (if piece.color == Black: (2, 0) else: (-2, 0))
|
||||||
proc testMoveOffsets(self: ChessBoard, move: Move): bool
|
|
||||||
|
|
||||||
|
|
||||||
proc getActiveColor*(self: ChessBoard): PieceColor =
|
proc getActiveColor*(self: ChessBoard): PieceColor =
|
||||||
|
@ -519,14 +518,48 @@ proc generateSlidingMoves(self: ChessBoard, location: Location): seq[Move] =
|
||||||
if otherPiece.color == piece.color:
|
if otherPiece.color == piece.color:
|
||||||
break
|
break
|
||||||
if otherPiece.color == piece.color.opposite:
|
if otherPiece.color == piece.color.opposite:
|
||||||
|
# Target square contains an enemy piece: capture
|
||||||
|
# it and stop going any further
|
||||||
result.add(Move(startSquare: location, targetSquare: square, piece: piece))
|
result.add(Move(startSquare: location, targetSquare: square, piece: piece))
|
||||||
break
|
break
|
||||||
# Target square is empty
|
# Target square is empty
|
||||||
result.add(Move(startSquare: location, targetSquare: square, piece: piece))
|
result.add(Move(startSquare: location, targetSquare: square, piece: piece))
|
||||||
|
|
||||||
|
|
||||||
|
proc generateKingMoves(self: ChessBoard, location: Location): seq[Move] =
|
||||||
|
## Generates moves for the king in the given location
|
||||||
|
var
|
||||||
|
piece = self.grid[location.row, location.col]
|
||||||
|
doAssert piece.kind == King, &"generateKingMoves called on a {piece.kind}"
|
||||||
|
var directions: seq[Location] = @[piece.topLeftDiagonal(),
|
||||||
|
piece.topRightDiagonal(),
|
||||||
|
piece.bottomRightDiagonal(),
|
||||||
|
piece.bottomLeftDiagonal(),
|
||||||
|
piece.topSide(),
|
||||||
|
piece.bottomSide(),
|
||||||
|
piece.leftSide(),
|
||||||
|
piece.rightSide()]
|
||||||
|
for direction in directions:
|
||||||
|
# Step in this direction once
|
||||||
|
let square: Location = location + direction
|
||||||
|
# End of board reached
|
||||||
|
if not square.isValid():
|
||||||
|
continue
|
||||||
|
let otherPiece = self.grid[square.row, square.col]
|
||||||
|
# A friendly piece is in the way
|
||||||
|
if otherPiece.color == piece.color:
|
||||||
|
continue
|
||||||
|
if otherPiece.color == piece.color.opposite:
|
||||||
|
# Target square contains an enemy piece: capture
|
||||||
|
# it
|
||||||
|
result.add(Move(startSquare: location, targetSquare: square, piece: piece))
|
||||||
|
continue
|
||||||
|
# Target square is empty
|
||||||
|
result.add(Move(startSquare: location, targetSquare: square, piece: piece))
|
||||||
|
|
||||||
|
|
||||||
proc generateMoves(self: ChessBoard, location: Location): seq[Move] =
|
proc generateMoves(self: ChessBoard, location: Location): seq[Move] =
|
||||||
## Returns the list of possible moves for the
|
## Returns the list of possible legal chess moves for the
|
||||||
## piece in the given location
|
## piece in the given location
|
||||||
let piece = self.grid[location.row, location.col]
|
let piece = self.grid[location.row, location.col]
|
||||||
case piece.kind:
|
case piece.kind:
|
||||||
|
@ -534,35 +567,12 @@ proc generateMoves(self: ChessBoard, location: Location): seq[Move] =
|
||||||
return self.generateSlidingMoves(location)
|
return self.generateSlidingMoves(location)
|
||||||
of Pawn:
|
of Pawn:
|
||||||
return self.generatePawnMoves(location)
|
return self.generatePawnMoves(location)
|
||||||
|
of King:
|
||||||
|
return self.generateKingMoves(location)
|
||||||
else:
|
else:
|
||||||
return @[]
|
return @[]
|
||||||
|
|
||||||
|
|
||||||
proc testMoveOffsets(self: ChessBoard, move: Move): bool =
|
|
||||||
## Returns true if the piece in the given
|
|
||||||
## move is pseudo-legal: this does not take pins
|
|
||||||
## nor checks into account, but other rules like
|
|
||||||
## double pawn pushes and en passant are validated
|
|
||||||
## here. Note that this is an internal method called
|
|
||||||
## by checkMove and it does not validate whether the
|
|
||||||
## target square is occupied or not (it is assumed the
|
|
||||||
## check has been performed beforehand, like checkMove
|
|
||||||
## does)
|
|
||||||
case move.piece.kind:
|
|
||||||
of Pawn:
|
|
||||||
return move in self.generatePawnMoves(move.startSquare)
|
|
||||||
of Bishop, Queen, Rook:
|
|
||||||
return move in self.generateSlidingMoves(move.startSquare)
|
|
||||||
of Knight:
|
|
||||||
# TODO
|
|
||||||
discard
|
|
||||||
of King:
|
|
||||||
# TODO
|
|
||||||
discard
|
|
||||||
else:
|
|
||||||
return false
|
|
||||||
|
|
||||||
|
|
||||||
proc getAttackers*(self: ChessBoard, square: string): seq[Piece] =
|
proc getAttackers*(self: ChessBoard, square: string): seq[Piece] =
|
||||||
## Returns all the attackers of the given square
|
## Returns all the attackers of the given square
|
||||||
let loc = square.algebraicToPosition()
|
let loc = square.algebraicToPosition()
|
||||||
|
@ -573,12 +583,31 @@ proc getAttackers*(self: ChessBoard, square: string): seq[Piece] =
|
||||||
if move.targetSquare == loc:
|
if move.targetSquare == loc:
|
||||||
result.add(move.piece)
|
result.add(move.piece)
|
||||||
|
|
||||||
|
|
||||||
|
proc getAttackersFor*(self: ChessBoard, square: string, color: PieceColor): seq[Piece] =
|
||||||
|
## Returns all the attackers of the given square
|
||||||
|
## for the given color
|
||||||
|
let loc = square.algebraicToPosition()
|
||||||
|
case color:
|
||||||
|
of White:
|
||||||
|
for move in self.attacked.black:
|
||||||
|
if move.targetSquare == loc:
|
||||||
|
result.add(move.piece)
|
||||||
|
of Black:
|
||||||
|
for move in self.attacked.white:
|
||||||
|
if move.targetSquare == loc:
|
||||||
|
result.add(move.piece)
|
||||||
|
else:
|
||||||
|
discard
|
||||||
|
|
||||||
# We don't use getAttackers because this one only cares about whether
|
# We don't use getAttackers because this one only cares about whether
|
||||||
# the square is attacked or not (and can therefore exit earlier than
|
# the square is attacked or not (and can therefore exit earlier than
|
||||||
# getAttackers)
|
# getAttackers)
|
||||||
proc isAttacked*(self: ChessBoard, loc: Location): bool =
|
proc isAttacked*(self: ChessBoard, loc: Location): bool =
|
||||||
## Returns whether the given location is attacked
|
## Returns whether the given location is attacked
|
||||||
## by the current opponent
|
## by the opponent. If the location is empty, this
|
||||||
|
## function returns true regardless of which color
|
||||||
|
## the attackers are
|
||||||
let piece = self.grid[loc.row, loc.col]
|
let piece = self.grid[loc.row, loc.col]
|
||||||
case piece.color:
|
case piece.color:
|
||||||
of White:
|
of White:
|
||||||
|
@ -603,10 +632,9 @@ proc isAttacked*(self: ChessBoard, loc: Location): bool =
|
||||||
discard
|
discard
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
proc isAttacked*(self: ChessBoard, square: string): bool =
|
proc isAttacked*(self: ChessBoard, square: string): bool =
|
||||||
## Returns whether the given square is attacked
|
## Returns whether the given square is attacked
|
||||||
## by the current inactive color
|
## by its opponent
|
||||||
return self.isAttacked(square.algebraicToPosition())
|
return self.isAttacked(square.algebraicToPosition())
|
||||||
|
|
||||||
|
|
||||||
|
@ -623,38 +651,41 @@ proc updateAttackedSquares(self: ChessBoard) =
|
||||||
# Go over each piece one by one and see which squares
|
# Go over each piece one by one and see which squares
|
||||||
# it currently attacks
|
# it currently attacks
|
||||||
|
|
||||||
# White pawns
|
# Pawns
|
||||||
for loc in self.pieces.white.pawns:
|
for loc in self.pieces.white.pawns:
|
||||||
for move in self.generateMoves(loc):
|
for move in self.generateMoves(loc):
|
||||||
self.attacked.white.add(move)
|
self.attacked.white.add(move)
|
||||||
# Black pawns
|
# Bishops
|
||||||
for loc in self.pieces.black.pawns:
|
|
||||||
for move in self.generateMoves(loc):
|
|
||||||
self.attacked.black.add(move)
|
|
||||||
# White bishops
|
|
||||||
for loc in self.pieces.white.bishops:
|
for loc in self.pieces.white.bishops:
|
||||||
for move in self.generateMoves(loc):
|
for move in self.generateMoves(loc):
|
||||||
self.attacked.white.add(move)
|
self.attacked.white.add(move)
|
||||||
# Black bishops
|
# rooks
|
||||||
for loc in self.pieces.black.bishops:
|
|
||||||
for move in self.generateMoves(loc):
|
|
||||||
self.attacked.black.add(move)
|
|
||||||
# White rooks
|
|
||||||
for loc in self.pieces.white.rooks:
|
for loc in self.pieces.white.rooks:
|
||||||
for move in self.generateMoves(loc):
|
for move in self.generateMoves(loc):
|
||||||
self.attacked.white.add(move)
|
self.attacked.white.add(move)
|
||||||
# Black rooks
|
# Queens
|
||||||
for loc in self.pieces.black.rooks:
|
|
||||||
for move in self.generateMoves(loc):
|
|
||||||
self.attacked.black.add(move)
|
|
||||||
# White queens
|
|
||||||
for loc in self.pieces.white.queens:
|
for loc in self.pieces.white.queens:
|
||||||
for move in self.generateMoves(loc):
|
for move in self.generateMoves(loc):
|
||||||
self.attacked.white.add(move)
|
self.attacked.white.add(move)
|
||||||
# Black Queens
|
# King
|
||||||
|
for move in self.generateMoves(self.pieces.white.king):
|
||||||
|
self.attacked.white.add(move)
|
||||||
|
|
||||||
|
# Same for black
|
||||||
|
for loc in self.pieces.black.pawns:
|
||||||
|
for move in self.generateMoves(loc):
|
||||||
|
self.attacked.black.add(move)
|
||||||
|
for loc in self.pieces.black.bishops:
|
||||||
|
for move in self.generateMoves(loc):
|
||||||
|
self.attacked.black.add(move)
|
||||||
|
for loc in self.pieces.black.rooks:
|
||||||
|
for move in self.generateMoves(loc):
|
||||||
|
self.attacked.black.add(move)
|
||||||
for loc in self.pieces.black.queens:
|
for loc in self.pieces.black.queens:
|
||||||
for move in self.generateMoves(loc):
|
for move in self.generateMoves(loc):
|
||||||
self.attacked.black.add(move)
|
self.attacked.black.add(move)
|
||||||
|
for move in self.generateMoves(self.pieces.black.king):
|
||||||
|
self.attacked.black.add(move)
|
||||||
|
|
||||||
|
|
||||||
proc removePiece(self: ChessBoard, location: Location) =
|
proc removePiece(self: ChessBoard, location: Location) =
|
||||||
|
@ -763,18 +794,26 @@ proc updatePositions(self: ChessBoard, move: Move) =
|
||||||
self.grid[move.targetSquare.row, move.targetSquare.col] = move.piece
|
self.grid[move.targetSquare.row, move.targetSquare.col] = move.piece
|
||||||
|
|
||||||
|
|
||||||
proc inCheck(self: ChessBoard): bool =
|
proc inCheck*(self: ChessBoard, color: PieceColor = None): bool =
|
||||||
## Returns whether the active color's
|
## Returns whether the given color's
|
||||||
## king is in check
|
## king is in check. If the color is
|
||||||
case self.turn:
|
## set to None, checks are checked
|
||||||
|
## for the active color's king
|
||||||
|
case color:
|
||||||
of White:
|
of White:
|
||||||
return self.isAttacked(self.pieces.white.king)
|
return self.isAttacked(self.pieces.white.king)
|
||||||
of Black:
|
of Black:
|
||||||
return self.isAttacked(self.pieces.black.king)
|
return self.isAttacked(self.pieces.black.king)
|
||||||
else:
|
of None:
|
||||||
# Unreachable
|
case self.turn:
|
||||||
discard
|
of White:
|
||||||
|
return self.isAttacked(self.pieces.white.king)
|
||||||
|
of Black:
|
||||||
|
return self.isAttacked(self.pieces.black.king)
|
||||||
|
else:
|
||||||
|
# Unreachable
|
||||||
|
discard
|
||||||
|
|
||||||
|
|
||||||
proc doMove(self: ChessBoard, move: Move) =
|
proc doMove(self: ChessBoard, move: Move) =
|
||||||
## Internal function called by makeMove after
|
## Internal function called by makeMove after
|
||||||
|
@ -787,6 +826,8 @@ proc doMove(self: ChessBoard, move: Move) =
|
||||||
# pawn has moved
|
# pawn has moved
|
||||||
if self.enPassantSquare != emptyMove() and self.enPassantSquare.piece.color == self.turn.opposite():
|
if self.enPassantSquare != emptyMove() and self.enPassantSquare.piece.color == self.turn.opposite():
|
||||||
self.enPassantSquare = emptyMove()
|
self.enPassantSquare = emptyMove()
|
||||||
|
self.turn = self.turn.opposite()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
proc undoMove(self: ChessBoard, move: Move): Move {.discardable.} =
|
proc undoMove(self: ChessBoard, move: Move): Move {.discardable.} =
|
||||||
|
@ -813,18 +854,15 @@ proc checkMove(self: ChessBoard, move: Move): bool =
|
||||||
# being moved: illegal!
|
# being moved: illegal!
|
||||||
if destination.kind != Empty and destination.color == self.turn:
|
if destination.kind != Empty and destination.color == self.turn:
|
||||||
return false
|
return false
|
||||||
if not self.testMoveOffsets(move):
|
if move notin self.generateMoves(move.startSquare):
|
||||||
# Piece cannot arrive to destination (either
|
# Piece cannot arrive to destination (blocked,
|
||||||
# because it is blocked or because the moving
|
# pinned, or otherwise invalid move)
|
||||||
# pattern is incorrect)
|
|
||||||
return false
|
return false
|
||||||
echo move
|
|
||||||
self.doMove(move)
|
self.doMove(move)
|
||||||
defer: self.undoMove(move)
|
defer: self.undoMove(move)
|
||||||
# Move would reveal an attack
|
# Move would reveal an attack
|
||||||
# on our king: not allowed
|
# on our king: not allowed
|
||||||
if self.inCheck():
|
if self.inCheck(move.piece.color):
|
||||||
echo "in check"
|
|
||||||
return false
|
return false
|
||||||
# All checks have passed: move is legal
|
# All checks have passed: move is legal
|
||||||
result = true
|
result = true
|
||||||
|
@ -835,8 +873,13 @@ proc makeMove*(self: ChessBoard, move: Move): Move {.discardable.} =
|
||||||
result = move
|
result = move
|
||||||
if not self.checkMove(move):
|
if not self.checkMove(move):
|
||||||
return emptyMove()
|
return emptyMove()
|
||||||
|
# 50 move rule
|
||||||
|
if move.piece.kind != Pawn and not self.isCapture(move):
|
||||||
|
inc(self.halfMoveClock)
|
||||||
|
else:
|
||||||
|
self.halfMoveClock = 0
|
||||||
self.doMove(result)
|
self.doMove(result)
|
||||||
self.turn = self.turn.opposite()
|
|
||||||
|
|
||||||
|
|
||||||
proc makeMove*(self: ChessBoard, startSquare, targetSquare: string): Move {.discardable.} =
|
proc makeMove*(self: ChessBoard, startSquare, targetSquare: string): Move {.discardable.} =
|
||||||
|
|
|
@ -3,20 +3,35 @@ import std/strformat
|
||||||
import std/strutils
|
import std/strutils
|
||||||
|
|
||||||
|
|
||||||
var
|
|
||||||
board = newChessboardFromFEN("rnbqkbnr/8/8/8/8/8/8/RNBQKBNR w KQkq - 0 1")
|
|
||||||
startSquare: string
|
|
||||||
targetSquare: string
|
|
||||||
move: Move
|
|
||||||
|
|
||||||
while true:
|
|
||||||
echo board.pretty()
|
|
||||||
echo &"Turn: {board.getActiveColor()}"
|
when isMainModule:
|
||||||
stdout.write("From -> ")
|
setControlCHook(proc () {.noconv.} = echo ""; quit(0))
|
||||||
startSquare = readLine(stdin).strip(chars={'\0', ' '})
|
var
|
||||||
stdout.write("To -> ")
|
board = newChessboardFromFEN("rnbqkbnr/8/8/8/8/8/8/RNBQKBNR w KQkq - 0 1")
|
||||||
targetSquare = readLine(stdin)
|
startSquare: string
|
||||||
try:
|
targetSquare: string
|
||||||
move = board.makeMove(startSquare, targetSquare)
|
data: string
|
||||||
except ValueError:
|
move: Move
|
||||||
echo &"Error: {getCurrentExceptionMsg()}"
|
|
||||||
|
|
||||||
|
while true:
|
||||||
|
echo &"{board.pretty()}"
|
||||||
|
echo &"Turn: {board.getActiveColor()}"
|
||||||
|
if board.inCheck():
|
||||||
|
echo &"Check!"
|
||||||
|
stdout.write("Move (from, to) -> ")
|
||||||
|
try:
|
||||||
|
data = readLine(stdin).strip(chars={'\0', ' '})
|
||||||
|
except IOError:
|
||||||
|
echo ""
|
||||||
|
break
|
||||||
|
if len(data) != 4:
|
||||||
|
continue
|
||||||
|
startSquare = data[0..1]
|
||||||
|
targetSquare = data[2..3]
|
||||||
|
try:
|
||||||
|
move = board.makeMove(startSquare, targetSquare)
|
||||||
|
except ValueError:
|
||||||
|
echo &"Error: {getCurrentExceptionMsg()}"
|
||||||
|
|
Loading…
Reference in New Issue