mirror of
				https://github.com/tiyn/yeschess.git
				synced 2025-10-31 02:41:16 +01:00 
			
		
		
		
	chess/game: claimable draw at 3-fold repitition added
This commit is contained in:
		| @@ -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 | ||||||
|   | |||||||
							
								
								
									
										49
									
								
								chess.nim
									
									
									
									
									
								
							
							
						
						
									
										49
									
								
								chess.nim
									
									
									
									
									
								
							| @@ -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 | ||||||
|   | |||||||
							
								
								
									
										7
									
								
								game.nim
									
									
									
									
									
								
							
							
						
						
									
										7
									
								
								game.nim
									
									
									
									
									
								
							| @@ -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): | ||||||
|   | |||||||
							
								
								
									
										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.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() | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user