2048 is a single-player sliding block puzzle game designed by Italian web developer Gabriele Cirulli. The game's objective is to slide numbered tiles on a grid to combine them in order to create a tile with the number 2048. However, one can continue to play the game after reaching the goal, creating tiles with larger numbers. Read more about the original game here on Wikipedia.
I started to work on the Simon game as the 2nd Milestone Project for the Fullstack Web Developer course of Code Institute. One day - when I was still in the planning phase of my version of the Simon game I was playing 2048 on my phone and the idea struck me to develop my 2048 game instead of Simon. It seemed more challenging and I wanted to test my ability.
I was right, it was much more complex, it includes complicated logic and a lot to handle. Making it properly responsive had its own challenges as well.
These are the exact resons why I loved developing it! I hope you love playing it and I equally hope you like some of my solutions.
Be careful when playing, it's addictive :) HAVE FUN!!!
My version is available here.
I wasn't going to copy the exact user experience of the original game. There were four specific elements I wanted to add:
- a short description of what the game is about (with link to details) and how to play -- even on small screens,
- on-screen arrows for game control,
- my own colour scheme,
- Undo option. (The original web version of the game didn't include undo option, the mobile app version I used to play has it, but it is not possible to undo at game end...)
Translating it to user stoies I wanted to:
- let the user realise what this game is and how to play, regardless of the platform they came accross it.
- let the user click on-screen buttons for control.
- provide a harmonic colour experience, while keeping the original idea of changing tile colour with increasing tile value.
- build in the opportunity of undoing in case of accidentially sliding tiles to unwanted direction or bad luck with random tile position or value when getting a new tile. Especially when it results in game over.
I didn't incorporate a main navigation menu as I deemed it irrelevant considering that the main functionalities of the game can be accessed through the on-screen buttons, the interactivity lays in the gameplay itself.
Based on these I drew the following wireframes:
Description from the original game:
2048 is played on a gray 4×4 grid, with numbered tiles that slide smoothly when a player moves them. Every turn, a new tile will randomly appear in an empty spot on the board with a value of either 2 or 4. Tiles slide as far as possible in the chosen direction until they are stopped by either another tile or the edge of the grid. If two tiles of the same number collide while moving, they will merge into a tile with the total value of the two tiles that collided. The resulting tile cannot merge with another tile again in the same move.
A scoreboard on the top keeps track of the user's score. The user's score starts at zero, and is increased whenever two tiles combine, by the value of the new tile. As with many arcade games, the user's best score is shown alongside the current score.
The game is won when a tile with a value of 2048 appears on the board, hence the name of the game. After reaching the 2048 tile, players can continue to play (beyond the 2048 tile) to reach higher scores. When the player has no legal moves (there are no empty spaces and no adjacent tiles with the same value), the game ends.
On top of the necessary start game/new game, my version includes:
- Undo
- On-screen arrows for game control
Game control option are:
- Keyboard arrows
- On-screen arrow buttons
- Screen swipe for touch-screen devices.
HTML and CSS used for the look and feel of the game, as well as Bootstrap framework, more specifically Bootswatch Cyborg theme. Besides the built in responsive functionality of Bootstrap, I used further media queries to generate more appropriate size variants and to identify landscape/portrait format. I also used Sass to generate the colour scheme for the game tiles as well as the style attributes for the tile positioning classes.
The gameplay functionality was developed in JavaScript. I used jQuery library to simplify DOM manipulation.
At first I concentrated on the maths part of the game and started with a simple grid where I calculated the value of each position of the grid whenever the user indicated a move. I got stuck with emulating the moves, numbers were changing on the position, but I realised I had to use actual tiles and have them sliding on user input.
Then I implemented the tiles and re-coded the controls. This solution works with two particular classes assigned to each tiles. One determines the look and feel of it (colour, font size, font colour), the second sets the position.
Instead of writing separate functions for moving to each directions I decided to pass the direction as an argument to the function that handles the swipe.
There are event handlers attached to each button as well as the keyboard arrows and touch screen. The 'New game' and 'Undo' buttons call their respective functions directly, while the arrow keys, buttons and touch-screen swiping call the same function, named onUserInput, which takes the direction as its argument.
The newGame function initializes all the variables, cleans the gameboard by deleting all the content of the game-board <div>
and starts the game by randomizing two tiles then waiting for user input.
In order to manage the undoing of the last moves, the status of the game board and the current score are recorded to respective arrays before the actual move takes place. Once the user clicks the undo button, the last element is read from both arrays and the previous board state and score are reset accordingly. The length of the arrays storing game play and score history is limited, as there can be several thousand moves in a single game play.
The following steps are performed once a user indicated the required direction:
- recording position and value of all tiles currently on the board into the
gamePlayHistory
array, - recording the current score to the
scoreHistory
array, - shifting the tiles by passing the direction into the function handling the move
shiftTiles(direction)
, - checks if there was a move (new tiles are only to be created if tiles were actually sliding, if a user indicated a direction, but there are no tiles to be moved that way, no new tile needed)
- if there was a move then generates a new tile,
- checks whether there is a potential move, otherwise it is game over,
- updates the score,
- checks if the aim of the game was reached.
The game can end either:
- if there is no potential move. In this case a Game Over message is displayed:
- if the aim of the game is reached and the player doesn’t want to continue playing. (Once 2048 reached, they can still continue, but they are congratulated!). In this case a 'You Won' message is displayed:
During testing, I wanted to test:
- design and responsiveness: to see if I like the look and feel of the game on different devices and screen sizes,
- functioning and operability in different browsers (mobile and pc),
- against user stories,
- against misbehaviour (defensive design).
I performed manual tests on available devices (Android 6.0.1 and 8.1 on Sony and Samsung, iPhone 4S, SE and 6, Google Chrome 75.0.3770.142 both on laptop and desktop) and asked family members to do the same by checking page layout, clicking the link to Wikipedia page as well as playing the game. I've also checked all different sizes available in Chrome Dev Tools.
The outcome was that in case of landscape orientation on a tablet the screen width is 'large' enough to display the game descripton but it wouldn't fit to the screen (user would have to scroll down to be able to read it). Instead of shrinking the gameboard, I decided to modify the UX and go for the tooltip solution for landscape tablets.
Bootstrap makes the page responsive by displaying certain elements differently depending on the screen size, but doesn't change the size of the gameboard itself. This created bad user experience, because sizing the gameboard to small mobile screens resulted in too small gameboard on larger devices and vice versa. I used media queries and generated three different gameboard size variants to overcome this issue.
For the testing of most gameplay solutions I simulated the gameboard by inserting the appropriate 'tiles' into the HTML file, then faked game in play status in the javaScript by forcing the variable that checks whether there is a game in play. (this has been deleted from the production version).
I have also extensively used debugger
, alert
and console.log
to check values of certain variables at any given moment as well as the flow of the code.
There were several bugs, a few of the interesting ones:
-
At a certain point in the development there was a bug causing two tiles appearing at the same board position. In order to find the cause of the problem I inserted a function to determine when that happened. I decided to leave it in the final version (it is part of the
isItGameOver()
function, just in case, although the bug has been eliminated. -
Pressing the arrow buttons/keys fast resulted in starting another move, before the previous move has finished. To overcome the problem, I implemented a
readyStatus
boolean variable, set it tofalse
every time when an actual move starts. When the new tile has been generated and displayed after a move, it is set totrue
. On user input, if thereadyStatus
isn'ttrue
the user input is neglected.
Chrome
The entire development was done in Chrome 75.0, didn't require further testing on desktop, but I also tested mobile version on different Android phones (Sony, Samsung).
Safari
Tested on iPhone 6, SE, 4S.
Firefox
I tested all the functionality in Firefox on desktop version 69.0 (64-bit). All features worked properly.
- "let the user realise what this game is and how to play, regardless of the platform they came accross it." --> either the tooltip or the description under the gameboard informs the user.
- let the user click on-screen buttons for control. --> done, works nice (hover status is simulated briefly when touch-screen is swiped)
- provide a harmonic colour experience, while keeping the original idea of changing tile colour with increasing tile value. --> Generating the colours by a code helped achieve it.
- build in the opportunity of undoing in case of accidentially sliding tiles to unwanted direction or bad luck with random tile position or value when getting a new tile. Especially when it results in game over. --> specifically tested by generating game over situations, works well.
I tested unexpected user behaviour - tried to misbehave. This is how I found the bug of pressing arrow buttons very fast resulted in starting moves before the previous move has finished. Even though I eliminated it by checking ready status, when the gameboard is full of tiles and the quick keystrokes are continued, a tile appeared off-grid: tile element was generated with undefined position style ('gt-position-style-undefined-undefined'), therefore were positioned off-grid. I'm simply deleting the tiles with erroneous position classes as soon as they are created to overcome this issue.
I deployed the site on GitHub Pages from the master branch. Any future changes committed to the master branch will automatically update the deployed site. By default on GitHub, the landing page must be named index.html, therefore it may not be renamed.
I also used GitHub for version control. There was only one isntace I had to revert to an earlier version, but only to the last committed change,
therefore a git checkout <filename>
solved my issue.
- production version uses minified CSS and javaScript files to reduce file size and loading time and potentially mobile data usage,
debugger;
andconsole.log();
statements are eliminated from the production version,- variables overridden for testing purposes are re-set to standard functionality.
The text for the description and the tooltip - as well as for this README file is taken from Wikipedia article about the original game, I altered it to shorten and be more succint.
Pictures used throughout the project are all screenshots from the project.
Thanks to Ali Ashik - my mentor - for the inspiration and avice on important but not so obvious considerations like:
- IIFE
- DOM manipulation is consuming resources, so we should assign variables to them.
I used Stackoverflow and w3schools.com to help me better understand certain JavaScript functionalities and overcome obstacles as well as fix bugs.
Advice from Code Institute Slack community has also helped me to learn about debugging in JavaScript.
Solution for cloning an array to avoid changing with the original array is from Samantha Ming: https://www.samanthaming.com/tidbits/35-es6-way-to-clone-an-array
Tooltip css solution is from tutorialzine: Create inline help tips for your site with a bit of CSS
Swipe detection and identification of direction is based on tutorials from www.javascriptkit.com
I used JSHint.com to validate my javaScript code.
I used javaScript-minifier and css-minifier to create the minified files.