forked from prathimacode-hub/Awesome_Python_Scripts
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathminesweeper.py
204 lines (168 loc) · 7.94 KB
/
minesweeper.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
import random
import re
# lets create a board object to represent the minesweeper game
# this is so that we can just say "create a new board object", or
# "dig here", or "render this game for this object"
class Board:
def __init__(self, dim_size, num_bombs):
# let's keep track of these parameters. they'll be helpful later
self.dim_size = dim_size
self.num_bombs = num_bombs
# let's create the board
# helper function!
self.board = self.make_new_board() # plant the bombs
self.assign_values_to_board()
# initialize a set to keep track of which locations we've uncovered
# we'll save (row,col) tuples into this set
self.dug = set() # if we dig at 0, 0, then self.dug = {(0,0)}
def make_new_board(self):
# construct a new board based on the dim size and num bombs
# we should construct the list of lists here (or whatever representation you prefer,
# but since we have a 2-D board, list of lists is most natural)
# generate a new board
board = [[None for _ in range(self.dim_size)] for _ in range(self.dim_size)]
# this creates an array like this:
# [[None, None, ..., None],
# [None, None, ..., None],
# [... ],
# [None, None, ..., None]]
# we can see how this represents a board!
# plant the bombs
bombs_planted = 0
while bombs_planted < self.num_bombs:
loc = random.randint(0, self.dim_size ** 2 - 1) # return a random integer N such that a <= N <= b
row = loc // self.dim_size # we want the number of times dim_size goes into loc to tell us what row to look at
col = loc % self.dim_size # we want the remainder to tell us what index in that row to look at
if board[row][col] == '*':
# this means we've actually planted a bomb there already so keep going
continue
board[row][col] = '*' # plant the bomb
bombs_planted += 1
return board
def assign_values_to_board(self):
# now that we have the bombs planted, let's assign a number 0-8 for all the empty spaces, which
# represents how many neighboring bombs there are. we can precompute these and it'll save us some
# effort checking what's around the board later on :)
for r in range(self.dim_size):
for c in range(self.dim_size):
if self.board[r][c] == '*':
# if this is already a bomb, we don't want to calculate anything
continue
self.board[r][c] = self.get_num_neighboring_bombs(r, c)
def get_num_neighboring_bombs(self, row, col):
# let's iterate through each of the neighboring positions and sum number of bombs
# top left: (row-1, col-1)
# top middle: (row-1, col)
# top right: (row-1, col+1)
# left: (row, col-1)
# right: (row, col+1)
# bottom left: (row+1, col-1)
# bottom middle: (row+1, col)
# bottom right: (row+1, col+1)
# make sure to not go out of bounds!
num_neighboring_bombs = 0
for r in range(max(0, row - 1), min(self.dim_size - 1, row + 1) + 1):
for c in range(max(0, col - 1), min(self.dim_size - 1, col + 1) + 1):
if r == row and c == col:
# our original location, don't check
continue
if self.board[r][c] == '*':
num_neighboring_bombs += 1
return num_neighboring_bombs
def dig(self, row, col):
# dig at that location!
# return True if successful dig, False if bomb dug
# a few scenarios:
# hit a bomb -> game over
# dig at location with neighboring bombs -> finish dig
# dig at location with no neighboring bombs -> recursively dig neighbors!
self.dug.add((row, col)) # keep track that we dug here
if self.board[row][col] == '*':
return False
elif self.board[row][col] > 0:
return True
# self.board[row][col] == 0
for r in range(max(0, row - 1), min(self.dim_size - 1, row + 1) + 1):
for c in range(max(0, col - 1), min(self.dim_size - 1, col + 1) + 1):
if (r, c) in self.dug:
continue # don't dig where you've already dug
self.dig(r, c)
# if our initial dig didn't hit a bomb, we *shouldn't* hit a bomb here
return True
def __str__(self):
# this is a magic function where if you call print on this object,
# it'll print out what this function returns!
# return a string that shows the board to the player
# first let's create a new array that represents what the user would see
visible_board = [[None for _ in range(self.dim_size)] for _ in range(self.dim_size)]
for row in range(self.dim_size):
for col in range(self.dim_size):
if (row, col) in self.dug:
visible_board[row][col] = str(self.board[row][col])
else:
visible_board[row][col] = ' '
# put this together in a string
string_rep = ''
# get max column widths for printing
widths = []
for idx in range(self.dim_size):
columns = map(lambda x: x[idx], visible_board)
widths.append(
len(
max(columns, key=len)
)
)
# print the csv strings
indices = [i for i in range(self.dim_size)]
indices_row = ' '
cells = []
for idx, col in enumerate(indices):
format = '%-' + str(widths[idx]) + "s"
cells.append(format % (col))
indices_row += ' '.join(cells)
indices_row += ' \n'
for i in range(len(visible_board)):
row = visible_board[i]
string_rep += f'{i} |'
cells = []
for idx, col in enumerate(row):
format = '%-' + str(widths[idx]) + "s"
cells.append(format % (col))
string_rep += ' |'.join(cells)
string_rep += ' |\n'
str_len = int(len(string_rep) / self.dim_size)
string_rep = indices_row + '-' * str_len + '\n' + string_rep + '-' * str_len
return string_rep
# play the game
def play(dim_size=10, num_bombs=10):
# Step 1: create the board and plant the bombs
board = Board(dim_size, num_bombs)
# Step 2: show the user the board and ask for where they want to dig
# Step 3a: if location is a bomb, show game over message
# Step 3b: if location is not a bomb, dig recursively until each square is at least
# next to a bomb
# Step 4: repeat steps 2 and 3a/b until there are no more places to dig -> VICTORY!
safe = True
while len(board.dug) < board.dim_size ** 2 - num_bombs:
print(board)
# 0,0 or 0, 0 or 0, 0
user_input = re.split(',(\\s)*', input("Where would you like to dig? Input as row,col: ")) # '0, 3'
row, col = int(user_input[0]), int(user_input[-1])
if row < 0 or row >= board.dim_size or col < 0 or col >= dim_size:
print("Invalid location. Try again.")
continue
# if it's valid, we dig
safe = board.dig(row, col)
if not safe:
# dug a bomb ahhhhhhh
break # (game over rip)
# 2 ways to end loop, lets check which one
if safe:
print("CONGRATULATIONS!!!! YOU ARE VICTORIOUS!")
else:
print("SORRY GAME OVER :(")
# let's reveal the whole board!
board.dug = [(r, c) for r in range(board.dim_size) for c in range(board.dim_size)]
print(board)
if __name__ == '__main__': # good practice :)
play()