@@ -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 ( / \b s n a k e - s n a k e b o d y - a l i v e \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 ( / \b s n a k e - s n a k e b o d y - a l i v e \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