-
Notifications
You must be signed in to change notification settings - Fork 4
/
runner.py
186 lines (169 loc) · 7.2 KB
/
runner.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
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
import os
import time
import copy
from array import array
import hashlib
import inspect
import shared
import David_AI_v9 as ai
initialTime = 5
timePerMove = 1
turnsToPlayFor = 250
extraRepeatTime = 0.1
competitorNames = [
'David_AI_v9',
'David_AI_v8',
'David_AI_v7',
'David_AI_v6',
'David_AI_v5',
'David_AI_v4',
'David_AI_v3',
'David_AI_v2',
'David_AI_v1',
'Iain_AI_v2',
'Iain_AI_v1',
'Michael_AI_v1_3',
'Michael_AI_v1_2',
'Michael_AI_v1_1',
'Michael_AI_v1_0',
'Robert_AI',
'no_search',
'no_move_AI',
'random_AI',
# 'Human_player'
]
for name in competitorNames:
exec('import ' + name)
competitors = [eval(name) for name in competitorNames]
initialBoard = '''
r n b q k b n r
p p p p p p p p
. . . . . . . .
. . . . . . . .
. . . . . . . .
. . . . . . . .
P P P P P P P P
R N B Q K B N R'''
def print_state(_turn, board, run_time, white_time_remaining, black_time_remaining, white, black, repeat):
ai.recalculate_position_values(ai.to_array(board))
print(f'----- {white.__name__} vs {black.__name__} match {repeat} move {_turn} -----')
print('\n'.join(' '.join(piece for piece in row)for row in board.__reversed__()) + '\n')
print('{} took: {:.3f} seconds'.format('white' if _turn % 2 else 'black', run_time))
print('white time: {:.3f}'.format(white_time_remaining))
print('black time: {:.3f}'.format(black_time_remaining))
print('score: {:.1f}'.format(ai.evaluate(ai.to_array(board))))
def legal_moves(history, player_is_white):
""""Generates a list of legal moves. Missing castling, en-passant."""
board = ai.to_array(history)[-1]
moves = list(ai.legal_moves(board, player_is_white))
assert type(moves[0][0]) == array
moves = [ai.from_array(move) for move, _score in moves]
return moves
def make_file_name(white, black, repeat):
return rf'results/{white.__name__} vs {black.__name__} repeat {repeat}.txt'
def source_hash(player):
# this line makes things the same on windows and unix
normalised_source = '\n'.join(inspect.getsource(player).split())
return hashlib.sha256(normalised_source.encode()).hexdigest()
def match(white, black, repeat):
"""This plays a single match between the white and black players and records the result."""
print(f'\nMatch {repeat} between {white.__name__} on white and {black.__name__} on black')
# -------- turns and time -------
black_moves = white_moves = 0
black_time_taken = white_time_taken = 0
history = [[[piece for piece in line] for line in initialBoard.replace(' ', '').split()]]
history[0].reverse()
to_record = {'score': 0.5, 'cause': 'Draw due to reaching {} turns'.format(turnsToPlayFor)}
for turn in range(1, 1+turnsToPlayFor):
player_is_white = turn % 2
start_time = time.process_time()
white_time = initialTime + white_moves * (timePerMove + (repeat - 1) * extraRepeatTime) - white_time_taken
black_time = initialTime + black_moves * (timePerMove + (repeat - 1) * extraRepeatTime) - black_time_taken
try:
chosen_move = (white if player_is_white else black).main(
copy.deepcopy(history), white_time, black_time)
except shared.ThreeFoldRepetition:
to_record = {'score': 0.5, 'cause': '{} called a draw with the threefold repetition rule'.format(
'White' if player_is_white else 'Black')}
break
except shared.FiftyMoveException:
to_record = {'score': 0.5, 'cause': '{} called a draw with the 50 move rule'.format(
'White' if player_is_white else 'Black')}
break
run_time = time.process_time() - start_time
assert isinstance(chosen_move, list)
assert len(chosen_move) == 8
for row in chosen_move:
assert isinstance(row, list)
assert len(row) == 8
for char in row:
assert isinstance(char, str)
assert len(char) == 1
assert char in 'KQRBNP.pnbrqk'
if player_is_white:
white_time_taken += run_time
white_time = initialTime + white_moves * (timePerMove + (repeat - 1) * extraRepeatTime) - white_time_taken
white_moves += 1
else:
black_time_taken += run_time
black_time = initialTime + black_moves * (timePerMove + (repeat - 1) * extraRepeatTime) - black_time_taken
black_moves += 1
print_state(turn, chosen_move, run_time, white_time, black_time, white, black, repeat)
if chosen_move not in legal_moves(history, player_is_white):
if player_is_white:
to_record = {'score': 0, 'cause': 'Black won because white made an illegal move'}
else:
to_record = {'score': 1, 'cause': 'White won because black made an illegal move'}
break
# is_check can fail if it is is handed an illegal move
# so these print statements are after the legal move check
if ai.is_check(ai.to_array(chosen_move), True):
print('white is in check')
if ai.is_check(ai.to_array(chosen_move), False):
print('black is in check')
print()
if ai.is_checkmate(ai.to_array(chosen_move), True):
to_record = {'score': 0, 'cause': 'Black won by checkmate'}
break
if ai.is_checkmate(ai.to_array(chosen_move), False):
to_record = {'score': 1, 'cause': 'White won by checkmate'}
break
if ai.is_stalemate(ai.to_array(chosen_move)):
to_record = {'score': 0.5, 'cause': 'Draw due to stalemate'}
break
if white_time < 0:
to_record = {'score': 0, 'cause': 'Black won due to white running out of time'}
break
if black_time < 0:
to_record = {'score': 1, 'cause': 'White won due to black running out of time'}
break
# once the move has been shown valid add it to the history
history.append(chosen_move)
to_record['white_time_taken'] = white_time_taken
to_record['black_time_taken'] = black_time_taken
to_record['white_moves'] = white_moves
to_record['black_moves'] = black_moves
print(to_record['cause'])
open(make_file_name(white, black, repeat), 'w').write(current_versions+str(to_record))
if __name__ == '__main__':
tournamentStartTime = time.perf_counter()
for repeat in range(1, 1000):
for white in competitors:
for black in competitors:
if white == black:
continue
file_name = make_file_name(white, black, repeat)
current_versions = (
f'{source_hash(white)} vs {source_hash(black)} repeat {repeat}\n')
try:
file = open(file_name)
previous_versions = file.readline()
if current_versions == previous_versions:
# then skip this match
continue
except (FileNotFoundError, SyntaxError):
pass
match(white, black, repeat)
if os.name == 'posix':
# it can take a while to run and I want to do other things in the mean time
os.system('say "tournament finished"')