/
chessai.py
123 lines (103 loc) · 3.96 KB
/
chessai.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
from chesslogic import *
import random
import math
# Base class for AIs
class BaseAI:
def __init__(self, board, is_white, name):
# Composition
self.board = board
self.name = name
self.is_white = is_white
# Virtual function
def get_ai_move(self):
raise NotImplementedError("AI should implement get_ai_move function")
# Plays moves randomly
class RandomPlayer(BaseAI):
def __init__(self, board, is_white=False):
super().__init__(board, is_white, "random player")
print("initialize random player")
def get_ai_move(self):
moves = self.board.generate_moves()
return random.choice(moves)
class MinMaxPlayer(BaseAI):
def __init__(self, board, is_white, heuristic, depth = 1):
super().__init__(board, is_white, "minmax player")
self.heuristic = heuristic
self.depth = depth
print("initialize minmax player\ndepth:", depth)
def _min_max_rec(self, maximize, depth, root_white):
if depth == 0:
h = self.heuristic(self.board)
return h if root_white else -h
else:
moves = self.board.generate_moves()
best_score = -math.inf if maximize else math.inf
# TODO refactor this
for move in moves:
origin, target, promotion = move
try:
game_status = self.board.move_piece(origin, target, promotion)
except:
# TODO could pop illegal moves
# TODO handle case where no moves
continue
if game_status == BoardStatus.Checkmate:
self.board.revert_last_move()
if maximize:
# AI wants to avoid being checkmated
return -math.inf
else:
# AI wants to checkmate
return math.inf
if game_status == BoardStatus.Stalemate:
self.board.revert_last_move()
return 0
score = self._min_max_rec(not maximize, depth - 1, root_white)
if ((maximize and score > best_score) or
(not maximize and score < best_score)
):
best_score = score
self.board.revert_last_move()
return best_score
# @param maximize: maximize on AI turn, minimize opponent turn
# @param depth: depth of recursion. at least 1.
def _min_max(self, depth):
maximize = True
moves = self.board.generate_moves()
best_move = None
best_score = -math.inf if maximize else math.inf
# TODO refactor this?
root_is_white = self.board.white_turn
for move in moves:
origin, target, promotion = move
try:
self.board.move_piece(origin, target, promotion)
except ValueError as e:
print(e)
# TODO could pop illegal moves
continue
score = self._min_max_rec(not maximize, depth - 1, root_is_white)
if ((maximize and score > best_score) or
(not maximize and score < best_score)
):
best_score = score
best_move = move
self.board.revert_last_move()
return best_move
def get_ai_move(self):
print("calculating move...")
return self._min_max(self.depth)
class NegaMax(BaseAI):
pass
class MonteCarlo(BaseAI):
pass
class MinMaxMonteCarlo(BaseAI):
pass
material_value = {Pawn: 1, Knight: 3, Bishop: 3, Rook: 5, Queen: 9, King: 0}
def material_heuristic(board: Board):
score = 0
for piece_type in board.white_pieces:
score += len(board.white_pieces[piece_type]) * material_value[piece_type]
for piece_type in board.black_pieces:
score -= len(board.black_pieces[piece_type]) * material_value[piece_type]
return score