HiveBrain v1.2.0
Get Started
← Back to all entries
patternpythonMinor

ASCII art minesweeper clone

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
artasciicloneminesweeper

Problem

I wanted to code a small ASCII art minesweeper clone, to see what it looked like and how much code it needed.

The result is not so bad, under 80 new lines of code in total. It looks kinda readable to me, but I wrote it just know, so I'm biased.

Input checking...

I feel like I got pretty much obsessed with input checking, so much that entering garbage will always produce nice error reports to the user and not crash.

Parsing coordinates is a worthy effort

Validating and parsing coordinates of the form LETTER + NUMBER is a pain, but I feel like that input format is user friendly, so it is going to remain that way.

Separation of concerns

The interaction with the outside world is all inside the main minesweeper function, because mixing concerns of logic and interaction together makes the code unreadable.

Doctests

The code has some doctests, as I feel like seeing an example usage of a function makes the code easier to read (apart from giving a sanity check).

Re-use

I re-used some of my old code, a module about the game of life and a module about the user interaction for this. You can find them in a gist.

```
import doctest
import string
import random

import life
from user_interface import general_input

MINE = -1

def minesweeper():
size = general_input("The size of the board? ", type_=int, set_=range(1, 9+1))
mines = general_input("The number of mines? ", type_=int, set_=range(1, size*size+1))
board = random_mineswepper_board(size, mines)

already_seen = set()
while True:
print(''.join(show_board(board, already_seen)))
raw_coordinates = general_input(
"Enter the coordinates: ",
costum_validation = lambda coord: is_coordinate_input_valid(coord, board),
costum_validation_error = """
Coordinate must be in the form LETTER + NUMBER
And must not exceed the size of the board.
Example: B4
"""
)
x, y = parse_coordinates(raw_coordinates)

already_seen.add((x,y)

Solution

Overall, your code is quite good. But there's always room for improvement:

Syntax & Spelling

-
Your question is tagged as python-2.7 so use print as a statement rather than as a function, or, better yet, use from __future__ import print_function to gain access to this feature.

-
Indexing with magic numbers, such as coords[1], is ugly. Prefer sequence unpacking: col, row = coords

-
Overuse of the ternary operator:

if True if cheat else (x, y) in already_seen:


This is probably the most confusing line of Python code I have ever come across.
Equivalent to:

if cheat or (x, y) in already_seen:


Which would you rather read when trying to comprehend someone else's code?

-
Use xrange instead of range unless you need to keep the list

-
custom, not costum

-
neighbors, not neightbors

Structure & Logic

-
Overall, this seems well structured, with sensible use of functions.
However, is_mine is only used once and should probably be inlined—here, the function does not provide a higher level of abstraction than just writing out the code: board[x][y] == MINE

-
Your coordinate validation can be simplified. Instead of scanning through all these lists, use the convenience methods defined in str:

try:
    col, row = coords
    return (col.isalpha() and
            row.isdigit() and
            col.upper() < string.ascii_uppercase[board_size] and
            int(row) <= board_size)
except ValueError:
    return False


Notice that I replaced board with board_size in is_coordinate_input_valid. There's no need to pass in the whole board when all we need is its size.

-
show_board does not actually print the board, so it should be renamed to something like format_board. In it, you do this twice:

for x, _ in enumerate(board):


Why bother using enumerate when you through away the second element of the result? Just use

for x in xrange(len(board)):


-
If MINE = 'M' instead of MINE = -1, then you could avoid another ternary expression when printing the board: yield str(board[x][y])

-
There's no need to call random.seed at the bottom.

Apart from these (mostly minor) issues, the code is easy to follow. Kudos for using doctests!

Code Snippets

if True if cheat else (x, y) in already_seen:
if cheat or (x, y) in already_seen:
try:
    col, row = coords
    return (col.isalpha() and
            row.isdigit() and
            col.upper() < string.ascii_uppercase[board_size] and
            int(row) <= board_size)
except ValueError:
    return False
for x, _ in enumerate(board):
for x in xrange(len(board)):

Context

StackExchange Code Review Q#113190, answer score: 9

Revisions (0)

No revisions yet.