Skip to content

Instantly share code, notes, and snippets.

@ksylvan
Last active June 11, 2020 22:46

Revisions

  1. ksylvan revised this gist Jun 11, 2020. 1 changed file with 40 additions and 0 deletions.
    40 changes: 40 additions & 0 deletions mines.py
    Original file line number Diff line number Diff line change
    @@ -103,3 +103,43 @@ def __setitem__(self, k, v):
    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
  2. ksylvan created this gist Jun 11, 2020.
    105 changes: 105 additions & 0 deletions mines.py
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,105 @@
    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("")