Variable updating multiple times

Viewed 214

I am trying to make the game 2048 and in the game if we tap a direction and the position of the numbers do not change then no new number is spawned.

I tried to implement that in python and the program seems to have problems. The game thinks two different states are the same when they aren't. The problem is with the algorithm.

I cannot figure out why I am getting the "same" output.

I have tried changing the position of current but it has yielded the same result.

EDIT: I removed the pygame elements and I can see that the problem is with the algorithm.

import random
import time
game =[[0, 0, 0, 0],
       [0, 0, 0, 0],
       [0, 0, 0, 0],
       [0, 0, 0, 0]]

def inverse(game):
    game2 = [[0,0,0,0],[0,0,0,0],[0,0,0,0],[0,0,0,0]]
    for x in range(len(game)):
        for y in range(len(game[x])):
            game2[y][x] = int(game[x][y])
    return game2

def spawn(game):
    e = []
    for x in range(4):
        for y in range(4):
            if game[x][y]==0:
                e.append((x,y))
    place = random.choice(e)
    nums = [2,2,2,2,4]
    num = random.choice(nums)
    x,y=place[0],place[1]
    game[x][y] = num
    return game

def left(game):
    for i in range(len(game)-1):
        for x in range(len(game)):
            for y in range(len(game[x])):
                if y in (1,2,3): 
                    if game[x][y-1] == 0 or game[x][y-1] == game[x][y]:
                        game[x][y-1] += game[x][y]
                        game[x][y] = 0                        
    return game

def right(game):
    for i in range(len(game)-1):
        for x in range(len(game)):
            for y in range(len(game[x])):
                if y in (0,1,2): 
                    if game[x][y+1] == 0 or game[x][y+1] == game[x][y]:
                        game[x][y+1] += game[x][y]
                        game[x][y] = 0                      
    return game

def down(game):
    game2 = inverse(game)
    for i in range(len(game)-1):
        for x in range(len(game2)):
            for y in range(len(game2[x])):
                if y in (0,1,2): 
                    if game2[x][y+1] == 0 or game2[x][y+1] == game2[x][y]:
                        game2[x][y+1] += game2[x][y]
                        game2[x][y] = 0
    game = inverse(game2)
    return game

def up(game):
    game2 = inverse(game)
    for i in range(len(game)-1):
        for x in range(len(game2)):
            for y in range(len(game2[x])):
                if y in (1,2,3): 
                    if game2[x][y-1] == 0 or game2[x][y-1] == game2[x][y]:
                        game2[x][y-1] += game2[x][y]
                        game2[x][y] = 0
    game = inverse(game2)
    return game

def display(game):
    for i in game:
        print(i)

win = False
game = spawn(game)
game = spawn(game)

running = True

current = list(game)

while running:
    game_2d = [y for x in game for y in x]
    empty = 16
    for x in game_2d:
        if x != 0:
            empty-=1
    
    if 2048 in game_2d:
        print("win")
        running = False

    elif empty == 0 and 2048 not in game_2d:
        print("lose")
        running = False

    else:
        display(game)
        current = list(game)
        x = input("Enter l / r / u / d / q: ")
        x = x.lower()
        x = x[0]
        if x == "l":
            game = left(game)
        elif x == "r":
            game = right(game)
        elif x == "u":
            game = up(game)
        elif x == "d":
            game = down(game)
        elif x == "q":
            running = False
        if current == game:
            print("\nsame\n")
            pass
        else:
            spawn(game)
3 Answers

I think the problem is that when you do current = list(game), you are trying to save the state of game so you can later compare current to game to see if game has changed. But that statement instead just sets current and game to contain the same lists, so even when one or more of the contained lists is changed, current is still equal to game. To fix it, you could import the copy module and set current to copy.deepcopy(game).

It looks like this was only a problem with the left and right moves, because the up and down moves created new lists with the inverse function, so after an up or down move, current and game no longer contained the same lists.

I see three issues, first as @DennisSparrow touched on, you're using a shallow copy where you should be using a deep copy. Consider the following which is analogous to your current = list(game) statement:

>>> a = [[1],[2],[3]]
>>> b = list(a)
>>> a[1][0] = 4
>>> a
[[1], [4], [3]]
>>> b
[[1], [4], [3]]
>>> 

So your current doesn't save the state as you believe it does.

The next issue is the direction in which you consolidate. If your user indicates right, I don't believe you should update that row from left to right:

for y in range(len(game[x])):

I believe you need to do it right to left:

for y in range(len(game[x]) - 2, -1, -1):

as this answer notes:

it is preferable to scan in the opposite direction of the player's move

Finally, I don't know if this is a problem, but if your game sees:

[2, 2, 2, 2]

and the user indicates left, you end up with:

[8, 0, 0, 0]

but another game I tried on the Internet, came up with:

