chess: refactoring setters and try

Setters are not needed as all the assigning gets handled inside the  file.
All Setters were removed.
The try-statements were used to excessively and were changed for manual checking
master
TiynGER 4 years ago
parent c4f7e3b98d
commit c5cf2fd737

@ -22,3 +22,8 @@ You can simply run the tests with `nim c -r test.nim`.
Documentation is written into the code via DocGen. Documentation is written into the code via DocGen.
For this reason it is not saved in this repository. For this reason it is not saved in this repository.
To extract it into html run `nim doc --project --index:on --outdir:htmldocs game.nim` To extract it into html run `nim doc --project --index:on --outdir:htmldocs game.nim`
### Board Representation
Due to easier off the board checking a
[10x12](https://www.chessprogramming.org/10x12_Board) board is used.

@ -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`.

@ -1588,5 +1588,22 @@ testSuite GameTest of TestSuite:
else: else:
self.check(not test) self.check(not test)
method testcheckedMoveFaultyInput() =
var test: bool
self.setup()
let startPos = self.game
test = self.game.checkedMove(notationToMove("aaaa", Color.White))
self.check(not test)
test = (self.game == startPos)
self.check(test)
test = self.game.checkedMove(notationToMove("1bb6&111", Color.White))
self.check(not test)
test = (self.game == startPos)
self.check(test)
test = self.game.checkedMove(notationToMove("e1g1sdfa", Color.White))
self.check(not test)
test = (self.game == startPos)
self.check(test)
when isMainModule: when isMainModule:
einheit.runTests() einheit.runTests()

Loading…
Cancel
Save