More bug fixing
This commit is contained in:
parent
f65d426ccf
commit
89a96eaf52
|
@ -315,6 +315,8 @@ proc newChessboard: ChessBoard =
|
|||
func coordToIndex(row, col: int): int {.inline.} = (row * 8) + col
|
||||
func `[]`(self: seq[Piece], row, column: Natural): Piece {.inline.} = self[coordToIndex(row, column)]
|
||||
proc `[]=`(self: var seq[Piece], row, column: Natural, piece: Piece) {.inline.} = self[coordToIndex(row, column)] = piece
|
||||
func `[]`(self: seq[Piece], loc: Location): Piece {.inline.} = self[loc.row, loc.col]
|
||||
proc `[]=`(self: var seq[Piece], loc: Location, piece: Piece) {.inline.} = self[loc.row, loc.col] = piece
|
||||
|
||||
|
||||
proc newChessboardFromFEN*(fen: string): ChessBoard =
|
||||
|
@ -718,6 +720,15 @@ proc canCastle*(self: ChessBoard, color: PieceColor = None): tuple[queen, king:
|
|||
# Unreachable
|
||||
discard
|
||||
|
||||
# We only need to check for attacked squares up until
|
||||
# the point where the king arrives on the target castling
|
||||
# square, but we _also_ need to make sure the path is free
|
||||
# of obstacles for the rook to move past the king. This variable
|
||||
# becomes false once the king has arrived on its target square so
|
||||
# that we don't prevent castling when it would otherwise be allowed
|
||||
# (for an example see r3k2r/p1pNqpb1/bn2pnp1/3P4/1p2P3/2N2Q1p/PPPBBPPP/R3K2R b KQkq - 0 1)
|
||||
var checkAttacks = true
|
||||
|
||||
if result.king:
|
||||
# Short castle
|
||||
var
|
||||
|
@ -734,12 +745,19 @@ proc canCastle*(self: ChessBoard, color: PieceColor = None): tuple[queen, king:
|
|||
if otherPiece.color != None:
|
||||
result.king = false
|
||||
break
|
||||
if self.isAttacked(location, color.opposite()):
|
||||
|
||||
if checkAttacks and self.isAttacked(location, color.opposite()):
|
||||
result.king = false
|
||||
break
|
||||
|
||||
# King has arrived at the target square: we no longer
|
||||
# need to check whether subsequent squares are free from
|
||||
# attacks
|
||||
if location == shortCastleKing() + loc:
|
||||
checkAttacks = false
|
||||
|
||||
if result.queen:
|
||||
checkAttacks = true
|
||||
# Long castle
|
||||
var
|
||||
location = loc
|
||||
|
@ -755,10 +773,14 @@ proc canCastle*(self: ChessBoard, color: PieceColor = None): tuple[queen, king:
|
|||
if otherPiece.color != None:
|
||||
result.queen = false
|
||||
break
|
||||
if self.isAttacked(location, color.opposite()):
|
||||
|
||||
if checkAttacks and self.isAttacked(location, color.opposite()):
|
||||
result.queen = false
|
||||
break
|
||||
|
||||
if location == longCastleKing() + loc:
|
||||
checkAttacks = false
|
||||
|
||||
|
||||
proc getCheckResolutions(self: ChessBoard, color: PieceColor): seq[Location] =
|
||||
## Returns the squares that need to be covered to
|
||||
|
@ -803,31 +825,32 @@ proc generatePawnMoves(self: ChessBoard, location: Location): seq[Move] =
|
|||
## location
|
||||
var
|
||||
piece = self.grid[location.row, location.col]
|
||||
targets: seq[Location] = @[]
|
||||
directions: seq[Location] = @[]
|
||||
doAssert piece.kind == Pawn, &"generatePawnMoves called on a {piece.kind}"
|
||||
# Pawns can move forward one square
|
||||
let forward = piece.color.topSide() + location
|
||||
let forward = location + piece.color.topSide()
|
||||
# Only if the square is empty though
|
||||
if forward.isValid() and self.grid[forward.row, forward.col].color == None:
|
||||
targets.add(forward)
|
||||
if forward.isValid() and self.grid[forward].color == None:
|
||||
directions.add(piece.color.topSide())
|
||||
# If the pawn is on its first rank, it can push two squares
|
||||
if location.row == piece.getStartRow():
|
||||
let double = location + piece.color.doublePush()
|
||||
# Check that both squares are empty
|
||||
if double.isValid() and self.grid[forward.row, forward.col].color == None and self.grid[double.row, double.col].color == None:
|
||||
targets.add(double)
|
||||
if double.isValid() and self.grid[forward].color == None and self.grid[double].color == None:
|
||||
directions.add(piece.color.doublePush())
|
||||
let enPassantPawn = self.getEnPassantTarget() + piece.color.opposite().topSide()
|
||||
# They can also move one square on either of their
|
||||
# forward diagonals, but only for captures and en passant
|
||||
for diagonal in [location + piece.color.topRightDiagonal(), location + piece.color.topLeftDiagonal()]:
|
||||
if diagonal.isValid():
|
||||
let otherPiece = self.grid[diagonal.row, diagonal.col]
|
||||
if diagonal == self.position.enPassantSquare and self.grid[enPassantPawn.row, enPassantPawn.col].color == piece.color.opposite():
|
||||
for diagonal in [piece.color.topRightDiagonal(), piece.color.topLeftDiagonal()]:
|
||||
let target = location + diagonal
|
||||
if target.isValid():
|
||||
let otherPiece = self.grid[target]
|
||||
if target == self.position.enPassantSquare and self.grid[enPassantPawn].color == piece.color.opposite():
|
||||
# Ensure en passant doesn't create a check
|
||||
let king = self.getKing(piece.color)
|
||||
var ok = true
|
||||
if king.row == location.row:
|
||||
var current = location + piece.color.rightSide()
|
||||
var current = location
|
||||
while true:
|
||||
current = current + piece.color.rightSide()
|
||||
if not current.isValid():
|
||||
|
@ -837,28 +860,30 @@ proc generatePawnMoves(self: ChessBoard, location: Location): seq[Move] =
|
|||
break
|
||||
if p.color == None:
|
||||
continue
|
||||
# Bishops can't create checks through en passant
|
||||
# Bishops can't create checks through en passant (neither can knights)
|
||||
if p.color == piece.color.opposite() and p.kind in [Queen, Rook]:
|
||||
ok = false
|
||||
break
|
||||
if ok:
|
||||
targets.add(diagonal)
|
||||
elif otherPiece.color == piece.color.opposite() and otherPiece.kind != King:
|
||||
targets.add(diagonal)
|
||||
directions.add(diagonal)
|
||||
elif otherPiece.color == piece.color.opposite() and otherPiece.kind != King: # Can't capture the king!
|
||||
directions.add(diagonal)
|
||||
# Check for pins
|
||||
let pinned = self.getPinnedDirections(location)
|
||||
if pinned.len() > 0:
|
||||
var newTargets: seq[Location] = @[]
|
||||
for target in targets:
|
||||
if target in pinned:
|
||||
newTargets.add(target)
|
||||
targets = newTargets
|
||||
var newDirections: seq[Location] = @[]
|
||||
for direction in directions:
|
||||
if direction in pinned:
|
||||
newDirections.add(direction)
|
||||
directions = newDirections
|
||||
let checked = self.inCheck()
|
||||
let resolutions = if not checked: @[] else: self.getCheckResolutions(piece.color)
|
||||
var targetPiece: Piece
|
||||
for target in targets:
|
||||
for direction in directions:
|
||||
let target = location + direction
|
||||
if checked and target notin resolutions:
|
||||
continue
|
||||
targetPiece = self.grid[target.row, target.col]
|
||||
targetPiece = self.grid[target]
|
||||
var flags: uint16 = Default.uint16
|
||||
if targetPiece.color != None:
|
||||
flags = flags or Capture.uint16
|
||||
|
@ -1557,6 +1582,7 @@ proc doMove(self: ChessBoard, move: Move) =
|
|||
self.spawnPiece(move.targetSquare, Piece(kind: Queen, color: piece.color))
|
||||
else:
|
||||
discard
|
||||
self.updateAttackedSquares()
|
||||
|
||||
if move.isCapture():
|
||||
# Get rid of captured pieces
|
||||
|
@ -2079,15 +2105,11 @@ proc handlePositionCommand(board: var ChessBoard, command: seq[string]) =
|
|||
echo board.pretty()
|
||||
|
||||
|
||||
proc handleUCICommand(board: var ChessBoard, command: seq[string]): bool =
|
||||
if len(command) != 1:
|
||||
echo "error: uci: invalid number of arguments"
|
||||
return false
|
||||
proc handleUCICommand(board: var ChessBoard, command: seq[string]) =
|
||||
echo "id name Nimfish 0.1"
|
||||
echo "id author Nocturn9x & Contributors (see LICENSE)"
|
||||
# TODO
|
||||
echo "uciok"
|
||||
return true
|
||||
|
||||
|
||||
const HELP_TEXT = """Nimfish help menu:
|
||||
|
@ -2117,11 +2139,12 @@ const HELP_TEXT = """Nimfish help menu:
|
|||
- move <move>: Perform the given move in algebraic notation
|
||||
- castle: Print castling rights for each side
|
||||
- check: Print if the current side to move is in check
|
||||
- undo: Undoes the last move that was performed. Can be used in succession
|
||||
- undo, u: Undoes the last move that was performed. Can be used in succession
|
||||
- turn: Print which side is to move
|
||||
- ep: Print the current en passant target
|
||||
- pretty: Shorthand for "position pretty"
|
||||
- print: Shorthand for "position print"
|
||||
- fen: Shorthand for "position fen" (print only!)
|
||||
- get <square>: Get the piece on the given square
|
||||
- uci: enter UCI mode (WIP)
|
||||
"""
|
||||
|
@ -2148,8 +2171,8 @@ proc main: int =
|
|||
|
||||
case cmd[0]:
|
||||
of "uci":
|
||||
if handleUCICommand(board, cmd):
|
||||
uciMode = true
|
||||
handleUCICommand(board, cmd)
|
||||
uciMode = true
|
||||
of "clear":
|
||||
echo "\x1Bc"
|
||||
of "help":
|
||||
|
@ -2160,9 +2183,9 @@ proc main: int =
|
|||
handlePositionCommand(board, cmd)
|
||||
of "move":
|
||||
handleMoveCommand(board, cmd)
|
||||
of "pretty", "print":
|
||||
of "pretty", "print", "fen":
|
||||
handlePositionCommand(board, @["position", cmd[0]])
|
||||
of "undo":
|
||||
of "undo", "u":
|
||||
board.undoLastMove()
|
||||
of "turn":
|
||||
echo &"Active color: {board.getActiveColor()}"
|
||||
|
|
Loading…
Reference in New Issue