Minesweeper, python Tkinter, Checking close by tiles

Is there any smart way to compress the following code:

def __total(self, x, y):
count = 0
for x_offset in range(-1, 2):
x_index = x + x_offset
for y_offset in range(-1, 2):
y_index = y + y_offset
if 0 <= x_index < self.Size and 0 <= y_index < self.__Size:
count += self.__buttons[y_index][x_index].mine
if not count:
for x_offset in range(-1, 2):
x_index = x + x_offset
for y_offset in range(-1, 2):
y_index = y + y_offset
if 0 <= x_index < self.__Size and 0 <= y_index < self.__Size:
self.__push(x_index, y_index)
return count

I have been trying forever with an extreme "coder-blackout" not being able to succeed....

• A question: Do you mean in the line with: # THIS LINE if 0 <= x_index < self.Size and 0 <= y_index < self.__Size: count += self.__buttons[y_index][x_index].mine if not count: self.Size or self.__Size ? Okay. I will assume for now, that you meant __Size. def __total(self, x, y, count, old): if (not count and old) or not old: for x_index in range(-1+x, 2+x): for y_index in range(-1+y, 2+y): if 0 <= x_index < self.Size and 0 <= y_index < self.__Size: if old: self.__push(x_index, y_index) else: count += self.__bu – Georg Friedrich Apr 2 '16 at 14:49
• That won't work. In the original code, count starts out at 0, so it must have been added to at some point when it was 0. Yours never will. On a side note, you should use else:, not elif: because elif, like if, expects a condition to follow. It's like else if ... – zondo Apr 2 '16 at 15:05
• Yeah thanks. As I said. Not so familiar with python. – Georg Friedrich Apr 2 '16 at 15:06
• Something like that? – Georg Friedrich Apr 2 '16 at 15:10
• Yeah, except that count += ... in your first block and def __total... in your second block are indented by four extra spaces. My first comment about not working still applies, though. – zondo Apr 2 '16 at 15:13

Okay. Improved my answer. You can call this code as normal with self.__total(x,y). In my own test, it showed the exact same results as your code has.

def __total(self, x, y, count=0, old=False):
if (not count and old) or not old:
for x_index in range(-1+x, 2+x):
for y_index in range(-1+y, 2+y):
if 0 <= x_index < self.__Size and 0 <= y_index < self.__Size:
if old:
self.__push(x_index, y_index)
else:
count += self.__buttons[y_index][x_index].mine

if old:
return count
return self.__total(x,y, count, True)

Another solution would be using Numpy:

import numpy as np

def __total(self, x,y):
count = np.sum( self.__buttons[ x-1 if x>0 else 0:x+2, y-1 if y>0 else 0:y+2 ] )
if not count:
for x_offset in range(-1, 2):
x_index = x + x_offset
for y_offset in range(-1, 2):
y_index = y + y_offset
if 0 <= x_index < self.__Size and 0 <= y_index < self.__Size:
self.__push(x_index, y_index)
return count

The problem with this code is, that you can't use array class, but would need a Numpy array of all mines in a extra variable. But it would speed up your calculations just because of the nature of Numpy.

• I understand what the code is doing. The question for optimisation is: Could you do instead of a __push maybe just a give a array. With that you could easily filter an area and change all the values on it. Example: UI is your array that represents the player field: You can easily change with that all the values in the range to 1 with UI[posx:posx+3,posy:posy+3] = 1 – Georg Friedrich Apr 2 '16 at 16:05

This code is actually doing three things:

1. Identifying the neighbors
2. Counting the mines in the neighbors
3. Conditionally opening up the neighbors (I think that's what you intend?)

Therefore, you would be better served with three functions. The fundamental one is:

def neighbors(self, x, y):
for xx in (x - 1, x, x + 1):
if not (0 <= xx < self.Size): continue
for yy in (y - 1, y, y + 1):
if not (0 <= yy < self.__Size): continue
yield xx, yy

Then you could write (2) and (3) like:

mine_count = sum(self.__buttons[yy][xx].mine for xx, yy in self.neighbors(x, y))

Note that self.Size violates PEP 8 naming conventions, and the similarity with self.__Size is confusing. Also, your use of double-underscores is probably inappropriate.