-
Notifications
You must be signed in to change notification settings - Fork 0
/
move_selection.py
162 lines (142 loc) · 5.13 KB
/
move_selection.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
from math import inf
from math import fabs
from constants import EPSILON
from board_evaluation import evaluation_mode
from input import square_input_from_mouse
import gui
import random
import chess
def human_move(board, eval_mode, max_depth, screen):
"""
Lets the human player choose a legal move.
:param board: current chess board.
:type board: chess.Board.
:param eval_mode: unused value for compatibility.
:type eval_mode: int key for dictionary.
:param max_depth: unused value for compatibility.
:type max_depth: int.
:param screen: Pygame screen.
:type screen: pygame.Surface.
:return: selected move.
:rtype: chess.Move.
"""
from_square = square_input_from_mouse(board, screen)
if from_square == None:
return None
to_square = square_input_from_mouse(board, screen)
if to_square == None:
return None
try:
move = board.find_move(from_square, to_square)
move_is_legal = True
except:
move_is_legal = False
while not move_is_legal:
gui.ilegal_move_gui(screen)
from_square = square_input_from_mouse(board, screen)
if from_square == None:
return None
to_square = square_input_from_mouse(board, screen)
if to_square == None:
return None
try:
move = board.find_move(from_square, to_square)
move_is_legal = True
except:
move_is_legal = False
return move
def random_move(board, eval_mode, max_depth):
"""
Chooses a random legal move.
:param board: current chess board.
:type board: chess.Board.
:param eval_mode: unused value for compatibility.
:type eval_mode: int key for dictionary.
:param max_depth: unused value for compatibility.
:type max_depth: int.
:return: selected random move.
:rtype: chess.Move.
"""
return random.choice(list(board.legal_moves))
def alpha_beta_prunning(board, eval_mode, max_depth):
"""
Chooses a random best move according to the search tree of max_depth and evaluation function.
:param board: current chess board.
:type board: chess.Board.
:param eval_mode: which function to use for evaluation.
:type eval_mode: int key for dictionary.
:param max_depth: max depth for the search tree.
:type max_depth: int.
:return: selected random best move.
:rtype: chess.Move.
"""
best_move = random.choice(list(board.legal_moves))
board.push(best_move)
best_value = minimax(board, eval_mode, max_depth, board.turn, -inf, inf)
board.pop()
for move in board.legal_moves:
board.push(move)
value = minimax(board, eval_mode, max_depth, board.turn, -inf, inf)
board.pop()
if (board.turn == chess.WHITE and value > best_value) or (board.turn == chess.BLACK and value < best_value):
best_value = value
best_move = move
return best_move
def minimax(board, eval_mode, depth_left, side, alpha, beta):
if depth_left == 0 or board.is_game_over():
return evaluation_mode[eval_mode](board)
if board.turn is chess.WHITE:
max_evaluation = -inf
for move in board.legal_moves:
board.push(move)
evaluation = minimax(board, eval_mode, depth_left - 1, side, alpha, beta)
board.pop()
max_evaluation = max(max_evaluation, evaluation)
alpha = max(alpha, evaluation)
if beta <= alpha:
break
return max_evaluation
else:
min_evaluation = inf
for move in board.legal_moves:
board.push(move)
evaluation = minimax(board, eval_mode, depth_left - 1, side, alpha, beta)
board.pop()
min_evaluation = min(min_evaluation, evaluation)
beta = min(beta, evaluation)
if beta <= alpha:
break
return min_evaluation
def greedy_move(board, eval_mode, max_depth):
"""
Chooses the greedy move, i. e, the one that will maximize the evaluation function next ply.
:param board: current chess board.
:type board: chess.Board.
:param eval_mode: unused value for compatibility.
:type eval_mode: int key for dictionary.
:param max_depth: max depth for the search tree.
:type max_depth: int.
:return: Greedy selected move.
:rtype: chess.Move.
"""
best_move = random.choice(list(board.legal_moves))
board.push(best_move)
best_value = evaluation_mode[eval_mode](board, max_depth - 1)
board.pop()
for move in board.legal_moves:
board.push(move)
value = evaluation_mode[eval_mode](board, max_depth - 1)
board.pop()
if (board.turn == chess.WHITE and value > best_value) or (board.turn == chess.BLACK and value < best_value):
best_value = value
best_move = move
return best_move
# Move Selector Options
HUMAN_MOVE = 0
RANDOM_MOVE = 1
ALPHA_BETA_MOVE = 2
GREEDY_MOVE = 3
selection_mode = {HUMAN_MOVE: human_move, RANDOM_MOVE: random_move,
ALPHA_BETA_MOVE: alpha_beta_prunning,
GREEDY_MOVE: greedy_move,
}