chess/game: claimable draw at 3-fold repitition added

master
TiynGER 4 years ago
parent 7caa4db120
commit 9ed8b886db

@ -8,7 +8,6 @@ A chess engine is planned.
## Todo ## Todo
- draw by - draw by
- 3-fold repitition
- 50-move rule - 50-move rule
## Usage ## Usage

@ -1,5 +1,6 @@
import tables import tables
from strutils import parseInt from strutils import parseInt
import algorithm
type type
Color* = enum Color* = enum
@ -8,18 +9,26 @@ type
Board* = array[0..119, int] Board* = array[0..119, int]
## Board that checks if pieces moved ## Board that checks if pieces moved
Moved* = array[0..119, bool] Moved* = array[0..119, bool]
## Castle rights for each player
CastleRights = tuple
wk: bool
wq: bool
bk: bool
bq: bool
## Game as object of different values ## Game as object of different values
Game* = object Game* = object
board*: Board board*: Board
moved: Moved moved: Moved
toMove*: Color toMove*: Color
previousBoard*: seq[Board]
previousCastleRights*: seq[CastleRights]
## Move as object ## Move as object
Move* = object Move* = object
start: int start: int
dest: int dest: int
color: Color color: Color
prom: int prom: int
## Amount of pieces ## Amount of pieces of a player
Pieces = tuple Pieces = tuple
p: int p: int
k: int k: int
@ -222,7 +231,7 @@ proc initMoved(): Moved =
proc initGame*(): Game = proc initGame*(): Game =
## Create and return a Game object. ## Create and return a Game object.
let game = Game(board: initBoard(), moved: initMoved(), let game = Game(board: initBoard(), moved: initMoved(),
to_move: Color.White) to_move: Color.White, previousBoard: @[], previousCastleRights: @[])
return game return game
proc initGame*(board: array[0..63, int], color: Color): Game = proc initGame*(board: array[0..63, int], color: Color): Game =
@ -235,7 +244,7 @@ proc initGame*(board: array[0..63, int], color: Color): Game =
same_piece = (board[ind] != compare[ind]) same_piece = (board[ind] != compare[ind])
moved.setField(ind, same_piece) moved.setField(ind, same_piece)
let game = Game(board: board, moved: moved, let game = Game(board: board, moved: moved,
to_move: color) to_move: color, previousBoard: @[], previousCastleRights: @[])
return game return game
proc getMove*(start: int, dest: int, prom: int, color: Color): Move = proc getMove*(start: int, dest: int, prom: int, color: Color): Move =
@ -294,6 +303,18 @@ proc indToField*(ind: int): string =
if FileChar[file] == file_ind: if FileChar[file] == file_ind:
return $file & $line return $file & $line
proc genCastleRights(moved: Moved): CastleRights =
## Generate rights to castle from given `moved`
let wk = not moved.getField(fieldToInd("e1")) and not moved.getField(
fieldToInd("h1"))
let wq = not moved.getField(fieldToInd("e1")) and not moved.getField(
fieldToInd("a1"))
let bk = not moved.getField(fieldToInd("e8")) and not moved.getField(
fieldToInd("h8"))
let bq = not moved.getField(fieldToInd("e8")) and not moved.getField(
fieldToInd("a8"))
return (wk, wq, bk, bq)
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: try:
@ -709,6 +730,9 @@ proc castling(game: var Game, kstart: int, dest_kingside: bool,
except IndexDefect, ValueError: except IndexDefect, ValueError:
return false return false
proc getPrevBoard*(game: Game): seq[Board] =
return game.previousBoard
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`
@ -745,6 +769,10 @@ proc checkedMove*(game: var Game, move: Move): bool {.discardable.} =
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.getField(dest) == PawnID * ord(color): game.board.getField(dest) == PawnID * ord(color):
game.board.setField(dest, prom) game.board.setField(dest, prom)
var prevBoard = game.previousBoard
var prevCastle = game.previousCastleRights
game.previousBoard.add(game.board)
game.previousCastleRights.add(game.moved.genCastleRights())
return true return true
except IndexDefect, ValueError: except IndexDefect, ValueError:
return false return false
@ -757,6 +785,21 @@ proc isCheckmate*(game: Game, color: Color): bool =
## Checks if a player of a given `color` in a `game` is checkmate. ## Checks if a player of a given `color` in a `game` is checkmate.
return game.hasNoMoves(color) and game.isInCheck(color) return game.hasNoMoves(color) and game.isInCheck(color)
proc threeMoveRep(game: Game): bool =
## Checks if a `rep`-times repitition happened on the last move of the `game`.
var lastState = game.previousBoard[game.previousBoard.high]
var lastCastleRights = game.previousCastleRights[game.previousBoard.high]
var reps = 0
for stateInd in (game.previousBoard.low)..(game.previousBoard.high):
if (game.previousBoard[stateInd] == lastState and game.previousCastleRights[
stateInd] == lastCastleRights):
reps = reps + 1
return reps >= 3
proc isDrawClaimable*(game: Game): bool =
## Checks if a draw is claimable by either player.
return game.threeMoveRep()
proc isStalemate*(game: Game, color: Color): bool = proc isStalemate*(game: Game, color: Color): bool =
## Checks if a player of a given `color` in a `game` is stalemate. ## Checks if a player of a given `color` in a `game` is stalemate.
return (game.hasNoMoves(color) and not game.isInCheck(color)) or return (game.hasNoMoves(color) and not game.isInCheck(color)) or

@ -5,6 +5,7 @@ import ./chess
proc runGame(): void = proc runGame(): void =
var game = initGame() var game = initGame()
var draw: string
game.echoBoard(game.toMove) game.echoBoard(game.toMove)
while not game.isCheckmate(game.toMove) and not game.isStalemate(game.toMove): while not game.isCheckmate(game.toMove) and not game.isStalemate(game.toMove):
echo "Make a move" echo "Make a move"
@ -13,6 +14,12 @@ proc runGame(): void =
while not game.checkedMove(notationToMove(move, game.toMove)): while not game.checkedMove(notationToMove(move, game.toMove)):
move = readLine(stdin) move = readLine(stdin)
game.echoBoard(game.toMove) game.echoBoard(game.toMove)
if (game.isDrawClaimable):
echo "Do you want to claim a draw? (y/N)"
draw = readLine(stdin)
if (draw == "y"):
echo "Draw claimed"
break
if game.isCheckmate(game.toMove): if game.isCheckmate(game.toMove):
echo $game.toMove & " was checkmated" echo $game.toMove & " was checkmated"
if game.isStalemate(game.toMove): if game.isStalemate(game.toMove):

@ -268,6 +268,31 @@ testSuite GameTest of TestSuite:
self.check(not self.game.isStalemate(Color.Black)) self.check(not self.game.isStalemate(Color.Black))
self.check(not self.game.isStalemate(Color.White)) self.check(not self.game.isStalemate(Color.White))
method testIsDrawClaimableThreeFoldRepTrue() =
self.setup()
self.game.checkedMove(notationToMove("g1f3", Color.White))
self.game.checkedMove(notationToMove("g8f6", Color.Black))
self.game.checkedMove(notationToMove("f3g1", Color.White))
self.game.checkedMove(notationToMove("f6g8", Color.Black))
self.game.checkedMove(notationToMove("g1f3", Color.White))
self.game.checkedMove(notationToMove("g8f6", Color.Black))
self.game.checkedMove(notationToMove("f3g1", Color.White))
self.game.checkedMove(notationToMove("f6g8", Color.Black))
self.game.checkedMove(notationToMove("g1f3", Color.White))
self.check(self.game.isDrawClaimable())
method testIsDrawClaimableThreeFoldRepFalse() =
self.setup()
self.game.checkedMove(notationToMove("g1f3", Color.White))
self.game.checkedMove(notationToMove("g8f6", Color.Black))
self.game.checkedMove(notationToMove("f3g1", Color.White))
self.game.checkedMove(notationToMove("f6g8", Color.Black))
self.game.checkedMove(notationToMove("g1f3", Color.White))
self.game.checkedMove(notationToMove("g8f6", Color.Black))
self.game.checkedMove(notationToMove("f3g1", Color.White))
self.game.checkedMove(notationToMove("f6g8", Color.Black))
self.check(not self.game.isDrawClaimable())
## Tests for Pawn moves ## Tests for Pawn moves
method testCheckedMovePawnSingleTrue() = method testCheckedMovePawnSingleTrue() =
self.setup() self.setup()

Loading…
Cancel
Save