Fix diagonal checks for black
This commit is contained in:
parent
17f15e682c
commit
e782935fd7
|
@ -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 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 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))
|
||||
|
@ -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 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))
|
||||
proc testMoveOffsets(self: ChessBoard, move: Move): bool
|
||||
|
||||
|
||||
proc getActiveColor*(self: ChessBoard): PieceColor =
|
||||
|
@ -519,14 +518,48 @@ proc generateSlidingMoves(self: ChessBoard, location: Location): seq[Move] =
|
|||
if otherPiece.color == piece.color:
|
||||
break
|
||||
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))
|
||||
break
|
||||
# Target square is empty
|
||||
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] =
|
||||
## Returns the list of possible moves for the
|
||||
## Returns the list of possible legal chess moves for the
|
||||
## piece in the given location
|
||||
let piece = self.grid[location.row, location.col]
|
||||
case piece.kind:
|
||||
|
@ -534,35 +567,12 @@ proc generateMoves(self: ChessBoard, location: Location): seq[Move] =
|
|||
return self.generateSlidingMoves(location)
|
||||
of Pawn:
|
||||
return self.generatePawnMoves(location)
|
||||
of King:
|
||||
return self.generateKingMoves(location)
|
||||
else:
|
||||
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] =
|
||||
## Returns all the attackers of the given square
|
||||
let loc = square.algebraicToPosition()
|
||||
|
@ -573,12 +583,31 @@ proc getAttackers*(self: ChessBoard, square: string): seq[Piece] =
|
|||
if move.targetSquare == loc:
|
||||
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
|
||||
# the square is attacked or not (and can therefore exit earlier than
|
||||
# getAttackers)
|
||||
proc isAttacked*(self: ChessBoard, loc: Location): bool =
|
||||
## 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]
|
||||
case piece.color:
|
||||
of White:
|
||||
|
@ -603,10 +632,9 @@ proc isAttacked*(self: ChessBoard, loc: Location): bool =
|
|||
discard
|
||||
|
||||
|
||||
|
||||
proc isAttacked*(self: ChessBoard, square: string): bool =
|
||||
## Returns whether the given square is attacked
|
||||
## by the current inactive color
|
||||
## by its opponent
|
||||
return self.isAttacked(square.algebraicToPosition())
|
||||
|
||||
|
||||
|
@ -623,38 +651,41 @@ proc updateAttackedSquares(self: ChessBoard) =
|
|||
# Go over each piece one by one and see which squares
|
||||
# it currently attacks
|
||||
|
||||
# White pawns
|
||||
# Pawns
|
||||
for loc in self.pieces.white.pawns:
|
||||
for move in self.generateMoves(loc):
|
||||
self.attacked.white.add(move)
|
||||
# Black pawns
|
||||
for loc in self.pieces.black.pawns:
|
||||
for move in self.generateMoves(loc):
|
||||
self.attacked.black.add(move)
|
||||
# White bishops
|
||||
# Bishops
|
||||
for loc in self.pieces.white.bishops:
|
||||
for move in self.generateMoves(loc):
|
||||
self.attacked.white.add(move)
|
||||
# Black bishops
|
||||
for loc in self.pieces.black.bishops:
|
||||
for move in self.generateMoves(loc):
|
||||
self.attacked.black.add(move)
|
||||
# White rooks
|
||||
# rooks
|
||||
for loc in self.pieces.white.rooks:
|
||||
for move in self.generateMoves(loc):
|
||||
self.attacked.white.add(move)
|
||||
# Black rooks
|
||||
for loc in self.pieces.black.rooks:
|
||||
for move in self.generateMoves(loc):
|
||||
self.attacked.black.add(move)
|
||||
# White queens
|
||||
# Queens
|
||||
for loc in self.pieces.white.queens:
|
||||
for move in self.generateMoves(loc):
|
||||
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 move in self.generateMoves(loc):
|
||||
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) =
|
||||
|
@ -763,18 +794,26 @@ proc updatePositions(self: ChessBoard, move: Move) =
|
|||
self.grid[move.targetSquare.row, move.targetSquare.col] = move.piece
|
||||
|
||||
|
||||
proc inCheck(self: ChessBoard): bool =
|
||||
## Returns whether the active color's
|
||||
## king is in check
|
||||
case self.turn:
|
||||
proc inCheck*(self: ChessBoard, color: PieceColor = None): bool =
|
||||
## Returns whether the given color's
|
||||
## king is in check. If the color is
|
||||
## set to None, checks are checked
|
||||
## for the active color's king
|
||||
case color:
|
||||
of White:
|
||||
return self.isAttacked(self.pieces.white.king)
|
||||
of Black:
|
||||
return self.isAttacked(self.pieces.black.king)
|
||||
else:
|
||||
# Unreachable
|
||||
discard
|
||||
|
||||
of None:
|
||||
case self.turn:
|
||||
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) =
|
||||
## Internal function called by makeMove after
|
||||
|
@ -787,6 +826,8 @@ proc doMove(self: ChessBoard, move: Move) =
|
|||
# pawn has moved
|
||||
if self.enPassantSquare != emptyMove() and self.enPassantSquare.piece.color == self.turn.opposite():
|
||||
self.enPassantSquare = emptyMove()
|
||||
self.turn = self.turn.opposite()
|
||||
|
||||
|
||||
|
||||
proc undoMove(self: ChessBoard, move: Move): Move {.discardable.} =
|
||||
|
@ -813,18 +854,15 @@ proc checkMove(self: ChessBoard, move: Move): bool =
|
|||
# being moved: illegal!
|
||||
if destination.kind != Empty and destination.color == self.turn:
|
||||
return false
|
||||
if not self.testMoveOffsets(move):
|
||||
# Piece cannot arrive to destination (either
|
||||
# because it is blocked or because the moving
|
||||
# pattern is incorrect)
|
||||
if move notin self.generateMoves(move.startSquare):
|
||||
# Piece cannot arrive to destination (blocked,
|
||||
# pinned, or otherwise invalid move)
|
||||
return false
|
||||
echo move
|
||||
self.doMove(move)
|
||||
defer: self.undoMove(move)
|
||||
# Move would reveal an attack
|
||||
# on our king: not allowed
|
||||
if self.inCheck():
|
||||
echo "in check"
|
||||
if self.inCheck(move.piece.color):
|
||||
return false
|
||||
# All checks have passed: move is legal
|
||||
result = true
|
||||
|
@ -835,8 +873,13 @@ proc makeMove*(self: ChessBoard, move: Move): Move {.discardable.} =
|
|||
result = move
|
||||
if not self.checkMove(move):
|
||||
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.turn = self.turn.opposite()
|
||||
|
||||
|
||||
|
||||
proc makeMove*(self: ChessBoard, startSquare, targetSquare: string): Move {.discardable.} =
|
||||
|
|
|
@ -3,20 +3,35 @@ import std/strformat
|
|||
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()}"
|
||||
stdout.write("From -> ")
|
||||
startSquare = readLine(stdin).strip(chars={'\0', ' '})
|
||||
stdout.write("To -> ")
|
||||
targetSquare = readLine(stdin)
|
||||
try:
|
||||
move = board.makeMove(startSquare, targetSquare)
|
||||
except ValueError:
|
||||
echo &"Error: {getCurrentExceptionMsg()}"
|
||||
|
||||
|
||||
when isMainModule:
|
||||
setControlCHook(proc () {.noconv.} = echo ""; quit(0))
|
||||
var
|
||||
board = newChessboardFromFEN("rnbqkbnr/8/8/8/8/8/8/RNBQKBNR w KQkq - 0 1")
|
||||
startSquare: string
|
||||
targetSquare: string
|
||||
data: string
|
||||
move: Move
|
||||
|
||||
|
||||
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