<commit_before>/**
 * The MIT License (MIT)
 *
 * Copyright (c) 2014 Rich Friedel
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *
 * Created by Rich Friedel on 9/27/2014.
 */

// General Constants
var NUMBER_OF_CARDS = 16;

// String Constants
var HEADER_TITLE = "Match The Sight Words";
var HEADER_MATCH_SUCCESS = "YAY!!! YOU FOUND A MATCH!!!";
var HEADER_MATCH_FAIL = "NO MATCH!";
var HEADER_GAME_COMPLETE = "YOU MATCHED ALL THE CARDS!!!";

// Audio Constants
var AUDIO_WELCOME = "dialog_welcome";
var AUDIO_MATCH_SUCCESS = "dialog_match_success";
var AUDIO_MATCH_FAIL = "dialog_match_fail";
var AUDIO_GAME_COMPLETE = "dialog_game_complete";

// Object that will hold the card ID and the card value
var flippedCard = {cardOneId: "", cardTwoId: "", cardOneVal: "", cardTwoVal: ""};

// Array of cards that have been matched throughout the game
var matchedCards = [];

// Flip counter
var flips = 0;

// Player score
var score = 0;

// Sight Words
var sightWords = ["a", "an", "and", "am", "are", "as", "at", "ate", "away", "be", "big", "black", "blue", "brown",
    "but", "came", "can", "come", "did", "do", "down", "eat", "eight", "find", "five", "for", "four", "get", "go",
    "good", "green", "has", "have", "he", "her", "here", "hers", "his", "him", "hum", "in", "into", "I", "is", "it",
    "like", "look", "little", "make", "me", "my", "must", "new", "nine", "no", "not", "of", "on", "one", "orange",
    "our", "out", "play", "pretty", "please", "purple", "ran", "red", "run", "said", "say", "saw", "see", "seven",
    "she", "six", "small", "so", "soon", "ten", "that", "the", "they", "them", "there", "this", "that", "three",
    "to", "too", "two", "up", "was", "we", "white", "why", "what", "who", "with", "year", "yes", "your", "yellow",
    "you", "yours"];

/**
 * Function that initializes and starts the game
 */
function startGame() {
    if ($(".flip-container").hasClass("flipped")) {
        // We are restarting the game. Clear the flipped and locked classes so that the cards flip back
        // over and are unlocked, that way the user can click them.
        $(".flip-container").removeClass("flipped locked");

        // Reset the flips counter
        flips = 0;

        // Reset the score counter
        score = 0;

        // Clear the matched cards array
        // Per this thread on StackOverflow: http://stackoverflow.com/a/1232046/520186
        // this is the fastest way of clearing the array because the array is referenced in other functions.
        while (matchedCards.length > 0) {
            matchedCards.pop();
        }
    }

    // Get eight words from the sight words array...
    var sightWordsToUse = generateSightWords();

    // Play the welcome sound
    $(playSound(AUDIO_WELCOME)).on('ended', function () {
        // Populate the cards with the generated sight words
        populateCardsWithWords(sightWordsToUse);

        // Allow the user to click the cards
        detectCardClick()
    });
}

/**
 * This is where all the work happens
 */
function detectCardClick() {
    $(".flip-container").on("click", function () {
        // Check to see if there is a processing lock on the cards
        if (isProcessingLock()) { // Locked
            return;
        }

        // Increment the flips
        flips === 2 ? flips = 1 : flips += 1;

        // Check to see which flip we are attempting
        if (flips === 1) { // First flip
            // Get card ID
            flippedCard.cardOneId = $(this).attr('id');
            // Get card value
            flippedCard.cardOneVal = $("#" + flippedCard.cardOneId).find(".card-text").text().toLowerCase();

            // Check to see if the card is locked
            if (isCardLocked(flippedCard.cardOneId)) {
                flips = 0;
                return;
            }

            // Lock the card
            lockCard(flippedCard.cardOneId);

            // Set the processing lock while audio is playing
            addProcessingLock();

            // Turn the card over
            flipCard(this);

            $(playSound(flippedCard.cardOneVal)).on("ended", function () {
                removeProcessingLock();
            });

        } else if (flips === 2) { // Second flip
            // Get card ID
            flippedCard.cardTwoId = $(this).attr('id');
            // Get card value
            flippedCard.cardTwoVal = $("#" + flippedCard.cardTwoId).find(".card-text").text().toLowerCase();

            // Check to see if card is locked
            if (isCardLocked(flippedCard.cardTwoId)) {
                // Card is locked, reset the flip counter
                flips = 1;
                // Return because there is nothing more to do
                return;
            }

            // Lock the card
            lockCard(flippedCard.cardTwoId);

            // Set the processing lock while audio is playing
            addProcessingLock();

            // Turn the second card
            flipCard(this);

            // Play the card value audio
            $(playSound(flippedCard.cardTwoVal)).on('ended', function () {
                // Check to see if we have a match
                if (flippedCard.cardOneVal === flippedCard.cardTwoVal) { // WOOHOO!!! WE FOUND A MATCH!!!
                    // Write success msg to the page alert area
                    $(".header").text(HEADER_MATCH_SUCCESS);

                    // Add the cards to the matched cards array
                    matchedCards.push(flippedCard.cardOneId, flippedCard.cardTwoId);

                    $(playSound(AUDIO_MATCH_SUCCESS)).on('ended', function () {
                        // Check to see if there are any more cards to match. If there are not, the player wins!
                        if (matchedCards.length === NUMBER_OF_CARDS) {
                            $(".header").text(HEADER_GAME_COMPLETE);
                            $(playSound(AUDIO_GAME_COMPLETE)).on("ended", function () {
                                startGame();
                            });
                        } else {
                            $(".header").text(HEADER_TITLE);
                        }
                        // Remove the processing lock on the cards
                        removeProcessingLock();
                    });
                } else {
                    $(".header").text(HEADER_MATCH_FAIL);

                    $(playSound(AUDIO_MATCH_FAIL)).on('ended', function () {
                        // Remove the lock from both cards
                        unlockCard(flippedCard.cardOneId);
                        unlockCard(flippedCard.cardTwoId);

                        // Flip both cards back over
                        flipCard("#" + flippedCard.cardOneId);
                        flipCard("#" + flippedCard.cardTwoId);

                        $(".header").text(HEADER_TITLE);

                        removeProcessingLock();
                    });
                }
            });
        }
    });
}

/// UTILITY FUNCTIONS ///

/**
 * Generates an array of 8 unique words randomly taken from the sightWords array
 *
 * @returns {Array} Returns an Array of sight words
 */
function generateSightWords() {
    var sightWordsArr = [];
    var numberOfWords = 8;
    var sightWordsArrayLen = sightWords.length - 1;
    var indexArr = [];

    // Generate 8 numbers with the lowest being 0 and the highest being the main sight words array length
    while (indexArr.length < numberOfWords) {
        var rndIndex = Math.floor((Math.random() * sightWordsArrayLen));

        // Each number need to be unique
        if ($.inArray(rndIndex, indexArr) <= -1) {
            indexArr.push(rndIndex);
        }
    }

    $.each(indexArr, function (index, value) {
        sightWordsArr.push(sightWords[value]);
    });

    return sightWordsArr;
}

/**
 * Populates the cards with the words from the passed array
 *
 * @param wordsArray {Array} An array of words that will be used to populate the cards with
 */
function populateCardsWithWords(wordsArray) {
    var a1 = wordsArray.concat(wordsArray);
    var a2 = shuffleArray(a1);

    for (var i = 1; i <= NUMBER_OF_CARDS; i++) {
        $("#card-" + i).find(".card-text").text(a2[i - 1]);
    }
}

/**
 * Randomize array element order in-place.
 * Using Fisher-Yates shuffle algorithm.
 *
 * @param arrayToShuffle {Array} The array that is to be shuffled
 * @returns {Array} Returns a randomly shuffled array
 */
function shuffleArray(arrayToShuffle) {
    for (var i = arrayToShuffle.length - 1; i > 0; i--) {
        var j = Math.floor(Math.random() * (i + 1));
        var temp = arrayToShuffle[i];
        arrayToShuffle[i] = arrayToShuffle[j];
        arrayToShuffle[j] = temp;
    }
    return arrayToShuffle;
}

/**
 * Plays an audio clip
 *
 * Currently, the audio clip is set to auto play.
 *
 * @param audioName {string} Name of the audio clip without the extension
 * @returns {HTMLElement} Returns the audio element
 */
function playSound(audioName) {

    var completedAudioSamples = ["a", "an", "and", "am", "are", "as", "at", "ate", "away", "be", "big", "black",
        "blue", "brown", "but", "came", "can", "come", "did", "do", "down", "eat", "eight", "dialog_welcome",
        "dialog_find_a_match", "dialog_match_success", "dialog_match_fail", "dialog_game_complete"];

    if (completedAudioSamples.indexOf(audioName) === -1) {
        audioName = "null";
    }

    var audioElement = document.createElement('audio');
    audioElement.setAttribute('src', 'audio/' + audioName + '.mp3');
    audioElement.setAttribute('autoplay', 'autoplay');

    return audioElement;
}

/**
 * Turns the card
 *
 * @param ele {string} Element that needs to be flipped
 */
function flipCard(ele) {
    // Flip the card and set it to flipped
    $(ele).toggleClass("flipped");
}

/**
 * Adds a processing lock to all of the cards.
 */
function addProcessingLock() {
    $(".flip-container").addClass("processing");
}

/**
 * Removes the processing lock from all of the cards.
 */
function removeProcessingLock() {
    $(".flip-container").removeClass("processing");
}

/**
 * Checks to see if there is a processing lock on any of the cards
 *
 * @returns {boolean} Returns TRUE if there is a processing lock active on the cards
 */
function isProcessingLock() {
    if ($(".flip-container").hasClass("processing")) {
        return true;
    }

    return false;
}

/**
 * Places a lock on a single card
 *
 * @param cardId {string} The ID of the card to lock
 */
function lockCard(cardId) {
    $("#" + cardId).addClass("locked");
}

/**
 * Removes a lock on a single card
 *
 * @param cardId {string} The ID of the card to unlock
 */
function unlockCard(cardId) {
    $("#" + cardId).removeClass("locked");
}

/**
 * Checks the locked state of the card
 *
 * @param cardId {string} The ID of the card that needs to be checked
 * @returns {boolean} Returns TRUE if the card is actively locked
 */
function isCardLocked(cardId) {
    if ($("#" + cardId).hasClass("locked")) {
        return true;
    }

    return false;
}<commit_msg>Made the flipCard param that is passed more explicit Simplified some of the if/else statements Added some comment verbiage to make what is happening a bit more clear Fixed the javadoc for the flipCard() function<commit_after>/**
 * The MIT License (MIT)
 *
 * Copyright (c) 2014 Rich Friedel
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in all
 * copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 * SOFTWARE.
 *
 * Created by Rich Friedel on 9/27/2014.
 */

