Answered step by step
Verified Expert Solution
Link Copied!

Question

1 Approved Answer

Use Python 3 Problem: 2048 is a single-player sliding block puzzle game. The game's objective is to slide numbered tiles typically on a 4x4 grid

Use Python 3

Problem: 2048 is a single-player sliding block puzzle game. The game's objective is to slide numbered tiles typically on a 4x4 grid to combine them to create a tile with the number 2048. The tiles are slid all together either upwards, downwards, left or right and go all together to one side of the grid, stopped only by either the border of the grid or a neighbouring filled tile. Two adjacent tiles in the same direction of the slide with the same values are summed in one tile. A tile participates in a merge only once in each round. At each round, a new empty tile is filled with a 2 or 4 and the player slides the tiles of the board in one direction (up, down, left or right) again. The player wins if the value 2048 is reached, and loses if all the tiles are filled and no sliding is possible.

Here is a typical snapshot of a game of 2048 in progress played on a smartphone, tablet or browser. image text in transcribed

In this assignment, you will incrementally develop a program to play the game 2048. This assignment is designed to help you understand how to compartmentalize your code into isolated functioning components and then bring them together to achieve the main task. While the recommended approach you should take for this task has been described for you, the skeleton code provided will allow you to harness your creativity whilst placing certain restrictions to help you meet the program specifications.

2048 Gameplay

As introduced above, 2048 is a tile-sliding puzzle game where the objective is to maximize your score. The game is (usually) played on (4 x 4) grid of tiles. Each tile can be a blank tile or be marked with a value that is a power of 2. Figure 1 illustrates what a typical 2048 state (which we will call s0 from here on) looks like midplay.

image text in transcribed

During each move, the player can take one of four possible actions: {LEFT, RIGHT, UP, DOWN} which collapses the grid left, right, up or down respectively. When the grid is collapsed in any direction, all the numbers shift to blank tiles in that direction till they reach the end of the table or reach a tile with a number in it already. During the shift, identical numbers are added together, and different numbers stack next to each other. For example, for state s0, if the player chooses the UP action, the table will be collapsed upward resulting in the new state, s'0 shown in Figure 2.

In this state s'0 ( Figure 2), we can see that the 2 in B2 moved up to A2 and the 8 in A3 remained where it was as it was at the topmost end of the column initially. The 4 in A4 also did not move but when the 4 in C4 moved up, it was added to the first one since identical numbers are merged (summed up). The 2 in D4 moved up to B4. However, if a tile with a number was already merged with an identical number during one round, it will not be merged again with another identical tile during the same move.

image text in transcribed

In the example above, when sliding to the left the row on the left, it results in the row on the right. The two 4s are merged to give an 8, but now the two resulting 8s are not merged in this current move.

How do we score?

Points are scored for any additions that occur during a move with the value being equal to the sum of the addition i.e., the action to go from s0to s'0 in Figures 1 and 2, results in 8 points. After each move, a (2) or a (4) is placed on a random empty tile, therefore over time the number of blank tiles decreases. The player "wins" if a tile with (2048) is formed, however, the game does not stop. The game stops when the grid is no longer collapsible in any of the four directions (when there are no legal actions available.).

To get used to the rules of the game you can play the game at https://gabrielecirulli.github.io/2048/.

Tasks to do

In this assignment you will be building a program to play 2048 piece by piece. To do so you are provided with a skeleton code in the file game.py. This file contains two classes: Grid and Game. You will be working (primarily) in the Grid class. This class contains attributes and methods that allow you to perform actions involving the grid of the game. The __init__() function is parameterized by:

row, the number of rows of the grid (default value of 4);

col, the number of columns;

initial, the number of random initial tiles filled at the beginning of the game.

This class also contains the attribute score, the score of the game and emptiesSet, a list which contains the set of all empty tiles of the grid. Finally, the class contains the grid attribute, which is the variable that stores the grid/state of the game and it is initialized by createGrid().

Task 1

Your first task will be to implement the function createGrid() that takes two parameters row and col and returns the grid for the game. You will be using a list of lists to represent the grid. The list should contain row number of lists, each with a length col. All the elements of this data structure should be 0. Once you are done implementing the function, append the following lines to the bottom of your program:

