Make your own 2048 in Python!
Created on Jan. 25, 2019 by Antoine Mazières
Presentation
During this exercise you will build your own 2048 game!
If you never played, have some fun here for a moment :)
Introducing Numpy
There is many ways to do this, but here we will consider the game's board as a matrice and use numpy to program the game. If you installed Anaconda Python, you already have this module.
Numpy is a Python module introducing matrices and many other linear algebra data types and functions. It is widely used for scientific computing, area in which Python is excelling.
If you're familiar with Matlab or Octave, have a look at this page which compare most famous operations form one language to the other.
Here are a few examples:
Note
By convention, we import numpy like this to avoid to have to type numpy
each time we want to use it in our program.
>>> import numpy as np
Initiate a 4x4 matrice of zeros considered as integers:
>>> m = np.zeros((4,4), dtype=int)
>>> print(m)
[[0 0 0 0]
[0 0 0 0]
[0 0 0 0]
[0 0 0 0]]
Changing the value of one or many elements:
>>> m[3,2] = 1
>>> print(m)
[[0 0 0 0]
[0 0 0 0]
[0 0 0 0]
[0 0 1 0]]
>>> m[3,:] = 1
>>> print(m)
[[0 0 0 0]
[0 0 0 0]
[0 0 0 0]
[1 1 1 1]]
Getting the position of specific values:
>>> np.where(m == 1)
(array([3, 3, 3, 3]), array([0, 1, 2, 3]))
Some core elements of Numpy are implemented in the C language making it super disco fast.
2048 Rules
The board is a 4 by 4 matrice (i.e. a list of 4 lists of length 4). When initialized, all positions are empty except 2 randomly chosen positions set to 2.
Then, the player interact with the game by entering a direction (up, down, right, left) and 3 things happens:
- All elements fall such as gravity whose pulling in that direction.
- If two positions with the same value are one under the other when "falling", they merge by adding their values into one single position.
- A new elements pops in a random empty position, of value
2
80% of the time and of value4
20% of the time.
The player looses if no empty position are available to add new elements.
The player wins if one position reach the value 2048.
You must implement the game's logic and not merely the examples given as illustrations of the exercises which don't cover all possible cases.
Exercise 1 :: Initiate the grid
Provide the function init_grid()
that returns a random board
for starting the game.
The correction robot will import your script and execute
the init_grid()
function, expecting something like:
>>> import numpy as np
>>> from solution import *
>>> grid = init_grid()
>>> print(grid)
[[0 0 0 0]
[0 0 0 0]
[0 0 0 2]
[0 0 0 2]]
>>> type(grid)
<class 'numpy.ndarray'>
Exercise 2 :: Add new element to the grid
Provide the function add_new(grid)
that will add either a 2 (80% of the time)
or a 4 (20% of the time) somewhere empty in any board.
If no empty position is available, add_new
should
just return the board passed as argument.
>>> import numpy as np
>>> from solution import *
>>> grid = init_grid()
>>> print(grid)
[[0 0 0 0]
[2 0 2 0]
[0 0 0 0]
[0 0 0 0]]
>>> grid = add_new(grid)
>>> print(grid)
[[0 0 0 0]
[2 0 2 0]
[0 0 0 4]
[0 0 0 0]]
>>> type(grid)
<class 'numpy.ndarray'>
Exercise 3 :: Roll some row
Provide the function rollin_row(row)
that will roll
the row to the left and returns it, such as:
>>> from solution import *
>>> row = [2, 0, 0, 2]
>>> row = rollin_row(row)
>>> print(row)
[4, 0, 0, 0]
>>> row = [2, 4, 4, 2]
>>> row = rollin_row(row)
>>> print(row)
[2, 8, 2, 0]
Here it doesn't matter if row
is a numpy object or a classic python list.
Exercise 4 :: Roll the grid
Provide the function rollin(grid, direction)
where grid
is the grid to be
moved and direction
the direction to be taken,
l
(left), r
(right), u
(up) and d
(down).
rollin
should return the new grid.
>>> print(grid)
[[ 8 0 16 0]
[ 4 0 0 0]
[ 4 0 2 0]
[ 0 32 2 0]]
>>> grid = rollin(grid, 'd')
>>> print(grid)
[[ 0 0 0 0]
[ 0 0 0 0]
[ 8 0 16 0]
[ 8 32 4 0]]
>>> type(grid)
<class 'numpy.ndarray'>
Exercise 5 :: Create an interactive 2048 game !
Note
This part is not mandatory
You have already built important elements of the game. A few things still need to be done, among which:
- add a new element (2 or 4) at each turn, when possible
- Make it playable from a terminal according to player's inputs
- Handle when the grid is stuck or a wrong command is entered, why not with Exceptions
- Analyse the grid to know when the user wins or looses.
Provide the function my2048()
that let the user play 2048, such as:
>>> my2048()
[[0 0 0 2]
[0 0 0 0]
[0 0 0 0]
[0 0 0 2]]
u
[[0 0 2 4]
[0 0 0 0]
[0 0 0 0]
[0 0 0 0]]
l
[[2 4 0 0]
[0 0 0 0]
[2 0 0 0]
[0 0 0 0]]
l
The grid is stuck.
Move in another direction.
[[2 4 0 0]
[0 0 0 0]
[2 0 0 0]
[0 0 0 0]]
u
[[4 4 0 0]
[0 2 0 0]
[0 0 0 0]
[0 0 0 0]]
e
Unknown command.
Press l, r, u or d + Enter.
[[4 4 0 0]
[0 2 0 0]
[0 0 0 0]
[0 0 0 0]]
r
[[0 0 0 8]
[0 0 2 2]
[0 0 0 0]
[0 0 0 0]]
[[ 2 8 8 4]
[ 8 32 16 64]
[16 8 4 2]
[ 8 4 2 4]]
l
[[ 2 16 4 4]
[ 8 32 16 64]
[16 8 4 2]
[ 8 4 2 4]]
l
[[ 2 16 8 2]
[ 8 32 16 64]
[16 8 4 2]
[ 8 4 2 4]]
Game Over ! :(
Exercise 6 :: Make python play for you !
Note
This part is not mandatory
Provide another version of my2048()
named ai2048()
that will choose itself which direction to go until the game ends.
Super disco Bonus :: make an AI that wins !