-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcheckers.py
More file actions
244 lines (235 loc) · 12.8 KB
/
Copy pathcheckers.py
File metadata and controls
244 lines (235 loc) · 12.8 KB
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
"""
Author: Hannah Webb
Creation Date: 12/10/25
Purpose: Builds all components of checkers game.
"""
class Board:
def __init__(self, size=""):
self.size = size
def create(self): # creates board string
row = ""
board = ""
self.size = int(self.size)
row_num = self.size
# creates numbered red player rows
for i in range((self.size//2) - 1):
row += str(row_num) + "|"
while len(row) < (self.size * 2):
if (self.size - i) % 2 == 0:
row += "-|r|"
else:
row += "r|-|"
row_num -= 1
board += row + "\n"
row = ""
# creates numbered empty middle rows
for i in range(2):
row += str(row_num)
while len(row) < (self.size * 2):
row += "|-"
row += "|"
row_num -= 1
board += row + "\n"
row = ""
# creates numbered black player rows
for i in range((self.size//2) - 1):
row += str(row_num) + "|"
while len(row) < (self.size * 2):
if (row_num) % 2 == 0:
row += "-|b|"
else:
row += "b|-|"
row_num -= 1
board += row + "\n"
row = ""
row_num % 2
# creates column letter reference row
row = " "
for k in range(self.size):
row += chr(k + 65)+ " "
board += row + "\n"
return board
# ---------------------------------------------------------------
class MainMenu: # sets up your game
def __init__(self):
print("\n-----------------------CHECKERS-----------------------")
# ---------------------------------------------------------------
class Piece: # piece creator
def __init__(self, board, color, name):
self.board = board
self.color = color
self.name = name
# ---------------------------------------------------------------
class Game: # contains all actions that can happen in a game
def __init__(self):
self.board = None
self.turn = "b"
def set(self): # set up your game board
size = ""
while size == "" or size not in "468":
size = input("Select a SIZE of board (4, 6, or 8): ")
self.board = Board(size)
print("\n" + self.board.create())
return
def get_piece_at_name(self, name, board): # retrieves piece at name (e.g. "b" at "a1")
name = name.lower()
col = name[0]
row_num = int(name[1])
coli = (ord(col) - 97) * 2 + 3
row_len = (self.board.size * 2) + 3
diff = (self.board.size - row_num) * row_len
pos = diff + coli
return board[pos - 1]
def get_pos_at_name(self, name, board): # retrieves str pos at name (e.g. "-" at 12)
row_num = int(name[1])
col = name[0].lower()
row_len = (self.board.size * 2) + 3
row_start = (self.board.size - row_num) * row_len
diff = (ord(col) - ord('a')) * 2 + 2
return row_start + diff
def is_valid_position(self, name, size): # checks if the name is one even on the board (e.g. False that "d6" is on a size 4 board)
name = name.lower()
if len(name) != 2:
return False
col = name[0].lower()
row = name[1]
if ord(row) < ord('1') or ord(row) > ord(str(size)):
return False
if ord(col) < ord('a') or ord(col) > ord('a') + size - 1:
return False
return True
def get_moves(self, name, board): # returns string of all possible moves the player can make with a chosen piece (e.g. "b2 " for piece "a1")
move_set = ""
piece = self.get_piece_at_name(name, board)
size = int(self.board.size)
def ur(p): # returns upper right piece name
return chr(ord(p[0]) + 1) + str(int(p[1]) + 1)
def ul(p): # returns upper left piece name
return chr(ord(p[0]) - 1) + str(int(p[1]) + 1)
def lr(p): # returns lower right piece name
return chr(ord(p[0]) + 1) + str(int(p[1]) - 1)
def ll(p): # returns lower left piece name
return chr(ord(p[0]) - 1) + str(int(p[1]) - 1)
ur_name = ur(name) # gets upper right piece name
if self.is_valid_position(ur_name, size) and (piece.lower() == "b" or piece == "R"): # checks if UR is a valid position and if piece can move to the UR
if self.get_piece_at_name(ur_name, board) == "-": # if UR is free, add that move to move set (a normal move)
move_set += ur_name + " "
# if UR is red and our piece is black OR if UR is black piece and our piece is red...
elif ((self.get_piece_at_name(ur_name, board).lower() == "r") and (piece.lower() == "b")) or ((self.get_piece_at_name(ur_name, board).lower() == "b") and (piece.lower() == "r")):
if self.is_valid_position(ur(ur_name), size) and self.get_piece_at_name(ur(ur_name), board) == "-": # and if the UR of UR is free
move_set += ur(ur_name) + " " # add the UR of UR to move set (a JUMP move)
ul_name = ul(name) # gets upper left piece name
if self.is_valid_position(ul_name, size) and (piece.lower() == "b" or piece == "R"): # checks if UL is a valid position and if piece can move to the UL
if self.get_piece_at_name(ul_name, board) == "-": # if UL is free, add that move to move set (a normal move)
move_set += ul_name + " "
# if UR is red and our piece is black OR if UR is black piece and our piece is red...
elif ((self.get_piece_at_name(ul_name, board).lower() == "r") and (piece.lower() == "b")) or ((self.get_piece_at_name(ul_name, board).lower() == "b") and (piece.lower() == "r")):
if self.is_valid_position(ul(ul_name), size) and self.get_piece_at_name(ul(ul_name), board) == "-": # and if the UL of UL is free
move_set += ul(ul_name) + " " # add the UL of UL to move set (a JUMP move)
lr_name = lr(name) # gets lower right piece name
if self.is_valid_position(lr_name, size) and (piece.lower() == "r" or piece == "B"): # checks if LR is a valid position and if piece can move to the LR
if self.get_piece_at_name(lr_name, board) == "-": # if LR is free, add that move to move set (a normal move)
move_set += lr_name + " "
# if LR is red and our piece is black OR if LR is black piece and our piece is red...
elif ((self.get_piece_at_name(lr_name, board).lower() == "r") and (piece.lower() == "b")) or ((self.get_piece_at_name(lr_name, board).lower() == "b") and (piece.lower() == "r")):
if self.is_valid_position(lr(lr_name), size) and self.get_piece_at_name(lr(lr_name), board) == "-": # and if the LR of LR is free
move_set += lr(lr_name) + " " # add the LR of LR to move set (a JUMP move)
ll_name = ll(name) # gets lower left piece name
if self.is_valid_position(ll_name, size) and (piece.lower() == "r" or piece == "B"): # checks if LL is a valid position and if piece can move to the LL
if self.get_piece_at_name(ll_name, board) == "-": # if LL is free, add that move to move set (a normal move)
move_set += ll_name + " "
# if LL is red and our piece is black OR if LL is black piece and our piece is red...
elif ((self.get_piece_at_name(ll_name, board).lower() == "r") and (piece.lower() == "b")) or ((self.get_piece_at_name(ll_name, board).lower() == "b") and (piece.lower() == "r")):
if self.is_valid_position(ll(ll_name), size) and self.get_piece_at_name(ll(ll_name), board) == "-": # and if the LL of LL is free
move_set += ll(ll_name) + " " # add the LL of LL to move set (a JUMP move)
return move_set
def select_move(self, name, moves, board):
# prompts player for a valid move
user_move = input("Select a move to make (Available moves: " + moves + "): ").lower().strip()
print("\n")
while user_move not in moves or user_move is None or user_move == "":
print("Invalid move, please try again")
user_move = input("Select a move to make (Available moves: " + moves + "): ").lower().strip()
# first, let's create a board where we have moved to the new index and replaced the old
new_pos = self.get_pos_at_name(user_move, board)
old_pos = self.get_pos_at_name(name, board)
old_piece = self.get_piece_at_name(name, board)
if new_pos > old_pos: # if we're moving in a forward direction
new_board = board[:old_pos] + "-" + board[old_pos + 1:new_pos] + old_piece + board[new_pos + 1:]
else:
new_board = board[:new_pos] + old_piece + board[new_pos + 1:old_pos] + "-" + board[old_pos + 1:]
# next, let's check if a piece was jumped and remove that piece if it was
if abs(int(user_move[1]) - int(name[1])) == 2:
jumped_row = str((int(user_move[1]) + int(name[1]))//2)
jumped_col = chr((ord(user_move[0]) + ord(name[0]))//2)
jumped_name = jumped_col + jumped_row
jumped_pos = self.get_pos_at_name(jumped_name, new_board)
new_board = new_board[:jumped_pos] + "-" + new_board[jumped_pos + 1:]
return new_board
def select_piece(self, board):
# prompts player for a valid piece
user_piece = input("Select a valid piece by column then row (e.g. c1): ")
valid_pieces = self.turn + self.turn.upper()
while not self.is_valid_position(user_piece, int(self.board.size)) or self.get_piece_at_name(user_piece, board) not in valid_pieces or self.get_moves(user_piece, board) == "":
print("Invalid piece, please try again")
user_piece = input("Select a valid piece by column then row (e.g. c1): ")
return user_piece
def king(self, board):
# if there is a kingable piece, king that piece and return the updated board
kinged_board = board
row_len = (self.board.size * 2) + 3
first_row_start = 0
first_row_end = row_len
last_row_start = row_len * (self.board.size - 1)
last_row_end = row_len * self.board.size
first_row = kinged_board[first_row_start:first_row_end]
if 'b' in first_row: # if there's a 'b' that's made it to the first row, king that piece
i = first_row_start + first_row.index("b")
kinged_board = kinged_board[:i] + "B" + kinged_board[i + 1:]
last_row = kinged_board[last_row_start:last_row_end]
if "r" in last_row: # if there's a 'r' that's made it to the last row, king that piece
i = last_row_start + last_row.index("r")
kinged_board = kinged_board[:i] + "R" + kinged_board[i + 1:]
return kinged_board
def play(self):
board_str = self.board.create()
# get a board slice that excludes bottom letters, so there's no B when we check for a win
board_slice = board_str[:(((self.board.size*2) + 3) * self.board.size)]
# win condition: if there are still r's and b's in the board slice, then keep playing
while (("r" in board_slice or "R" in board_slice) and ("b" in board_slice or "B" in board_slice)):
# print whose turn it is
if self.turn == "b":
print("Black's turn to move")
elif self.turn == "r":
print("Red's turn to move")
# select a valid piece
piece = Piece(board_str, self.turn, self.select_piece(board_str))
# select and execute a valid move
board_str = self.select_move(piece.name, self.get_moves(piece.name, board_str), board_str)
# checks if any pieces can be kinged
board_str = self.king(board_str)
# print updated board
print(board_str)
board_slice = board_str[:(((self.board.size*2) + 3) * self.board.size)]
# change turn
if self.turn == "b":
self.turn = "r"
elif self.turn == "r":
self.turn = "b"
if "r" in board_slice or "R" in board_slice: # print winner banners
print("-----------------------RED WINS-----------------------\n")
else:
print("----------------------BLACK WINS----------------------\n")
# ---------------------------------------------------------------
if __name__ == "__main__":
playing = "y" # note that this implementation allows the user to play unlimited games
while playing.lower() == "y":
menu = MainMenu()
new_game = Game()
new_game.set()
new_game.play()
playing = input("Play again? (y/n): ")
while playing.lower() != "y" and playing.lower() != "n":
print("Invalid choice. Please try again")
playing = input("Play again? (y/n): ")
print("Thanks for playing!")