Skip to content

Commit 5313298

Browse files
authored
Merge pull request patorjk#59 from Coteh/win-condition
Fix game crash when beating the game and add a win condition
2 parents bfc9ef4 + e2f3119 commit 5313298

File tree

5 files changed

+99
-48
lines changed

5 files changed

+99
-48
lines changed

css/dark-snake.css

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -122,7 +122,7 @@ a.snake-link:hover {
122122
display: block;
123123
}
124124

125-
.snake-try-again-dialog {
125+
.snake-try-again-dialog, .snake-win-dialog {
126126
padding: 8px;
127127
margin: 0px;
128128
background-color: black;
@@ -138,4 +138,4 @@ a.snake-link:hover {
138138
margin-left: -158px;
139139
text-align: center;
140140
display: none;
141-
}
141+
}

css/green-snake.css

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ a.snake-link:hover {
124124
display: block;
125125
}
126126

127-
.snake-try-again-dialog {
127+
.snake-try-again-dialog, .snake-win-dialog {
128128
padding: 8px;
129129
margin: 0px;
130130
background-color: #000000;
@@ -140,4 +140,4 @@ a.snake-link:hover {
140140
margin-left: -158px;
141141
text-align: center;
142142
display: none;
143-
}
143+
}

css/light-snake.css

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ a.snake-link:hover {
107107
text-align: center;
108108
display: block;
109109
}
110-
.snake-try-again-dialog {
110+
.snake-try-again-dialog, .snake-win-dialog {
111111
padding: 8px;
112112
margin: 0px;
113113
background-color: #000000;

css/main-snake.css

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ a.snake-link:hover {
119119
display: block;
120120
}
121121

122-
.snake-try-again-dialog {
122+
.snake-try-again-dialog, .snake-win-dialog {
123123
padding: 8px;
124124
margin: 0px;
125125
background-color: #000000;
@@ -135,4 +135,4 @@ a.snake-link:hover {
135135
margin-left: -158px;
136136
text-align: center;
137137
display: none;
138-
}
138+
}

js/snake.js

Lines changed: 92 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -208,6 +208,24 @@ SNAKE.Snake = SNAKE.Snake || (function() {
208208
blockPool[blockPool.length] = tempBlock;
209209
}
210210

211+
function recordScore() {
212+
var highScore = localStorage.jsSnakeHighScore;
213+
if (me.snakeLength > highScore) {
214+
alert('Congratulations! You have beaten your previous high score, which was ' + highScore + '.');
215+
localStorage.setItem('jsSnakeHighScore', me.snakeLength);
216+
}
217+
}
218+
219+
function handleEndCondition(handleFunc) {
220+
recordScore();
221+
me.snakeHead.elm.style.zIndex = getNextHighestZIndex(me.snakeBody);
222+
me.snakeHead.elm.className = me.snakeHead.elm.className.replace(/\bsnake-snakebody-alive\b/, '')
223+
me.snakeHead.elm.className += " snake-snakebody-dead";
224+
225+
isDead = true;
226+
handleFunc();
227+
}
228+
211229
// ----- public methods -----
212230

213231
me.setPaused = function(val) {
@@ -323,14 +341,19 @@ SNAKE.Snake = SNAKE.Snake || (function() {
323341
me.handleDeath();
324342
} else if (grid[newHead.row][newHead.col] === playingBoard.getGridFoodValue()) {
325343
grid[newHead.row][newHead.col] = 1;
326-
me.eatFood();
344+
if (!me.eatFood()) {
345+
me.handleWin();
346+
return;
347+
}
327348
setTimeout(function(){me.go();}, snakeSpeed);
328349
}
329350
};
330351

331352
/**
332353
* This method is called when it is determined that the snake has eaten some food.
333354
* @method eatFood
355+
* @return {bool} Whether a new food was able to spawn (true)
356+
* or not (false) after the snake eats food.
334357
*/
335358
me.eatFood = function() {
336359
if (blockPool.length <= growthIncr) {
@@ -354,28 +377,27 @@ SNAKE.Snake = SNAKE.Snake || (function() {
354377
me.snakeTail.next = me.snakeHead;
355378
me.snakeHead.prev = me.snakeTail;
356379

357-
playingBoard.foodEaten();
380+
if (!playingBoard.foodEaten()) {
381+
return false;
382+
}
383+
384+
return true;
358385
};
359386

360387
/**
361388
* This method handles what happens when the snake dies.
362389
* @method handleDeath
363390
*/
364391
me.handleDeath = function() {
365-
function recordScore () {
366-
var highScore = localStorage.jsSnakeHighScore;
367-
if (me.snakeLength > highScore) {
368-
alert('Congratulations! You have beaten your previous high score, which was ' + highScore + '.');
369-
localStorage.setItem('jsSnakeHighScore', me.snakeLength);
370-
}
371-
}
372-
recordScore();
373-
me.snakeHead.elm.style.zIndex = getNextHighestZIndex(me.snakeBody);
374-
me.snakeHead.elm.className = me.snakeHead.elm.className.replace(/\bsnake-snakebody-alive\b/,'')
375-
me.snakeHead.elm.className += " snake-snakebody-dead";
392+
handleEndCondition(playingBoard.handleDeath);
393+
};
376394

377-
isDead = true;
378-
playingBoard.handleDeath();
395+
/**
396+
* This method handles what happens when the snake wins.
397+
* @method handleDeath
398+
*/
399+
me.handleWin = function() {
400+
handleEndCondition(playingBoard.handleWin);
379401
};
380402

381403
/**
@@ -506,6 +528,7 @@ SNAKE.Food = SNAKE.Food || (function() {
506528
/**
507529
* Randomly places the food onto an available location on the playing board.
508530
* @method randomlyPlaceFood
531+
* @return {bool} Whether a food was able to spawn (true) or not (false).
509532
*/
510533
me.randomlyPlaceFood = function() {
511534
// if there exist some food, clear its presence from the board
@@ -523,12 +546,11 @@ SNAKE.Food = SNAKE.Food || (function() {
523546
col = getRandomPosition(1, maxCols);
524547

525548
// in some cases there may not be any room to put food anywhere
526-
// instead of freezing, exit out
549+
// instead of freezing, exit out (and return false to indicate
550+
// that the player beat the game)
527551
numTries++;
528552
if (numTries > 20000){
529-
row = -1;
530-
col = -1;
531-
break;
553+
return false;
532554
}
533555
}
534556

@@ -537,6 +559,7 @@ SNAKE.Food = SNAKE.Food || (function() {
537559
fColumn = col;
538560
elmFood.style.top = row * playingBoard.getBlockHeight() + "px";
539561
elmFood.style.left = col * playingBoard.getBlockWidth() + "px";
562+
return true;
540563
};
541564
};
542565
})();
@@ -625,7 +648,7 @@ SNAKE.Board = SNAKE.Board || (function() {
625648
myKeyListener,
626649
isPaused = false,//note: both the board and the snake can be paused
627650
// Board components
628-
elmContainer, elmPlayingField, elmAboutPanel, elmLengthPanel, elmHighscorePanel, elmWelcome, elmTryAgain, elmPauseScreen;
651+
elmContainer, elmPlayingField, elmAboutPanel, elmLengthPanel, elmHighscorePanel, elmWelcome, elmTryAgain, elmWin, elmPauseScreen;
629652

630653
// --- public variables ---
631654
me.grid = [];
@@ -661,6 +684,7 @@ SNAKE.Board = SNAKE.Board || (function() {
661684

662685
elmWelcome = createWelcomeElement();
663686
elmTryAgain = createTryAgainElement();
687+
elmWin = createWinElement();
664688

665689
SNAKE.addEventListener( elmContainer, "keyup", function(evt) {
666690
if (!evt) var evt = window.event;
@@ -680,6 +704,7 @@ SNAKE.Board = SNAKE.Board || (function() {
680704
elmContainer.appendChild(elmHighscorePanel);
681705
elmContainer.appendChild(elmWelcome);
682706
elmContainer.appendChild(elmTryAgain);
707+
elmContainer.appendChild(elmWin);
683708

684709
mySnake = new SNAKE.Snake({playingBoard:me,startRow:2,startCol:2});
685710
myFood = new SNAKE.Food({playingBoard: me});
@@ -728,38 +753,56 @@ SNAKE.Board = SNAKE.Board || (function() {
728753
return tmpElm;
729754
}
730755

731-
function createTryAgainElement() {
756+
function createGameEndElement(message, elmId, elmClassName) {
732757
var tmpElm = document.createElement("div");
733-
tmpElm.id = "sbTryAgain" + myId;
734-
tmpElm.className = "snake-try-again-dialog";
758+
tmpElm.id = elmId + myId;
759+
tmpElm.className = elmClassName;
735760

736-
var tryAgainTxt = document.createElement("div");
737-
tryAgainTxt.innerHTML = "JavaScript Snake<p></p>You died :(<p></p>";
738-
var tryAgainStart = document.createElement("button");
739-
tryAgainStart.appendChild( document.createTextNode("Play Again?"));
761+
var gameEndTxt = document.createElement("div");
762+
gameEndTxt.innerHTML = "JavaScript Snake<p></p>" + message + "<p></p>";
763+
var gameEndStart = document.createElement("button");
764+
gameEndStart.appendChild(document.createTextNode("Play Again?"));
740765

741-
var reloadGame = function() {
766+
var reloadGame = function () {
742767
tmpElm.style.display = "none";
743768
me.resetBoard();
744769
me.setBoardState(1);
745770
me.getBoardContainer().focus();
746771
};
747772

748-
var kbTryAgainShortcut = function(evt) {
749-
if (boardState !== 0 || tmpElm.style.display !== "block") {return;}
773+
var kbGameEndShortcut = function (evt) {
774+
if (boardState !== 0 || tmpElm.style.display !== "block") { return; }
750775
if (!evt) var evt = window.event;
751776
var keyNum = (evt.which) ? evt.which : evt.keyCode;
752777
if (keyNum === 32 || keyNum === 13) {
753778
reloadGame();
754779
}
755780
};
756-
SNAKE.addEventListener(window, "keyup", kbTryAgainShortcut, true);
781+
SNAKE.addEventListener(window, "keyup", kbGameEndShortcut, true);
757782

758-
SNAKE.addEventListener(tryAgainStart, "click", reloadGame, false);
759-
tmpElm.appendChild(tryAgainTxt);
760-
tmpElm.appendChild(tryAgainStart);
783+
SNAKE.addEventListener(gameEndStart, "click", reloadGame, false);
784+
tmpElm.appendChild(gameEndTxt);
785+
tmpElm.appendChild(gameEndStart);
761786
return tmpElm;
762787
}
788+
789+
function createTryAgainElement() {
790+
return createGameEndElement("You died :(", "sbTryAgain", "snake-try-again-dialog");
791+
}
792+
793+
function createWinElement() {
794+
return createGameEndElement("You win! :D", "sbWin", "snake-win-dialog");
795+
}
796+
797+
function handleEndCondition(elmDialog) {
798+
var index = Math.max(getNextHighestZIndex(mySnake.snakeBody), getNextHighestZIndex({ tmp: { elm: myFood.getFoodElement() } }));
799+
elmContainer.removeChild(elmDialog);
800+
elmContainer.appendChild(elmDialog);
801+
elmDialog.style.zIndex = index;
802+
elmDialog.style.display = "block";
803+
me.setBoardState(0);
804+
}
805+
763806
// ---------------------------------------------------------------------
764807
// public functions
765808
// ---------------------------------------------------------------------
@@ -978,6 +1021,8 @@ SNAKE.Board = SNAKE.Board || (function() {
9781021
/**
9791022
* This method is called when the snake has eaten some food.
9801023
* @method foodEaten
1024+
* @return {bool} Whether a new food was able to spawn (true)
1025+
* or not (false) after the snake eats food.
9811026
*/
9821027
me.foodEaten = function() {
9831028
elmLengthPanel.innerHTML = "Length: " + mySnake.snakeLength;
@@ -986,20 +1031,26 @@ SNAKE.Board = SNAKE.Board || (function() {
9861031
localStorage.setItem("jsSnakeHighScore", mySnake.snakeLength);
9871032
elmHighscorePanel.innerHTML = "Highscore: " + localStorage.jsSnakeHighScore;
9881033
}
989-
myFood.randomlyPlaceFood();
1034+
if (!myFood.randomlyPlaceFood()) {
1035+
return false;
1036+
}
1037+
return true;
9901038
};
9911039

9921040
/**
9931041
* This method is called when the snake dies.
9941042
* @method handleDeath
9951043
*/
9961044
me.handleDeath = function() {
997-
var index = Math.max(getNextHighestZIndex( mySnake.snakeBody), getNextHighestZIndex( {tmp:{elm:myFood.getFoodElement()}} ));
998-
elmContainer.removeChild(elmTryAgain);
999-
elmContainer.appendChild(elmTryAgain);
1000-
elmTryAgain.style.zIndex = index;
1001-
elmTryAgain.style.display = "block";
1002-
me.setBoardState(0);
1045+
handleEndCondition(elmTryAgain);
1046+
};
1047+
1048+
/**
1049+
* This method is called when the snake wins.
1050+
* @method handleWin
1051+
*/
1052+
me.handleWin = function () {
1053+
handleEndCondition(elmWin);
10031054
};
10041055

10051056
// ---------------------------------------------------------------------

0 commit comments

Comments
 (0)