-
Notifications
You must be signed in to change notification settings - Fork 0
/
chessGame.py
165 lines (134 loc) · 4.76 KB
/
chessGame.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
"""Used in conjunction with mcts"""
from game import Game, State
import chess
from chess import Board
from copy import deepcopy
import collections
import chess.pgn
import numpy as np
class Chess(Game):
def __init__(self, fen=chess.STARTING_BOARD_FEN):
self.board = Board(fen)
def initial_state(self):
"""Returns the initial state of this game."""
return ChessState(self.board)
class ChessState(State):
def __init__(self, board):
self.board = deepcopy(board)
def is_terminal(self):
"""Determines if this state is terminal. Return value is true if so and false otherwise.
self -- a state
"""
return self.board.is_game_over()
def payoff(self):
"""Returns the payoff for player 0 at this terminal state.
self -- a terminal state
"""
return (
100
if self.board.outcome().winner == chess.WHITE
else -100
if self.board.outcome().winner == chess.BLACK
else 0
)
# Experiment terminal state payoffs
def actor(self):
"""Determines which player is the actor in this nonterminal state.
self -- a nonterminal state
"""
return self.board.turn
def get_actions(self):
"""Returns a list of possible actions in this nonterminal state.
The representation of each state is left to the implementation.
self -- a nonterminal state
"""
return list(self.board.legal_moves)
def is_legal(self, action):
"""Determines if the given action is legal in this state.
self -- a state
action -- an action
"""
return action in self.board.legal_moves
def successor(self, action):
"""Returns the state that results from the given action in this nonterminal state.
self -- a nonterminal state
action -- one of the actions in the list returned by get_actions for this state
"""
new_state = deepcopy(self)
new_state.board.push(action)
return new_state
def heuristic_evaluation(self):
"""Calculate the heuristic value of the board from the perspective of the white player."""
piece_values = {
"p": -1,
"n": -3.05,
"b": -3.33,
"r": -5.63,
"q": -9.5,
"k": 0, # kings are invaluable but shouldn't affect material count
"P": 1,
"N": 3.05,
"B": 3.33,
"R": 5.63,
"Q": 9.5,
"K": 0,
}
value = 0
for piece in self.board.piece_map().values():
value += piece_values.get(piece.symbol(), 0)
# Evaluate the mobility of both kings
white_king_mobility = self.king_mobility(chess.WHITE)
black_king_mobility = self.king_mobility(chess.BLACK)
mobility_factor = (
0
if len(self.board.move_stack) <= 20
else 0.1
* np.log(
len(self.board.move_stack)
) # Adjust this factor to tune the influence of mobility on the heuristic
)
# Calculate the mobility difference
if self.board.turn == chess.WHITE:
mobility_value = mobility_factor * (
white_king_mobility - black_king_mobility
)
else:
mobility_value = mobility_factor * (
black_king_mobility - white_king_mobility
)
return value + mobility_value
def king_mobility(self, color):
"""Calculates the number of legal moves available to the king for the given turn."""
king_position = self.board.king(color)
if king_position is None:
return (
0 # In case the king is not found (should not happen in legal states)
)
legal_moves = [
move for move in self.board.legal_moves if move.from_square == king_position
]
return len(legal_moves)
def to_game(self):
board = self.board
game = chess.pgn.Game()
# Undo all moves.
switchyard = collections.deque()
while board.move_stack:
switchyard.append(board.pop())
game.setup(board)
node = game
# Replay all moves.
while switchyard:
move = switchyard.pop()
node = node.add_variation(move)
board.push(move)
game.headers["Result"] = board.result()
return game
def __hash__(self):
return hash(self.board.board_fen()) * 2 + hash(self.board.turn)
def __eq__(self, other):
return (
isinstance(other, self.__class__)
and self.board.board_fen() == other.board.board_fen()
and self.board.turn == other.board.turn
)