|
I wrote my Simple 2048 game web application from scratch, without having a look at the source of the original game or
any other 2048 game related code. With a theoretical Javascript knowledge of more or less zero and the rudimentary experience of only 2 JavaScript
applications written in my whole life, I guess that my code is far away from what it would be, if a professional JavaScript developer would have
written it. On the other side, I think that the code, as it is, is rather simple to understand by people, who are new to this language, as I am,
and, most important, the application works all correctly...
|
My Simple 2048 game web application differs from the original game in the following:
- The player can choose among 4 board sizes: 3x3, 4x4, 5x5 and 6x6.
- The game is played with the mouse (and not with the arrow keys).
- The randomly positionned new tiles have always a value of 2 (instead of 2 or 4).
- A merged tile can (and always will) merge a further time when colliding with a tile with the same value.
- The game ends when any tile has a value of 2048 (in the original version, the game can be continued even if the aim has been reached).
|
Use the following link to load the online application
|
|
|
Click the following link to download the Simple 2048 game
Javascript code and all other files needed to run this application on your web server. Have a look at the ReadMe.txt file, included in the download
archive, for details about the different files, and where to place them on the server. As the application is pure Javascript, it is possible to run it in
any web browser without the need of a webserver. Use the following link to download
the offline version of Simple 2048 game.
|
Globally, the application works this way:
- When the player pushes one of the four "board" buttons, the function boardSelect({board-size})
is executed. It sets the actual board size to the function argument, initializes the two dimensional array, that contains the values of the board grid
elements (sets them to 0) and displays an empty board of the size selected.
- The game is started when the player pushes the "Start" button. This calls the function gameStart().
It generates the first random position "2" tile and displays the board (with the new tile).
- The tiles are moved when the player pushed one of the four "direction" buttons. This calls the function tilesMove({direction}). It moves the tiles according to the direction passed as argument (updates the two dimensional array), if the
game isn't yet over, generates the next random position "2" tile and displays the board (with the tiles moved and the new tile). If 2048 has been reached
on any tile, or if the grid is completely filled up, the game ends with a "win" or "lose" message.
|
Lets have a look at the code, step by step.
|
Global variables and constants.
|
First there are some global variable declarations, in particular the two dimensional array that is used to store the actual board
grid values. There are also some global constants arrays: one with all possible tile values and a corresponding one with these
tiles' color; a third one contains the font size that has to be used with a given board. In fact, I use a different tile size for each of the 4 boards and
this makes necessary the usage of diferent font sizes. The array with these font sizes becomes necessary as the font size is part of the value of the
style attribute of the corresponding HTML table field. As this style is dynamically applied by JavaScript, the font size value
has to be altered, and the values used for the different board sizes must be known by the script. Here the global variable and constant declarations:
let boardGrid = []; let boardSize = -1;
let newX = -1; let newY = -1;
let gameStarted = false; let gameEnded = false;
let gameWin = false; let gameLose = false;
for (i = 0; i < 6; i++) {
boardGrid[i] = [];
for (j = 0; j < 6; j++) {
boardGrid[i][j] = -1;
}
}
let tileValues = new Array(0, 2, 4, 8, 16, 32, 64, 128, 256, 1024, 2048);
let tileColors = new Array("White", "DodgerBlue", "DeepSkyBlue", "Cyan", "Aquamarine", "Lime", "GreenYellow", "Yellow", "Orange", "DarkOrange",
"Red", "DarkRed");
let tileFontSizes = new Array(30, 25, 20, 15);
|
Game board selection.
|
The function boardSelect({board-size}) is called, when the player pushes one of the "board" buttons on the webpage. It sets the
global variable boardSize to the actual board size, initializes the board by calling the function boardInit(), then displays
the (empty) board by calling the function boardDisplay(). The HTML code of the game webpage includes all 4 boards (as 4 tables),
included in the rows of a table. the | element of this table has a style attribute including a visibility property, that by this function is set to visible for the table row including the table
corresponding to the board actually selected, and to collapse for the other table rows. This way, one single row of the table,
i.e. one single of the 4 boards, will be displayed.
Code of the boardSelect({board-size}) function:
function boardSelect (bsize) {
boardSize = bsize;
boardInit();
boardDisplay();
gameStarted = false; gameEnded = false;
if (document.getElementById) {
let board = 'b' + bsize;
document.getElementById('b3').style.visibility = "collapse";
document.getElementById('b4').style.visibility = "collapse";
document.getElementById('b5').style.visibility = "collapse";
document.getElementById('b6').style.visibility = "collapse";
document.getElementById(board).style.visibility = "visible";
}
}
|
Code of the boardInit() function:
function boardInit() {
for (i = 0; i < 6; i++) {
for (j = 0; j < 6; j++) {
if (i < boardSize && j < boardSize) {
boardGrid[i][j] = 0;
}
else {
boardGrid[i][j] = -1;
}
}
}
}
|
Game start.
|
The function gameStart() is called when the player pushes the "Start" button on the webpage (this button becomes visible
together with the board grid). The function generates a new value "2" tile at a random position on the board (a 2 is inserted into the corresponding
cell of the boardGrid array) by calling the function tilesNew(). The new board layout is then displayed by calling
boardDisplay(). Note the usage of the global variable gameStarted: Set when the game actually is
started, further pushes on the "Start" button will have no effect (disabling the "Start" button until the player wants to play a new game by pushing
one of the "board" buttons). The reason for using newX and newY as global variables (instead of
defining them locally within the tilesNew() function, is to save the x- and y-position of the new tile, that I want to highlight
this tile using a color different from the standard color for value "2" tiles (cf. boardDisplay() function).
|
Code of the gameStart() function:
function gameStart() {
if (!gameStarted && !gameEnded) {
gameStarted = true;
tilesNew();
boardDisplay();
}
}
|
Code of the tilesNew() function:
function tilesNew() {
let found = false;
do {
newX = Math.floor(Math.random() * boardSize);
newY = Math.floor(Math.random() * boardSize);
if (boardGrid[newX][newY] == 0) {
found = true;
boardGrid[newX][newY] = 2;
}
} while (found == false);
}
|
Movement of the tiles.
|
The tiles are moved each time, the player pushes one of the "direction" buttons on the webpage. The function called in this case is tilesMove({direction}), the direction of the movement being passed as argument. The function first moves the tiles, merging those with
equal values, that collide during this movement (updating the cells of the two dimensional board grid array), then calls checkGameEnd()
to check if the winning value of 2048 has been reached resp. if there aren't any free places left on the board. If the game may continue, a new value "2"
tile is generated by calling the function tilesNew(). The new board layout (resuling from the tiles movement with possible tile
merging and the new value "2" tile, if any) is displayed calling boardDisplay(). I'm aware that it would have been possible to
largely shorten the code concerning the tile movement; just modify the code at your ease, if you consider this important...
|
Code of the tilesMove() function:
function tilesMove(direction) {
if (gameStarted && !gameEnded) {
if (direction == 'up') {
for (k = 1; k < boardSize; k++) {
for (i = 1; i < boardSize; i++) {
for (j = 0; j < boardSize; j++) {
if (boardGrid[i - 1][j] == 0) {
boardGrid[i - 1][j] = boardGrid[i][j];
boardGrid[i][j] = 0;
}
else if (boardGrid[i - 1][j] == boardGrid[i][j]) {
boardGrid[i - 1][j] = 2 * boardGrid[i][j];
boardGrid[i][j] = 0;
}
}
}
}
}
else if (direction == 'down') {
for (k = 1; k < boardSize; k++) {
for (i = boardSize - 2; i >= 0 ; i--) {
for (j = 0; j < boardSize; j++) {
if (boardGrid[i + 1][j] == 0) {
boardGrid[i + 1][j] = boardGrid[i][j];
boardGrid[i][j] = 0;
}
else if (boardGrid[i + 1][j] == boardGrid[i][j]) {
boardGrid[i + 1][j] = 2 * boardGrid[i][j];
boardGrid[i][j] = 0;
}
}
}
}
}
else if (direction == 'left') {
for (k = 1; k < boardSize; k++) {
for (j = 1; j < boardSize; j++) {
for (i = 0; i < boardSize; i++) {
if (boardGrid[i][j - 1] == 0) {
boardGrid[i][j - 1] = boardGrid[i][j];
boardGrid[i][j] = 0;
}
else if (boardGrid[i][j - 1] == boardGrid[i][j]) {
boardGrid[i][j - 1] = 2 * boardGrid[i][j];
boardGrid[i][j] = 0;
}
}
}
}
}
else if (direction == 'right') {
for (k = 1; k < boardSize; k++) {
for (j = boardSize - 2; j >= 0 ; j--) {
for (i = 0; i < boardSize; i++) {
if (boardGrid[i][j + 1] == 0) {
boardGrid[i][j + 1] = boardGrid[i][j];
boardGrid[i][j] = 0;
}
else if (boardGrid[i][j + 1] == boardGrid[i][j]) {
boardGrid[i][j + 1] = 2 * boardGrid[i][j];
boardGrid[i][j] = 0;
}
}
}
}
}
checkGameEnd();
if (!gameEnded) {
tilesNew();
}
boardDisplay();
if (gameEnded) {
if (gameWin == true) {
alert("Great! You did it!");
}
else {
alert("Game over! Maybe, if you try another one...");
}
}
}
}
|
Code of the checkGameEnd() function:
function checkGameEnd() {
gameWin = false; gameLose = true;
for (i = 0; i < boardSize; i++) {
for (j = 0; j < boardSize; j++) {
if (boardGrid[i][j] == 2048) {
gameWin = true;
}
else if (boardGrid[i][j] == 0) {
gameLose = false;
}
}
}
if (gameWin || gameLose) {
gameEnded = true;
newX = -1; newY = -1;
}
}
|
Display of the board.
|
The display of the board is done by calling the function boardDisplay(). All cells of the HTML tables corresponding to the boards
grid cells have an id attribute of the following form: b{board-size}_{cell-row}{cell-column}. This makes it easily possible to
change the cells' properties and value, using document.getElementById({id}). So, for each cell of the board grid, the value of the
<td> element is set to blank for an empty cell, or to the tile value, if there actually is a tile. Then the <td> background-color is set to the color
corresponding to the tile value (color retrieved from the tileColors array), and, as a given board has a given tile value font
size, the font size (retrieved from the tileFontSizes array) is also set. These two settings are done by changing the value of
the style attribute of the <td> element.
|
Code of the boardDisplay() function:
function boardDisplay() {
for (i = 0; i < boardSize; i++) {
for (j = 0; j < boardSize; j++) {
let tileId = 'b' + boardSize + '_' + i + j;
if (document.getElementById) {
if (boardGrid[i][j] == 0) {
document.getElementById(tileId).firstChild.data = "";
}
else {
document.getElementById(tileId).firstChild.data = boardGrid[i][j];
}
let tileColor = "White";
for (k = 0; k < 12; k++) {
if (boardGrid[i][j] == tileValues[k]) {
tileColor = tileColors[k];
if (boardGrid[i][j] == 2 && i == newX && j == newY) {
tileColor = "Blue";
}
}
}
let tileStyle = "background-color:" + tileColor + "; font-size:" + tileFontSizes[boardSize - 3];
document.getElementById(tileId).style = tileStyle;
}
}
}
}
|
|