 
						 
						 
						
							Battleship Web Game themed as Easter Egg Hunt. The goal of the game is to find all the missing easter eggs. This game can be played with a friend.
							
							The main challenge of the development process relied on taking responsibility over the art and audio department at the same time I developed all game functionalities.
						
/*
* Carlos Adan Cortes De la Fuente
* All rights reserved. Copyright (c)
* Email: dev@carlosadan.com
*/
'use strict';
// Controls the size of the grid
const GRID = {
	ROWS :    10,
	COLUMNS : 10
};
// It is going to tell how to draw the board everytime it need to be updated
const CELL_STATUS = {
	EMPTY :      0,
	HIDDEN_EGG : 1,
	FOUND_EGG :  2,
	MISSING :    3
};
// Represents the amount of eggs in each group everytime one is created
const EGG_TYPE = {
	TINY :   2,
	SMALL :  3,
	MEDIUM : 4,
	LARGE :  5
};
// Possible egg group directions
const DIRECTION = {
	UP :    0,
	DOWN :  1,
	LEFT :  2,
	RIGHT : 3,
};
class Grid {
	constructor() {
		this.resetGrid();
	}
	// Creates an empty grid
	resetGrid() {
		this.matrix = [];
		this.totalEggs = 0;
		this.eggsFound = 0;
		for(let i = 0; i < GRID.ROWS; i++) {
			this.matrix[i] = [];
			for (let j = 0; j < GRID.COLUMNS; j++) {
				this.matrix[i][j] = CELL_STATUS.EMPTY;
			}
		}
	}
	// Places the eggs randomly inside the grid
	placeEggsRandomly(eggBasket) {
		for (let eggType in eggBasket) {
			let eggGroups = eggBasket[eggType];
			// Generates the possible locations for every egg group in the egg basket
			// And places the egg in the matrix
			for (let i = 0; i < eggGroups; i++) {
				let coordinate = this.generateRandomCoordinate();
				let possibleDirections = this.getPossibleDirections(coordinate, eggType);
				// Tries to get a valid placement for the egg type
				while (!this.isCellEmpty(coordinate.row, coordinate.column) || possibleDirections.length == 0) {
					coordinate = this.generateRandomCoordinate();
					possibleDirections = this.getPossibleDirections(coordinate, eggType);
				}
				// Gets a random direction to populate the matrix
				let direction = this.chooseRandomDirection(possibleDirections);
				this.hideEggGroup(coordinate, eggType, direction);
			}
		}
	}
	// Gets a pair of random coordinates between the grid bounds
	generateRandomCoordinate() {
		return {
			row : Math.floor(Math.random() * GRID.ROWS),
			column : Math.floor(Math.random() * GRID.COLUMNS)
		};
	}
	// Returns a random direction value depending on the possible directions provided
	chooseRandomDirection(possibleDirections) {
		// Checks if there is only one direction to return
		if(possibleDirections.length == 1) {
			return possibleDirections[0];
		} else {
			let dir = possibleDirections[Math.floor(Math.random() * possibleDirections.length)];
			return possibleDirections[Math.floor(Math.random() * possibleDirections.length)];
		}
	}
	// Hide Egg
	hideEgg(row, column) {
		this.matrix[row][column] = CELL_STATUS.HIDDEN_EGG;
		this.totalEggs++;
	}
	// Hides the egg over a specific direction
	hideEggGroup(initialCoordinate, eggType, direction) {
		switch (DIRECTION[direction]) {
			case DIRECTION.UP:
				for (let i = 0; i < EGG_TYPE[eggType]; i++) {
					this.hideEgg(initialCoordinate.row - i, initialCoordinate.column);
				}
				break;
			case DIRECTION.DOWN:
				for (let i = 0; i < EGG_TYPE[eggType]; i++) {
					this.hideEgg(initialCoordinate.row + i, initialCoordinate.column);
				}
				break;
			case DIRECTION.LEFT:
				for (let i = 0; i < EGG_TYPE[eggType]; i++) {
					this.hideEgg(initialCoordinate.row, initialCoordinate.column - i);
				}
				break;
			case DIRECTION.RIGHT:
				for (let i = 0; i < EGG_TYPE[eggType]; i++) {
					this.hideEgg(initialCoordinate.row, initialCoordinate.column + i);
				}
				break;
		}
	}
	// Returns the possible directions that the eggGoup can be placed depending on the coordinate
	getPossibleDirections(coordinate, eggType) {
		let possibleDirections = [];
		for (let direction in DIRECTION) {
			switch(DIRECTION[direction]) {
				case DIRECTION.UP:
					if (this.isUpPossible(coordinate, EGG_TYPE[eggType])) {
						possibleDirections.push(direction);
					}
					break;
				case DIRECTION.DOWN:
					if (this.isDownPossible(coordinate, EGG_TYPE[eggType])) {
						possibleDirections.push(direction);
					}
					break;
				case DIRECTION.LEFT:
					if (this.isLeftPossible(coordinate, EGG_TYPE[eggType])) {
						possibleDirections.push(direction);
					}
					break;
				case DIRECTION.RIGHT:
					if (this.isRightPossible(coordinate, EGG_TYPE[eggType])) {
						possibleDirections.push(direction);
					}
					break;
			}
		}
		return possibleDirections;
	}
	// Checks from the given coordinate to go up placing the egg
	isUpPossible(coordinate, eggsToPlace) {
		let validDirection = false;
		// Checks if the row is too close to the upper boundary
		if (coordinate.row - (eggsToPlace - 1) >= 0) {
			validDirection = true;
			// Checks if the inbetween cells are empty
			for (let i = coordinate.row - 1; i > (coordinate.row - eggsToPlace); i--) {
				if (!this.isCellEmpty(i, coordinate.column)) {
					validDirection = false;
					break;
				}
			}
		}
		return validDirection;
	}
	// Checks from the given coordinate to go down placing the egg
	isDownPossible(coordinate, eggsToPlace) {
		let validDirection = false;
		// Checks if the row is too close to the lower boundary
		if (coordinate.row + (eggsToPlace - 1) < GRID.ROWS) {
			validDirection = true;
			// Checks if the inbetween cells are empty
			for (let i = coordinate.row + 1; i < (coordinate.row + eggsToPlace); i++) {
				if (!this.isCellEmpty(i, coordinate.column)) {
					validDirection = false;
					break;
				}
			}
		}
		return validDirection;
	}
	// Checks from the given coordinate to go left placing the egg
	isLeftPossible(coordinate, eggsToPlace) {
		let validDirection = false;
		// Checks if the column is too close to the left boundary
		if (coordinate.column - (eggsToPlace - 1) >= 0) {
			validDirection = true;
			// Checks if the inbetween cells are empty
			for (let i = (coordinate.column - 1); i > (coordinate.column - eggsToPlace); i--) {
				if (!this.isCellEmpty(coordinate.row, i)) {
					validDirection = false;
					break;
				}
			}
		}
		return validDirection;
	}
	// Checks from the given coordinate to go right placing the egg
	isRightPossible(coordinate, eggsToPlace) {
		let validDirection = false;
		// Checks if the column is too close to the right boundary
		if (coordinate.column + (eggsToPlace - 1) < GRID.COLUMNS) {
			validDirection = true;
			// Checks if the inbetween cells are empty
			for (let i = coordinate.column + 1; i < (coordinate.column + eggsToPlace); i++) {
				if (!this.isCellEmpty(coordinate.row, i)) {
					validDirection = false;
					break;
				}
			}
		}
		return validDirection;
	}
	// Returns if the coordinate is empty
	isCellEmpty(row, column) {
		return (this.matrix[row][column] === CELL_STATUS.EMPTY);
	}
	// Let the user know the selection of the cell is valid
	isValidGuessSelection(coordinate) {
		// Checks if the selected coordinate is a valid guess
		return (
			coordinate.row >= 0 &&
			coordinate.row < GRID.ROWS &&
			coordinate.column >= 0 &&
			coordinate.column < GRID.COLUMNS &&
			this.matrix[coordinate.row][coordinate.column] != CELL_STATUS.FOUND_EGG &&
			this.matrix[coordinate.row][coordinate.column] != CELL_STATUS.MISSING)
	}
	// Checks if there's an egg in the cell that the player selected
	isThereAnEgg(coordinate) {
		if(this.matrix[coordinate.row][coordinate.column] == CELL_STATUS.HIDDEN_EGG) {
			this.matrix[coordinate.row][coordinate.column] = CELL_STATUS.FOUND_EGG;
			this.eggsFound++;
			return true;
		} else if(this.matrix[coordinate.row][coordinate.column] == CELL_STATUS.EMPTY) {
			this.matrix[coordinate.row][coordinate.column] = CELL_STATUS.MISSING;
			return false;
		}
	}
}