2

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)
  • Could you provide a working code example? Trim the fonts and images, because your code doesn't work at all. – Benjamin Jul 28 '20 at 18:44
  • 1
    Another issue: could you explain, the result is "same"? Is it the problem with `pygame` or with the algorithm itself? – Benjamin Jul 28 '20 at 18:49
  • @Benjamin The problem is with the algorithm itself. I removed the pygame elements – Chips Master Jul 29 '20 at 03:13
  • `is` does not do what you think it does. All it checks is *object identity* - but you don't care whether the character typed by the user is the very same object as `"l"` or one of your other string literals, all you want to know is whether it is *equal* to one of those literals. And that is written as `==`. – jasonharper Jul 29 '20 at 03:16
  • @jasonharper I changed is to == but the result is still the same. – Chips Master Jul 29 '20 at 03:20
  • i ran your code which loos pretty fine for me. the "same output" gives when you gives an invalid move.or invalid letter. invalid move means you are pressing left and no moves could not be done when u press that particular direction in which case the 2048 game also behave like this. – Adhun Thalekkara Jul 29 '20 at 06:20

3 Answers3

0

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.

Dennis Sparrow
  • 938
  • 6
  • 13
  • `current = list(game)` does not make `current` and `game` refer to the same list. It creates a new list. However, the *sublists* are not copied. – user2357112 Jul 29 '20 at 04:09
0

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.

cdlane
  • 40,441
  • 5
  • 32
  • 81
0

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]
Adhun Thalekkara
  • 713
  • 10
  • 23