Today's lab will write a simple implementation of the popular 2048 game by
by Gabriele Cirulli.
Our game is implemented with turtle graphics, so, it is easy to understand, but very, very slow.
Before starting our lab, try the real implementation of 2048 game to understand the basic rules. Note: we are not going to do scoring, just moving of tiles.
As in the Tic-Tac-Toe game in Tic Tac Toe Lab 2, we need to keep track of the previous moves. Our game board here is slightly larger, 4 x 4 squares instead of the 3 x 3 used in Tic-Tac-Toe, but the list of lists still works well to store the board. As in Tic-Tac-Toe, we will set the empty squares to the empty list:
grid = [["","","",""],["","","",""],["","","",""],["","","",""]]
How do we play the game? While there are empty squares, we ask the user for their move and then play the move. When there are no more empty squares, we conclude the game. This makes for a very simple main function:
def main(): grid = [["","","",""],["","","",""],["","","",""],["","","",""]] welcome() win,t = setUp(4,4) drawGrid(t,4,4) while squaresFilled(grid) < 16: #While there are empty squares, keep playing: generateNewSquare(t,grid) makeMove(t,grid) conclusion(win,t) main()
Let's fill in each of these functions. We need to include both the turtle and random libraries. We use the turtles for drawing and the random library to randomly generate a new square each round.
""" Katherine St. John, Spring 2015 Introductory Programming, Lehman College, CUNY A summing game based on 2048 by Gabriele Cirulli (http://gabrielecirulli.github.io/2048/) """ from turtle import * from random import * """ Welcome messages for the program""" def welcome(): print("Welcome to a summing game") print("For each move pick a direction") print("([L]eft, [R]ight, [U]p, or [D]own).") print() """ Sets up the screen with the origin in upper left corner """ def setUp(xMax,yMax): win = Screen() win.setworldcoordinates(-0.5, yMax+0.5,xMax+0.5,-0.5) t = Turtle() t.speed(10) t.hideturtle() return win,t """ Draws a grid to the graphics window""" def drawGrid(tic,xMax,yMax): #Draw the vertical bars of the game board: for i in range(0,xMax+1): tic.up() tic.goto(0,i) tic.down() tic.forward(yMax) #Draw the horizontal bars of the game board: tic.left(90) #Point the turtle in the right direction before drawing for i in range(0,yMax+1): tic.up() tic.goto(i,0) tic.down() tic.forward(xMax)
"""Return the number of squares that are filled""" def squaresFilled(grid): filled = 0 for i in range(4): for j in range(4): if grid[i][j] != "": filled = filled + 1 return(filled)Note that it uses nested loops to check if each square is empty. First, it looks at grid[0][0], grid[0][1], grid[0][2], grid[0][3], grid[1][0] ... and continues until it finishes checking grid[3][3].
def generateNewSquare(t,grid): x = randrange(4) y = randrange(4) while grid[x][y] != "": x = randrange(4) y = randrange(4) grid[x][y] = "2" fillSquareWithValue(t,x,y,"2") """ Fills in the square (x,y) and write value""" def fillSquareWithValue(t,x,y,value): t.up() t.goto(x+.1,y+.1) t.fillcolor("white") t.begin_fill() for i in range(4): t.forward(.8) t.right(90) t.end_fill() t.goto(x+.50,y+.85) t.write(value,align="center",font=('Arial', 70, 'normal'))Note that we call a helping function to draw the new value to the board that first fills the square with the background color white and then writes the value of the square onto it.
""" Write thank you message and close window on click""" def conclusion(win,t): t.up() t.goto(2,4.25) t.write("Thank you for playing!",align="center",font=('Arial', 20, 'normal')) win.exitonclick()
"""Asks user for move, makes move and returns the number of squares freed""" def makeMove(t,grid): move = input("Enter move([L]eft, [R]ight, [U]p, or [D]own):") if move == "L" or move == "l": moveLeft(grid) if move == "R" or move == "r": moveRight(grid) if move == "U" or move == "u": moveUp(grid) if move == "D" or move == "d": moveDown(grid) drawBoard(t,grid)We have created separate functions for moving left, right, up, and down, as well as a function to draw the board.
def drawBoard(t,grid): for i in range(4): for j in range(4): fillSquareWithValue(t,i,j,grid[i][j])
def moveLeft(grid): score = 0 for i in range(3): slideGridLeft(grid) for i in range(3): combineColumns(grid,i+1,i) slideGridLeft(grid) def moveRight(grid): score = 0 for i in range(3): slideGridRight(grid) for i in range(3,0,-1): combineColumns(grid,i-1,i) slideGridRight(grid) def moveUp(grid): for i in range(3): slideGridUp(grid) for i in range(3): combineRows(grid,i+1,i) slideGridUp(grid) def moveDown(grid): for i in range(3): slideGridDown(grid) for i in range(3,0,-1): combineRows(grid,i-1,i) slideGridDown(grid) def slideGridDown(grid): for i in range(4): for j in range(3,0,-1): if grid[i][j] == "" and grid[i][j-1] != "": grid[i][j] = grid[i][j-1] grid[i][j-1] = "" def slideGridUp(grid): for i in range(4): for j in range(3): if grid[i][j] == "" and grid[i][j+1] != "": grid[i][j] = grid[i][j+1] grid[i][j+1] = "" def combineRows(grid,source,target): for i in range(4): if grid[i][target] != "" and grid[i][target] == grid[i][source]: grid[i][target] = str(int(grid[i][target])+int(grid[i][source])) grid[i][source] = "" def slideGridRight(grid): for j in range(4): for i in range(3,0,-1): if grid[i][j] == "" and grid[i-1][j] != "": grid[i][j] = grid[i-1][j] grid[i-1][j] = "" def slideGridLeft(grid): for j in range(4): for i in range(3): if grid[i][j] == "" and grid[i+1][j] != "": grid[i][j] = grid[i+1][j] grid[i+1][j] = "" def combineColumns(grid,source,target): for i in range(4): if grid[target][i] != "" and grid[target][i] == grid[source][i]: grid[target][i] = str(int(grid[target][i])+int(grid[source][i])) grid[source][i] = ""
""" Katherine St. John, Spring 2015 Introductory Programming, Lehman College, CUNY A summing game based on 2048 by Gabriele Cirulli (http://gabrielecirulli.github.io/2048/) """ from turtle import * from random import * """ Welcome messages for the program""" def welcome(): print("Welcome to a summing game") print("For each move pick a direction") print("([L]eft, [R]ight, [U]p, or [D]own).") print() """ Sets up the screen with the origin in upper left corner """ def setUp(xMax,yMax): win = Screen() win.setworldcoordinates(-0.5, yMax+0.5,xMax+0.5,-0.5) t = Turtle() t.speed(10) t.hideturtle() return win,t """ Draws a grid to the graphics window""" def drawGrid(tic,xMax,yMax): #Draw the vertical bars of the game board: for i in range(0,xMax+1): tic.up() tic.goto(0,i) tic.down() tic.forward(yMax) #Draw the horizontal bars of the game board: tic.left(90) #Point the turtle in the right direction before drawing for i in range(0,yMax+1): tic.up() tic.goto(i,0) tic.down() tic.forward(xMax) """ Fills in the square (x,y) and write value""" def fillSquareWithValue(t,x,y,value): t.up() t.goto(x+.1,y+.1) t.fillcolor("white") t.begin_fill() for i in range(4): t.forward(.8) t.right(90) t.end_fill() t.goto(x+.50,y+.85) t.write(value,align="center",font=('Arial', 70, 'normal')) def drawBoard(t,grid): for i in range(4): for j in range(4): fillSquareWithValue(t,i,j,grid[i][j]) def generateNewSquare(t,grid): x = randrange(4) y = randrange(4) while grid[x][y] != "": x = randrange(4) y = randrange(4) grid[x][y] = "2" fillSquareWithValue(t,x,y,"2") def slideGridDown(grid): for i in range(4): for j in range(3,0,-1): if grid[i][j] == "" and grid[i][j-1] != "": grid[i][j] = grid[i][j-1] grid[i][j-1] = "" def slideGridUp(grid): for i in range(4): for j in range(3): if grid[i][j] == "" and grid[i][j+1] != "": grid[i][j] = grid[i][j+1] grid[i][j+1] = "" def combineRows(grid,source,target): for i in range(4): if grid[i][target] != "" and grid[i][target] == grid[i][source]: grid[i][target] = str(int(grid[i][target])+int(grid[i][source])) grid[i][source] = "" def slideGridRight(grid): for j in range(4): for i in range(3,0,-1): if grid[i][j] == "" and grid[i-1][j] != "": grid[i][j] = grid[i-1][j] grid[i-1][j] = "" def slideGridLeft(grid): for j in range(4): for i in range(3): if grid[i][j] == "" and grid[i+1][j] != "": grid[i][j] = grid[i+1][j] grid[i+1][j] = "" def combineColumns(grid,source,target): for i in range(4): if grid[target][i] != "" and grid[target][i] == grid[source][i]: grid[target][i] = str(int(grid[target][i])+int(grid[source][i])) grid[source][i] = "" def moveLeft(grid): score = 0 for i in range(3): slideGridLeft(grid) for i in range(3): combineColumns(grid,i+1,i) slideGridLeft(grid) def moveRight(grid): score = 0 for i in range(3): slideGridRight(grid) for i in range(3,0,-1): combineColumns(grid,i-1,i) slideGridRight(grid) def moveUp(grid): for i in range(3): slideGridUp(grid) for i in range(3): combineRows(grid,i+1,i) slideGridUp(grid) def moveDown(grid): for i in range(3): slideGridDown(grid) for i in range(3,0,-1): combineRows(grid,i-1,i) slideGridDown(grid) """Asks user for move, makes move and returns the number of squares freed""" def makeMove(t,grid): move = input("Enter move([L]eft, [R]ight, [U]p, or [D]own):") if move == "L" or move == "l": moveLeft(grid) if move == "R" or move == "r": moveRight(grid) if move == "U" or move == "u": moveUp(grid) if move == "D" or move == "d": moveDown(grid) drawBoard(t,grid) """Return the number of squares that are filled""" def squaresFilled(grid): filled = 0 for i in range(4): for j in range(4): if grid[i][j] != "": filled = filled + 1 return(filled) """ Write thank you message and close window on click""" def conclusion(win,t): t.up() t.goto(2,4.25) t.write("Thank you for playing!",align="center",font=('Arial', 20, 'normal')) win.exitonclick() def main(): grid = [["","","",""],["","","",""],["","","",""],["","","",""]] welcome() win,t = setUp(4,4) drawGrid(t,4,4) while squaresFilled(grid) < 16: #While there are empty squares, keep playing: generateNewSquare(t,grid) makeMove(t,grid) conclusion(win,t) main()
If you finish early, you may work on the programming problems.