@ -1,6 +1,5 @@
import tables
from strutils import parseInt
import algorithm
type
Color * = enum
@ -134,22 +133,6 @@ let
" h " : 0
} . newTable
proc setField ( board : var Board , field : int , val : int ) : bool {. discardable . } =
try :
if ( val in PieceChar ) :
board [ field ] = val
return true
return false
except Exception :
return false
proc setField ( moved : var Moved , field : int , val : bool ) : bool {. discardable . } =
try :
moved [ field ] = val
return true
except Exception :
return false
proc checkInsufficientMaterial ( board : Board ) : bool =
## Checks for combinations of pieces on a `board`, where no checkmate can be forced
var wp = 0
@ -252,7 +235,7 @@ proc initGame*(board: array[0..63, int], color: Color): Game =
var same_piece : bool
for ind in board . low .. board . high :
same_piece = ( board [ ind ] ! = compare [ ind ] )
moved . setField ( ind , same_piece )
moved [ ind ] = same_piece
let game = Game ( board : board , moved : moved ,
to_move : color , previousBoard : @ [ ] , previousCastleRights : @ [ ] ,
fiftyMoveCounter : 0 )
@ -324,204 +307,209 @@ proc genCastleRights(moved: Moved): CastleRights =
proc notationToMove * ( notation : string , color : Color ) : Move =
## Convert simplified algebraic chess `notation` to a move object, color of player is `color`.
try :
var move : Move
var start = fieldToInd ( notation [ 0 .. 1 ] )
var dest = fieldToInd ( notation [ 2 .. 3 ] )
move = getMove ( start , dest , color )
if ( len ( notation ) > 4 ) :
var promStr = $ notation [ 4 ]
var prom : int
case promStr :
of " Q " :
prom = WQueen * ord ( color )
of " R " :
prom = WRook * ord ( color )
of " B " :
prom = WBishop * ord ( color )
of " N " :
prom = WKnight * ord ( color )
move = getMove ( start , dest , prom , color )
return move
except IndexError :
var move : Move
return move
var move : Move
var start = fieldToInd ( notation [ 0 .. 1 ] )
var dest = fieldToInd ( notation [ 2 .. 3 ] )
move = getMove ( start , dest , color )
if ( len ( notation ) > 4 ) :
var promStr = $ notation [ 4 ]
var prom : int
case promStr :
of " Q " :
prom = WQueen * ord ( color )
of " R " :
prom = WRook * ord ( color )
of " B " :
prom = WBishop * ord ( color )
of " N " :
prom = WKnight * ord ( color )
move = getMove ( start , dest , prom , color )
return move
proc genBishopDests ( game : Game , field : int , color : Color ) : seq [ int ] =
## Generate possible destinations for a bishop with specific `color` located at index `field` of `game`.
## Returns a sequence of possible indices to move to.
try :
var res = newSeq [ int ] ( )
var dest : int
var target : int
for move in Bishop_Moves :
dest = field + move
target = game . board [ dest ]
while ( target ! = 999 and ( ord ( color ) * target < = 0 ) or target = =
WEnPassant or target = = - WEnPassant ) :
res . add ( dest )
if ( ord ( color ) * target < 0 and ord ( color ) * target > - WEnPassant ) :
break
dest = dest + move
target = game . board [ dest ]
return res
except IndexDefect :
if ( not field in game . board . low .. game . board . high ) :
return @ [ ]
var res = newSeq [ int ] ( )
var dest : int
var target : int
for move in Bishop_Moves :
dest = field + move
if ( not dest in game . board . low .. game . board . high ) :
continue
target = game . board [ dest ]
while ( target ! = 999 and ( ord ( color ) * target < = 0 ) or target = =
WEnPassant or target = = - WEnPassant ) :
res . add ( dest )
if ( ord ( color ) * target < 0 and ord ( color ) * target > - WEnPassant ) :
break
dest = dest + move
target = game . board [ dest ]
return res
proc genRookDests ( game : Game , field : int , color : Color ) : seq [ int ] =
## Generate possible destinations for a rook with specific `color` located at index `field` of `game`.
## Returns a sequence of possible indices to move to.
try :
var res = newSeq [ int ] ( )
var dest : int
var target : int
for move in Rook_Moves :
dest = field + move
target = game . board [ dest ]
while ( target ! = 999 and ( ord ( color ) * target < = 0 ) or target = =
WEnPassant or target = = - WEnPassant ) :
res . add ( dest )
if ( ord ( color ) * target < 0 and ord ( color ) * target > - WEnPassant ) :
break
dest = dest + move
target = game . board [ dest ]
return res
except IndexDefect :
if ( not field in game . board . low .. game . board . high ) :
return @ [ ]
var res = newSeq [ int ] ( )
var dest : int
var target : int
for move in Rook_Moves :
dest = field + move
if ( not dest in game . board . low .. game . board . high ) :
continue
target = game . board [ dest ]
while ( target ! = 999 and ( ord ( color ) * target < = 0 ) or target = =
WEnPassant or target = = - WEnPassant ) :
res . add ( dest )
if ( ord ( color ) * target < 0 and ord ( color ) * target > - WEnPassant ) :
break
dest = dest + move
target = game . board [ dest ]
return res
proc genQueenDests ( game : Game , field : int , color : Color ) : seq [ int ] =
## Generate possible destinations for a queen with specific `color` located at index `field` of `game`.
## Returns a sequence of possible indices to move to.
try :
var res = newSeq [ int ] ( )
var dest : int
var target : int
for move in Queen_Moves :
dest = field + move
target = game . board [ dest ]
while ( target ! = 999 and ( ord ( color ) * target < = 0 ) or target = =
WEnPassant or target = = - WEnPassant ) :
res . add ( dest )
if ( ord ( color ) * target < 0 and ord ( color ) * target > - WEnPassant ) :
break
dest = dest + move
target = game . board [ dest ]
return res
except IndexDefect :
if ( not field in game . board . low .. game . board . high ) :
return @ [ ]
var res = newSeq [ int ] ( )
var dest : int
var target : int
for move in Queen_Moves :
dest = field + move
if ( not dest in game . board . low .. game . board . high ) :
continue
target = game . board [ dest ]
while ( target ! = 999 and ( ord ( color ) * target < = 0 ) or target = =
WEnPassant or target = = - WEnPassant ) :
res . add ( dest )
if ( ord ( color ) * target < 0 and ord ( color ) * target > - WEnPassant ) :
break
dest = dest + move
target = game . board [ dest ]
return res
proc genKingCastleDest ( game : Game , field : int , color : Color ) : seq [ int ] =
## Generate possible castle destinations for a king with specific `color` located at index `field` of `game`
## Returns a sequence of possible indices to move to.
try :
var res = newSeq [ int ] ( )
var dest : int
var target : int
var half_dest : int
var half_target : int
for castle in King_Moves_White_Castle :
dest = field + castle
target = game . board [ dest ]
half_dest = field + ( int ) castle / 2
half_target = game . board [ half_dest ]
if ( target = = 999 or ( target ! = 0 ) ) :
continue
if ( half_target = = 999 or ( half_target ! = 0 ) ) :
continue
res . add ( dest )
return res
except IndexDefect :
if ( not field in game . board . low .. game . board . high ) :
return @ [ ]
var res = newSeq [ int ] ( )
var dest : int
var target : int
var half_dest : int
var half_target : int
for castle in King_Moves_White_Castle :
dest = field + castle
if ( not dest in game . board . low .. game . board . high ) :
continue
target = game . board [ dest ]
half_dest = field + ( int ) castle / 2
half_target = game . board [ half_dest ]
if ( target = = 999 or ( target ! = 0 ) ) :
continue
if ( half_target = = 999 or ( half_target ! = 0 ) ) :
continue
res . add ( dest )
return res
proc genKingDests ( game : Game , field : int , color : Color ) : seq [ int ] =
## Generate possible destinations for a king with specific `color` located at index `field` of `game`.
## Returns a sequence of possible indices to move to.
try :
var res = newSeq [ int ] ( )
var dest : int
var target : int
for move in King_Moves :
dest = field + move
target = game . board [ dest ]
if ( target = = 999 or ( ord ( color ) * target > 0 and ord ( color ) * target ! = WEnPassant ) ) :
continue
res . add ( dest )
res . add ( game . genKingCastleDest ( field , color ) )
return res
except IndexDefect :
if ( not field in game . board . low .. game . board . high ) :
return @ [ ]
var res = newSeq [ int ] ( )
var dest : int
var target : int
for move in King_Moves :
dest = field + move
if ( not dest in game . board . low .. game . board . high ) :
continue
target = game . board [ dest ]
if ( target = = 999 or ( ord ( color ) * target > 0 and ord ( color ) * target ! = WEnPassant ) ) :
continue
res . add ( dest )
res . add ( game . genKingCastleDest ( field , color ) )
return res
proc genKnightDests ( game : Game , field : int , color : Color ) : seq [ int ] =
## Generate possible destinations for a knight with specific `color` located at index `field` of `game`.
## Returns a sequence of possible indices to move to.
try :
var res = newSeq [ int ] ( )
var dest : int
var target : int
for move in Knight_Moves :
dest = field + move
target = game . board [ dest ]
if ( target = = 999 or ( ord ( color ) * target > 0 and ord ( color ) * target ! = WEnPassant ) ) :
continue
res . add ( dest )
return res
except IndexDefect :
if ( not field in game . board . low .. game . board . high ) :
return @ [ ]
var res = newSeq [ int ] ( )
var dest : int
var target : int
for move in Knight_Moves :
dest = field + move
if ( not dest in game . board . low .. game . board . high ) :
continue
target = game . board [ dest ]
if ( target = = 999 or ( ord ( color ) * target > 0 and ord ( color ) * target ! = WEnPassant ) ) :
continue
res . add ( dest )
return res
proc genPawnAttackDests ( game : Game , field : int , color : Color ) : seq [ int ] =
## Generate possible attack destinations for a pawn with specific `color` located at index `field` of `game`.
## Returns a sequence of possible indices to move to.
try :
var res = newSeq [ int ] ( )
var dest : int
var target : int
for attacks in Pawn_Moves_White_Attack :
dest = field + ( attacks * ord ( color ) )
target = game . board [ dest ]
if ( target = = 999 or ord ( color ) * target > = 0 ) :
continue
res . add ( dest )
return res
except IndexDefect :
if ( not field in game . board . low .. game . board . high ) :
return @ [ ]
var res = newSeq [ int ] ( )
var dest : int
var target : int
for attacks in Pawn_Moves_White_Attack :
dest = field + ( attacks * ord ( color ) )
if ( not dest in game . board . low .. game . board . high ) :
continue
target = game . board [ dest ]
if ( target = = 999 or ord ( color ) * target > = 0 ) :
continue
res . add ( dest )
return res
proc genPawnDoubleDests ( game : Game , field : int , color : Color ) : seq [ int ] =
## Generate possible double destinations for a pawn with specific `color` located at index `field` of `game`.
## Returns a sequence of possible indices to move to.
try :
var res = newSeq [ int ] ( )
var dest : int
var target : int
for doubles in Pawn_Moves_White_Double :
dest = field + doubles * ord ( color )
target = game . board [ dest ]
if ( game . moved [ field ] or ( target ! = 0 ) or (
game . board [ dest + ( S * ord ( color ) ) ] ! = 0 ) ) :
continue
res . add ( dest )
return res
except IndexDefect :
if ( not field in game . board . low .. game . board . high ) :
return @ [ ]
var res = newSeq [ int ] ( )
var dest : int
var target : int
for doubles in Pawn_Moves_White_Double :
dest = field + doubles * ord ( color )
if ( not dest in game . board . low .. game . board . high ) :
continue
target = game . board [ dest ]
if ( game . moved [ field ] or ( target ! = 0 ) or (
game . board [ dest + ( S * ord ( color ) ) ] ! = 0 ) ) :
continue
res . add ( dest )
return res
proc genPawnDests ( game : Game , field : int , color : Color ) : seq [ int ] =
## Generate possible destinations for a pawn with specific `color` located at index `field` of `game`.
## Returns a sequence of possible indices to move to.
try :
var res = newSeq [ int ] ( )
var dest : int
var target : int
for move in Pawn_Moves_White :
dest = field + move * ord ( color )
target = game . board [ dest ]
if ( target ! = 0 and target ! = ord ( color ) * WEnPassant ) :
continue
res . add ( dest )
res . add ( game . genPawnAttackDests ( field , color ) )
res . add ( game . genPawnDoubleDests ( field , color ) )
return res
except IndexDefect :
if ( not field in game . board . low .. game . board . high ) :
return @ [ ]
var res = newSeq [ int ] ( )
var dest : int
var target : int
for move in Pawn_Moves_White :
dest = field + move * ord ( color )
if ( not dest in game . board . low .. game . board . high ) :
continue
target = game . board [ dest ]
if ( target ! = 0 and target ! = ord ( color ) * WEnPassant ) :
continue
res . add ( dest )
res . add ( game . genPawnAttackDests ( field , color ) )
res . add ( game . genPawnDoubleDests ( field , color ) )
return res
proc pieceOn ( game : Game , color : Color , sequence : seq [ int ] ,
pieceID : int ) : bool =
@ -560,17 +548,12 @@ proc uncheckedMove(game: var Game, start: int, dest: int): bool {.discardable.}
## Moves a piece if possible from `start` position to `dest` position.
## Doesnt check boundaries, checks, movement.
## returns true if the piece moved, else false
try :
let piece = game . board [ start ]
if game . board . setField ( start , 0 ) :
if game . board . setField ( dest , piece ) :
game . moved . setField ( start , true )
game . moved . setField ( dest , true )
return true
else :
game . board . setField ( start , piece )
except IndexDefect , ValueError :
return false
let piece = game . board [ start ]
game . board [ start ] = 0
game . board [ dest ] = piece
game . moved [ start ] = true
game . moved [ dest ] = true
return true
proc moveLeadsToCheck ( game : Game , start : int , dest : int ,
color : Color ) : bool =
@ -583,7 +566,7 @@ proc removeEnPassant(board: var Board, color: Color): void =
## Removes every en passant of given `color` from the `game`.
for field in board . low .. board . high :
if board [ field ] = = ord ( color ) * WEnPassant :
board . setField ( field , 0 )
board [ field ] = 0
proc genLegalKnightMoves ( game : Game , field : int , color : Color ) : seq [ Move ] =
## Generates all legal knight moves starting from `field` in a `game` for a `color`.
@ -703,39 +686,36 @@ proc castling(game: var Game, kstart: int, dest_kingside: bool,
## Tries to castle in a given `game` with the king of a given `color` from `start`.
## `dest_kingside` for kingside castling, else castling is queenside.
## This process checks for the legality of the move and performs the switch of `game.to_move`
try :
if game . toMove ! = color :
return false
var kdest = kstart
var rstart : int
var rdest : int
if game . toMove ! = color :
return false
var kdest = kstart
var rstart : int
var rdest : int
if ( dest_kingside ) :
kdest = kstart + ( E + E )
rstart = kstart + ( E + E + E )
rdest = rstart + ( W + W )
else :
rstart = kstart + ( W + W + W + W )
rdest = rstart + ( E + E + E )
kdest = kstart + ( W + W )
if not game . moved [ kstart ] and not game . moved [ rstart ] :
var check = false
if ( dest_kingside ) :
kdest = kstart + ( E + E )
rstart = kstart + ( E + E + E )
rdest = rstart + ( W + W )
check = check or game . isAttacked ( kstart , color )
check = check or game . isAttacked ( kstart + ( E ) , color )
check = check or game . isAttacked ( kstart + ( E + E ) , color )
else :
rstart = kstart + ( W + W + W + W )
rdest = rstart + ( E + E + E )
kdest = kstart + ( W + W )
if not game . moved [ kstart ] and not game . moved [ rstart ] :
var check = false
if ( dest_kingside ) :
check = check or game . isAttacked ( kstart , color )
check = check or game . isAttacked ( kstart + ( E ) , color )
check = check or game . isAttacked ( kstart + ( E + E ) , color )
else :
check = check or game . isAttacked ( kstart , color )
check = check or game . isAttacked ( kstart + ( W ) , color )
check = check or game . isAttacked ( kstart + ( W + W ) , color )
if check :
return false
game . uncheckedMove ( kstart , kdest )
game . uncheckedMove ( rstart , rdest )
game . toMove = Color ( ord ( game . toMove ) * ( - 1 ) )
return true
return false
except IndexDefect , ValueError :
return false
check = check or game . isAttacked ( kstart , color )
check = check or game . isAttacked ( kstart + ( W ) , color )
check = check or game . isAttacked ( kstart + ( W + W ) , color )
if check :
return false
game . uncheckedMove ( kstart , kdest )
game . uncheckedMove ( rstart , rdest )
game . toMove = Color ( ord ( game . toMove ) * ( - 1 ) )
return true
return false
proc getPrevBoard * ( game : Game ) : seq [ Board ] =
return game . previousBoard
@ -743,53 +723,50 @@ proc getPrevBoard*(game: Game): seq[Board] =
proc checkedMove * ( game : var Game , move : Move ) : bool {. discardable . } =
## Tries to make a move in a given `game` with the piece of a given `color` from `start` to `dest`.
## This process checks for the legality of the move and performs the switch of `game.to_move`
try :
let start = move . start
let dest = move . dest
let color = move . color
let prom = move . prom
if game . toMove ! = color :
return false
var sequence = newSeq [ Move ] ( )
let piece = game . board [ start ]
var createEnPassant = false
var capturedEnPassant = false
var fiftyMoveRuleReset = false
var move : Move
move = getMove ( start , dest , color )
if ( piece = = WPawn * ord ( color ) ) :
createEnPassant = dest in game . genPawnDoubleDests ( start , color )
capturedEnPassant = ( game . board [ dest ] = = - 1 * ord ( color ) * WEnPassant )
fiftyMoveRuleReset = true
if ( game . board [ move . dest ] ! = 0 ) :
fiftyMoveRuleReset = true
sequence . add ( game . genLegalMoves ( start , color ) )
if ( move in sequence ) :
game . board . removeEnPassant ( color )
if ( piece = = WKing * ord ( color ) and ( start - dest = = ( W + W ) ) ) :
return game . castling ( start , true , color )
elif ( piece = = WKing * ord ( color ) and ( start - dest = = ( E + E ) ) ) :
return game . castling ( start , false , color )
else :
game . uncheckedMove ( start , dest )
game . toMove = Color ( ord ( game . toMove ) * ( - 1 ) )
if createEnPassant :
game . board . setField ( dest - ( N * ord ( color ) ) , WEnPassant * ord ( color ) )
if capturedEnPassant :
game . board . setField ( dest - ( N * ord ( color ) ) , 0 )
if ( ( 90 < dest and dest < 99 ) or ( 20 < dest and dest < 29 ) ) and
game . board [ dest ] = = WPawn * ord ( color ) :
game . board . setField ( dest , prom )
var prevBoard = game . previousBoard
var prevCastle = game . previousCastleRights
game . previousBoard . add ( game . board )
game . previousCastleRights . add ( game . moved . genCastleRights ( ) )
game . fiftyMoveCounter = game . fiftyMoveCounter + 1
if fiftyMoveRuleReset :
game . fiftyMoveCounter = 0
return true
except IndexDefect , ValueError :
let start = move . start
let dest = move . dest
let color = move . color
let prom = move . prom
if ( game . toMove ! = color or start = = - 1 or dest = = - 1 ) :
return false
var sequence = newSeq [ Move ] ( )
let piece = game . board [ start ]
var createEnPassant = false
var capturedEnPassant = false
var fiftyMoveRuleReset = false
var move : Move
move = getMove ( start , dest , color )
if ( piece = = WPawn * ord ( color ) ) :
createEnPassant = dest in game . genPawnDoubleDests ( start , color )
capturedEnPassant = ( game . board [ dest ] = = - 1 * ord ( color ) * WEnPassant )
fiftyMoveRuleReset = true
if ( game . board [ move . dest ] ! = 0 ) :
fiftyMoveRuleReset = true
sequence . add ( game . genLegalMoves ( start , color ) )
if ( move in sequence ) :
game . board . removeEnPassant ( color )
if ( piece = = WKing * ord ( color ) and ( start - dest = = ( W + W ) ) ) :
return game . castling ( start , true , color )
elif ( piece = = WKing * ord ( color ) and ( start - dest = = ( E + E ) ) ) :
return game . castling ( start , false , color )
else :
game . uncheckedMove ( start , dest )
game . toMove = Color ( ord ( game . toMove ) * ( - 1 ) )
if createEnPassant :
game . board [ dest - ( N * ord ( color ) ) ] = WEnPassant * ord ( color )
if capturedEnPassant :
game . board [ dest - ( N * ord ( color ) ) ] = 0
if ( ( 90 < dest and dest < 99 ) or ( 20 < dest and dest < 29 ) ) and
game . board [ dest ] = = WPawn * ord ( color ) :
game . board [ dest ] = prom
var prevBoard = game . previousBoard
var prevCastle = game . previousCastleRights
game . previousBoard . add ( game . board )
game . previousCastleRights . add ( game . moved . genCastleRights ( ) )
game . fiftyMoveCounter = game . fiftyMoveCounter + 1
if fiftyMoveRuleReset :
game . fiftyMoveCounter = 0
return true
proc hasNoMoves ( game : Game , color : Color ) : bool =
## Checks if a player of a given `color` has no legal moves in a `game`.