// General Constants
var NUMBER_OF_CARDS = 16;

// String Constants
var HEADER_TITLE = "Match The Sight Words";
var HEADER_MATCH_SUCCESS = "YAY!!! YOU FOUND A MATCH!!!";
var HEADER_MATCH_FAIL = "NO MATCH!";
var HEADER_GAME_COMPLETE = "YOU MATCHED ALL THE CARDS!!!";

// Audio Constants
var AUDIO_WELCOME = "dialog_welcome";
var AUDIO_MATCH_SUCCESS = "dialog_match_success";
var AUDIO_MATCH_FAIL = "dialog_match_fail";
var AUDIO_GAME_COMPLETE = "dialog_game_complete";

// Object that will hold the card ID and the card value
var flippedCard = {cardOneId: "", cardTwoId: "", cardOneVal: "", cardTwoVal: ""};

// Array of cards that have been matched throughout the game
var matchedCards = [];

// Flip counter
var flips = 0;

// Player score
var score = 0;

// Sight Words
var sightWords = ["a", "an", "and", "am", "are", "as", "at", "ate", "away", "be", "big", "black", "blue", "brown",
    "but", "came", "can", "come", "did", "do", "down", "eat", "eight", "find", "five", "for", "four", "get", "go",
    "good", "green", "has", "have", "he", "her", "here", "hers", "his", "him", "hum", "in", "into", "I", "is", "it",
    "like", "look", "little", "make", "me", "my", "must", "new", "nine", "no", "not", "of", "on", "one", "orange",
    "our", "out", "play", "pretty", "please", "purple", "ran", "red", "run", "said", "say", "saw", "see", "seven",
    "she", "six", "small", "so", "soon", "ten", "that", "the", "they", "them", "there", "this", "that", "three",
    "to", "too", "two", "up", "was", "we", "white", "why", "what", "who", "with", "year", "yes", "your", "yellow",
    "you", "yours"];

/**
 * Function that initializes and starts the game
 */
function startGame() {
    if ($(".flip-container").hasClass("flipped")) {
        // We are restarting the game. Clear the flipped and locked classes so that the cards flip back
        // over and are unlocked, that way the user can click them.
        $(".flip-container").removeClass("flipped locked");

        // Reset the flips counter
        flips = 0;

        // Reset the score counter
        score = 0;

        // Clear the matched cards array
        // Per this thread on StackOverflow: http://stackoverflow.com/a/1232046/520186
        // this is the fastest way of clearing the array because the array is referenced in other functions.
        while (matchedCards.length > 0) {
            matchedCards.pop();
        }
    }

    // Get eight words from the sight words array...
    var sightWordsToUse = generateSightWords();

    // Play the welcome sound
    $(playSound(AUDIO_WELCOME)).on('ended', function () {
        // Populate the cards with the generated sight words
        populateCardsWithWords(sightWordsToUse);

        // Allow the user to click the cards
        detectCardClick()
    });
}

/**
 * This is where all the work happens
 */
function detectCardClick() {
    $(".flip-container").on("click", function () {
        // Check to see if there is a processing lock on the cards
        if (isProcessingLock()) { // Locked
            return;
        }

        // Increment the flips
        flips === 2 ? flips = 1 : flips += 1;

        // Check to see which flip we are attempting
        if (flips === 1) { // First flip
            // Get card ID
            flippedCard.cardOneId = $(this).attr('id');
            // Get card value
            flippedCard.cardOneVal = $("#" + flippedCard.cardOneId).find(".card-text").text().toLowerCase();

            // Check to see if the card is locked
            if (isCardLocked(flippedCard.cardOneId)) {
                flips = 0;
                return;
            }

            // Lock the card so that the card cannot be flipped back
            // over once it has been selected.
            lockCard(flippedCard.cardOneId);

            // Set the processing lock while audio is playing so that none of the cards
            // can be flipped
            addProcessingLock();

            // Turn the card over
            flipCard("#" + flippedCard.cardOneId);

            $(playSound(flippedCard.cardOneVal)).on("ended", function () {
                removeProcessingLock();
            });

        } else if (flips === 2) { // Second flip
            // Get card ID
            flippedCard.cardTwoId = $(this).attr('id');
            // Get card value
            flippedCard.cardTwoVal = $("#" + flippedCard.cardTwoId).find(".card-text").text().toLowerCase();

            // Check to see if card is locked
            if (isCardLocked(flippedCard.cardTwoId)) {
                // Card is locked, reset the flip counter
                flips = 1;
                // Return because there is nothing more to do
                return;
            }

            // Lock the card
            lockCard(flippedCard.cardTwoId);

            // Set the processing lock while audio is playing
            addProcessingLock();

            // Turn the second card
            flipCard("#" + flippedCard.cardTwoId);

            // Play the card value audio
            $(playSound(flippedCard.cardTwoVal)).on('ended', function () {
                // Check to see if we have a match
                if (flippedCard.cardOneVal === flippedCard.cardTwoVal) { // WOOHOO!!! WE FOUND A MATCH!!!
                    // Write success msg to the page alert area
                    $(".header").text(HEADER_MATCH_SUCCESS);

                    // Add the cards to the matched cards array
                    matchedCards.push(flippedCard.cardOneId, flippedCard.cardTwoId);

                    $(playSound(AUDIO_MATCH_SUCCESS)).on('ended', function () {
                        // Check to see if there are any more cards to match. If there are not, the player wins!
                        if (matchedCards.length === NUMBER_OF_CARDS) {
                            $(".header").text(HEADER_GAME_COMPLETE);
                            $(playSound(AUDIO_GAME_COMPLETE)).on("ended", function () {
                                startGame();
                            });
                        } else {
                            $(".header").text(HEADER_TITLE);
                        }
                        // Remove the processing lock on the cards
                        removeProcessingLock();
                    });
                } else {
                    $(".header").text(HEADER_MATCH_FAIL);

                    $(playSound(AUDIO_MATCH_FAIL)).on('ended', function () {
                        // Remove the lock from both cards
                        unlockCard(flippedCard.cardOneId);
                        unlockCard(flippedCard.cardTwoId);

                        // Flip both cards back over
                        flipCard("#" + flippedCard.cardOneId);
                        flipCard("#" + flippedCard.cardTwoId);

                        $(".header").text(HEADER_TITLE);

                        removeProcessingLock();
                    });
                }
            });
        }
    });
}

/// UTILITY FUNCTIONS ///

/**
 * Generates an array of 8 unique words randomly taken from the sightWords array
 *
 * @returns {Array} Returns an Array of sight words
 */
function generateSightWords() {
    var sightWordsArr = [];
    var numberOfWords = 8;
    var sightWordsArrayLen = sightWords.length - 1;
    var indexArr = [];

    // Generate 8 numbers with the lowest being 0 and the highest being the main sight words array length
    while (indexArr.length < numberOfWords) {
        var rndIndex = Math.floor((Math.random() * sightWordsArrayLen));

        // Each number need to be unique
        if ($.inArray(rndIndex, indexArr) <= -1) {
            indexArr.push(rndIndex);
        }
    }

    $.each(indexArr, function (index, value) {
        sightWordsArr.push(sightWords[value]);
    });

    return sightWordsArr;
}

/**
 * Populates the cards with the words from the passed array
 *
 * @param wordsArray {Array} An array of words that will be used to populate the cards with
 */
function populateCardsWithWords(wordsArray) {
    var a1 = wordsArray.concat(wordsArray);
    var a2 = shuffleArray(a1);

    for (var i = 1; i <= NUMBER_OF_CARDS; i++) {
        $("#card-" + i).find(".card-text").text(a2[i - 1]);
    }
}

/**
 * Randomize array element order in-place.
 * Using Fisher-Yates shuffle algorithm.
 *
 * @param arrayToShuffle {Array} The array that is to be shuffled
 * @returns {Array} Returns a randomly shuffled array
 */
function shuffleArray(arrayToShuffle) {
    for (var i = arrayToShuffle.length - 1; i > 0; i--) {
        var j = Math.floor(Math.random() * (i + 1));
        var temp = arrayToShuffle[i];
        arrayToShuffle[i] = arrayToShuffle[j];
        arrayToShuffle[j] = temp;
    }
    return arrayToShuffle;
}

/**
 * Plays an audio clip
 *
 * Currently, the audio clip is set to auto play.
 *
 * @param audioName {string} Name of the audio clip without the extension
 * @returns {HTMLElement} Returns the audio element
 */
function playSound(audioName) {

    var completedAudioSamples = ["a", "an", "and", "am", "are", "as", "at", "ate", "away", "be", "big", "black",
        "blue", "brown", "but", "came", "can", "come", "did", "do", "down", "eat", "eight", "dialog_welcome",
        "dialog_find_a_match", "dialog_match_success", "dialog_match_fail", "dialog_game_complete"];

    if (completedAudioSamples.indexOf(audioName) === -1) {
        audioName = "null";
    }

    var audioElement = document.createElement('audio');
    audioElement.setAttribute('src', 'audio/' + audioName + '.mp3');
    audioElement.setAttribute('autoplay', 'autoplay');

    return audioElement;
}

/**
 * Turns the card
 *
 * @param cardId {string} The card ID that needs to be flipped
 */
function flipCard(cardId) {
    // Flip the card and set it to flipped
    $(cardId).toggleClass("flipped");
}

/**
 * Adds a processing lock to all of the cards.
 */
function addProcessingLock() {
    $(".flip-container").addClass("processing");
}

/**
 * Removes the processing lock from all of the cards.
 */
function removeProcessingLock() {
    $(".flip-container").removeClass("processing");
}

/**
 * Checks to see if there is a processing lock on any of the cards
 *
 * @returns {boolean} Returns TRUE if there is a processing lock active on the cards
 */
function isProcessingLock() {
    return $(".flip-container").hasClass("processing");
}

/**
 * Places a lock on a single card
 *
 * @param cardId {string} The ID of the card to lock
 */
function lockCard(cardId) {
    $("#" + cardId).addClass("locked");
}

/**
 * Removes a lock on a single card
 *
 * @param cardId {string} The ID of the card to unlock
 */
function unlockCard(cardId) {
    $("#" + cardId).removeClass("locked");
}

/**
 * Checks the locked state of the card
 *
 * @param cardId {string} The ID of the card that needs to be checked
 * @returns {boolean} Returns TRUE if the card is actively locked
 */
function isCardLocked(cardId) {
    return $("#" + cardId).hasClass("locked");
}<|endoftext|>


