Skip to content

Instantly share code, notes, and snippets.

@MasonM
Created September 9, 2016 18:05
Show Gist options
  • Save MasonM/6e81d65a624209be15e8707d71d22968 to your computer and use it in GitHub Desktop.
Save MasonM/6e81d65a624209be15e8707d71d22968 to your computer and use it in GitHub Desktop.
#!/usr/bin/env python3
import random
import collections
import re
class MinesweeperGrid:
def __init__(self, size, num_bombs):
self.size = size
self.num_bombs = num_bombs
self.num_opened = 0
self.__init_grid()
self.__place_bombs()
def __init_grid(self):
self.grid = []
for row in range(self.size):
self.grid.append([])
for col in range(self.size):
self.grid[row].append(MinesweeperGridCell(row, col))
def __place_bombs(self):
bombs = []
while True:
index = random.randint(0, self.size ** 2 - 1)
row = index // self.size
col = index % self.size
cell = self.grid[row][col]
if cell in bombs:
continue
cell.has_bomb = True
for neighbor in self.__get_neighbors(cell).values():
neighbor.num_surrounding_bombs += 1
bombs.append(cell)
if len(bombs) == self.num_bombs:
break
def has_won(self):
return self.num_opened == (self.size ** 2 - self.num_bombs)
def open(self, row, col):
cell = self.grid[row][col]
cell.opened = True
if cell.has_bomb:
return True
queue = collections.deque([cell])
while queue:
cell = queue.popleft()
cell.opened = True
self.num_opened += 1
if cell.num_surrounding_bombs != 0:
continue
for direction, neighbor in self.__get_neighbors(cell).items():
if not neighbor.has_bomb and \
not neighbor.opened and \
direction in ['above', 'below', 'left', 'right']:
neighbor.opened = True
queue.append(neighbor)
return False
def __get_neighbors(self, cell):
neighbors = {}
row, col = cell.row, cell.col
if row > 0:
neighbors['above'] = self.grid[row - 1][col]
if col > 0:
neighbors['above_left'] = self.grid[row - 1][col - 1]
if col < self.size - 1:
neighbors['above_right'] = self.grid[row - 1][col + 1]
if row < self.size - 1:
neighbors['below'] = self.grid[row + 1][col]
if col > 0:
neighbors['below_left'] = self.grid[row + 1][col - 1]
if col < self.size - 1:
neighbors['below_right'] = self.grid[row + 1][col + 1]
if col > 0:
neighbors['left'] = self.grid[row][col - 1]
if col < self.size - 1:
neighbors['right'] = self.grid[row][col + 1]
return neighbors
def render(self, show_all=False):
out = " " + "".join(map(str, range(1, self.size + 1))) + "\n"
for row_num, row in enumerate(self.grid):
out += "{} [{}]\n".format(row_num + 1, "".join(cell.render(show_all) for cell in row))
return out
class MinesweeperGridCell:
def __init__(self, row, col):
self.row = row
self.col = col
self.opened = False
self.has_bomb = False
self.num_surrounding_bombs = 0
def render(self, show_all=False):
if self.opened:
if self.has_bomb:
return '!'
if self.num_surrounding_bombs == 0:
return 'O'
return str(self.num_surrounding_bombs)
if show_all:
return 'X' if self.has_bomb else '_'
return '?'
class Minesweeper:
def __init__(self, size, num_bombs, debug=False):
self.debug = debug
self.grid = MinesweeperGrid(size, num_bombs)
def get_input(self):
user_input = input("Your move in row,col format:")
match = re.match("(\d+), *(\d+)", user_input)
if not match:
print("Invalid input")
return self.get_input()
row, col = match.groups()
if not row.isdigit() or not col.isdigit():
print("Invalid input")
return self.get_input()
return (int(row) - 1, int(col) - 1)
def play(self):
while True:
print(self.grid.render(self.debug))
row, col = self.get_input()
if self.grid.open(row, col):
print("You hit a bomb!")
break
if self.grid.has_won():
print("You win!")
break
print(self.grid.render(True))
debug = False
a = Minesweeper(4, 7, debug)
a.play()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment