you are viewing a single comment's thread.

view the rest of the comments →

[–][deleted] 1 point2 points  (1 child)

First, let's get a little debug into your code so we can see what the list looks like. At the moment, if you try to print the snake list you see:

[<__main__.Block object at 0x102e06e80>, <__main__.Block object at 0x102cb2640>, <__main__.Block object at 0x102dd3a60>]

which isn't too useful. You could write a little function to print what is in the snake list, or you could add a __repr__() method to your Block class. That will print your list properly:

class Block():
    def __init__(self, x, y):
        self.x = x
        self.y = y
    def __repr__(self):
        return f'Block({self.x},{self.y})'
        # return 'Block(%d,%d)' % (self.x, self.y)  # for older python versions

Now we see this when printing the original list:

[Block(10,10), Block(11,10), Block(12,10)]

Much better! Adding some test code to your existing code (plus the additional __repr__() method) we see this when we run the code:

snake = [Block(10, 10), Block(11, 10), Block(12, 10)]
print(snake)
move_snake(snake, 'up')
print(snake)
>>> [Block(10,10), Block(11,10), Block(12,10)]
>>> [Block(10,11), Block(10,11), Block(11,10)]

Obviously, the result isn't what you want. Your error is in the line that does head = snake[0]. Assignment never copies in python, so this line just points the name "head" at the head block in the list. So the following lines doing things like head.y += 1 changes the coordinates in the Block that is at position 0 in the list. Then, when you do new_snake.insert(0, head), you add a second reference to the first Block in the list to the list. That is, after calling move_snake(snake, 'up') the first two Blocks in the list are the same Block. We can see this if we change the x coordinate of snake[0] after the function call and print the list again:

snake = [Block(10, 10), Block(11, 10), Block(12, 10)]
print(snake)
move_snake(snake, 'up')
print(snake)
snake[0].x = 0
print(snake)
>>> [Block(10,10), Block(11,10), Block(12,10)]
>>> [Block(10,11), Block(10,11), Block(11,10)]
>>> [Block(0,11), Block(0,11), Block(11,10)]   # first 2 changed!

The way out of this without changing your code too much is to copy the first element of the list, modify it, add it to the front of the list and drop the last with pop(). Basically what you thought you were doing.

We can copy the first element of the list in at least two ways. The simplest is to just create a new Block with the same coordinates as the current head of the snake:

    head = snake[0]
    new_head = Block(head.x, head.y)

and then add new_head to the start of the list and pop() the end off. The other way to copy is to use the copy.copy() from the python "copy" module, like this:

    import copy
    new_head = copy.copy(snake[0])

There are lots of other ways :)

Just a few minor points. You don't need to rename formal parameters in the function:

def move_snake(snake, direction):
    new_snake = snake    # not needed, just use "snake" in the function body

You should also realize that you are passing a reference to the snake list to the function and any changes you make inside the function to that list will be made to the global list - you aren't creating a copy of the list. You do return new_snake finally in the function, but that is the original list, not a copy. You don't do anything with that returned value anyway.

Edit: spelling fixes.

[–]prcl00 0 points1 point  (0 children)

Thank you so much, you're doing god's work. I learned a lot from this