<commit_before>(function () {
	var canvas = document.getElementById('game-canvas'),
		canvasContext = canvas.getContext('2d'),
		fps = 30,
		ball;

	setInterval(updateAll, 1000 / fps);

	/* Ball Class */
	var Ball = function (x, y, speedX, speedY) {
		this.x = x;
		this.y = y;
		this.speedX = speedX;
		this.speedY = speedY;
	};

	Ball.prototype.updatePosition = function () {
		function _update(ball, coord, speed, boundary) {
			ball[coord] += ball[speed];
			if (ball[coord] > boundary || ball[coord] < 0) {
				ball[speed] *= -1;
			}
		}

		_update(this, 'x', 'speedX', canvas.width);
		_update(this, 'y', 'speedY', canvas.height);
	};

	Ball.prototype.draw = function () {
		drawCircle(this.x, this.y, 10, 'white');
	};
	/* End Ball Class */

	ball = new Ball(100, 100, 5, 7);

	function moveAll () {
		ball.updatePosition();
	}

	function drawRectangle (x, y, width, height, color) {
		canvasContext.fillStyle = color;
		canvasContext.fillRect(x, y, width, height);
	}

	function drawCircle (x, y, radius, color) {
		canvasContext.fillStyle = color;
		canvasContext.beginPath();
		canvasContext.arc(x, y, radius, 0, Math.PI * 2, true);
		canvasContext.fill();
	}

	function drawAll () {
		drawRectangle(0, 0, canvas.width, canvas.height, 'black');
		ball.draw();
	}

	function updateAll () {
		moveAll();
		drawAll();
	}
})();
<commit_msg>variables for the paddle<commit_after>(function () {
	var canvas = document.getElementById('game-canvas'),
		canvasContext = canvas.getContext('2d'),
		fps = 30,
		ball,
		paddle,
		paddleX = 400;

	const PADDLE_WIDTH = 100;
	const PADDLE_THICKNESS = 10;

	setInterval(updateAll, 1000 / fps);

	/* Ball Class */
	var Ball = function (x, y, speedX, speedY) {
		this.x = x;
		this.y = y;
		this.speedX = speedX;
		this.speedY = speedY;
	};

	Ball.prototype.updatePosition = function () {
		function _update(ball, coord, speed, boundary) {
			ball[coord] += ball[speed];
			if (ball[coord] > boundary || ball[coord] < 0) {
				ball[speed] *= -1;
			}
		}

		_update(this, 'x', 'speedX', canvas.width);
		_update(this, 'y', 'speedY', canvas.height);
	};

	Ball.prototype.draw = function () {
		drawCircle(this.x, this.y, 10, 'white');
	};
	/* End Ball Class */

	ball = new Ball(100, 100, 5, 7);

	function moveAll () {
		ball.updatePosition();
	}

	function drawRectangle (x, y, width, height, color) {
		canvasContext.fillStyle = color;
		canvasContext.fillRect(x, y, width, height);
	}

	function drawCircle (x, y, radius, color) {
		canvasContext.fillStyle = color;
		canvasContext.beginPath();
		canvasContext.arc(x, y, radius, 0, Math.PI * 2, true);
		canvasContext.fill();
	}

	function drawAll () {
		drawRectangle(0, 0, canvas.width, canvas.height, 'black');
		ball.draw();
	}

	function updateAll () {
		moveAll();
		drawAll();
	}
})();
<|endoftext|>


<commit_before>var innerWidth = window.innerWidth;
var innerHeight = window.innerHeight;
var gameRatio = innerWidth/innerHeight;
var game = new Phaser.Game(Math.floor(1000*gameRatio), 1000, Phaser.AUTO, 'phaser-example', { preload: preload, create: create, update: update, render: render });

function preload() {
	game.scale.scaleMode = Phaser.ScaleManager.SHOW_ALL;
	game.load.tilemap('map', './map/test.json', null, Phaser.Tilemap.TILED_JSON);
	game.load.image('kenney', './img/kenney.png');
	game.load.spritesheet('dude', './img/pilot_animation.png', 64, 64);

}

var player;
var facing = 'left';
var jumpTimer = 0;
var cursors;
var jumpButton;
var PI = 3.14;
var PI2 = PI/3;
var oldSpeed = 0;

var onGround = false;

var map;
var layer, layer2;

var inTerrain=0;

function create() {

	game.physics.startSystem(Phaser.Physics.P2JS);

	game.stage.backgroundColor = '#2d2d2d';

	map = game.add.tilemap('map');

	map.addTilesetImage('kenney');

	layer2 = map.createLayer('layer 2');
	layer = map.createLayer('layer 1');

	layer2.resizeWorld();
	layer.resizeWorld();

	//  Set the tiles for collision.
	//  Do this BEFORE generating the p2 bodies below.
	//map.setCollisionBetween(11, 220);

	//  Convert the tilemap layer into bodies. Only tiles that collide (see above) are created.
	//  This call returns an array of body objects which you can perform addition actions on if
	//  required. There is also a parameter to control optimising the map build.
	//game.physics.p2.convertTilemap(map, layer);

	var layerobjects_tiles = game.physics.p2.convertCollisionObjects(map,"objects1");

	game.physics.p2.restitution = 0.0;
	game.physics.p2.gravity.y = 1400;
	game.physics.p2.applyGravity = true;
	//game.physics.p2.gravity.x = 200;

	game.physics.p2.world.defaultContactMaterial.friction = 0.01;
	game.physics.p2.world.setGlobalStiffness(1e5);

	player = game.add.sprite(100, 200, 'dude');
	player.animations.add('speedy', [8, 9, 10], 10, true);
	player.animations.add('turn', [0, 1, 2], 10, true);
	player.animations.add('right', [3, 4, 5, 6, 7], 10, true);

	game.physics.p2.enable(player);

	//var spriteMaterial = game.physics.p2.createMaterial('spriteMaterial', player.body);
	//var worldMaterial = game.physics.p2.createMaterial('worldMaterial');
	//var boxMaterial = game.physics.p2.createMaterial('worldMaterial');

	player.body.damping = 0.1;
	player.body.collideWorldBounds = true;

	player.body.fixedRotation = false;
	//player.body.dynamic = true;
	//player.body.setMaterial(spriteMaterial);
	player.body.mass = 0.5;

	game.camera.follow(player);
	player.body.onBeginContact.add(blockHit, this);
	player.body.onEndContact.add(blockUnHit, this);

	cursors = game.input.keyboard.createCursorKeys();
	jumpButton = game.input.keyboard.addKey(Phaser.Keyboard.SPACEBAR);

	game.input.onTap.add(onTap, this);

}

function update() {
	//console.log(player.body.velocity.y);
	if (player.body.velocity.y<0.9 && player.body.velocity.y>-0.9){
		if (Math.abs(player.body.velocity.x)>10 && Math.abs(player.body.rotation)>0.2){
			//console.log("goal");
			player.body.rotation=0;
			player.body.angularForce=0;
		}
	}
	else if (player.body.velocity.y * player.body.velocity.x > 0){
		if (player.body.rotation<-0.1 && oldSpeed*player.body.velocity.x>0){
			//console.log("goal2");
			player.body.rotation=0;
			player.body.angularForce=100;
		}
	}
	else if (player.body.velocity.y * player.body.velocity.x < 0){
		if (player.body.rotation>0.1){
			//console.log("goal3");
			player.body.rotation-=0.2;
			player.body.angularForce=-100;
		}
	}
	oldSpeed = player.body.velocity.x;

	if (cursors.left.isDown)
	{
		player.body.force.x = -1200;
		//player.body.data.force[0] = 100;//moveLeft(200);

		if (facing != 'left')
		{
			player.animations.play('right');
			facing = 'left';
		}
	}
	else if (cursors.right.isDown)
	{
		player.body.force.x = 1200;//.moveRight(200);
		if (player.body.velocity.y>0){player.body.force.y += 5000;}

		if (facing != 'right')
		{
			player.animations.play('right');
			facing = 'right';
		}
	}
	else
	{
		//player.body.velocity.x = 800;

		if (Math.abs(player.body.velocity.x)<25 && Math.abs(player.body.rotation)<0.2 && facing != 'idle')
		{
			player.animations.stop();

			//    player.frame = 5;
			facing = 'idle';
			player.animations.play('turn');
		}
		else if (Math.abs(player.body.velocity.x)> 25 && facing != 'speedy'){
			facing = 'speedy';
			player.animations.play('speedy');
		}

	}

	if (jumpButton.isDown && game.time.now > jumpTimer && checkIfCanJump())
	{
		player.body.force.y = -50000;
		jumpTimer = game.time.now + 750;
	}

}

function onTap(pointer, doubleTap) {
	//magic
}

function blockHit (body, bodyB, shapeA, shapeB, equation) {
	if (body)
	{
		if (body.sprite===null){
			onGround = true;
			inTerrain++;
		}
	}
	else
	{
		//console.log("wall");
	}

}
function blockUnHit (body, bodyB, shapeA, shapeB, equation) {
	if (body)
	{
		if (body.sprite===null){
			onGround = false;
			inTerrain--;
		}
	}
	else
	{
		//console.log("wall");
	}

}

function checkIfCanJump() {

	var yAxis = p2.vec2.fromValues(0, 1);
	var result = false;

	for (var i = 0; i < game.physics.p2.world.narrowphase.contactEquations.length; i++)
	{
		var c = game.physics.p2.world.narrowphase.contactEquations[i];

		if (c.bodyA === player.body.data || c.bodyB === player.body.data)
		{
			var d = p2.vec2.dot(c.normalA, yAxis); // Normal dot Y-axis
			if (c.bodyA === player.body.data) d *= -1;
			if (d > 0.5) result = true;
		}
	}

	return result;

}

function render() {

}<commit_msg>Fix #4<commit_after>var innerWidth = window.innerWidth;
var innerHeight = window.innerHeight;
var gameRatio = innerWidth/innerHeight;
var game = new Phaser.Game(Math.floor(1000*gameRatio), 1000, Phaser.AUTO, 'phaser-example', { preload: preload, create: create, update: update, render: render });

function preload() {
	game.scale.scaleMode = Phaser.ScaleManager.SHOW_ALL;
	game.load.tilemap('map', './map/test.json', null, Phaser.Tilemap.TILED_JSON);
	game.load.image('kenney', './img/kenney.png');
	game.load.spritesheet('dude', './img/pilot_animation.png', 64, 64);

}

var player;
var facing = 'left';
var isTap = false;
var jumpTimer = 0;
var cursors;
var jumpButton;
var PI = 3.14;
var PI2 = 8*PI/18;
var PI3 = PI/3;
var PI4 = PI/4;
var oldSpeed = 0;

var onGround = false;

var map;
var layer, layer2;

var inTerrain=0;
var forceVector = 0;
var rotation = 0;

