-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.py
312 lines (284 loc) · 10.1 KB
/
main.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
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
# Try profiling the code at some point
# One Player I could make is one that outputs results of a saved game
import copy
import string
import numpy as np
LENGTH = 19
WIDTH = 15
MAN = "man"
EMPTY = "empty"
BALL = "ball"
DIRECTIONS = {
"N": np.array((-1, 0)),
"S": np.array((1, 0)),
"E": np.array((0, 1)),
"W": np.array((0, -1)),
"NW": np.array((-1, -1)),
"SW": np.array((1, -1)),
"SE": np.array((1, 1)),
"NE": np.array((-1, 1)),
}
# I have had lots of fun dealing with formatting problems caused by circle sizes
SMALL_WHITE_CIRCLE = "⚬" # Looks good on WordPress
SMALL_BLACK_CIRCLE = "•"
WHITE_CIRCLE = "○" # Looks good on GitHub
BLACK_CIRCLE = "●"
class HumanPlayer:
def make_move(self, board):
board.pretty_print_details()
possible_moves = board.get_all_moves()
human_move = ""
while (
human_move not in possible_moves
and human_move + " " not in possible_moves
):
human_move = input("Move: ")
if human_move in possible_moves:
return human_move
return human_move + " "
class Board:
"""A virtual board---can be printed out nicely"""
def __init__(
self,
side_to_move="Left",
moves_made=0,
array=None,
ball_at=np.array((7, 9)),
):
self.ball_at = ball_at
self.side_to_move = side_to_move
self.moves_made = moves_made
if array != None:
self.array = copy.deepcopy(array)
else:
self.array = [
[EMPTY for i in range(LENGTH)]
for j in range(WIDTH)
]
self.array[ball_at[0]][ball_at[1]] = BALL
def copy(self):
return Board(
side_to_move=self.side_to_move,
moves_made=self.moves_made,
array=self.array,
ball_at=self.ball_at,
)
def pretty_string_details(self, small=False):
if small:
white_circle = SMALL_WHITE_CIRCLE
black_circle = SMALL_BLACK_CIRCLE
else:
white_circle = WHITE_CIRCLE
black_circle = BLACK_CIRCLE
output = ""
output += " 1111111111\n"
output += " 1234567890123456789\n"
for i in range(WIDTH):
output += string.ascii_uppercase[i]
for j in range(LENGTH):
element = self.array[i][j]
if element == MAN:
output += white_circle
elif element == BALL:
output += black_circle
elif element == EMPTY:
output += "+"
else:
raise Exception(
"The array contains something it shouldn't: "
+ str(element)
)
output += "\n"
output += (
"Side to move: " + str(self.side_to_move) + "\n"
)
output += (
"Moves made: " + str(self.moves_made) + "\n"
)
output += "Ball at: " + str(self.ball_at) + "\n"
return output
def pretty_print(self, small=False):
print(self.pretty_string(small))
def pretty_print_details(self, small=False):
print(self.pretty_string_details(small))
def pretty_string(self, small=False):
if small:
white_circle = SMALL_WHITE_CIRCLE
black_circle = SMALL_BLACK_CIRCLE
else:
white_circle = WHITE_CIRCLE
black_circle = BLACK_CIRCLE
output = ""
for i in range(WIDTH):
for j in range(LENGTH):
element = self.array[i][j]
if element == MAN:
output += white_circle
elif element == BALL:
output += black_circle
elif element == EMPTY:
output += "+"
else:
raise Exception(
"The array contains something it shouldn't: "
+ str(element)
)
output += "\n"
return output
def increment(self):
if self.side_to_move == "Left":
self.side_to_move = "Right"
else:
self.side_to_move = "Left"
self.moves_made += 1
return self
def get_man_moves(self):
"""Returns a dictionary of all moves that involve
placing a man. e.g. key = 'B15', value = the future Board"""
moves = {}
for i in range(WIDTH):
for j in range(LENGTH):
if self.array[i][j] == EMPTY:
new = self.copy()
new.increment()
new.array[i][j] = MAN
name = string.ascii_uppercase[i] + str(
j + 1
)
moves[name] = new
return moves
def get_nearby_man_moves(self):
"""Like get_man_moves(), except we'd only place a man within 1
or 2 squares (in a 5x5 box) of an existing piece"""
moves = {}
for i in range(WIDTH):
for j in range(LENGTH):
if self.array[i][j] == EMPTY:
isolated = True
for ii in range(i - 2, i + 3):
if ii in range(WIDTH):
for jj in range(j - 2, j + 3):
if jj in range(LENGTH):
if (
self.array[ii][jj]
!= EMPTY
):
isolated = False
if not isolated:
new = self.copy()
new.increment()
new.array[i][j] = MAN
name = string.ascii_uppercase[
i
] + str(j + 1)
moves[name] = new
return moves
def get_ball_moves(self):
moves = {}
current_name = ""
return self.recursive_get_ball_moves(
moves, current_name
)
# I have to wrap the recursive method in this one,
# else I get a curious problem with the namespace.
def recursive_get_ball_moves(self, moves, current_name):
"""Recursive method. Note that 'self' changes"""
# print(moves)
for direction in DIRECTIONS.keys():
vec = DIRECTIONS[direction]
new = self.copy()
jump_length = 0
while jump_length == 0 or (
new.is_on_board(end_point)
and new.array[end_point[0]][end_point[1]]
== MAN
): # How far can we jump?
jump_length += 1
end_point = new.ball_at + jump_length * vec
# print(direction, jump_length, end_point)
if jump_length == 1:
continue # We didn't actually jump
if not new.is_on_board(end_point) and (
end_point - vec
)[1] not in (0, LENGTH - 1):
# "It is also legal for the ball to leave the board, but only by jumping over a man on the goal line"
continue # We jumped off the board illegally
for i in range(jump_length):
temp = (
new.ball_at + i * vec
) # Clear out what we jumped over
new.array[temp[0]][temp[1]] = EMPTY
if new.is_on_board(end_point):
# Move the ball
new.array[end_point[0]][end_point[1]] = BALL
new.ball_at = (
end_point # This may be off the board
)
# Now add the move to the dictionary
new_name = current_name + direction + " "
# print(new_name)
# print(moves)
moves[new_name] = new.copy().increment()
# print(moves)
# Now look for jump moves that started with this jump
moves = new.recursive_get_ball_moves(
moves, new_name
)
# print(moves)
return moves
def is_on_board(self, coordinates):
return coordinates[0] in range(
WIDTH
) and coordinates[1] in range(LENGTH)
def get_all_moves(self):
moves = self.get_man_moves()
moves.update(self.get_ball_moves())
return moves
class ListPlayer:
"""Rehashes a game"""
def __init__(
self, old_game, quieter=False, small=False
):
self.old_game = old_game
self.quieter = quieter
self.small = small
def make_move(self, board):
if self.quieter:
board.pretty_print(self.small)
else:
board.pretty_print_details(self.small)
return self.old_game[board.moves_made]
class PloddingPlayer:
"""Place man, jump, repeat"""
def make_move(self, board):
possible_moves = board.get_all_moves()
if board.side_to_move == "Left":
if "E " in possible_moves:
return "E "
i, j = board.ball_at
return string.ascii_uppercase[i] + str(j + 2)
else:
if "W " in possible_moves:
return "W "
i, j = board.ball_at
return string.ascii_uppercase[i] + str(j)
def run_game(players=[HumanPlayer(), HumanPlayer()]):
"""Conducts a game between two Players, [left, right]"""
num_moves_made = 0
moves_made = []
current_board = Board()
while True:
player = players[num_moves_made % 2]
move = player.make_move(current_board.copy())
moves_made.append(move)
print(moves_made)
# current_board.pretty_print_details()
current_board = current_board.get_all_moves()[move]
# Previous line makes the move, and returns an error if invalid
if current_board.ball_at[1] <= 0:
print("Right has won")
return
if current_board.ball_at[1] >= LENGTH - 1:
print("Left has won")
return
num_moves_made += 1