grid = Grid()

print(grid._grid)

If your function is implemented correctly, you should see the following output:

[[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]] 

Task 2

Once you are finished with Task 1, you can begin this task. Be sure to erase the test code from the previous task. Now, you will implement the setCell() and getCell() functions to set and access values in the grid. We can imagine the elements of the grid to be associated with a cell number. That is each element of the grid is a cell. So, in a (4 x 4) grid, the cells are numbered 0 through 15. As a result, instead of accessing an element using, say, grid._grid[1][2], we access that element with grid.getCell(6). Implement setCell() such that it takes two arguments, cell and val, and assigns the value of val to cell number cell in the grid. Implement getCell() such that it takes an argument, cell, and returns the value in the grid at cell number cell. Once you are done implementing the functions, append the following lines to the bottom of your program:

grid = Grid()

grid.setCell(6, 8)

print(grid._grid[1][2])

print(grid.getCell(6))

If your functions are implemented correctly, with the default values, you should see the following output:

8 8 

The variable_gridis actually considered to be a private attribute and should not be manipulated directly. All forms of manipulating the grid in your program should be done using setCell() and getCell() functions. Erase the test codes before continuing. Now, uncomment lines 16 and 17 in the program. The two lines should look like this:

for _ in range(self.initial):

 self.assignRandCell(init=True) 

Once you have uncommented those two lines. Add the following lines of code to the bottom of your program and run it:

grid = Grid()

grid.drawGrid()

Running your program should print output that looks like

| | | | 2 | | | | | | | | | | | | | 2 | | | 

Don't worry if the 2's are in different positions. The function assignRand() places 2's in random empty tiles. The function drawGrid() is also already done for you so it need not be changed or re-written.

Task 3

Implement the collapsible() function. This function should return True if the grid can be collapsed in any of the four directions. It should return False otherwise. Uncomment the line near the bottom that calls the function testCollapsible() and run the file with Python. If your implementation is correct it should print that all the tests have passed.

Hint: One way to know if the grid is collapsible is if there is an empty tile in the grid.

Task 4

In Task 4 you must complete the collapseRow() function. This function is given a list of numbers as an argument and it should return a LEFT-collapsed list, and a True if the list was collapsed or False if it was not. The code for your function must collapse the list conforming to the rules of 2048. That is, if it is passed the list [2, 0, 2, 4], after collapsing to the left the function should return the list [4, 4, 0, 0]. In order to test your implementation, you can use the tests.py file that is given to you. Uncomment the line near the bottom that calls the function testCollapseRow() and run the file with Python. If your implementation is correct it should print that all the tests have passed.

Task 5

In this task, you will use the collapseRow() function to implement collapseLeft(), collapseRight(), collapseDown() and collapseUp(). For collapseLeft(), you merely need to collapse every row of the grid using collapseRow(). collapseRight() is implemented similarly except, in this case, you would reverse each row before collapsing them. Use this idea, to implement collapseDown() and collapseUp(). All four functions should return True if the grid was collapsed and False otherwise. The texts.py file contains tests for collapseLeft() and collapseDown() that you can use to check your implementations.

Task 6

Implement the function updateEmptiesSet(). This function is used after every collapse action in the game. The function should update the emptiesSet list to contain the new set of empty cells after a collapse. Tests for this function can be found in the tests.py file.

The following are the provided files in python:

tests.py