function create() {

	game.physics.startSystem(Phaser.Physics.P2JS);

	game.stage.backgroundColor = '#2d2d2d';

	map = game.add.tilemap('map');

	map.addTilesetImage('kenney');

	layer2 = map.createLayer('layer 2');
	layer = map.createLayer('layer 1');

	layer2.resizeWorld();
	layer.resizeWorld();

	var layerobjects_tiles = game.physics.p2.convertCollisionObjects(map,"objects1");

	game.physics.p2.restitution = 0.0;
	game.physics.p2.stiffness = 200;
	//game.physics.p2.relaxation = 0.1;
	game.physics.p2.gravity.y = 1400;
	game.physics.p2.applyGravity = true;
	//game.physics.p2.gravity.x = 200;

	game.physics.p2.world.defaultContactMaterial.friction = 0.01;
	game.physics.p2.world.setGlobalStiffness(1e5);

	player = game.add.sprite(100, 200, 'dude');
	player.animations.add('speedy', [8, 9, 10], 10, true);
	player.animations.add('turn', [0, 1, 2], 10, true);
	player.animations.add('right', [3, 4, 5, 6, 7], 10, true);

	game.physics.p2.enable(player);

	//var spriteMaterial = game.physics.p2.createMaterial('spriteMaterial', player.body);
	//var worldMaterial = game.physics.p2.createMaterial('worldMaterial');
	//var boxMaterial = game.physics.p2.createMaterial('worldMaterial');

	player.body.damping = 0.1;
	player.body.angularDamping = 0.5;
	player.body.collideWorldBounds = true;

	player.body.fixedRotation = false;
	//player.body.dynamic = true;
	//player.body.setMaterial(spriteMaterial);
	player.body.mass = 0.5;

	game.camera.follow(player);
	player.body.onBeginContact.add(blockHit, this);
	player.body.onEndContact.add(blockUnHit, this);

	//cursors = game.input.keyboard.createCursorKeys();
	//jumpButton = game.input.keyboard.addKey(Phaser.Keyboard.SPACEBAR);

	game.input.onDown.add(onTap, this);
	game.input.onUp.add(onUnTap, this);

}

function update() {
	forceVector = player.body.velocity.y * player.body.velocity.x;
	rotation = player.body.rotation%PI;
	//console.log(player.body.angularForce);
	if (inTerrain){
		if (player.body.velocity.y<0.9 && player.body.velocity.y>-0.9){
			if (Math.abs(player.body.velocity.x)>10 && Math.abs(rotation)>PI4){
				//console.log("goal");
				player.body.rotation=0;
				player.body.angularForce=0;
			}
		}
		else if (forceVector > 0){
			if (rotation<-0.1 && oldSpeed*player.body.velocity.x>0){
				//console.log("goal2");
				player.body.rotation=0;
				player.body.angularForce=75;
			}
			else if (rotation>PI2){
				player.body.rotation=PI3;
			}
		}
		else if (forceVector < 0){
			if (rotation>0.1){
				//console.log("goal3", rotation);
				player.body.rotation=0;
				player.body.angularForce=-75;
			}
			else if (rotation<-PI2){
				player.body.rotation=-PI3;
			}
		}
	}
	else{
		if (rotation>PI4){player.body.rotation=PI4-0.05;}
		else if (rotation<-PI4){player.body.rotation=-PI4+0.05;}
	}
	oldSpeed = player.body.velocity.x;

	if (isTap){
		if (inTerrain){
			if (player.body.velocity.y>0){
				player.body.force.y += 500;
			}
			/*else if (player.body.velocity.y<-5){
				player.body.angularForce=-50;
			}*/
		}
		player.body.force.x += 750;
		if (facing != 'right')
		{
			player.animations.play('right');
			facing = 'right';
		}
	}
	else
	{
		//player.body.velocity.x = 800;

		if (Math.abs(player.body.velocity.x)<25 && Math.abs(player.body.rotation)<0.2 && facing != 'idle')
		{
			player.animations.stop();

			//    player.frame = 5;
			facing = 'idle';
			player.animations.play('turn');
		}
		else if (Math.abs(player.body.velocity.x)> 25 && facing != 'speedy'){
			facing = 'speedy';
			player.animations.play('speedy');
		}

	}
}

function onTap(pointer, doubleTap) {
	isTap = true;
}
function onUnTap(pointer, doubleTap) {
	isTap = false;
	player.body.angularForce = 0;
}

function blockHit (body, bodyB, shapeA, shapeB, equation) {
	if (body)
	{
		if (body.sprite===null){
			onGround = true;
			inTerrain++;
		}
	}
	else
	{
		//console.log("wall");
	}

}
function blockUnHit (body, bodyB, shapeA, shapeB, equation) {
	if (body)
	{
		if (body.sprite===null){
			onGround = false;
			inTerrain--;
		}
	}
	else
	{
		//console.log("wall");
	}

}

function render() {

}<|endoftext|>


<commit_before>/*global define:false, require: false, document:false, localStorage: false */
require.config({
    baseUrl: "js"
});

/*global define:false, window:false */
define(["draw", "field", "config", "3rd/domReady!"], function (draw, field, config) {
    "use strict";
    var score = 0,
        cfg = config.draw,
        size = config.game.size,
        cnvs,
        sel,
        scoreBoard;

    var isMoving = false; // if a move is being animated now

    var ih; // interval handler

    function minDist(i, j, graph) {
        var min = Number.MAX_VALUE,
            minIdx = -1,
            k;
        for (k = 0; k < size * size; k++) {
            if (graph[k].incl) {
                if (graph[k].dist < min) {
                    min = graph[k].dist;
                    minIdx = k;
                }
            }
        }
        return minIdx;
    }

    // shift from the current (i, j) to get to the neighbors
    var _neighbors = [[-1, 0], [0, -1], [1, 0], [0, 1]];

    // simple Dijkstra's algorithm implementation
    // TODO: inefficient minDist -- requires walk though entire graph
    // TODO: inefficient reduce -- might not be even needed, other conditions within
    // the cycle will be satisfied sooner
    function findPath(ib, jb, ie, je) {
        var graph = [],
            path = [],
            length,
            k;
        for (k = 0; k < size * size; k++) {
            graph[k] = { incl: 1, dist: Number.MAX_VALUE, prev: -1 };
        }
        graph[field.idx(ib, jb)].dist = 0;

        function avail(x, y) { return (y.incl) ? x + 1 : x; }

        var minIdx = -1;
        while (graph.reduce(avail, 0)) {
            minIdx = minDist(ib, jb, graph);
            var imin = Math.floor(minIdx / size);
            var jmin = minIdx % size;
            if (minIdx < 0) {
                break;
            }
            graph[minIdx].incl = 0;
            if (imin === ie && jmin === je) {
                break;
            }
            if (graph[minIdx].dist === Number.MAX_VALUE) {
                break;
            }
            for (k = 0; k < _neighbors.length; k++) {
                var ic, jc; // i and j of each neighbor
                var alt;
                ic = imin + _neighbors[k][0];
                jc = jmin + _neighbors[k][1];
                if (field.isInside(ic, jc) &&
                        !field.field[field.idx(ic, jc)].color &&
                        graph[field.idx(ic, jc)]) {

                    alt = graph[minIdx].dist + 1;
                    if (alt < graph[field.idx(ic, jc)].dist) {
                        graph[field.idx(ic, jc)].dist = alt;
                        graph[field.idx(ic, jc)].prev = minIdx;
                    }
                }
            }
        }
        k = minIdx;
        if (k < 0) {
            return null; // path has not been found
        }
        while (graph[k].prev >= 0) {
            path.push(field.field[k]);
            k = graph[k].prev;
        }
        return path;
    }

    function select(event) {
        var x, y; // positions within the canvas
        var i, j; // matrix indices of the cell

        event.stopPropagation();

        // ignore selection if a ball is moving
        if (isMoving) {
            return;
        }

        x = event.pageX + (window.pageXScroll || 0) - cnvs.offsetLeft;
        y = event.pageY + (window.pageYScroll || 0) - cnvs.offsetTop;

        // NB: is not worth a separate function
        i = Math.floor(x / (cfg.cellSize + cfg.cellMargin));
        j = Math.floor(y / (cfg.cellSize + cfg.cellMargin));

        if (i >= size || j >= size) {
            return;
        }

        if (!sel) {
            // no selection. simply change selection to the picked cell if the ball
            // is there
            if (field.field[field.idx(i, j)].color) {
                draw.setSelection((sel = field.field[field.idx(i, j)]));
                return;
            }
        } else {
            var path = null;
            if (field.field[field.idx(i, j)].color) {   // if another ball is selected, redraw
                                                        // the ball and change selection
                draw.eraseCell(sel.i, sel.j);
                draw.drawBall(sel.ballX, sel.ballY, 3 / 10 * cfg.cellSize, sel.color);
                draw.setSelection((sel = field.field[field.idx(i, j)]));
            } else {
                if ((path = findPath(sel.i, sel.j, i, j)) !== null) {
                    isMoving = true;
                    draw.moveBall(sel.i, sel.j, path);
                    draw.setSelection((sel = null));
                } else {
                    draw.warnNoPath();
                }
            }
        }
    }

    function updateScoreBoard() {
        scoreBoard.innerHTML = score;
    }

    var _line_directions = [[1, -1], [1, 1], [1, 0], [0, 1]];

    function killBalls(i, j) {
        var pt = field.field[field.idx(i, j)], // what we just moved/inserted
            line = [],
            killed = 0,
            k;
        var di, dj;
        var ic, jc; // current ball index
        var sorter = function (x, y) { return (y.i - x.i) || (y.j - x.j); };
        line.push(pt);
        for (k = 0; k < _line_directions.length; k++) { // over 4 possible directions
            di = _line_directions[k][0];
            dj = _line_directions[k][1];
            ic = pt.i + di;
            jc = pt.j + dj;
            while (field.isInside(ic, jc) && field.field[field.idx(ic, jc)].color === pt.color) {
                line.push(field.field[field.idx(ic, jc)]);
                ic += di;
                jc += dj;
            }
            // reverse direction
            ic = pt.i - di;
            jc = pt.j - dj;
            while (field.isInside(ic, jc) && field.field[field.idx(ic, jc)].color === pt.color) {
                line.push(field.field[field.idx(ic, jc)]);
                ic -= di;
                jc -= dj;
            }
            if (line.length > 4) { // got line
                // sort and kill
                var origin = line.shift(),
                    lineSorted = line.sort(sorter),
                    b;
                for (b = 0; b < lineSorted.length; b++) {
                    lineSorted[b].color = null;
                    draw.eraseCell(lineSorted[b].i, lineSorted[b].j);
                    killed++;
                }
                line.unshift(origin);
                line.length = 1; // truncate
            } else {
                line.length = 1; // truncate
            }
        }
        if (killed) { // we have origin left at the beginning of the array
            line[0].color = null;
            draw.eraseCell(line[0].i, line[0].j);
            killed++;

            // update score
            // calculated as 10 for 5 balls, for more: 2*killed+(2*(killed-6)+1)
            if (killed === 5) {
                score += 10;
            } else {
                score += 2 * killed + 2 * (killed - 6) + 1;
            }
            updateScoreBoard();
        }
        return killed;
    }

    function newBalls() {
        var i;
        // three balls at a time
        function unoccupied(x, y) { if (!y.color) { x.push(y); } return x; }
        for (i = 0; i < 3; i++) {
            var ctx,
                rndIdx1,
                rndIdx2,
                avail = field.field.reduce(unoccupied, []),
                s;
            rndIdx1 = Math.floor(Math.random() * avail.length);
            s = avail[rndIdx1];
            rndIdx2 = Math.floor(Math.random() * config.draw.colors.length);
            s.color = config.draw.colors[rndIdx2];
            draw.drawBall(s.ballX, s.ballY, 3 / 10 * cfg.cellSize, s.color);
            if (!killBalls(s.i, s.j)) {
                if (avail.length === 1) {
                    return -1;
                }
            }
        }
        return 0;
    }

    function restartGame() {
        score = 0;
        updateScoreBoard();
        field.initField();
        draw.initCanvas(cnvs);
        draw.setSelection((sel = null));
        draw.restartTimers();
        newBalls();
    }

    function checkAndInsert(evnt) {
        isMoving = false;
        if (!killBalls(evnt.detail.ball.i, evnt.detail.ball.j)) {
            if (newBalls() < 0) {
                var maxScore = parseInt(localStorage.getItem("maxScore"), 10);
                if (isNaN(maxScore)) {
                    localStorage.setItem("maxScore", 0);
                    maxScore = 0;
                }
                if (score > maxScore) {
                    localStorage.setItem("maxScore", score);
                    window.alert("Congratulations!\n\nNew record: " + score);
                } else {
                    window.alert("Game over...\n\nYour score is: " + score);
                }
                restartGame();
            }
        }
    }

    // this module is loaded on DOM ready, DOM manipulations below are safe
    cnvs = document.getElementById("playground");
    scoreBoard = document.getElementById("score");
    draw.initCanvas(cnvs);
    var footer = document.getElementById("footer");
    footer.style.width = cnvs.style.width;
    scoreBoard.style.minWidth = scoreBoard.offsetWidth + "px";
    scoreBoard.textContent = "0";
    footer.style.visibility = "visible";
    var restart = document.getElementById("restart");
    newBalls();
    cnvs.addEventListener("click", select);
    cnvs.addEventListener("doneAnimating", checkAndInsert);
    restart.addEventListener("click", restartGame);
});<commit_msg>Keep track of pending selection.<commit_after>/*global define:false, require: false, document:false, localStorage: false */
require.config({
    baseUrl: "js"
});

