[Tutor] Improving My Simple Game Code for Speed, Memory and Learning
Dave Angel
davea at davea.name
Sat Jan 3 04:21:11 CET 2015
On 01/02/2015 09:00 PM, WolfRage wrote:
> Python3.4+ Linux Mint 17.1 but the code will be cross platform (Mobile,
> Windows, Linux, OSX).
>
> First an explanation of how the game works: The game is a simple
> matching game but with a twist. Instead of matching a straight 3 in a
> row, we have some rules that only certain combinations will result in an
> elimination. In general 2 of the same value in a row with with 1 of 2
> other possible values will eliminate all three nodes. Eliminations can
> occur either horizontally or vertically but not diagonally. Each tile
> has a value now, that value is used when evaluating the rules. Currently
> there are only 5 allowed values of 0-4 although more could be added
> later, so any solution needs to be expandable.
> These are the basic rules that allow for an elimination to occur(values
> are put in quotes to distinguish values):
> 2 "5"s followed or proceeded by a "19" will eliminate all 3 nodes.
> 2 "5"s followed or proceeded by an "11" will eliminate all 3 nodes.
> 2 "6"s followed or proceeded by a "5" will eliminate all 3 nodes.
> 2 "6"s followed or proceeded by a "19" will eliminate all 3 nodes.
> 2 "11"s followed or proceeded by a "6" will eliminate all 3 nodes.
> 2 "11"s followed or proceeded by a "20" will eliminate all 3 nodes.
> 2 "19"s followed or proceeded by a "20" will eliminate all 3 nodes.
> 2 "19"s followed or proceeded by an "11" will eliminate all 3 nodes.
> 2 "20"s followed or proceeded by a "6" will eliminate all 3 nodes.
> 2 "20"s followed or proceeded by a "5" will eliminate all 3 nodes.
>
> The main focus of the code is the find_eliminations method. I think its
> implementation is smart since it uses a math trick, but then again I
> wrote it.
> My math trick works by first finding 2 matching nodes next to each other
> out of every 3 nodes, then adding the values of all 3 nodes together and
> checking for specific totals. None of the possible totals overlap.
>
> Here is the code:
>
> import random
>
>
> class GameTile():
> def __init__(self, value, col, row, **kwargs):
> # id is grid (X,Y) which is equal to grid (col,row)
> self.id = str(col) + ',' + str(row)
> self.col = col
> self.row = row
> self.value = value
> self.eliminated = False
>
> def __str__(self):
> #return '%d, %d' % (self.col, self.row)
> if len(str(self.value)) == 1:
> return ' ' + str(self.value)
> return str(self.value)
>
>
> class GameGrid():
> def __init__(self, cols=8, rows=7, **kwargs):
> self.cols = cols
> self.rows = rows
> self.make_grid()
>
> def make_grid(self):
> # grid is 2d array as x, y ie [x][y].
> self.grid = []
> for row_num in range(self.rows):
> self.grid.append([GameTile(value=random.choice([5, 6, 11,
> 19, 20]), col=col_num,
> row=row_num) for col_num in range(self.cols)])
>
> def print_by_col(self):
> # As in going down the column assuming we started at the top.
> for col_num in range(self.cols):
> for row_list in self.grid:
> print(row_list[col_num])
>
> def print_by_row(self):
> # As in going right across the row assuming we started at the
> left.
> for row in self.grid:
> for node in row:
> print(node)
>
> def check_bounds(self, x, y):
> return (0 <= x < self.rows) and (0 <= y < self.cols)
>
> def lookup_node(self, x, y):
> if not self.check_bounds(x, y):
> return False
> return self.grid[x][y]
>
> def draw(self):
> for col in self.grid:
> print(end='| ')
> for node in col:
> print(node, end=' | ')
> print()
>
> def find_eliminations(self):
> # I define 3 variables for holding the 3 nodes/tiles that I am
> # currently checking to see if an elimination possibility exists.
> # It uses a math trick to check for elimination by adding the
> values
> # and checking for specific totals. None of the possible totals
> overlap.
> #First Down the columns.
> for col_num in range(self.cols):
> first = None
> second = None
> third = None
> for row_list in self.grid:
> if row_list[col_num].row == 0:
> first = row_list[col_num]
> elif row_list[col_num].row == 1:
> second = row_list[col_num]
> elif row_list[col_num].row == 2:
> third = row_list[col_num]
> else:
> first = second
> second = third
> third = row_list[col_num]
This code is way too complex for what it accomplishes. See below.
> if third is not None:
> self.check_total_and_eliminate(first, second, third)
> # Now across the rows.
> for row in self.grid:
> first = None
> second = None
> third = None
> for node in row:
> if node.col == 0:
> first = node
> elif node.col == 1:
> second = node
> elif node.col == 2:
> third = node
> else:
> first = second
> second = third
> third = node
If the self.rows is 3, this is equivalent to just:
first, second, third = row
If the self.rows is larger, it's equivalent to:
first, second, third = row[p: p+3] for some value of p
> if third is not None:
> self.check_total_and_eliminate(first, second, third)
But even better, let the method check_total_and_eliminate take a slice
as its argument.
self.check-total_and_eliminate(row[p: p+3])
> # Set all eliminated nodes to a value of 0.
> for col in self.grid:
> for node in col:
> if node.eliminated is True:
> node.eliminated = False
> node.value = 0
>
> def check_total_and_eliminate(self, first, second, third):
> total = None
> if first.value == second.value:
> total = first.value + second.value + third.value
> elif second.value == third.value:
> total = first.value + second.value + third.value
> if total == 17 or total == 21 or total == 28 or total == 29 or \
> total == 31 or total == 42 or total == 45 or total == 46 \
> or total == 49 or total == 58:
> first.eliminated = True
> second.eliminated = True
> third.eliminated = True
>
This doesn't come close to implementing the test you describe above.
For example, a total of 29 could be your first case, 5,5,19. But it
could also be 4,4,21. Or 6,6,17. Etc.
I suggest instead that you compare the values against a table of
interesting values. If you make your table a list of 3-tuples, it can
be searched simply by:
if tuple(nodes) in table:
do-something
So you don't need the separate variables first, second, and third at all.
>
>
> grid = GameGrid(4, 8)
> grid.draw()
> grid.find_eliminations()
> print('After Eliminations')
> grid.draw()
>
>
> # END CODE
>
> After this part has been improved, I then need to search the columns
> backwards to be able to drop any floating values. Any none zero values
> should not have a zero value below it. That is what I mean by drop
> floating values. I think this will be simple by just using range method
> to count backwards. I will be working on coding that in the meantime.
>
for what you've described so far, it might be convenient to store the
board and its transposed equivalent. Then search the one for columns,
and the other for rows. That's a later optimization, but it might save
time getting the more complicated parts figured out.
--
DaveA
More information about the Tutor
mailing list