Reputation: 45
I'm trying to make a function for a minesweeper game I'm making. This function's purpose is to reveal an element given the x and y (where it is located). This is probably not the most elegant way to accomplish it, but I'm making a list of ['-'] for each tile titled newField
. A '-' represents a hidden block (where you don't know if its a bomb or how many bombs are surrounding it). Then, I change one element from the newField
to equal its corresponding block from the listField
(which is a list of lists. Each list within it represents a row). An X represents a bomb and the numbers represent how many bombs surround in.
In minesweeper, when a block with zero bombs surrounding it is revealed, it's surrounding blocks are also revealed. I've made a functioned titled revealSurroundings
that accomplishes this. When I run revealSurroundings
in the function revealElement
as I do below, it freezes my computer. However, if I run revealSurroundings
outside of the function revealElement
it works fine.
If anyone has any advice on how to solve this issue and/or make it more efficient(because I know the method I'm using is very expensive), please let me know.
wl = 10
listField =
[['1', '1', '0', '0', '0', '0', '0', '0', '1', 'X'],
['X', '2', '2', '2', '2', '2', '2', '1', '1', '1'],
['1', '2', 'X', 'X', '2', 'X', 'X', '2', '2', '2'],
['1', '2', '3', '2', '2', '2', '3', '4', 'X', 'X'],
['1', 'X', '1', '0', '0', '1', '3', 'X', 'X', '3'],
['1', '2', '2', '1', '0', '1', 'X', 'X', '4', '2'],
['2', '3', 'X', '1', '0', '1', '2', '2', '2', 'X'],
['X', 'X', '2', '1', '1', '1', '1', '0', '1', '1'],
['4', '5', '4', '3', '3', 'X', '2', '1', '0', '0'],
['X', 'X', 'X', 'X', 'X', '3', 'X', '1', '0', '0']]
hiddenField = ['-' for i in range(wl*wl)]
def removeN(ls, n, iterations):
items = ls
try:
for i in range(iterations):
items.remove(n)
except:
pass
return items
def revealElement(wl, x, y, userField):
yReal = y-1
xReal = x-1
newField = list(userField)
removeN(newField, '\n', wl-1)
newField[yReal*wl + xReal] = listField[yReal][xReal]
if newField[yReal*wl + xReal] == '0':
revealSurroundings(wl, x, y, userField)
for i in range(wl-1, 0, -1): # go backwards
newField.insert(wl*i, '\n')
return "".join(newField) # make it a string
def revealSurroundings(wl, x, y, userField):
yReal = y-1
xReal = x-1
newField = userField
try:
newField = revealElement(wl, x+1, y, newField)
except:
pass
#right
try:
if xReal != 0:
newField = revealElement(wl, x-1, y, newField)
except:
pass
#left
try:
if yReal != 0:
newField = revealElement(wl, x, y-1, newField)
except:
pass
#up
try:
newField = revealElement(wl, x, y+1, newField)
except:
pass
#down
try:
if yReal != 0:
newField = revealElement(wl, x+1, y-1, newField)
except:
pass
#upper-right
try:
if yReal != 0 and xReal != 0:
newField = revealElement(wl, x-1, y-1, newField)
except:
pass
#upper left
try:
newField = revealElement(wl, x+1, y+1, newField)
except:
pass
#bottom-right
try:
if xReal != 0:
newField= revealElement(wl, x-1, y+1, newField)
except:
pass
#bottom-left
return newField
print revealSurroundings(10, 7, 2, hiddenField)
Upvotes: 3
Views: 145
Reputation: 179
The problem seems to be that when you run the revealSurroundings
within revealElement
, you're creating a never-ending loop.
When you run revealElement
, if the element is 0 the function revealSurroundings
is run. Within revealSurroundings
you run revealElement
. If new element revealed is also a zero, it once again runs revealSurroundings
and detects the zero from the first iteration of revealElement
.
This begins an infinite loop that never ends. I suggest adding another conditional in revealSurroundings
, such as checking to see if you already revealed the characters next to it through a simple if statement. I would also recommend re-writing the code altogether because of what @gorlen stated.
Upvotes: 2
Reputation: 56965
This logic seems like overkill for a basic Minesweeper reveal algorithm. A few thoughts:
__str__
method to print a board and pass the grid into the initializer. There's no need to stringify the grid for algorithmic purposes--leave it as a list internally.Tile
class/recordtype/dict with properties for visible
, status
/contents
. This lets you store all necessary grid data in one data structure.wl
must be the board size, but this is redundant information since lists already have a len
property that's guaranteed consistent and is semantically meaningful.snake_case
instead of camelCase
for all variables and functions. UpperCamelCase
for class names. See PEP-8.end
keyword or braces, so anything less than 4 spaces makes it difficult to disambiguate blocks. try
/except
blocks for logic that's easily converted to conditionals, especially if you're using Pokemon exceptions. You may inadvertently suppress errors other than those you intend to, and it's an abuse of a construct that's not designed for general-purpose control flow.yReal = y-1; xReal = x-1
is likely causing confusion because it's performed on every frame recursively. If you have to do this, move the logic out of the algorithm and normalize/denormalize as close to the IO interface as possible.Here's a re-write suggestion:
class Minesweeper:
class Tile:
def __init__(self, mark, visible=False):
self.mark = mark
self.visible = visible
def __str__(self):
return self.mark if self.visible else "-"
neighbors = [(x, y) for x in range(-1, 2)
for y in range(-1, 2) if x or y]
def __init__(self, field):
self.field = [[self.Tile(x) for x in row] for row in field]
def in_bounds(self, x, y):
return y >= 0 and y < len(self.field) and \
x >= 0 and x < len(self.field[y])
def reveal(self, x, y):
if self.in_bounds(x, y) and not self.field[y][x].visible and \
self.field[y][x].mark == "0":
self.field[y][x].visible = True
for dx, dy in self.neighbors:
self.reveal(x + dx, y + dy)
def __str__(self):
return "".join("".join(map(str, row)) + "\n" for row in self.field)
if __name__ == "__main__":
field = [
"00000",
"12210",
"1XX10",
]
board = Minesweeper(field)
print board
board.reveal(3, 0)
print board
Output:
-----
-----
-----
00000
----0
----0
Upvotes: 1