-
Notifications
You must be signed in to change notification settings - Fork 0
/
reversegam.py
281 lines (225 loc) · 9.33 KB
/
reversegam.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
"""Reversegam has an 8*8 board and tiles that are black on one side and white
on the other (our game will use Os and Xs instead). Two players take turns
placing tiles of their chosen color—black or white—on the board. When a player
places a tile on the board, any of the opponent's tiles that are between
the new tile and the other tiles of the player's color are flipped. The goal
of the game is to end with more tiles of your color than your opponent's color."""
import re
import time
from copy import deepcopy
NEW_BOARD = [list(" " * 8) for _ in range(8)]
COLUMNS = "abcdefgh"
def draw_board(board: list[list[str]]):
"""prints out board with moves from the passed list"""
print()
print(" a b c d e f g h ")
print(" +-+-+-+-+-+-+-+-+ ")
for row in range(len(board)):
print(f"{row + 1}|", end="")
for column in range(len(board[row])):
print(board[row][column] + "|", end="")
print()
print(" +-+-+-+-+-+-+-+-+ ")
print()
def get_scores(
player_mark: str, opponent_mark: str, board: list[list[str]]
) -> tuple[int, int]:
"""returns scores"""
flattened_board = [item for row in board for item in row]
player_scores = flattened_board.count(player_mark)
opponent_scores = flattened_board.count(opponent_mark)
return (player_scores, opponent_scores)
def check_direction(
direction: str, coordinates: list[int], board: list[list[str]]
) -> tuple[list[int], str]:
"""returns coordinates of checked direction from selected position and the mark in the space"""
row = coordinates[0]
column = coordinates[1]
if direction == "up":
new_row = row - 1
new_column = column
new_mark = board[new_row][new_column]
elif direction == "down":
new_row = row + 1
new_column = column
new_mark = board[new_row][new_column]
elif direction == "left":
new_row = row
new_column = column - 1
new_mark = board[new_row][new_column]
elif direction == "right":
new_row = row
new_column = column + 1
new_mark = board[new_row][new_column]
elif direction == "diagonal0":
new_row = row - 1
new_column = column + 1
new_mark = board[new_row][new_column]
elif direction == "diagonal1":
new_row = row + 1
new_column = column + 1
new_mark = board[new_row][new_column]
elif direction == "diagonal2":
new_row = row + 1
new_column = column - 1
new_mark = board[new_row][new_column]
elif direction == "diagonal3":
new_row = row - 1
new_column = column - 1
new_mark = board[new_row][new_column]
else:
raise Exception
return ([new_row, new_column], new_mark)
def not_within_the_board(row: int, column: int, direction: str) -> bool:
"""checks for movement outside the board"""
if (
(row == 0 and direction in ["up", "diagonal0", "diagonal3"])
or (column == 0 and direction in ["left", "diagonal2", "diagonal3"])
or (row == 7 and direction in ["down", "diagonal1", "diagonal2"])
or (column == 7 and direction in ["right", "diagonal0", "diagonal1"])
):
return True
else:
return False
def valid_move(player_mark: str, coordinates: str, board: list[list[str]]):
"""confirms whether selected position is a valid move and flips necessary tiles"""
if len(coordinates) == 2:
column = COLUMNS.index(coordinates[0])
row = int(coordinates[1]) - 1
if board[row][column] == " ":
opponent_mark = "X" if player_mark == "O" else "O"
directions = [
"up",
"down",
"left",
"right",
"diagonal0",
"diagonal1",
"diagonal2",
"diagonal3",
]
found_valid_move = False
# starting from selected position check all directions for opponent's mark
for direction in directions:
# move within the board
if not_within_the_board(row, column, direction):
continue
new_coordinates, new_mark = check_direction(
direction, [row, column], board
)
may_be_flipped = []
# if opponent's mark is found go further looking for player's mark
while new_mark == opponent_mark:
# save each opponent's mark coordinates
may_be_flipped.append(new_coordinates)
# move within the board
if not_within_the_board(
new_coordinates[0], new_coordinates[1], direction
):
break
new_coordinates, new_mark = check_direction(
direction, [new_coordinates[0], new_coordinates[1]], board
)
if new_mark == player_mark:
# if player's mark is found flip all opponent marks between it
# and the selected position
for x, y in may_be_flipped:
board[x][y] = player_mark
found_valid_move = True
if found_valid_move:
board[row][column] = player_mark
return True
return False
def get_allowed_moves(player_mark: str, board: list[list[str]]) -> list[str]:
"""returns list of coordinates that are allowed moves for the player"""
allowed_moves = []
for row in range(1, 9):
for column in COLUMNS:
coordinates = column + str(row)
# copy of the playing board for simulation
dummy_board = deepcopy(board)
# board only gets changed if it's a valid move
if valid_move(player_mark, coordinates, dummy_board):
allowed_moves.append(coordinates)
return allowed_moves
def user_plays(player_mark: str, allowed_moves: list[str], board: list[list[str]]):
"""retrieves valid coordinates from user and plays with it"""
# retrieves valid coordinates from user
coordinates = ""
while True:
print("Where do you want to play? ([a~h][1~8]) eg. a1, b4, g7, etc.")
coordinates = input()
if re.match("^[a-h][1-8]$", coordinates) is None:
print(
"Invalid coordinate. Valid coordinates: ([a~h][1~8]) eg. a1, b4, g7, etc."
)
elif coordinates not in allowed_moves:
print(
"Invalid move. Valid moves are empty spaces that flip opponent tiles."
)
else:
break
# play with the chosen valid coordinates on the playing board
valid_move(player_mark, coordinates, board)
def computer_plays(ai_mark: str, allowed_moves: list[str], board: list[list[str]]):
"""retrieves coordinates with highest score and plays with it"""
opponent_mark = "X" if ai_mark == "O" else "O"
scores = {}
for coordinates in allowed_moves:
# copy of the playing board for simulation
dummy_board = deepcopy(board)
# play with the valid move on the simulation board
valid_move(ai_mark, coordinates, dummy_board)
# get the score of the move and store them
scores[coordinates] = get_scores(ai_mark, opponent_mark, dummy_board)[0]
# get coordinate with max score
coordinate_max = max(scores, key=lambda x: scores[x], default="")
# play with the max score on the playing board
valid_move(ai_mark, coordinate_max, board)
print(f"Computer plays: {coordinate_max}")
def print_results(user_score: int, computer_score: int) -> None:
"""prints results at the end of the game"""
print("GAME OVER!")
if user_score > computer_score:
print("YOU WON")
elif user_score < computer_score:
print("COMPUTER WINS")
else:
print("IT'S A TIE")
if __name__ == "__main__":
playing_board = deepcopy(NEW_BOARD)
centers = {"X": [[3, 3], [4, 4]], "O": [[3, 4], [4, 3]]}
for key, value in centers.items():
playing_board[value[0][0]][value[0][1]] = key
playing_board[value[1][0]][value[1][1]] = key
print("R E V E R S E G A M")
time.sleep(2)
print("LET'S BEGIN")
draw_board(playing_board)
user_mark = ""
while user_mark != "X" and user_mark != "O":
print("Choose X or O:")
user_mark = input().upper()
computer_mark = "X" if user_mark == "O" else "O"
while True:
print("SCORES:")
user_score, computer_score = get_scores(user_mark, computer_mark, playing_board)
print(f"You: {user_score}")
print(f"Computer: {computer_score}")
print()
allowed_user_moves = get_allowed_moves(user_mark, playing_board)
if allowed_user_moves:
user_plays(user_mark, allowed_user_moves, playing_board)
draw_board(playing_board)
else:
print_results(user_score, computer_score)
break
print("Computer plays...", end="\r")
time.sleep(2)
allowed_computer_moves = get_allowed_moves(computer_mark, playing_board)
if allowed_computer_moves:
computer_plays(computer_mark, allowed_computer_moves, playing_board)
draw_board(playing_board)
else:
print_results(user_score, computer_score)
break