Skip to content

Instantly share code, notes, and snippets.

@tkshill
Created June 7, 2021 23:15
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tkshill/dafd645d9c12928662f0144a95309dba to your computer and use it in GitHub Desktop.
Save tkshill/dafd645d9c12928662f0144a95309dba to your computer and use it in GitHub Desktop.
Python 3.10 with Typing and MyPy
from typing import NewType, Literal, Union, Tuple
# Our standard grid is a 2d array of cells that are either a mine
# or not a mine
Mine = Literal["Mine"]
NotAMine = Literal["NotAMine"]
Cell = Union[Mine, NotAMine]
Grid = list[list[Cell]]
# Our annotated grid is a 2d array of cells that are either the number
# of mines nearby or NoMinesNearby or a Mine
NoMinesNearby = Literal["NoMinesNearby"]
MinesNearby = NewType('MinesNearby', int)
AnnotatedCell = Union[NoMinesNearby, MinesNearby, Mine]
AnnotatedGrid = list[list[AnnotatedCell]]
def annotate(minefield: list[str]) -> list[str]:
"""Given a list of strings representing rows in a minesweeper table,
returns a list of strings representing annotated rows."""
# if all rows aren't the same length as the first row, throw an error
if not all(len(row) == len(minefield[0]) for row in minefield):
raise ValueError("Strings must be the same length")
grid: Grid = [list(map(toCell, row)) for row in minefield]
annotatedGrid = toAnnotatedGrid(grid)
result = ["".join((toChar(ac) for ac in row)) for row in annotatedGrid]
return result
def toCell(char: str) -> Cell:
match char:
case "*": return "Mine"
case " ": return "NotAMine"
case _: raise ValueError("Should be whitespace or an asterisk")
def toAnnotatedGrid(grid: Grid) -> AnnotatedGrid:
annotatedGrid: AnnotatedGrid = []
for i, row in enumerate(grid):
annotatedGrid.append([])
for j, cell in enumerate(row):
if cell == "Mine": # just pass along to the annotated grid
annotatedGrid[i].append(cell)
else:
mineNumber = countMines(grid, i, j)
if mineNumber == 0:
annotatedGrid[i].append("NoMinesNearby")
else:
annotatedGrid[i].append(MinesNearby(mineNumber))
return annotatedGrid
def countMines(grid: Grid, i: int, j: int) -> int:
"""Given a grid and the position of cell in said grid,
count the number of mines around that cell."""
# using max and min to clamp range down to values that exist in grid
rowRange = range(max(i-1, 0), min(i+2, len(grid)))
colRange = range(max(j-1, 0), min(j+2, len(grid[i])))
minesFound = 0
for r in rowRange:
for c in colRange:
if grid[r][c] == "Mine":
minesFound += 1
return minesFound
def toChar(ac: AnnotatedCell) -> str:
match ac:
case "NoMinesNearby": return " "
case "Mine": return "*"
case _: return str(ac) # convert our mines nearby count to string
@laoshaw
Copy link

laoshaw commented Aug 31, 2022

Tuple, Union are no longer needed in 3.10?

@tkshill
Copy link
Author

tkshill commented Sep 25, 2022

Tuple, Union are no longer needed in 3.10?

Yes, I just found the PEPs that address this thanks to your comment. Unions can be achieved with | and Tuples simply need the []. Thank you. Very much appreciated.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment