From 664546ef9a44a3cf99a7fd4ae491a98695d16693 Mon Sep 17 00:00:00 2001 From: Tim Petricola Date: Mon, 10 Mar 2014 16:02:16 -0400 Subject: [PATCH 1/6] Store best score in localStorage Dependency injection and hide best score for incompatible browsers --- index.html | 6 +++++- js/application.js | 2 +- js/game_manager.js | 17 ++++++++++++----- js/html_actuator.js | 15 ++++++++++++++- js/local_score_manager.js | 24 ++++++++++++++++++++++++ style/main.css | 21 +++++++++++++++------ style/main.scss | 18 +++++++++++++++--- 7 files changed, 86 insertions(+), 17 deletions(-) create mode 100644 js/local_score_manager.js diff --git a/index.html b/index.html index acb945b..cf017fc 100644 --- a/index.html +++ b/index.html @@ -11,6 +11,7 @@ + @@ -22,7 +23,10 @@

2048

-
0
+
+
0
+
0
+

Join the numbers and get to the 2048 tile!

diff --git a/js/application.js b/js/application.js index 656a88d..036ca3a 100644 --- a/js/application.js +++ b/js/application.js @@ -1,6 +1,6 @@ document.addEventListener("DOMContentLoaded", function () { // Wait till the browser is ready to render the game (avoids glitches) window.requestAnimationFrame(function () { - var manager = new GameManager(4, KeyboardInputManager, HTMLActuator); + new GameManager(4, KeyboardInputManager, HTMLActuator, LocalScoreManager); }); }); diff --git a/js/game_manager.js b/js/game_manager.js index 1880d48..11974c1 100644 --- a/js/game_manager.js +++ b/js/game_manager.js @@ -1,7 +1,8 @@ -function GameManager(size, InputManager, Actuator) { +function GameManager(size, InputManager, Actuator, ScoreManager) { this.size = size; // Size of the grid this.inputManager = new InputManager; - this.actuator = new Actuator; + this.scoreManager = new ScoreManager; + this.actuator = new Actuator(this.scoreManager.isSupported()); this.startTiles = 2; @@ -51,11 +52,17 @@ GameManager.prototype.addRandomTile = function () { // Sends the updated grid to the actuator GameManager.prototype.actuate = function () { + if (this.scoreManager.get() < this.score) { + this.scoreManager.set(this.score); + } + this.actuator.actuate(this.grid, { - score: this.score, - over: this.over, - won: this.won + score: this.score, + over: this.over, + won: this.won, + bestScore: this.scoreManager.get() }); + }; // Save all tile positions and remove merger info diff --git a/js/html_actuator.js b/js/html_actuator.js index f1c3308..b6aec40 100644 --- a/js/html_actuator.js +++ b/js/html_actuator.js @@ -1,9 +1,15 @@ -function HTMLActuator() { +function HTMLActuator(bestScoreSupported) { this.tileContainer = document.getElementsByClassName("tile-container")[0]; this.scoreContainer = document.getElementsByClassName("score-container")[0]; + this.bestContainer = document.getElementsByClassName("best-container")[0]; this.messageContainer = document.getElementsByClassName("game-message")[0]; this.score = 0; + this.bestScoreSupported = bestScoreSupported; + + if (!this.bestScoreSupported) { + this.bestContainer.style.display = "none"; + } } HTMLActuator.prototype.actuate = function (grid, metadata) { @@ -21,6 +27,7 @@ HTMLActuator.prototype.actuate = function (grid, metadata) { }); self.updateScore(metadata.score); + self.updateBestScore(metadata.bestScore); if (metadata.over) self.message(false); // You lose if (metadata.won) self.message(true); // You win! @@ -103,6 +110,12 @@ HTMLActuator.prototype.updateScore = function (score) { } }; +HTMLActuator.prototype.updateBestScore = function (bestScore) { + if (this.bestScoreSupported) { + this.bestContainer.textContent = bestScore; + } +}; + HTMLActuator.prototype.message = function (won) { var type = won ? "game-won" : "game-over"; var message = won ? "You win!" : "Game over!" diff --git a/js/local_score_manager.js b/js/local_score_manager.js new file mode 100644 index 0000000..400633b --- /dev/null +++ b/js/local_score_manager.js @@ -0,0 +1,24 @@ +function LocalScoreManager() { + this.key = 'bestScore'; +} + +LocalScoreManager.prototype.get = function () { + if (!this.isSupported()) { + return 0; + } + + return localStorage.getItem(this.key); +}; + +LocalScoreManager.prototype.set = function (score) { + if (!this.isSupported()) { + return false; + } + + localStorage.setItem(this.key, score); +}; + +LocalScoreManager.prototype.isSupported = function () { + return !!window.localStorage; +}; + diff --git a/style/main.css b/style/main.css index f5760a4..784aeb5 100644 --- a/style/main.css +++ b/style/main.css @@ -49,9 +49,12 @@ h1.title { top: -50px; opacity: 0; } } -.score-container { +.scores-container { + float: right; } + +.score-container, .best-container { position: relative; - float: right; + display: inline-block; background: #bbada0; padding: 15px 25px; font-size: 25px; @@ -60,19 +63,19 @@ h1.title { font-weight: bold; border-radius: 3px; color: white; - margin-top: 8px; } - .score-container:after { + margin-top: 8px; + text-align: center; } + .score-container:after, .best-container:after { position: absolute; width: 100%; top: 10px; left: 0; - content: "Score"; text-transform: uppercase; font-size: 13px; line-height: 13px; text-align: center; color: #eee4da; } - .score-container .score-addition { + .score-container .score-addition, .best-container .score-addition { position: absolute; right: 30px; color: red; @@ -86,6 +89,12 @@ h1.title { -webkit-animation-fill-mode: both; -moz-animation-fill-mode: both; } +.score-container:after { + content: "Score"; } + +.best-container:after { + content: "Best"; } + p { margin-top: 0; margin-bottom: 10px; diff --git a/style/main.scss b/style/main.scss index 5343c6d..96cdc7b 100644 --- a/style/main.scss +++ b/style/main.scss @@ -58,11 +58,15 @@ h1.title { } } -.score-container { +.scores-container { + float: right; +} + +.score-container, .best-container { $height: 25px; position: relative; - float: right; + display: inline-block; background: $game-container-background; padding: 15px 25px; font-size: $height; @@ -72,13 +76,13 @@ h1.title { border-radius: 3px; color: white; margin-top: 8px; + text-align: center; &:after { position: absolute; width: 100%; top: 10px; left: 0; - content: "Score"; text-transform: uppercase; font-size: 13px; line-height: 13px; @@ -100,6 +104,14 @@ h1.title { } } +.score-container:after { + content: "Score"; +} + +.best-container:after { + content: "Best" +} + p { margin-top: 0; margin-bottom: 10px; From fda0e8405fbc912241279fafda59cb03c98f4a48 Mon Sep 17 00:00:00 2001 From: Tim Petricola Date: Mon, 10 Mar 2014 17:18:54 -0400 Subject: [PATCH 2/6] Fix win/over in local --- js/html_actuator.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/js/html_actuator.js b/js/html_actuator.js index b6aec40..3e51195 100644 --- a/js/html_actuator.js +++ b/js/html_actuator.js @@ -118,9 +118,11 @@ HTMLActuator.prototype.updateBestScore = function (bestScore) { HTMLActuator.prototype.message = function (won) { var type = won ? "game-won" : "game-over"; - var message = won ? "You win!" : "Game over!" + var message = won ? "You win!" : "Game over!"; - if (ga) ga("send", "event", "game", "end", type, this.score); + if (typeof ga !== "undefined") { + ga("send", "event", "game", "end", type, this.score); + } this.messageContainer.classList.add(type); this.messageContainer.getElementsByTagName("p")[0].textContent = message; From d6da8d99251266445beaead0da79f0085756c054 Mon Sep 17 00:00:00 2001 From: Tim Petricola Date: Tue, 11 Mar 2014 11:52:02 -0400 Subject: [PATCH 3/6] localStorage fallback --- js/game_manager.js | 2 +- js/html_actuator.js | 11 ++--------- js/local_score_manager.js | 33 ++++++++++++++++++++------------- 3 files changed, 23 insertions(+), 23 deletions(-) diff --git a/js/game_manager.js b/js/game_manager.js index 11974c1..91c0390 100644 --- a/js/game_manager.js +++ b/js/game_manager.js @@ -2,7 +2,7 @@ function GameManager(size, InputManager, Actuator, ScoreManager) { this.size = size; // Size of the grid this.inputManager = new InputManager; this.scoreManager = new ScoreManager; - this.actuator = new Actuator(this.scoreManager.isSupported()); + this.actuator = new Actuator; this.startTiles = 2; diff --git a/js/html_actuator.js b/js/html_actuator.js index 3e51195..36e1d75 100644 --- a/js/html_actuator.js +++ b/js/html_actuator.js @@ -1,15 +1,10 @@ -function HTMLActuator(bestScoreSupported) { +function HTMLActuator() { this.tileContainer = document.getElementsByClassName("tile-container")[0]; this.scoreContainer = document.getElementsByClassName("score-container")[0]; this.bestContainer = document.getElementsByClassName("best-container")[0]; this.messageContainer = document.getElementsByClassName("game-message")[0]; this.score = 0; - this.bestScoreSupported = bestScoreSupported; - - if (!this.bestScoreSupported) { - this.bestContainer.style.display = "none"; - } } HTMLActuator.prototype.actuate = function (grid, metadata) { @@ -111,9 +106,7 @@ HTMLActuator.prototype.updateScore = function (score) { }; HTMLActuator.prototype.updateBestScore = function (bestScore) { - if (this.bestScoreSupported) { - this.bestContainer.textContent = bestScore; - } + this.bestContainer.textContent = bestScore; }; HTMLActuator.prototype.message = function (won) { diff --git a/js/local_score_manager.js b/js/local_score_manager.js index 400633b..95c6966 100644 --- a/js/local_score_manager.js +++ b/js/local_score_manager.js @@ -1,24 +1,31 @@ +window.fakeStorage = { + _data : {}, + setItem : function (id, val) { + console.log('set'); + return this._data[id] = String(val); + }, + getItem : function (id) { + return this._data.hasOwnProperty(id) ? this._data[id] : undefined; + }, + removeItem : function (id) { return delete this._data[id]; }, + clear : function () { return this._data = {}; } +}; + function LocalScoreManager() { + var localSupported = !!window.localStorage; this.key = 'bestScore'; + this.storage = localSupported ? window.localStorage : window.fakeStorage; } LocalScoreManager.prototype.get = function () { - if (!this.isSupported()) { - return 0; + var score = this.storage.getItem(this.key); + if (typeof score === "undefined" || score === null) { + score = 0; } - - return localStorage.getItem(this.key); + return score; }; LocalScoreManager.prototype.set = function (score) { - if (!this.isSupported()) { - return false; - } - - localStorage.setItem(this.key, score); -}; - -LocalScoreManager.prototype.isSupported = function () { - return !!window.localStorage; + this.storage.setItem(this.key, score); }; From 95a2676b3b5e00cfae2e9e84e7b49ae3fb51b24b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Gr=C3=A9gory=20HOULLIER?= Date: Wed, 12 Mar 2014 08:59:12 +0100 Subject: [PATCH 4/6] Use querySelector instead of getElementsByClassName --- js/html_actuator.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/js/html_actuator.js b/js/html_actuator.js index 94a8f35..186a63f 100644 --- a/js/html_actuator.js +++ b/js/html_actuator.js @@ -1,7 +1,7 @@ function HTMLActuator() { - this.tileContainer = document.getElementsByClassName("tile-container")[0]; - this.scoreContainer = document.getElementsByClassName("score-container")[0]; - this.messageContainer = document.getElementsByClassName("game-message")[0]; + this.tileContainer = document.querySelector(".tile-container"); + this.scoreContainer = document.querySelector(".score-container"); + this.messageContainer = document.querySelector(".game-message"); this.score = 0; } From b080e5300db93f1ade4308b4c2ff6b064bfcc101 Mon Sep 17 00:00:00 2001 From: Gabriele Cirulli Date: Wed, 12 Mar 2014 12:15:57 +0100 Subject: [PATCH 5/6] add small code tweaks --- js/game_manager.js | 2 -- js/html_actuator.js | 2 +- js/keyboard_input_manager.js | 14 ++++++++------ js/local_score_manager.js | 7 ++++--- 4 files changed, 13 insertions(+), 12 deletions(-) diff --git a/js/game_manager.js b/js/game_manager.js index 91c0390..01ce04c 100644 --- a/js/game_manager.js +++ b/js/game_manager.js @@ -211,8 +211,6 @@ GameManager.prototype.tileMatchesAvailable = function () { var cell = { x: x + vector.x, y: y + vector.y }; var other = self.grid.cellContent(cell); - if (other) { - } if (other && other.value === tile.value) { return true; // These two tiles can be merged diff --git a/js/html_actuator.js b/js/html_actuator.js index 0984570..5562ce9 100644 --- a/js/html_actuator.js +++ b/js/html_actuator.js @@ -1,7 +1,7 @@ function HTMLActuator() { this.tileContainer = document.querySelector(".tile-container"); this.scoreContainer = document.querySelector(".score-container"); - this.bestContainer = document.querySelector(".best-container"); + this.bestContainer = document.querySelector(".best-container"); this.messageContainer = document.querySelector(".game-message"); this.score = 0; diff --git a/js/keyboard_input_manager.js b/js/keyboard_input_manager.js index bcd6f19..24dde7a 100644 --- a/js/keyboard_input_manager.js +++ b/js/keyboard_input_manager.js @@ -59,20 +59,22 @@ KeyboardInputManager.prototype.listen = function () { // Listen to swipe events var touchStartClientX, touchStartClientY; var gameContainer = document.getElementsByClassName("game-container")[0]; - gameContainer.addEventListener("touchstart", function(event) { + + gameContainer.addEventListener("touchstart", function (event) { if (event.touches.length > 1) return; touchStartClientX = event.touches[0].clientX; touchStartClientY = event.touches[0].clientY; event.preventDefault(); }); - gameContainer.addEventListener("touchmove", function(event) { + + gameContainer.addEventListener("touchmove", function (event) { event.preventDefault(); }); - gameContainer.addEventListener("touchend", function(event) { - if (event.touches.length > 0) { - return; - } + + gameContainer.addEventListener("touchend", function (event) { + if (event.touches.length > 0) return; + var dx = event.changedTouches[0].clientX - touchStartClientX; var absDx = Math.abs(dx); diff --git a/js/local_score_manager.js b/js/local_score_manager.js index 95c6966..7e35cd1 100644 --- a/js/local_score_manager.js +++ b/js/local_score_manager.js @@ -1,7 +1,7 @@ window.fakeStorage = { _data : {}, setItem : function (id, val) { - console.log('set'); + console.log("set"); return this._data[id] = String(val); }, getItem : function (id) { @@ -13,8 +13,9 @@ window.fakeStorage = { function LocalScoreManager() { var localSupported = !!window.localStorage; - this.key = 'bestScore'; - this.storage = localSupported ? window.localStorage : window.fakeStorage; + + this.key = "bestScore"; + this.storage = localSupported ? window.localStorage : window.fakeStorage; } LocalScoreManager.prototype.get = function () { From 385b65d086d910a7cd02625aaa2dd96d32acd37c Mon Sep 17 00:00:00 2001 From: Gabriele Cirulli Date: Wed, 12 Mar 2014 12:55:40 +0100 Subject: [PATCH 6/6] mobile tweaks for best score --- style/main.css | 12 ++++++++---- style/main.scss | 13 +++++++++++-- 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/style/main.css b/style/main.css index 06f4ed5..c28f9be 100644 --- a/style/main.css +++ b/style/main.css @@ -50,7 +50,8 @@ h1.title { opacity: 0; } } .scores-container { - float: right; } + float: right; + text-align: right; } .score-container, .best-container { position: relative; @@ -460,14 +461,17 @@ hr { padding: 0 20px; } h1.title { - font-size: 50px; } + font-size: 27px; + margin-top: 15px; } .container { width: 280px; margin: 0 auto; } - .score-container { - margin-top: 0; } + .score-container, .best-container { + margin-top: 0; + padding: 15px 10px; + min-width: 40px; } .heading { margin-bottom: 10px; } diff --git a/style/main.scss b/style/main.scss index 4ffe138..b5e2609 100644 --- a/style/main.scss +++ b/style/main.scss @@ -60,6 +60,7 @@ h1.title { .scores-container { float: right; + text-align: right; } .score-container, .best-container { @@ -445,7 +446,8 @@ hr { } h1.title { - font-size: 50px; + font-size: 27px; + margin-top: 15px; } .container { @@ -453,8 +455,15 @@ hr { margin: 0 auto; } - .score-container { + // .scores-container { + // float: left; + // clear: left; + // } + + .score-container, .best-container { margin-top: 0; + padding: 15px 10px; + min-width: 40px; } .heading {