/*global define:false, window:false */
define(["draw", "field", "config", "3rd/domReady!"], function (draw, field, config) {
    "use strict";
    var score = 0,
        cfg = config.draw,
        size = config.game.size,
        cnvs,
        sel,
        scoreBoard;

    var isMoving = false, // if a move is being animated now
        pendingSelection = null; // if there was a selection during the move, keep track of it

    var ih; // interval handler

    function minDist(i, j, graph) {
        var min = Number.MAX_VALUE,
            minIdx = -1,
            k;
        for (k = 0; k < size * size; k++) {
            if (graph[k].incl) {
                if (graph[k].dist < min) {
                    min = graph[k].dist;
                    minIdx = k;
                }
            }
        }
        return minIdx;
    }

    // shift from the current (i, j) to get to the neighbors
    var _neighbors = [[-1, 0], [0, -1], [1, 0], [0, 1]];

    // simple Dijkstra's algorithm implementation
    // TODO: inefficient minDist -- requires walk though entire graph
    // TODO: inefficient reduce -- might not be even needed, other conditions within
    // the cycle will be satisfied sooner
    function findPath(ib, jb, ie, je) {
        var graph = [],
            path = [],
            length,
            k;
        for (k = 0; k < size * size; k++) {
            graph[k] = { incl: 1, dist: Number.MAX_VALUE, prev: -1 };
        }
        graph[field.idx(ib, jb)].dist = 0;

        function avail(x, y) { return (y.incl) ? x + 1 : x; }

        var minIdx = -1;
        while (graph.reduce(avail, 0)) {
            minIdx = minDist(ib, jb, graph);
            var imin = Math.floor(minIdx / size);
            var jmin = minIdx % size;
            if (minIdx < 0) {
                break;
            }
            graph[minIdx].incl = 0;
            if (imin === ie && jmin === je) {
                break;
            }
            if (graph[minIdx].dist === Number.MAX_VALUE) {
                break;
            }
            for (k = 0; k < _neighbors.length; k++) {
                var ic, jc; // i and j of each neighbor
                var alt;
                ic = imin + _neighbors[k][0];
                jc = jmin + _neighbors[k][1];
                if (field.isInside(ic, jc) &&
                        !field.field[field.idx(ic, jc)].color &&
                        graph[field.idx(ic, jc)]) {

                    alt = graph[minIdx].dist + 1;
                    if (alt < graph[field.idx(ic, jc)].dist) {
                        graph[field.idx(ic, jc)].dist = alt;
                        graph[field.idx(ic, jc)].prev = minIdx;
                    }
                }
            }
        }
        k = minIdx;
        if (k < 0) {
            return null; // path has not been found
        }
        while (graph[k].prev >= 0) {
            path.push(field.field[k]);
            k = graph[k].prev;
        }
        return path;
    }

    function _selectAt(i, j) {

        if (!sel) {
            // no selection. simply change selection to the picked cell if the ball
            // is there
            if (field.field[field.idx(i, j)].color) {
                draw.setSelection((sel = field.field[field.idx(i, j)]));
                return;
            }
        } else {
            var path = null;
            if (field.field[field.idx(i, j)].color) {   // if another ball is selected, redraw
                                                        // the ball and change selection
                draw.eraseCell(sel.i, sel.j);
                draw.drawBall(sel.ballX, sel.ballY, 3 / 10 * cfg.cellSize, sel.color);
                draw.setSelection((sel = field.field[field.idx(i, j)]));
            } else {
                if ((path = findPath(sel.i, sel.j, i, j)) !== null) {
                    isMoving = true;
                    draw.moveBall(sel.i, sel.j, path);
                    draw.setSelection((sel = null));
                } else {
                    draw.warnNoPath();
                }
            }
        }
    }

    function select(event) {
        var x, y; // positions within the canvas
        var i, j; // matrix indices of the cell

        event.stopPropagation();

        x = event.pageX + (window.pageXScroll || 0) - cnvs.offsetLeft;
        y = event.pageY + (window.pageYScroll || 0) - cnvs.offsetTop;

        // NB: is not worth a separate function
        i = Math.floor(x / (cfg.cellSize + cfg.cellMargin));
        j = Math.floor(y / (cfg.cellSize + cfg.cellMargin));

        if (i >= size || j >= size) {
            return;
        }

        // ignore selection if a ball is moving
        if (isMoving) {
            pendingSelection = [i, j];
        } else {
            _selectAt(i, j);
        }
    }

    function updateScoreBoard() {
        scoreBoard.innerHTML = score;
    }

    var _line_directions = [[1, -1], [1, 1], [1, 0], [0, 1]];

    function killBalls(i, j) {
        var pt = field.field[field.idx(i, j)], // what we just moved/inserted
            line = [],
            killed = 0,
            k;
        var di, dj;
        var ic, jc; // current ball index
        var sorter = function (x, y) { return (y.i - x.i) || (y.j - x.j); };
        line.push(pt);
        for (k = 0; k < _line_directions.length; k++) { // over 4 possible directions
            di = _line_directions[k][0];
            dj = _line_directions[k][1];
            ic = pt.i + di;
            jc = pt.j + dj;
            while (field.isInside(ic, jc) && field.field[field.idx(ic, jc)].color === pt.color) {
                line.push(field.field[field.idx(ic, jc)]);
                ic += di;
                jc += dj;
            }
            // reverse direction
            ic = pt.i - di;
            jc = pt.j - dj;
            while (field.isInside(ic, jc) && field.field[field.idx(ic, jc)].color === pt.color) {
                line.push(field.field[field.idx(ic, jc)]);
                ic -= di;
                jc -= dj;
            }
            if (line.length > 4) { // got line
                // sort and kill
                var origin = line.shift(),
                    lineSorted = line.sort(sorter),
                    b;
                for (b = 0; b < lineSorted.length; b++) {
                    lineSorted[b].color = null;
                    draw.eraseCell(lineSorted[b].i, lineSorted[b].j);
                    killed++;
                }
                line.unshift(origin);
                line.length = 1; // truncate
            } else {
                line.length = 1; // truncate
            }
        }
        if (killed) { // we have origin left at the beginning of the array
            line[0].color = null;
            draw.eraseCell(line[0].i, line[0].j);
            killed++;

            // update score
            // calculated as 10 for 5 balls, for more: 2*killed+(2*(killed-6)+1)
            if (killed === 5) {
                score += 10;
            } else {
                score += 2 * killed + 2 * (killed - 6) + 1;
            }
            updateScoreBoard();
        }
        return killed;
    }

    function newBalls() {
        var i;
        // three balls at a time
        function unoccupied(x, y) { if (!y.color) { x.push(y); } return x; }
        for (i = 0; i < 3; i++) {
            var ctx,
                rndIdx1,
                rndIdx2,
                avail = field.field.reduce(unoccupied, []),
                s;
            rndIdx1 = Math.floor(Math.random() * avail.length);
            s = avail[rndIdx1];
            rndIdx2 = Math.floor(Math.random() * config.draw.colors.length);
            s.color = config.draw.colors[rndIdx2];
            draw.drawBall(s.ballX, s.ballY, 3 / 10 * cfg.cellSize, s.color);
            if (!killBalls(s.i, s.j)) {
                if (avail.length === 1) {
                    return -1;
                }
            }
        }
        return 0;
    }

    function restartGame() {
        score = 0;
        updateScoreBoard();
        field.initField();
        draw.initCanvas(cnvs);
        draw.setSelection((sel = null));
        draw.restartTimers();
        newBalls();
    }

    function checkAndInsert(evnt) {
        if (isMoving) {
            isMoving = false;
            _selectAt(pendingSelection[0], pendingSelection[1]);
            pendingSelection = null;
        }
        if (!killBalls(evnt.detail.ball.i, evnt.detail.ball.j)) {
            if (newBalls() < 0) {
                var maxScore = parseInt(localStorage.getItem("maxScore"), 10);
                if (isNaN(maxScore)) {
                    localStorage.setItem("maxScore", 0);
                    maxScore = 0;
                }
                if (score > maxScore) {
                    localStorage.setItem("maxScore", score);
                    window.alert("Congratulations!\n\nNew record: " + score);
                } else {
                    window.alert("Game over...\n\nYour score is: " + score);
                }
                restartGame();
            }
        }
    }

    // this module is loaded on DOM ready, DOM manipulations below are safe
    cnvs = document.getElementById("playground");
    scoreBoard = document.getElementById("score");
    draw.initCanvas(cnvs);
    var footer = document.getElementById("footer");
    footer.style.width = cnvs.style.width;
    scoreBoard.style.minWidth = scoreBoard.offsetWidth + "px";
    scoreBoard.textContent = "0";
    footer.style.visibility = "visible";
    var restart = document.getElementById("restart");
    newBalls();
    cnvs.addEventListener("click", select);
    cnvs.addEventListener("doneAnimating", checkAndInsert);
    restart.addEventListener("click", restartGame);
});<|endoftext|>