import game def testCollapseRow(): print("Running tests for collapseRow()") grid = game.Grid() a = [2, 0, 0, 0] b = [2, 0, 2, 0] c = [2, 2, 2, 0] d = [2, 0, 2, 2] e = [8, 8, 16, 8] f = [2, 0, 2, 4] g = [2, 8, 4, 4] a_sol = ([2, 0, 0, 0], False) b_sol = ([4, 0, 0, 0], True) c_sol = ([4, 2, 0, 0], True) d_sol = ([4, 2, 0, 0], True) e_sol = ([16, 16, 8, 0], True) f_sol = ([4, 4, 0, 0], True) g_sol = ([2, 8, 8, 0], True) if grid.collapseRow(a) == a_sol: print("Test (a) passed.") else: print("Test (a) failed") if grid.collapseRow(b) == b_sol: print("Test (b) passed.") else: print("Test (b) failed") if grid.collapseRow(c) == c_sol: print("Test (c) passed.") else: print("Test (c) failed") if grid.collapseRow(d) == d_sol: print("Test (d) passed.") else: print("Test (d) failed") if grid.collapseRow(e) == e_sol: print("Test (e) passed.") else: print("Test (e) failed") if grid.collapseRow(f) == f_sol: print("Test (f) passed.") else: print("Test (f) failed") if grid.collapseRow(g) == g_sol: print("Test (g) passed.") else: print("Test (g) failed") def testCollapseLeft(): print("Running test for collapseLeft()") grid = game.Grid() grid._grid = [[0, 0, 0, 0], [0, 0, 0, 4], [2, 0, 2, 16], [2, 4, 4, 2]] sol = [[0, 0, 0, 0], [4, 0, 0, 0], [4, 16, 0, 0], [2, 8, 2, 0]] grid.collapseLeft() test = grid._grid if sol == test: print("Left collapse test passed.") else: print("Test case failed.") def testCollapseDown(): print("Running test for collapseDown()") grid = game.Grid() grid._grid = [[2, 8, 2, 4], [8, 4, 8, 2], [2, 4, 2, 4], [4, 2, 4, 2]] sol = [[2, 0, 2, 4], [8, 8, 8, 2], [2, 8, 2, 4], [4, 2, 4, 2]] grid.collapseDown() test = grid._grid if sol == test: print("Down collapse test passed.") else: print("Test case failed.") def testCollapsible(): print("Running test for collapsible()") grid = game.Grid() grid.emptiesSet = [0, 1, 2, 3, 4, 5, 6, 8] grid._grid = [[0, 0, 0, 0], [0, 0, 0, 4], [2, 0, 2, 16], [2, 4, 4, 2]] if grid.collapsible(): print('Test (a) passed.') else: print('Test (a) failed.') grid.emptiesSet = [] grid._grid = [[2, 8, 2, 4], [4, 4, 8, 2], [2, 8, 2, 4], [4, 2, 4, 2]] if grid.collapsible(): print('Test (b) passed.') else: print('Test (b) failed.') grid.emptiesSet = [] grid._grid = [[2, 8, 2, 4], [8, 4, 8, 2], [2, 4, 2, 4], [4, 2, 4, 2]] if grid.collapsible(): print('Test (c) passed.') else: print('Test (c) failed.') grid.emptiesSet = [] grid._grid = [[2, 4, 2, 4], [4, 2, 4, 2], [2, 4, 2, 4], [4, 2, 4, 2]] if not grid.collapsible(): print('Test (d) passed.') else: print('Test (d) failed.') def testEmpties(): print("Running test for updateEmptiesSet()") grid = game.Grid() grid._grid = [[0, 0, 0, 0], [0, 0, 0, 4], [2, 0, 2, 16], [2, 4, 4, 0]] grid.updateEmptiesSet() truth = [0, 1, 2, 3, 4, 5, 6, 9, 15] if grid.emptiesSet == truth: print('Empties set test passed.') else: print('Empties set test failed.') # Uncomment the tests for the function you want to check #testCollapseRow() #testCollapseLeft() #testCollapseDown() #testCollapsible() #testEmpties()

game.py

