Skip to content

Commit 9977826

Browse files
author
Sai Rahul
authored
This is the code to play the game
1 parent c0cbd92 commit 9977826

File tree

1 file changed

+205
-0
lines changed

1 file changed

+205
-0
lines changed
+205
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
"""
2+
Implementation of command-line minesweeper by Sai Rahul
3+
"""
4+
5+
import random
6+
import re
7+
8+
# lets create a board object to represent the minesweeper game
9+
# this is so that we can just say "create a new board object", or
10+
# "dig here", or "render this game for this object"
11+
class Board:
12+
def __init__(self, dim_size, num_bombs):
13+
# let's keep track of these parameters. they'll be helpful later
14+
self.dim_size = dim_size
15+
self.num_bombs = num_bombs
16+
17+
# let's create the board
18+
# helper function!
19+
self.board = self.make_new_board() # plant the bombs
20+
self.assign_values_to_board()
21+
22+
# initialize a set to keep track of which locations we've uncovered
23+
# we'll save (row,col) tuples into this set
24+
self.dug = set() # if we dig at 0, 0, then self.dug = {(0,0)}
25+
26+
def make_new_board(self):
27+
# construct a new board based on the dim size and num bombs
28+
# we should construct the list of lists here (or whatever representation you prefer,
29+
# but since we have a 2-D board, list of lists is most natural)
30+
31+
# generate a new board
32+
board = [[None for _ in range(self.dim_size)] for _ in range(self.dim_size)]
33+
# this creates an array like this:
34+
# [[None, None, ..., None],
35+
# [None, None, ..., None],
36+
# [... ],
37+
# [None, None, ..., None]]
38+
# we can see how this represents a board!
39+
40+
# plant the bombs
41+
bombs_planted = 0
42+
while bombs_planted < self.num_bombs:
43+
loc = random.randint(0, self.dim_size**2 - 1) # return a random integer N such that a <= N <= b
44+
row = loc // self.dim_size # we want the number of times dim_size goes into loc to tell us what row to look at
45+
col = loc % self.dim_size # we want the remainder to tell us what index in that row to look at
46+
47+
if board[row][col] == '*':
48+
# this means we've actually planted a bomb there already so keep going
49+
continue
50+
51+
board[row][col] = '*' # plant the bomb
52+
bombs_planted += 1
53+
54+
return board
55+
56+
def assign_values_to_board(self):
57+
# now that we have the bombs planted, let's assign a number 0-8 for all the empty spaces, which
58+
# represents how many neighboring bombs there are. we can precompute these and it'll save us some
59+
# effort checking what's around the board later on :)
60+
for r in range(self.dim_size):
61+
for c in range(self.dim_size):
62+
if self.board[r][c] == '*':
63+
# if this is already a bomb, we don't want to calculate anything
64+
continue
65+
self.board[r][c] = self.get_num_neighboring_bombs(r, c)
66+
67+
def get_num_neighboring_bombs(self, row, col):
68+
# let's iterate through each of the neighboring positions and sum number of bombs
69+
# top left: (row-1, col-1)
70+
# top middle: (row-1, col)
71+
# top right: (row-1, col+1)
72+
# left: (row, col-1)
73+
# right: (row, col+1)
74+
# bottom left: (row+1, col-1)
75+
# bottom middle: (row+1, col)
76+
# bottom right: (row+1, col+1)
77+
78+
# make sure to not go out of bounds!
79+
80+
num_neighboring_bombs = 0
81+
for r in range(max(0, row-1), min(self.dim_size-1, row+1)+1):
82+
for c in range(max(0, col-1), min(self.dim_size-1, col+1)+1):
83+
if r == row and c == col:
84+
# our original location, don't check
85+
continue
86+
if self.board[r][c] == '*':
87+
num_neighboring_bombs += 1
88+
89+
return num_neighboring_bombs
90+
91+
def dig(self, row, col):
92+
# dig at that location!
93+
# return True if successful dig, False if bomb dug
94+
95+
# a few scenarios:
96+
# hit a bomb -> game over
97+
# dig at location with neighboring bombs -> finish dig
98+
# dig at location with no neighboring bombs -> recursively dig neighbors!
99+
100+
self.dug.add((row, col)) # keep track that we dug here
101+
102+
if self.board[row][col] == '*':
103+
return False
104+
elif self.board[row][col] > 0:
105+
return True
106+
107+
# self.board[row][col] == 0
108+
for r in range(max(0, row-1), min(self.dim_size-1, row+1)+1):
109+
for c in range(max(0, col-1), min(self.dim_size-1, col+1)+1):
110+
if (r, c) in self.dug:
111+
continue # don't dig where you've already dug
112+
self.dig(r, c)
113+
114+
# if our initial dig didn't hit a bomb, we *shouldn't* hit a bomb here
115+
return True
116+
117+
def __str__(self):
118+
# this is a magic function where if you call print on this object,
119+
# it'll print out what this function returns!
120+
# return a string that shows the board to the player
121+
122+
# first let's create a new array that represents what the user would see
123+
visible_board = [[None for _ in range(self.dim_size)] for _ in range(self.dim_size)]
124+
for row in range(self.dim_size):
125+
for col in range(self.dim_size):
126+
if (row,col) in self.dug:
127+
visible_board[row][col] = str(self.board[row][col])
128+
else:
129+
visible_board[row][col] = ' '
130+
131+
# put this together in a string
132+
string_rep = ''
133+
# get max column widths for printing
134+
widths = []
135+
for idx in range(self.dim_size):
136+
columns = map(lambda x: x[idx], visible_board)
137+
widths.append(
138+
len(
139+
max(columns, key = len)
140+
)
141+
)
142+
143+
# print the csv strings
144+
indices = [i for i in range(self.dim_size)]
145+
indices_row = ' '
146+
cells = []
147+
for idx, col in enumerate(indices):
148+
format = '%-' + str(widths[idx]) + "s"
149+
cells.append(format % (col))
150+
indices_row += ' '.join(cells)
151+
indices_row += ' \n'
152+
153+
for i in range(len(visible_board)):
154+
row = visible_board[i]
155+
string_rep += f'{i} |'
156+
cells = []
157+
for idx, col in enumerate(row):
158+
format = '%-' + str(widths[idx]) + "s"
159+
cells.append(format % (col))
160+
string_rep += ' |'.join(cells)
161+
string_rep += ' |\n'
162+
163+
str_len = int(len(string_rep) / self.dim_size)
164+
string_rep = indices_row + '-'*str_len + '\n' + string_rep + '-'*str_len
165+
166+
return string_rep
167+
168+
# play the game
169+
def play(dim_size=10, num_bombs=10):
170+
# Step 1: create the board and plant the bombs
171+
board = Board(dim_size, num_bombs)
172+
173+
# Step 2: show the user the board and ask for where they want to dig
174+
# Step 3a: if location is a bomb, show game over message
175+
# Step 3b: if location is not a bomb, dig recursively until each square is at least
176+
# next to a bomb
177+
# Step 4: repeat steps 2 and 3a/b until there are no more places to dig -> VICTORY!
178+
safe = True
179+
180+
while len(board.dug) < board.dim_size ** 2 - num_bombs:
181+
print(board)
182+
# 0,0 or 0, 0 or 0, 0
183+
user_input = re.split(',(\\s)*', input("Where would you like to dig? Input as row,col: ")) # '0, 3'
184+
row, col = int(user_input[0]), int(user_input[-1])
185+
if row < 0 or row >= board.dim_size or col < 0 or col >= dim_size:
186+
print("Invalid location. Try again.")
187+
continue
188+
189+
# if it's valid, we dig
190+
safe = board.dig(row, col)
191+
if not safe:
192+
# dug a bomb ahhhhhhh
193+
break # (game over rip)
194+
195+
# 2 ways to end loop, lets check which one
196+
if safe:
197+
print("CONGRATULATIONS!!!! YOU ARE VICTORIOUS!")
198+
else:
199+
print("SORRY GAME OVER :(")
200+
# let's reveal the whole board!
201+
board.dug = [(r,c) for r in range(board.dim_size) for c in range(board.dim_size)]
202+
print(board)
203+
204+
if __name__ == '__main__': # good practice :)
205+
play()

0 commit comments

Comments
 (0)