<commit_before>//initialize game board
var Game = function(count){
  this.gameBoard = init(count);

  function init(count) {
    var grid = [];
    for (var i=0; i<count; i++){
      var row = [];
      switch(grid.length) {
        case 3:
          var toAdd = 12;
          break;
        case 2:
          var toAdd = 8;
          break;
        case 1:
          var toAdd = 4;
          break;
        default:
          var toAdd = 0;
      }
      for (var j=0; j<count; j++){
        row.push(j+toAdd+1);
        if(j==count-1){
          grid.push(row);
        }
      }
    }
    return grid;
  }
}

var game = new Game(4);<commit_msg>Add display method for gameboard<commit_after>//initialize game board
var Game = function(count){
  this.gameBoard = init(count);

  function init(count) {
    var grid = [];
    for (var i=0; i<count; i++){
      var row = [];
      switch(grid.length) {
        case 3:
          var toAdd = 12;
          break;
        case 2:
          var toAdd = 8;
          break;
        case 1:
          var toAdd = 4;
          break;
        default:
          var toAdd = 0;
      }
      for (var j=0; j<count; j++){
        row.push(j+toAdd+1);
        if(j==count-1){
          grid.push(row);
        }
      }
    }
    return grid;
  }
}

// display 2d array on table in html
Game.prototype.display = function() {
  // initialize html
  $(".gameBoard").html("");

  $(".gameBoard").append('

');

  for (var i = 0; i < this.gameBoard.length; i ++){
    $(".gameBoard tbody").append("

");

    for (var j = 0; j < this.gameBoard[i].length; j ++){
      $(".row" + i).append("" + this.gameBoard[i][j] + "	");
    }

  }
}

var game = new Game(4);
game.display();<|endoftext|>


<commit_before>var Game = {
	currency:  10,

	count: undefined,
	roster: undefined,
	market: undefined,
	cpsDisplay: undefined,

	handle: undefined,

	workers: [],
	companies: [],

	init: function(_workers, _companies) {
		var self = this;

		this.count = $('#currency-display');
		this.roster = $('#roster-container');
		this.market = $('#market-container');
		this.cpsDisplay = $('#cps-display');

		$.each(_workers, function(index, _worker) {
			var newWorker = Worker(_worker).init();
			self.workers.push(newWorker);
		});

		$.each(_companies, function(index, _company) {
			var newCompany = Company(_company).init();
			self.companies.push(newCompany);
		});

		this.handle = window.setInterval(function() {
			self._tick();
		}, 10);
	},

	_tick: function() {
		$.each(this.companies, function(index, company) {
			company.produce();
			company.check();
		});

		$.each(this.workers, function(index, worker) {
			worker.produce();
			worker.check();
		});

		this.count.text(this.currency.toFixed(2));
	},

	cps: function() {
		var cps = 0;

		$.each(this.companies, function(index, company) {
			cps += company.production * company.quantity;
			console.log(cps);
		});

		$.each(this.workers, function(index, worker) {
			cps += worker.production * worker.quantity;
			console.log(cps);
		});

		this.cpsDisplay.text(cps);
	}

};

var Company = function(options) {
	return $.extend({
		quantity: 0,
		increase: 1.15,

		button: undefined,

		produce: function() {
			Game.currency += this.quantity * this.production / 100;
		},

		check: function() {
			this.button.toggleClass('disabled', this.cost > Game.currency);
		},

		buy: function() {
			if(this.cost <= Game.currency){
				Game.currency -= this.cost;

				this.quantity++;
				this.cost = Math.ceil(this.cost * this.increase);
				this.button.text(this.name + ' - ' + this.cost);

				Game.cps();
			};
		},

		init: function() {
			var self = this;
			var row = undefined;

			this.row = $("<div/>")
			.addClass("row")

			Game.market.append(this.row);

			this.button = $("<div/>")
			.attr('class', 'waves-effect waves-light btn-large')
			.text(this.name + " - " + this.cost)
			.click(function(){
				self.buy();
			});

			this.row.append(this.button);

			this.check();

			return this;
		}

	}, options);
};

var Worker = function(options) {
	return $.extend({
		quantity: 0,
		increase: 1.11,

		button: undefined,

		produce: function() {
			Game.currency += this.quantity * this.production / 100;
		},

		check: function() {
			this.button.toggleClass('disabled', this.cost > Game.currency);
		},

		buy: function() {
			if(this.cost <= Game.currency){
				Game.currency -= this.cost;

				this.quantity++;
				this.cost = Math.ceil(this.cost * this.increase);
				this.button.text(this.name + ' - ' + this.cost);
				console.log('hi');
				Game.cps();
			};
		},

		init: function() {
			var self = this;
			var row = undefined;

			this.row = $("<div/>")
			.addClass("row")

			Game.roster.append(this.row);

			this.button = $("<div/>")
			.attr('class', 'waves-effect waves-light btn-large')
			.text(this.name + " - " + this.cost)
			.click(function() {
				self.buy();
			});

			this.row.append(this.button);

			this.check();

			return this;
		}

	}, options);
};

_companies = [
{
	name: "Small company",
	cost: 10,
	production: 1
},
{
	name: "Medium company",
	cost: 20,
	production: 3
},
{
	name: "Big company",
	cost: 50,
	production: 5
}
];

_workers = [
{
	name: "Junior employee",
	cost: 10,
	production: 1
},
{
	name: "Senior employee",
	cost: 20,
	production: 3
},
{
	name: "Master & Commander",
	cost: 50,
	production: 5
}
];

Game.init(_workers, _companies);

// (function refreshUI() {
// 	setTimeout(refreshUI, 10);
// })();<commit_msg>Switch from setInterval to setTimeout for the loop<commit_after>var Game = {
	currency:  10,

	count: undefined,
	roster: undefined,
	market: undefined,
	cpsDisplay: undefined,

	// handle: undefined,

	workers: [],
	companies: [],

	init: function(_workers, _companies) {
		var self = this;

		this.count = $('#currency-display');
		this.roster = $('#roster-container');
		this.market = $('#market-container');
		this.cpsDisplay = $('#cps-display');

		$.each(_workers, function(index, _worker) {
			var newWorker = Worker(_worker).init();
			self.workers.push(newWorker);
		});

		$.each(_companies, function(index, _company) {
			var newCompany = Company(_company).init();
			self.companies.push(newCompany);
		});

		// this.handle = window.setInterval(function() {
		// 	self._tick();
		// }, 10);
},

_tick: function() {
	$.each(this.companies, function(index, company) {
		company.produce();
		company.check();
	});

	$.each(this.workers, function(index, worker) {
		worker.produce();
		worker.check();
	});

	this.count.text(this.currency.toFixed(2));
},

cps: function() {
	var cps = 0;

	$.each(this.companies, function(index, company) {
		cps += company.production * company.quantity;
	});

	$.each(this.workers, function(index, worker) {
		cps += worker.production * worker.quantity;
	});

	this.cpsDisplay.text(cps);
}

};

var Company = function(options) {
	return $.extend({
		quantity: 0,
		increase: 1.15,

		button: undefined,

		produce: function() {
			Game.currency += this.quantity * this.production / 100;
		},

		check: function() {
			this.button.toggleClass('disabled', this.cost > Game.currency);
		},

		buy: function() {
			if(this.cost <= Game.currency){
				Game.currency -= this.cost;

				this.quantity++;
				this.cost = Math.ceil(this.cost * this.increase);
				this.button.text(this.name + ' - ' + this.cost);

				Game.cps();
			};
		},

		init: function() {
			var self = this;
			var row = undefined;

			this.row = $("<div/>")
			.addClass("row")

			Game.market.append(this.row);

			this.button = $("<div/>")
			.attr('class', 'waves-effect waves-light btn-large')
			.text(this.name + " - " + this.cost)
			.click(function(){
				self.buy();
			});

			this.row.append(this.button);

			this.check();

			return this;
		}

	}, options);
};

var Worker = function(options) {
	return $.extend({
		quantity: 0,
		increase: 1.11,

		button: undefined,

		produce: function() {
			Game.currency += this.quantity * this.production / 100;
		},

		check: function() {
			this.button.toggleClass('disabled', this.cost > Game.currency);
		},

		buy: function() {
			if(this.cost <= Game.currency){
				Game.currency -= this.cost;

				this.quantity++;
				this.cost = Math.ceil(this.cost * this.increase);
				this.button.text(this.name + ' - ' + this.cost);
				Game.cps();
			};
		},

		init: function() {
			var self = this;
			var row = undefined;

			this.row = $("<div/>")
			.addClass("row")

			Game.roster.append(this.row);

			this.button = $("<div/>")
			.attr('class', 'waves-effect waves-light btn-large')
			.text(this.name + " - " + this.cost)
			.click(function() {
				self.buy();
			});

			this.row.append(this.button);

			this.check();

			return this;
		}

	}, options);
};

_companies = [
{
	name: "Small company",
	cost: 10,
	production: 1
},
{
	name: "Medium company",
	cost: 20,
	production: 3
},
{
	name: "Big company",
	cost: 50,
	production: 5
}
];

_workers = [
{
	name: "Junior employee",
	cost: 10,
	production: 1
},
{
	name: "Senior employee",
	cost: 20,
	production: 3
},
{
	name: "Master & Commander",
	cost: 50,
	production: 5
}
];

Game.init(_workers, _companies);

(function refreshUI() {
	Game._tick();
	setTimeout(refreshUI, 10);
})();<|endoftext|>


<commit_before>/*****************************************************
*
* Designed and programmed by Mohamed Adam Chaieb.
*
******************************************************/

/*
  Constructs an empty 4 by 4 grid.
*/
function Grid() {
  this.moveMap = [];
  this.tiles = [[null,null,null,null],[null,null,null,null],[null,null,null,null],[null,null,null,null]];
  this.newTile = null;
  this.score = 0;
  this.done = false;
};

/*
  Iterator for the tiles in the grid, applies the given function
  to all tiles in the grid. The order of iteration is decided by
  the direction parameter
*/
Grid.prototype.eachCell = function(dir, fun) {
  if(!dir || dir < 2) {
    for (var i = globalGame.gridSize-1; i >= 0; i--) {
      for (var j = globalGame.gridSize-1; j >= 0; j--) {
        fun(i, j, this.tiles[i][j]);
      };
    };
  } else {
    for (var i = 0; i < globalGame.gridSize; i++) {
      for (var j = 0; j < globalGame.gridSize; j++) {
        fun(i, j, this.tiles[i][j]);
      };
    };
  };
};

/*
  Inserts the tile in the grid in the correct cell
*/
Grid.prototype.insertTile = function(tile) {
  this.tiles[tile.x][tile.y] = tile;
};

/*
  Removes the tile from the grid, does nothing when passed null
*/
Grid.prototype.removeTile = function(tile) {
  this.tiles[tile.x][tile.y] = null;
};

/*
  Updates the position of the specified tile, without removing it from the grid.
*/
Grid.prototype.updateTile = function(tile, newPos) {
  this.removeTile(tile);
  tile.updatePosition(newPos);
  this.insertTile(tile);
}

/*
  Returns an array of all the free positions in the grid
*/
Grid.prototype.freePositions = function() {
  var free = [];
  var self = this;
  this.eachCell(null, function(x,y,tile) {
    if(!tile)
      free.push({x: x, y: y});
  });
  return free;
};

/*
  Returns a random empty position.
*/
Grid.prototype.getRandomPosition = function() {
  var freeTiles = this.freePositions();
  if(freeTiles.length)
    return freeTiles[Math.floor(Math.random()*freeTiles.length)];
};

/*
  Initializes the grid with two tiles.
*/
Grid.prototype.init = function() {
  this.generateTile();
  this.generateTile();
  this.newTile = null;
};

/*
  Returns a tuple {newPos, merge} of the new position of the tile at the given
  position, and a boolean indicating if the tile merged.
*/
Grid.prototype.getMovePosition = function(pos, dir) {
  var merge = false;
  var newPos = {x:pos.x, y:pos.y};
  var inc = 1;
  var result;
  switch(dir) {
    case 0: //up
      // Find the upmost valid position, then depending on the tile that's right after,
      // determine if you need to merge or not
      result = this.moveUp(pos, dir);
      break;
    case 1: //right
      result = this.moveRight(pos, dir);
      break;
    case 2: //down
      result = this.moveDown(pos, dir);
      break;
    case 3: //left
      result = this.moveLeft(pos, dir);
      break;
  }
  return result;
};

Grid.prototype.moveUp = function(pos, dir) {
  var merge = false;
  var newPos = {x:pos.x, y:pos.y};
  var inc = 1;
  while(pos.y+inc < globalGame.gridSize && this.tiles[pos.x][pos.y+inc] == null) {
    inc++;
  }
  if(this.tiles[pos.x][pos.y+inc] == null) {
    newPos = {x:pos.x, y:pos.y+inc-1}
    return {newPos: newPos, merge: merge};
  } else if(this.tiles[pos.x][pos.y+inc]) {
    if(this.tiles[pos.x][pos.y+inc].level == this.tiles[pos.x][pos.y].level) {
      newPos = {x: pos.x, y: pos.y+inc};
      merge = true;
    }
    else
      newPos = {x: pos.x, y: pos.y+inc-1};
  }
  return {newPos: newPos, merge: merge};
}

Grid.prototype.moveDown = function(pos, dir) {
  var merge = false;
  var newPos = {x:pos.x, y:pos.y};
  var inc = 1;
  while(pos.y-inc >= 0) {
    if(this.tiles[pos.x][pos.y-inc] == null)
      inc++;
    else break;
  }
  if(this.tiles[pos.x][pos.y-inc] == null) {
    newPos = {x:pos.x, y:pos.y-inc+1}
    return {newPos: newPos, merge: merge};
  } else if(this.tiles[pos.x][pos.y-inc]) {
    if(this.tiles[pos.x][pos.y-inc].level == this.tiles[pos.x][pos.y].level) {
      newPos = {x: pos.x, y: pos.y-inc};
      merge = true;
    }
    else
      newPos = {x: pos.x, y: pos.y-inc+1};
  }
  return {newPos : newPos, merge: merge};
}

Grid.prototype.moveLeft = function(pos, dir) {
  var merge = false;
  var newPos = {x:pos.x, y:pos.y};
  var inc = 1;
  while(pos.x-inc >= 0) {
    if(this.tiles[pos.x-inc][pos.y] == null)
      inc++;
    else break;
  };
  if(this.tiles[pos.x-inc] == null) {
    newPos = {x:pos.x-inc+1, y:pos.y}
    return {newPos : newPos, merge: merge};
  } else if(this.tiles[pos.x-inc][pos.y]) {
    if(this.tiles[pos.x-inc][pos.y].level == this.tiles[pos.x][pos.y].level) {
      newPos = {x: pos.x-inc, y: pos.y};
      merge = true;
    }
    else
      newPos = {x: pos.x-inc+1, y: pos.y};
  };
  return {newPos : newPos, merge: merge};
}

Grid.prototype.moveRight = function(pos, dir) {
  var merge = false;
  var newPos = {x:pos.x, y:pos.y};
  while(this.isNextTileEmpty(newPos, dir)) {
    newPos = this.getNewPosition(newPos,dir);
  }
  var nextTile = this.getNextTile(newPos, dir);
  if(nextTile && nextTile.level == this.getTile(pos).level) {
    newPos = this.getNewPosition(newPos, dir);
    merge = true;
  }
  return {newPos: newPos, merge: merge};
}

Grid.prototype.isOutOfBounds = function(pos) {
  return (pos.x >= globalGame.gridSize || pos.y >= globalGame.gridSize);
}

Grid.prototype.isNextTileEmpty = function(pos, dir) {
  var newPos = this.getNewPosition(pos, dir);
  if(this.isOutOfBounds(newPos))
    return false;
  return (this.getTile(newPos) == null);
}

Grid.prototype.getNextTile = function(pos, dir) {
  return this.getTile(this.getNewPosition(pos, dir));
}

Grid.prototype.getNewPosition = function(pos, dir) {
  var newPos = {x:pos.x, y:pos.y};
  switch(dir) {
    case 0: //up
      newPos.y++;
      break;
    case 1: //right
      newPos.x++;
      break;
    case 2: //down
      newPos.y--;
      break;
    case 3: //left
      newPos.x--;
      break;
  }
  return newPos;
}

Grid.prototype.getTile = function(pos) {
  if(this.isOutOfBounds(pos))
    return false;
  return this.tiles[pos.x][pos.y];
}

/*
  Generates a new tile at a random position and adds it to the grid
*/
Grid.prototype.generateTile = function() {
  this.newTile = new Tile(this.getRandomPosition(), Math.floor(Math.random()*2));
  this.insertTile(this.newTile);
};

/*
  Returns a new grid with an updated state
*/
Grid.prototype.update = function(dir) {
  var update;
  var self = this;
  this.moveMap = [];
  this.eachCell(dir, function(x,y,tile) {
    if(tile) {
      update = self.getMovePosition({x:x, y:y}, dir);
      if(update.merge) {
        self.score += Math.pow(2, tile.level+1);
        self.removeTile(tile);
        self.insertTile(new Tile(update.newPos, tile.level+1));
        self.addToMoveMap({oldPos: {x:x, y:y}, newPos: update.newPos, level: tile.level+1});
        if(tile.level+1 === 10)
          this.done = true;
      } else {
        self.updateTile(tile, update.newPos)
        self.addToMoveMap({oldPos: {x:x, y:y}, newPos: update.newPos, level: tile.level});
      };
    };
  });
  if(this.differentState())
    this.generateTile();
  else this.newTile = null;
  if(this.gameOver() || this.done) {
    globalGame.gameOverHandler();
  };
};

/*
  Returns true if the previous state is different from the current one.
*/
Grid.prototype.differentState = function() {
  // Check where all the tiles in the movement map are going, if they're staying in place,
  // then the state is the same
  for (var i = 0; i < this.moveMap.length; i++) {
    if(this.moveMap[i]) {
      if(this.moveMap[i].oldPos.x != this.moveMap[i].newPos.x || this.moveMap[i].oldPos.y != this.moveMap[i].newPos.y)
        return true;
    }
  };
  return false;
};

/*
  Checks whether the game is over or not.
*/
Grid.prototype.gameOver = function() {
  var self = this;
  var adjacent = [];
  var free = this.freePositions();
  var g = true
  if(free.length === 0) {
    //check if there exists two adjacent cells with the same level
    this.eachCell(null, function(x,y,tile) {
      if(tile) {
        adjacent = self.adjacentTiles(tile);
        for (var i = 0; i < adjacent.length; i++) {
          if(adjacent[i].level === tile.level) {
            g = false;
            break;
          };
        };
      };
    });
  } else g = false;
  return g;
};

/*
  Returns an array of adjacent tiles.
*/
Grid.prototype.adjacentTiles = function(tile) {
  var adjacent = [];
  if(this.tiles[tile.x+1]) {
    if (this.tiles[tile.x+1][tile.y])
      adjacent.push(this.tiles[tile.x+1][tile.y]);
  } if (this.tiles[tile.x-1]) {
    if (this.tiles[tile.x-1][tile.y])
      adjacent.push(this.tiles[tile.x-1][tile.y]);
  } if (this.tiles[tile.x][tile.y+1]) {
    adjacent.push(this.tiles[tile.x][tile.y+1]);
  } if (this.tiles[tile.x][tile.y-1]) {
    adjacent.push(this.tiles[tile.x][tile.y-1]);
  };
  return adjacent;
};

/*
  Inserts the movement object in place so that the moveMap is sorted
  from the largest coordinates to the smaller coordinates (from {3,3}
  to {0,0}).
  TODO: might need some refactoring to sort sub-sequences by tile height.
*/
Grid.prototype.addToMoveMap = function(move) {
  var index = move.newPos.x+move.newPos.y*globalGame.gridSize;
  while(this.moveMap[index])
    index--;
  if(index < 0)
    this.moveMap.push(move);
  else this.moveMap[index] = move;
};<commit_msg>Refactored getMovePosition<commit_after>/*****************************************************
*
* Designed and programmed by Mohamed Adam Chaieb.
*
******************************************************/

/*
  Constructs an empty 4 by 4 grid.
*/
function Grid() {
  this.moveMap = [];
  this.tiles = [[null,null,null,null],[null,null,null,null],[null,null,null,null],[null,null,null,null]];
  this.newTile = null;
  this.score = 0;
  this.done = false;
};

/*
  Iterator for the tiles in the grid, applies the given function
  to all tiles in the grid. The order of iteration is decided by
  the direction parameter
*/
Grid.prototype.eachCell = function(dir, fun) {
  if(!dir || dir < 2) {
    for (var i = globalGame.gridSize-1; i >= 0; i--) {
      for (var j = globalGame.gridSize-1; j >= 0; j--) {
        fun(i, j, this.tiles[i][j]);
      };
    };
  } else {
    for (var i = 0; i < globalGame.gridSize; i++) {
      for (var j = 0; j < globalGame.gridSize; j++) {
        fun(i, j, this.tiles[i][j]);
      };
    };
  };
};

/*
  Inserts the tile in the grid in the correct cell
*/
Grid.prototype.insertTile = function(tile) {
  this.tiles[tile.x][tile.y] = tile;
};

/*
  Removes the tile from the grid, does nothing when passed null
*/
Grid.prototype.removeTile = function(tile) {
  this.tiles[tile.x][tile.y] = null;
};

/*
  Updates the position of the specified tile, without removing it from the grid.
*/
Grid.prototype.updateTile = function(tile, newPos) {
  this.removeTile(tile);
  tile.updatePosition(newPos);
  this.insertTile(tile);
}

/*
  Returns an array of all the free positions in the grid
*/
Grid.prototype.freePositions = function() {
  var free = [];
  var self = this;
  this.eachCell(null, function(x,y,tile) {
    if(!tile)
      free.push({x: x, y: y});
  });
  return free;
};

/*
  Returns a random empty position.
*/
Grid.prototype.getRandomPosition = function() {
  var freeTiles = this.freePositions();
  if(freeTiles.length)
    return freeTiles[Math.floor(Math.random()*freeTiles.length)];
};

/*
  Initializes the grid with two tiles.
*/
Grid.prototype.init = function() {
  this.generateTile();
  this.generateTile();
  this.newTile = null;
};

/*
  Returns a tuple {newPos, merge} of the new position of the tile at the given
  position, and a boolean indicating if the tile merged.
*/
Grid.prototype.getMovePosition = function(pos, dir) {
  var merge = false;
  var newPos = {x:pos.x, y:pos.y};
  while(this.isNextTileEmpty(newPos, dir)) {
    newPos = this.getNewPosition(newPos,dir);
  }
  var nextTile = this.getNextTile(newPos, dir);
  if(nextTile && nextTile.level == this.getTile(pos).level) {
    newPos = this.getNewPosition(newPos, dir);
    merge = true;
  }
  return {newPos: newPos, merge: merge};
}

Grid.prototype.isOutOfBounds = function(pos) {
  return (pos.x >= globalGame.gridSize || pos.y >= globalGame.gridSize || pos.x < 0 || pos.y < 0);
}

Grid.prototype.isNextTileEmpty = function(pos, dir) {
  var newPos = this.getNewPosition(pos, dir);
  if(this.isOutOfBounds(newPos))
    return false;
  return (this.getTile(newPos) == null);
}

Grid.prototype.getNextTile = function(pos, dir) {
  return this.getTile(this.getNewPosition(pos, dir));
}

Grid.prototype.getNewPosition = function(pos, dir) {
  var newPos = {x:pos.x, y:pos.y};
  switch(dir) {
    case 0: //up
      newPos.y++;
      break;
    case 1: //right
      newPos.x++;
      break;
    case 2: //down
      newPos.y--;
      break;
    case 3: //left
      newPos.x--;
      break;
  }
  return newPos;
}

Grid.prototype.getTile = function(pos) {
  if(this.isOutOfBounds(pos))
    return false;
  return this.tiles[pos.x][pos.y];
}

/*
  Generates a new tile at a random position and adds it to the grid
*/
Grid.prototype.generateTile = function() {
  this.newTile = new Tile(this.getRandomPosition(), Math.floor(Math.random()*2));
  this.insertTile(this.newTile);
};

/*
  Returns a new grid with an updated state
*/
Grid.prototype.update = function(dir) {
  var update;
  var self = this;
  this.moveMap = [];
  this.eachCell(dir, function(x,y,tile) {
    if(tile) {
      update = self.getMovePosition({x:x, y:y}, dir);
      if(update.merge) {
        self.score += Math.pow(2, tile.level+1);
        self.removeTile(tile);
        self.insertTile(new Tile(update.newPos, tile.level+1));
        self.addToMoveMap({oldPos: {x:x, y:y}, newPos: update.newPos, level: tile.level+1});
        if(tile.level+1 === 10)
          this.done = true;
      } else {
        self.updateTile(tile, update.newPos)
        self.addToMoveMap({oldPos: {x:x, y:y}, newPos: update.newPos, level: tile.level});
      };
    };
  });
  if(this.differentState())
    this.generateTile();
  else this.newTile = null;
  if(this.gameOver() || this.done) {
    globalGame.gameOverHandler();
  };
};

/*
  Returns true if the previous state is different from the current one.
*/
Grid.prototype.differentState = function() {
  // Check where all the tiles in the movement map are going, if they're staying in place,
  // then the state is the same
  for (var i = 0; i < this.moveMap.length; i++) {
    if(this.moveMap[i]) {
      if(this.moveMap[i].oldPos.x != this.moveMap[i].newPos.x || this.moveMap[i].oldPos.y != this.moveMap[i].newPos.y)
        return true;
    }
  };
  return false;
};

/*
  Checks whether the game is over or not.
*/
Grid.prototype.gameOver = function() {
  var self = this;
  var adjacent = [];
  var free = this.freePositions();
  var g = true
  if(free.length === 0) {
    //check if there exists two adjacent cells with the same level
    this.eachCell(null, function(x,y,tile) {
      if(tile) {
        adjacent = self.adjacentTiles(tile);
        for (var i = 0; i < adjacent.length; i++) {
          if(adjacent[i].level === tile.level) {
            g = false;
            break;
          };
        };
      };
    });
  } else g = false;
  return g;
};

/*
  Returns an array of adjacent tiles.
*/
Grid.prototype.adjacentTiles = function(tile) {
  var adjacent = [];
  if(this.tiles[tile.x+1]) {
    if (this.tiles[tile.x+1][tile.y])
      adjacent.push(this.tiles[tile.x+1][tile.y]);
  } if (this.tiles[tile.x-1]) {
    if (this.tiles[tile.x-1][tile.y])
      adjacent.push(this.tiles[tile.x-1][tile.y]);
  } if (this.tiles[tile.x][tile.y+1]) {
    adjacent.push(this.tiles[tile.x][tile.y+1]);
  } if (this.tiles[tile.x][tile.y-1]) {
    adjacent.push(this.tiles[tile.x][tile.y-1]);
  };
  return adjacent;
};

/*
  Inserts the movement object in place so that the moveMap is sorted
  from the largest coordinates to the smaller coordinates (from {3,3}
  to {0,0}).
  TODO: might need some refactoring to sort sub-sequences by tile height.
*/
Grid.prototype.addToMoveMap = function(move) {
  var index = move.newPos.x+move.newPos.y*globalGame.gridSize;
  while(this.moveMap[index])
    index--;
  if(index < 0)
    this.moveMap.push(move);
  else this.moveMap[index] = move;
};<|endoftext|>


<commit_before>document.write('\
\
  <meta name="description" content="Hablo de Software, diseño y Anime">\
  <meta name="author" content="Rubén - twitter.com/cont3mpo">\
  <meta name="viewport" content="width=device-width, shrink-to-fit=no, initial-scale=1">\
  <link rel="shortcut icon" type="image/png" href="img/favicon.png">\
  <link rel="preload stylesheet" href="css/normalize.css" as="style">\
  <link rel="preload stylesheet" href="css/style.css" as="style">\
\
');
<commit_msg>Update head.js<commit_after>document.write('\
\
  <meta name="description" content="Hablo de Software, diseño y Anime">\
  <meta name="author" content="Rubén - twitter.com/cont3mpo">\
  <meta name="viewport" content="width=device-width, shrink-to-fit=no, initial-scale=1">\
  <link rel="shortcut icon" type="image/png" href="img/favicon.png">\
  <link rel="preload stylesheet" href="css/style.css" as="style">\
\
');
<|endoftext|>


<commit_before>var app = angular.module('HelpApp', ['ngMaterial']);
// Config
app.config(function ($mdIconProvider) {
    $mdIconProvider.defaultIconSet('/img/mdi.svg');
    console.info('Successfully initialized!');
});
app.controller('HelpController', function ($scope, $mdDialog) {
    console.info('Loaded HelpController!');
    $scope.sendFeedback = function (ev) {
        $mdDialog.show({
            controller: FeedbackController,
            templateUrl: '/templates/feedbackhelp.html',
            parent: angular.element(document.body),
            targetEvent: ev,
            clickOutsideToClose: true
        })
            .then(function (answer) {
                $scope.status = 'Your answer was: "' + answer + '".';
            }, function () {
                $scope.status = 'You cancelled the dialog.';
            });
    };
});
app.controller('SearchController', function ($scope) {
    console.info('Loaded SearchController!');
});
// Sidenav
app.controller('SideNavController', function ($scope, $mdSidenav) {
    console.info('SideNavController successfully loaded!');

    $scope.top = [
        { url: 'https://chan4077.github.io', title: 'Main', icon: 'home' },
        { url: 'https://chan4077.github.io/about', title: 'About Me', icon: 'account-box' },
        { url: 'https://chan4077.github.io/blog', title: 'Blog', icon: 'blogger' },
        { url: 'https://chan4077.github.io/media', title: 'Social Media', icon: 'google-plus' },
        { url: 'https://chan4077.github.io/angular', title: 'Angular', icon: 'angular' },
    ];
    $scope.other = [
        { url: 'https://github.com/Chan4077/chan4077.github.io', title: 'Project Page', icon: 'github-circle' },
        { url: 'https://chan4077.github.io/preferences', title: 'Preferences', icon: 'settings' },
        { url: 'https://chan4077.github.io/help', title: 'Get Help', icon: 'help-circle' },
    ];
    $scope.openLeftMenu = function () {
        $mdSidenav('left').toggle();
    };
})<commit_msg>Add $scope.search<commit_after>var app = angular.module('HelpApp', ['ngMaterial']);
// Config
app.config(function ($mdIconProvider) {
    $mdIconProvider.defaultIconSet('/img/mdi.svg');
    console.info('Successfully initialized!');
});
app.controller('HelpController', function ($scope, $mdDialog) {
    console.info('Loaded HelpController!');
    $scope.sendFeedback = function (ev) {
        $mdDialog.show({
            controller: FeedbackController,
            templateUrl: '/templates/feedbackhelp.html',
            parent: angular.element(document.body),
            targetEvent: ev,
            clickOutsideToClose: true
        })
            .then(function (answer) {
                $scope.status = 'Your answer was: "' + answer + '".';
            }, function () {
                $scope.status = 'You cancelled the dialog.';
            });
    };
});
app.controller('SearchController', function ($scope) {
    console.info('Loaded SearchController!');
    $scope.search = ' ';
});
// Sidenav
app.controller('SideNavController', function ($scope, $mdSidenav) {
    console.info('SideNavController successfully loaded!');

    $scope.top = [
        { url: 'https://chan4077.github.io', title: 'Main', icon: 'home' },
        { url: 'https://chan4077.github.io/about', title: 'About Me', icon: 'account-box' },
        { url: 'https://chan4077.github.io/blog', title: 'Blog', icon: 'blogger' },
        { url: 'https://chan4077.github.io/media', title: 'Social Media', icon: 'google-plus' },
        { url: 'https://chan4077.github.io/angular', title: 'Angular', icon: 'angular' },
    ];
    $scope.other = [
        { url: 'https://github.com/Chan4077/chan4077.github.io', title: 'Project Page', icon: 'github-circle' },
        { url: 'https://chan4077.github.io/preferences', title: 'Preferences', icon: 'settings' },
        { url: 'https://chan4077.github.io/help', title: 'Get Help', icon: 'help-circle' },
    ];
    $scope.openLeftMenu = function () {
        $mdSidenav('left').toggle();
    };
})<|endoftext|>