import random as rnd import os import sys class Grid(): def __init__(self, row=4, col=4, initial=2): self.row = row # number of rows in grid self.col = col # number of columns in grid self.initial = initial # number of initial cells filled self.score = 0 self._grid = self.createGrid(row, col) # creates the grid specified above self.emptiesSet = list(range(row * col)) # list of empty cells for _ in range(self.initial): # assignation to two random cells self.assignRandCell(init=True) def createGrid(self, row, col): """ Create the grid here using the arguments row and col as the number of rows and columns of the grid to be made. The function should return the grid to be used in __init__() """ pass def setCell(self, cell, val): """ This function should take two arguments cell and val and assign the cell of the grid numbered 'cell' the value in val. This function does not need to return anything. You should use this function to change values of the grid. """ pass def getCell(self, cell): """" This function should return the value in cell number 'cell' of the grid. You should use this function to access values of the grid """ pass def assignRandCell(self, init=False): """ This function assigns a random empty cell of the grid a value of 2 or 4. In __init__() it only assigns cells the value of 2. The distribution is set so that 75% of the time the random cell is assigned a value of 2 and 25% of the time a random cell is assigned a value of 4 """ if len(self.emptiesSet): cell = rnd.sample(self.emptiesSet, 1)[0] if init: self.setCell(cell, 2) else: cdf = rnd.random() if cdf > 0.75: self.setCell(cell, 4) else: self.setCell(cell, 2) self.emptiesSet.remove(cell) def drawGrid(self): """ This function draws the grid representing the state of the game grid """ for i in range(self.row): line = '\t|' for j in range(self.col): if not self.getCell((i * self.row) + j): line += ' '.center(5) + '|' else: line += str(self.getCell((i * self.row) + j)).center(5) + '|' print(line) print() def updateEmptiesSet(self): """ This function should update the list of empty cells of the grid. """ pass def collapsible(self): """ This function should test if the grid of the game is collapsible in any direction (left, right, up or down.) It should return True if the grid is collapsible. It should return False otherwise. """ pass def collapseRow(self, lst): """ This function takes a list lst and collapses it to the LEFT. This function should return two values: 1. the collapsed list and 2. True if the list is collapsed and False otherwise. """ pass def collapseLeft(self): """ This function should use collapseRow() to collapse all the rows in the grid to the LEFT. This function should return True if any row of the grid is collapsed and False otherwise. """ pass def collapseRight(self): """ This function should use collapseRow() to collapse all the rows in the grid to the RIGHT. This function should return True if any row of the grid is collapsed and False otherwise. """ pass def collapseUp(self): """ This function should use collapseRow() to collapse all the columns in the grid to UPWARD. This function should return True if any column of the grid is collapsed and False otherwise. """ pass def collapseDown(self): """ This function should use collapseRow() to collapse all the columns in the grid to DOWNWARD. This function should return True if any column of the grid is collapsed and False otherwise. """ pass class Game(): def __init__(self, row=4, col=4, initial=2): """ Creates a game grid and begins the game """ self.game = Grid(row, col, initial) self.play() def printPrompt(self): """ Prints the instructions and the game grid with a move prompt """ if sys.platform == 'win32': os.system("cls") else: os.system("clear") print('Press "w", "a", "s", or "d" to move Up, Left, Down or Right respectively.') print('Enter "p" to quit. ') self.game.drawGrid() print(' Score: ' + str(self.game.score)) def play(self): moves = {'w' : 'Up', 'a' : 'Left', 's' : 'Down', 'd' : 'Right'} stop = False collapsible = True while not stop and collapsible: self.printPrompt() key = input(' Enter a move: ') while not key in list(moves.keys()) + ['p']: self.printPrompt() key = input(' Enter a move: ') if key == 'p': stop = True else: move = getattr(self.game, 'collapse' + moves[key]) collapsed = move() if collapsed: self.game.assignRandCell() collapsible = self.game.collapsible() if not collapsible: if sys.platform == 'win32': os.system("cls") else: os.system("clear") print() self.game.drawGrid() print(' Score: ' + str(self.game.score)) print('No more legal moves.') def main(): game = Game() #main()

BEST 11196 2 16 2 20488 SCORE 11196 16 64 1024 8 16 256 32 4 Join the numbers and get to the 2048 tlel 2 2 4

Step by Step Solution

There are 3 Steps involved in it

Step: 1

blur-text-image

Get Instant Access with AI-Powered Solutions

See step-by-step solutions with expert insights and AI powered tools for academic success

Step: 2

blur-text-image

Step: 3

blur-text-image

Ace Your Homework with AI

Get the answers you need in no time with our AI-driven, step-by-step assistance

Get Started

Students also viewed these Databases questions