@ -1,6 +1,5 @@
import tables
import tables
from strutils import parseInt
from strutils import parseInt
import algorithm
type
type
Color * = enum
Color * = enum
@ -134,22 +133,6 @@ let
" h " : 0
" h " : 0
} . newTable
} . 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 =
proc checkInsufficientMaterial ( board : Board ) : bool =
## Checks for combinations of pieces on a `board`, where no checkmate can be forced
## Checks for combinations of pieces on a `board`, where no checkmate can be forced
var wp = 0
var wp = 0
@ -252,7 +235,7 @@ proc initGame*(board: array[0..63, int], color: Color): Game =
var same_piece : bool
var same_piece : bool
for ind in board . low .. board . high :
for ind in board . low .. board . high :
same_piece = ( board [ ind ] ! = compare [ ind ] )
same_piece = ( board [ ind ] ! = compare [ ind ] )
moved . setField ( ind , same_piece )
moved [ ind ] = same_piece
let game = Game ( board : board , moved : moved ,
let game = Game ( board : board , moved : moved ,
to_move : color , previousBoard : @ [ ] , previousCastleRights : @ [ ] ,
to_move : color , previousBoard : @ [ ] , previousCastleRights : @ [ ] ,
fiftyMoveCounter : 0 )
fiftyMoveCounter : 0 )
@ -324,7 +307,6 @@ proc genCastleRights(moved: Moved): CastleRights =
proc notationToMove * ( notation : string , color : Color ) : Move =
proc notationToMove * ( notation : string , color : Color ) : Move =
## Convert simplified algebraic chess `notation` to a move object, color of player is `color`.
## Convert simplified algebraic chess `notation` to a move object, color of player is `color`.
try :
var move : Move
var move : Move
var start = fieldToInd ( notation [ 0 .. 1 ] )
var start = fieldToInd ( notation [ 0 .. 1 ] )
var dest = fieldToInd ( notation [ 2 .. 3 ] )
var dest = fieldToInd ( notation [ 2 .. 3 ] )
@ -343,19 +325,19 @@ proc notationToMove*(notation: string, color: Color): Move =
prom = WKnight * ord ( color )
prom = WKnight * ord ( color )
move = getMove ( start , dest , prom , color )
move = getMove ( start , dest , prom , color )
return move
return move
except IndexError :
var move : Move
return move
proc genBishopDests ( game : Game , field : int , color : Color ) : seq [ int ] =
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`.
## Generate possible destinations for a bishop with specific `color` located at index `field` of `game`.
## Returns a sequence of possible indices to move to.
## Returns a sequence of possible indices to move to.
try :
if ( not field in game . board . low .. game . board . high ) :
return @ [ ]
var res = newSeq [ int ] ( )
var res = newSeq [ int ] ( )
var dest : int
var dest : int
var target : int
var target : int
for move in Bishop_Moves :
for move in Bishop_Moves :
dest = field + move
dest = field + move
if ( not dest in game . board . low .. game . board . high ) :
continue
target = game . board [ dest ]
target = game . board [ dest ]
while ( target ! = 999 and ( ord ( color ) * target < = 0 ) or target = =
while ( target ! = 999 and ( ord ( color ) * target < = 0 ) or target = =
WEnPassant or target = = - WEnPassant ) :
WEnPassant or target = = - WEnPassant ) :
@ -365,18 +347,19 @@ proc genBishopDests(game: Game, field: int, color: Color): seq[int] =
dest = dest + move
dest = dest + move
target = game . board [ dest ]
target = game . board [ dest ]
return res
return res
except IndexDefect :
return @ [ ]
proc genRookDests ( game : Game , field : int , color : Color ) : seq [ int ] =
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`.
## Generate possible destinations for a rook with specific `color` located at index `field` of `game`.
## Returns a sequence of possible indices to move to.
## Returns a sequence of possible indices to move to.
try :
if ( not field in game . board . low .. game . board . high ) :
return @ [ ]
var res = newSeq [ int ] ( )
var res = newSeq [ int ] ( )
var dest : int
var dest : int
var target : int
var target : int
for move in Rook_Moves :
for move in Rook_Moves :
dest = field + move
dest = field + move
if ( not dest in game . board . low .. game . board . high ) :
continue
target = game . board [ dest ]
target = game . board [ dest ]
while ( target ! = 999 and ( ord ( color ) * target < = 0 ) or target = =
while ( target ! = 999 and ( ord ( color ) * target < = 0 ) or target = =
WEnPassant or target = = - WEnPassant ) :
WEnPassant or target = = - WEnPassant ) :
@ -386,18 +369,19 @@ proc genRookDests(game: Game, field: int, color: Color): seq[int] =
dest = dest + move
dest = dest + move
target = game . board [ dest ]
target = game . board [ dest ]
return res
return res
except IndexDefect :
return @ [ ]
proc genQueenDests ( game : Game , field : int , color : Color ) : seq [ int ] =
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`.
## Generate possible destinations for a queen with specific `color` located at index `field` of `game`.
## Returns a sequence of possible indices to move to.
## Returns a sequence of possible indices to move to.
try :
if ( not field in game . board . low .. game . board . high ) :
return @ [ ]
var res = newSeq [ int ] ( )
var res = newSeq [ int ] ( )
var dest : int
var dest : int
var target : int
var target : int
for move in Queen_Moves :
for move in Queen_Moves :
dest = field + move
dest = field + move
if ( not dest in game . board . low .. game . board . high ) :
continue
target = game . board [ dest ]
target = game . board [ dest ]
while ( target ! = 999 and ( ord ( color ) * target < = 0 ) or target = =
while ( target ! = 999 and ( ord ( color ) * target < = 0 ) or target = =
WEnPassant or target = = - WEnPassant ) :
WEnPassant or target = = - WEnPassant ) :
@ -407,13 +391,12 @@ proc genQueenDests(game: Game, field: int, color: Color): seq[int] =
dest = dest + move
dest = dest + move
target = game . board [ dest ]
target = game . board [ dest ]
return res
return res
except IndexDefect :
return @ [ ]
proc genKingCastleDest ( game : Game , field : int , color : Color ) : seq [ int ] =
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`
## 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.
## Returns a sequence of possible indices to move to.
try :
if ( not field in game . board . low .. game . board . high ) :
return @ [ ]
var res = newSeq [ int ] ( )
var res = newSeq [ int ] ( )
var dest : int
var dest : int
var target : int
var target : int
@ -421,6 +404,8 @@ proc genKingCastleDest(game: Game, field: int, color: Color): seq[int] =
var half_target : int
var half_target : int
for castle in King_Moves_White_Castle :
for castle in King_Moves_White_Castle :
dest = field + castle
dest = field + castle
if ( not dest in game . board . low .. game . board . high ) :
continue
target = game . board [ dest ]
target = game . board [ dest ]
half_dest = field + ( int ) castle / 2
half_dest = field + ( int ) castle / 2
half_target = game . board [ half_dest ]
half_target = game . board [ half_dest ]
@ -430,89 +415,94 @@ proc genKingCastleDest(game: Game, field: int, color: Color): seq[int] =
continue
continue
res . add ( dest )
res . add ( dest )
return res
return res
except IndexDefect :
return @ [ ]
proc genKingDests ( game : Game , field : int , color : Color ) : seq [ int ] =
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`.
## Generate possible destinations for a king with specific `color` located at index `field` of `game`.
## Returns a sequence of possible indices to move to.
## Returns a sequence of possible indices to move to.
try :
if ( not field in game . board . low .. game . board . high ) :
return @ [ ]
var res = newSeq [ int ] ( )
var res = newSeq [ int ] ( )
var dest : int
var dest : int
var target : int
var target : int
for move in King_Moves :
for move in King_Moves :
dest = field + move
dest = field + move
if ( not dest in game . board . low .. game . board . high ) :
continue
target = game . board [ dest ]
target = game . board [ dest ]
if ( target = = 999 or ( ord ( color ) * target > 0 and ord ( color ) * target ! = WEnPassant ) ) :
if ( target = = 999 or ( ord ( color ) * target > 0 and ord ( color ) * target ! = WEnPassant ) ) :
continue
continue
res . add ( dest )
res . add ( dest )
res . add ( game . genKingCastleDest ( field , color ) )
res . add ( game . genKingCastleDest ( field , color ) )
return res
return res
except IndexDefect :
return @ [ ]
proc genKnightDests ( game : Game , field : int , color : Color ) : seq [ int ] =
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`.
## Generate possible destinations for a knight with specific `color` located at index `field` of `game`.
## Returns a sequence of possible indices to move to.
## Returns a sequence of possible indices to move to.
try :
if ( not field in game . board . low .. game . board . high ) :
return @ [ ]
var res = newSeq [ int ] ( )
var res = newSeq [ int ] ( )
var dest : int
var dest : int
var target : int
var target : int
for move in Knight_Moves :
for move in Knight_Moves :
dest = field + move
dest = field + move
if ( not dest in game . board . low .. game . board . high ) :
continue
target = game . board [ dest ]
target = game . board [ dest ]
if ( target = = 999 or ( ord ( color ) * target > 0 and ord ( color ) * target ! = WEnPassant ) ) :
if ( target = = 999 or ( ord ( color ) * target > 0 and ord ( color ) * target ! = WEnPassant ) ) :
continue
continue
res . add ( dest )
res . add ( dest )
return res
return res
except IndexDefect :
return @ [ ]
proc genPawnAttackDests ( game : Game , field : int , color : Color ) : seq [ int ] =
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`.
## 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.
## Returns a sequence of possible indices to move to.
try :
if ( not field in game . board . low .. game . board . high ) :
return @ [ ]
var res = newSeq [ int ] ( )
var res = newSeq [ int ] ( )
var dest : int
var dest : int
var target : int
var target : int
for attacks in Pawn_Moves_White_Attack :
for attacks in Pawn_Moves_White_Attack :
dest = field + ( attacks * ord ( color ) )
dest = field + ( attacks * ord ( color ) )
if ( not dest in game . board . low .. game . board . high ) :
continue
target = game . board [ dest ]
target = game . board [ dest ]
if ( target = = 999 or ord ( color ) * target > = 0 ) :
if ( target = = 999 or ord ( color ) * target > = 0 ) :
continue
continue
res . add ( dest )
res . add ( dest )
return res
return res
except IndexDefect :
return @ [ ]
proc genPawnDoubleDests ( game : Game , field : int , color : Color ) : seq [ int ] =
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`.
## 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.
## Returns a sequence of possible indices to move to.
try :
if ( not field in game . board . low .. game . board . high ) :
return @ [ ]
var res = newSeq [ int ] ( )
var res = newSeq [ int ] ( )
var dest : int
var dest : int
var target : int
var target : int
for doubles in Pawn_Moves_White_Double :
for doubles in Pawn_Moves_White_Double :
dest = field + doubles * ord ( color )
dest = field + doubles * ord ( color )
if ( not dest in game . board . low .. game . board . high ) :
continue
target = game . board [ dest ]
target = game . board [ dest ]
if ( game . moved [ field ] or ( target ! = 0 ) or (
if ( game . moved [ field ] or ( target ! = 0 ) or (
game . board [ dest + ( S * ord ( color ) ) ] ! = 0 ) ) :
game . board [ dest + ( S * ord ( color ) ) ] ! = 0 ) ) :
continue
continue
res . add ( dest )
res . add ( dest )
return res
return res
except IndexDefect :
return @ [ ]
proc genPawnDests ( game : Game , field : int , color : Color ) : seq [ int ] =
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`.
## Generate possible destinations for a pawn with specific `color` located at index `field` of `game`.
## Returns a sequence of possible indices to move to.
## Returns a sequence of possible indices to move to.
try :
if ( not field in game . board . low .. game . board . high ) :
return @ [ ]
var res = newSeq [ int ] ( )
var res = newSeq [ int ] ( )
var dest : int
var dest : int
var target : int
var target : int
for move in Pawn_Moves_White :
for move in Pawn_Moves_White :
dest = field + move * ord ( color )
dest = field + move * ord ( color )
if ( not dest in game . board . low .. game . board . high ) :
continue
target = game . board [ dest ]
target = game . board [ dest ]
if ( target ! = 0 and target ! = ord ( color ) * WEnPassant ) :
if ( target ! = 0 and target ! = ord ( color ) * WEnPassant ) :
continue
continue
@ -520,8 +510,6 @@ proc genPawnDests(game: Game, field: int, color: Color): seq[int] =
res . add ( game . genPawnAttackDests ( field , color ) )
res . add ( game . genPawnAttackDests ( field , color ) )
res . add ( game . genPawnDoubleDests ( field , color ) )
res . add ( game . genPawnDoubleDests ( field , color ) )
return res
return res
except IndexDefect :
return @ [ ]
proc pieceOn ( game : Game , color : Color , sequence : seq [ int ] ,
proc pieceOn ( game : Game , color : Color , sequence : seq [ int ] ,
pieceID : int ) : bool =
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.
## Moves a piece if possible from `start` position to `dest` position.
## Doesnt check boundaries, checks, movement.
## Doesnt check boundaries, checks, movement.
## returns true if the piece moved, else false
## returns true if the piece moved, else false
try :
let piece = game . board [ start ]
let piece = game . board [ start ]
if game . board . setField ( start , 0 ) :
game . board [ start ] = 0
if game . board . setField ( dest , piece ) :
game . board [ dest ] = piece
game . moved . setField ( start , true )
game . moved [ start ] = true
game . moved . setField ( dest , true )
game . moved [ dest ] = true
return true
return true
else :
game . board . setField ( start , piece )
except IndexDefect , ValueError :
return false
proc moveLeadsToCheck ( game : Game , start : int , dest : int ,
proc moveLeadsToCheck ( game : Game , start : int , dest : int ,
color : Color ) : bool =
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`.
## Removes every en passant of given `color` from the `game`.
for field in board . low .. board . high :
for field in board . low .. board . high :
if board [ field ] = = ord ( color ) * WEnPassant :
if board [ field ] = = ord ( color ) * WEnPassant :
board . setField ( field , 0 )
board [ field ] = 0
proc genLegalKnightMoves ( game : Game , field : int , color : Color ) : seq [ Move ] =
proc genLegalKnightMoves ( game : Game , field : int , color : Color ) : seq [ Move ] =
## Generates all legal knight moves starting from `field` in a `game` for a `color`.
## Generates all legal knight moves starting from `field` in a `game` for a `color`.
@ -703,7 +686,6 @@ 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`.
## 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.
## `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`
## This process checks for the legality of the move and performs the switch of `game.to_move`
try :
if game . toMove ! = color :
if game . toMove ! = color :
return false
return false
var kdest = kstart
var kdest = kstart
@ -734,8 +716,6 @@ proc castling(game: var Game, kstart: int, dest_kingside: bool,
game . toMove = Color ( ord ( game . toMove ) * ( - 1 ) )
game . toMove = Color ( ord ( game . toMove ) * ( - 1 ) )
return true
return true
return false
return false
except IndexDefect , ValueError :
return false
proc getPrevBoard * ( game : Game ) : seq [ Board ] =
proc getPrevBoard * ( game : Game ) : seq [ Board ] =
return game . previousBoard
return game . previousBoard
@ -743,12 +723,11 @@ proc getPrevBoard*(game: Game): seq[Board] =
proc checkedMove * ( game : var Game , move : Move ) : bool {. discardable . } =
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`.
## 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`
## This process checks for the legality of the move and performs the switch of `game.to_move`
try :
let start = move . start
let start = move . start
let dest = move . dest
let dest = move . dest
let color = move . color
let color = move . color
let prom = move . prom
let prom = move . prom
if game . toMove ! = color :
if ( game . toMove ! = color or start = = - 1 or dest = = - 1 ) :
return false
return false
var sequence = newSeq [ Move ] ( )
var sequence = newSeq [ Move ] ( )
let piece = game . board [ start ]
let piece = game . board [ start ]
@ -774,12 +753,12 @@ proc checkedMove*(game: var Game, move: Move): bool {.discardable.} =
game . uncheckedMove ( start , dest )
game . uncheckedMove ( start , dest )
game . toMove = Color ( ord ( game . toMove ) * ( - 1 ) )
game . toMove = Color ( ord ( game . toMove ) * ( - 1 ) )
if createEnPassant :
if createEnPassant :
game . board . setField ( dest - ( N * ord ( color ) ) , WEnPassant * ord ( color ) )
game . board [ dest - ( N * ord ( color ) ) ] = WEnPassant * ord ( color )
if capturedEnPassant :
if capturedEnPassant :
game . board . setField ( dest - ( N * ord ( color ) ) , 0 )
game . board [ dest - ( N * ord ( color ) ) ] = 0
if ( ( 90 < dest and dest < 99 ) or ( 20 < dest and dest < 29 ) ) and
if ( ( 90 < dest and dest < 99 ) or ( 20 < dest and dest < 29 ) ) and
game . board [ dest ] = = WPawn * ord ( color ) :
game . board [ dest ] = = WPawn * ord ( color ) :
game . board . setField ( dest , prom )
game . board [ dest ] = prom
var prevBoard = game . previousBoard
var prevBoard = game . previousBoard
var prevCastle = game . previousCastleRights
var prevCastle = game . previousCastleRights
game . previousBoard . add ( game . board )
game . previousBoard . add ( game . board )
@ -788,8 +767,6 @@ proc checkedMove*(game: var Game, move: Move): bool {.discardable.} =
if fiftyMoveRuleReset :
if fiftyMoveRuleReset :
game . fiftyMoveCounter = 0
game . fiftyMoveCounter = 0
return true
return true
except IndexDefect , ValueError :
return false
proc hasNoMoves ( game : Game , color : Color ) : bool =
proc hasNoMoves ( game : Game , color : Color ) : bool =
## Checks if a player of a given `color` has no legal moves in a `game`.
## Checks if a player of a given `color` has no legal moves in a `game`.