Last active
June 11, 2020 22:46
-
-
Save ksylvan/e9d6ecbd62fb831f54a4131a0c55a8a2 to your computer and use it in GitHub Desktop.
MineSweeper
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from random import randrange | |
class MinesGrid: | |
# Implement a sparse array to optimize space. | |
MINE = -1 | |
class Row: | |
# Each row in the __a dict has the following | |
# structure a list, containing: | |
# [ empty_cells, {} ] | |
def __init__(self, cols): | |
self.__cols = cols | |
self.__empty_cells = list(range(cols)) | |
self.__r = {} | |
def __str__(self): | |
d = { MinesGrid.MINE: 'X', 0: '.' } | |
return "".join(map(str, | |
(d[self[i]] if self[i] in d else self[i] | |
for i in range(self.__cols)))) | |
def __repr__(self): | |
return f"Row(cols={self.__cols}, empty_cells={self.__empty_cells}, r={self.__r})" | |
def __getitem__(self, k): | |
return self.__r[k] if k in self.__r else 0 | |
def __setitem__(self, k, v): | |
if k not in self.__r: | |
if k not in self.__empty_cells: | |
raise ValueError(f"{k} is not in a valid range.") | |
if v == MinesGrid.MINE: | |
self.__empty_cells.remove(k) | |
self.__r[k] = v | |
@property | |
def number_empty(self): | |
return len(self.__empty_cells) | |
@property | |
def random_cell(self): | |
if l := self.number_empty: | |
r = randrange(l) | |
return self.__empty_cells[r] | |
return None | |
class Empty: | |
def __getitem__(self, k): | |
return 0 | |
def __init__(self, rows, cols, mines): | |
if rows <= 0 or cols <= 0: | |
raise ValueError(f"rows and columns need to be positive integers. rows={rows}, cols={cols}") | |
if mines > rows * cols: | |
raise ValueError(f"Too many mines: {mines}") | |
self.__rows = rows | |
self.__cols = cols | |
self.__empty = rows * cols | |
self.__a = {} # This a a dict of Row objects | |
self.__empty_row = MinesGrid.Empty() | |
for _ in range(mines): | |
self.__place_mine() | |
def __repr__(self): | |
return f"MinesGrid(rows={self.__rows}, cols={self.__cols}, empty={self.__empty}, rows={self.__a}" | |
def __str__(self): | |
empty = "." * self.__cols | |
return "\n".join(empty if r not in self.__a else str(self.__a[r]) | |
for r in range(self.__rows)) | |
def __place_mine(self): | |
if self.__empty: | |
r = randrange(self.__rows) | |
while r in self.__a and self.__a[r].number_empty == 0: | |
r = (r + 1) % self.__cols | |
if r not in self.__a: | |
self.__a[r] = MinesGrid.Row(self.__cols) | |
c = self.__a[r].random_cell | |
self.__a[r][c] = MinesGrid.MINE | |
self.__update_counts(r, c) | |
self.__empty -= 1 | |
def __update_counts(self, r, c): | |
t = ( | |
(-1,-1), (-1, 0), (-1, 1), | |
(0, -1), (0, 1), | |
(1, -1), (1, 0), (1, 1)) | |
for t1, t2 in t: | |
if 0 <= r+t1 < self.__rows and 0 <= c+t2 < self.__cols: | |
u_r, u_c = r+t1, c+t2 | |
if u_r not in self.__a: | |
self.__a[u_r] = MinesGrid.Row(self.__cols) | |
if self.__a[u_r][u_c] != MinesGrid.MINE: | |
self.__a[u_r][u_c] += 1 | |
def __getitem__(self, k): | |
return self.__a[k] if k in self.__a else self.__empty_row | |
def __setitem__(self, k, v): | |
self.__a[k] = v | |
if __name__ == "__main__": | |
tests = ((16, 12, 4), (8, 8, 5), (5,5, 20)) | |
for r,c,m in tests: | |
o = MinesGrid(r, c, m) | |
print(f"{r}X{c} Grid with {m} mines:\n") | |
print(o) | |
print("") | |
# Sample output: | |
# | |
# 16X12 Grid with 4 mines: | |
# | |
# 11.......... | |
# X1.......... | |
# 11.......... | |
# ............ | |
# ............ | |
# ............ | |
# ............ | |
# ............ | |
# ...111...... | |
# ...1X1...... | |
# ...222...... | |
# ...1X1...... | |
# ...111...... | |
# ............ | |
# .111........ | |
# .1X1........ | |
# | |
# 8X8 Grid with 5 mines: | |
# | |
# 11..111. | |
# X1..1X21 | |
# 11.1222X | |
# ...1X111 | |
# ...111.. | |
# ........ | |
# ......11 | |
# ......1X | |
# | |
# 5X5 Grid with 20 mines: | |
# | |
# XX4XX | |
# XX7XX | |
# XXXXX | |
# XXXXX | |
# X434X |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment