mirror of
https://github.com/tiyn/yeschess.git
synced 2025-04-01 14:47:44 +02:00
chess/game: claimable draw at 3-fold repitition added
This commit is contained in:
parent
7caa4db120
commit
9ed8b886db
@ -8,7 +8,6 @@ A chess engine is planned.
|
||||
## Todo
|
||||
|
||||
- draw by
|
||||
- 3-fold repitition
|
||||
- 50-move rule
|
||||
|
||||
## Usage
|
||||
|
49
chess.nim
49
chess.nim
@ -1,5 +1,6 @@
|
||||
import tables
|
||||
from strutils import parseInt
|
||||
import algorithm
|
||||
|
||||
type
|
||||
Color* = enum
|
||||
@ -8,18 +9,26 @@ type
|
||||
Board* = array[0..119, int]
|
||||
## Board that checks if pieces moved
|
||||
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* = object
|
||||
board*: Board
|
||||
moved: Moved
|
||||
toMove*: Color
|
||||
previousBoard*: seq[Board]
|
||||
previousCastleRights*: seq[CastleRights]
|
||||
## Move as object
|
||||
Move* = object
|
||||
start: int
|
||||
dest: int
|
||||
color: Color
|
||||
prom: int
|
||||
## Amount of pieces
|
||||
## Amount of pieces of a player
|
||||
Pieces = tuple
|
||||
p: int
|
||||
k: int
|
||||
@ -222,7 +231,7 @@ proc initMoved(): Moved =
|
||||
proc initGame*(): Game =
|
||||
## Create and return a Game object.
|
||||
let game = Game(board: initBoard(), moved: initMoved(),
|
||||
to_move: Color.White)
|
||||
to_move: Color.White, previousBoard: @[], previousCastleRights: @[])
|
||||
return 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])
|
||||
moved.setField(ind, same_piece)
|
||||
let game = Game(board: board, moved: moved,
|
||||
to_move: color)
|
||||
to_move: color, previousBoard: @[], previousCastleRights: @[])
|
||||
return game
|
||||
|
||||
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:
|
||||
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 =
|
||||
## Convert simplified algebraic chess `notation` to a move object, color of player is `color`.
|
||||
try:
|
||||
@ -709,6 +730,9 @@ proc castling(game: var Game, kstart: int, dest_kingside: bool,
|
||||
except IndexDefect, ValueError:
|
||||
return false
|
||||
|
||||
proc getPrevBoard*(game: Game): seq[Board] =
|
||||
return game.previousBoard
|
||||
|
||||
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`
|
||||
@ -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
|
||||
game.board.getField(dest) == PawnID * 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())
|
||||
return true
|
||||
except IndexDefect, ValueError:
|
||||
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.
|
||||
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 =
|
||||
## Checks if a player of a given `color` in a `game` is stalemate.
|
||||
return (game.hasNoMoves(color) and not game.isInCheck(color)) or
|
||||
|
7
game.nim
7
game.nim
@ -5,6 +5,7 @@ import ./chess
|
||||
|
||||
proc runGame(): void =
|
||||
var game = initGame()
|
||||
var draw: string
|
||||
game.echoBoard(game.toMove)
|
||||
while not game.isCheckmate(game.toMove) and not game.isStalemate(game.toMove):
|
||||
echo "Make a move"
|
||||
@ -13,6 +14,12 @@ proc runGame(): void =
|
||||
while not game.checkedMove(notationToMove(move, game.toMove)):
|
||||
move = readLine(stdin)
|
||||
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):
|
||||
echo $game.toMove & " was checkmated"
|
||||
if game.isStalemate(game.toMove):
|
||||
|
25
test.nim
25
test.nim
@ -268,6 +268,31 @@ testSuite GameTest of TestSuite:
|
||||
self.check(not self.game.isStalemate(Color.Black))
|
||||
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
|
||||
method testCheckedMovePawnSingleTrue() =
|
||||
self.setup()
|
||||
|
Loading…
x
Reference in New Issue
Block a user