[4, 4, 0, 0]

So you might want to check the rules on this one. That earlier answer notes:

mark the [number] as merged, thus not allowing further merges

where these marks are cleared before the next move.

i don exactly under stood the problem, but the thing understood is for example if u move left and right if there is no change in list of elements happened it gives an output same and do not introduce a new element. i think the problem is because u have not deep copied the list instead of that u called an instance.

so i using tried deepcopy and i got this

import random
import time

game = [[0, 0, 0, 0],
        [0, 0, 0, 0],
        [0, 0, 0, 0],
        [0, 0, 0, 0]]


def inverse(game):
    game2 = [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]
    for x in range(len(game)):
        for y in range(len(game[x])):
            game2[y][x] = int(game[x][y])
    return game2


def spawn(game):
    e = []
    for x in range(4):
        for y in range(4):
            if game[x][y] == 0:
                e.append((x, y))
    place = random.choice(e)
    nums = [2, 2, 2, 2, 4]
    num = random.choice(nums)
    x, y = place[0], place[1]
    game[x][y] = num
    return game


def left(game):
    for i in range(len(game) - 1):
        for x in range(len(game)):
            for y in range(len(game[x])):
                if y in (1, 2, 3):
                    if game[x][y - 1] == 0 or game[x][y - 1] == game[x][y]:
                        game[x][y - 1] += game[x][y]
                        game[x][y] = 0
    return game


def right(game):
    for i in range(len(game) - 1):
        for x in range(len(game)):
            for y in range(len(game[x])):
                if y in (0, 1, 2):
                    if game[x][y + 1] == 0 or game[x][y + 1] == game[x][y]:
                        game[x][y + 1] += game[x][y]
                        game[x][y] = 0
    return game


def down(game):
    game2 = inverse(game)
    for i in range(len(game) - 1):
        for x in range(len(game2)):
            for y in range(len(game2[x])):
                if y in (0, 1, 2):
                    if game2[x][y + 1] == 0 or game2[x][y + 1] == game2[x][y]:
                        game2[x][y + 1] += game2[x][y]
                        game2[x][y] = 0
    game = inverse(game2)
    return game


def up(game):
    game2 = inverse(game)
    for i in range(len(game) - 1):
        for x in range(len(game2)):
            for y in range(len(game2[x])):
                if y in (1, 2, 3):
                    if game2[x][y - 1] == 0 or game2[x][y - 1] == game2[x][y]:
                        game2[x][y - 1] += game2[x][y]
                        game2[x][y] = 0
    game = inverse(game2)
    return game


def display(game):
    for i in game:
        print(i)


win = False
game = spawn(game)
game = spawn(game)

running = True
import copy
current = copy.deepcopy(list(game))

while running:
    game_2d = [y for x in game for y in x]
    empty = 16
    for x in game_2d:
        if x != 0:
            empty -= 1

    if 2048 in game_2d:
        print("win")
        running = False

    elif empty == 0 and 2048 not in game_2d:
        print("lose")
        running = False

    else:
        display(game)
        current = copy.deepcopy(list(game))
        x = input("Enter l / r / u / d / q: ")
        x = x.lower()
        x = x[0]
        if x == "l":
            game = left(game)
        elif x == "r":
            game = right(game)
        elif x == "u":
            game = up(game)
        elif x == "d":
            game = down(game)
        elif x == "q":
            running = False

        if current == game:
            print(current,game)
            print("\nsame\n")
            pass
        else:
            spawn(game)

OUTPUT

[0, 0, 0, 0]
[2, 0, 0, 0]
[0, 0, 0, 0]
[0, 0, 2, 0]
Enter l / r / u / d / q: l
[0, 0, 0, 4]
[2, 0, 0, 0]
[0, 0, 0, 0]
[2, 0, 0, 0]
Enter l / r / u / d / q: r
[0, 0, 0, 4]
[0, 0, 0, 2]
[0, 0, 0, 0]
[0, 2, 0, 2]
Enter l / r / u / d / q: l
[4, 0, 0, 0]
[2, 0, 0, 2]
[0, 0, 0, 0]
[4, 0, 0, 0]
Enter l / r / u / d / q: r
[0, 2, 0, 4]
[0, 0, 0, 4]
[0, 0, 0, 0]
[0, 0, 0, 4]
Enter l / r / u / d / q: d
[0, 0, 0, 0]
[0, 0, 2, 0]
[0, 0, 0, 8]
[0, 2, 0, 4]
Enter l / r / u / d / q: u
[0, 2, 2, 8]
[0, 0, 0, 4]
[0, 0, 0, 0]
[0, 0, 2, 0]
Enter l / r / u / d / q: d
[0, 0, 2, 0]
[0, 0, 0, 0]
[0, 0, 0, 8]
[0, 2, 4, 4]
Related