 # MineSweeper_Python_Version

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