patternpythonMinor
ASCII art minesweeper clone
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
Separation of concerns
The interaction with the outside world is all inside the main
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)
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
-
Indexing with magic numbers, such as
-
Overuse of the ternary operator:
This is probably the most confusing line of Python code I have ever come across.
Equivalent to:
Which would you rather read when trying to comprehend someone else's code?
-
Use
-
custom, not costum
-
neighbors, not neightbors
Structure & Logic
-
Overall, this seems well structured, with sensible use of functions.
However,
-
Your coordinate validation can be simplified. Instead of scanning through all these lists, use the convenience methods defined in
Notice that I replaced
-
Why bother using
-
If
-
There's no need to call
Apart from these (mostly minor) issues, the code is easy to follow. Kudos for using doctests!
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 FalseNotice 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 Falsefor x, _ in enumerate(board):for x in xrange(len(board)):Context
StackExchange Code Review Q#113190, answer score: 9
Revisions (0)
No revisions yet.