Source: game.js

var c = require("./ttConstants");
var ManualTurn = require("./manual_turn.js");
var Component = require("./component.js");
var inherits = require('util').inherits;

/**
 * The Game class
 * @constructor
 * @extends {Component}
 * @param {Player|Array} players - A list of players
 * @param {Board} board - The game board
*/
function Game(players, board) {
  Component.call(this);
  this.players = players,
  this.board = board;
  this.dice = [];
  this.randomizeCurrentPlayer();
  this.turnMap = null;
  this.moveType = c.moveTypeDice; // manual movement or dicerolls
  this.proposedMove = {}; // for c.moveTypeManual
  this.moveEvaluationType = c.moveEvaluationTypeLandingAction;
};

inherits(Game, Component);

/**
 * Method to set turnMap of the game once it is created
 * This is required!
 * @param {Turn} turnMap - A turn object to be used by the game
*/
Game.prototype.setTurn = function(turnMap) {
  this.turnMap = turnMap;
};

/**
 * Method to call from the view to update the game state
 * @param {string} message - A string saying which state to transition to
*/
Game.prototype.updateState = function(message) {
  this.turnMap.updateState(message, this);
};

/**
 * Set the game's state machine
 * @param {TurnMap} turnMap - the game's state machine
*/
Game.prototype.setTurnMap = function(turnMap) { 
  this.turnMap = turnMap;
};

/**
 * Set the move type for this game
 * @param {string} moveType - The move type. See ttConstants
*/
Game.prototype.setMoveType = function(moveType) { 
  this.moveType = moveType;
  if (moveType == c.moveTypeManual) { 
    this.proposedMove = {};
  } 
};

/**
 * Callback method from the view when a token is clicked
 * To be overridden by the subclass
 * @abstract
 * @parram {Token} token - The token object that was clicked
*/
Game.prototype.tokenClicked = function(token) {
  throw new Error('must be implemented by subclass!');
};

/**
 * Callback method from the view when a tile is clicked
 * To be overridden by the subclass
 * @abstract
 * @param {Tile} tile - the tile object that was clicked in the view
*/
Game.prototype.tileClicked = function(tile) {
  throw new Error('must be implemented by subclass!');
};

/**
 * Check the Game State to see if a player has won the game
 * @abstract
 * @return {boolean}
*/
Game.prototype.isGameOver = function() {
  // TODO - check this method every transition in the state machine
  throw new Error('must be implemented by subclass!');
};

/**
 * Set the current player to a random player
 * Used to decide who goes first at the beginning of the game
*/
Game.prototype.randomizeCurrentPlayer = function() {
  this.currentPlayer = Math.floor(Math.random() * this.players.length);
};

/**
 * Roll the dice
 * Dice are represented as an array of ints stored in the state
 * @param {int} numberOfDice - The number of dice to roll
 * @param {int} sides - How many sides should be on the dice (defaults to 6)
*/
Game.prototype.rollDice = function(numberOfDice, sides) {
  if (!sides) {
    sides = 6;
  }
  this.dice = [];
  var message = "You rolled a ";
  for (var i = 0; i < numberOfDice; i++) {
    var roll = Math.floor(Math.random() * sides) + 1;
    this.dice.push(roll);
    message = message.concat(roll + ", ");
  }

  this.sendMessage(message);
};

/**
 * Checks to see if the first two dice are the same value
 * @returns {Boolean}
*/
Game.prototype.isDoubles = function(dice) {
  return dice.length > 1 && dice[0] === dice[1];
};

/**
 * Returns the current player
 * @returns {Player}
*/
Game.prototype.getCurrentPlayer = function() {
  return this.players[this.currentPlayer];
};

/**
 * Sets the last moved token for later reference
 * @param {Token} token - Last moved token
 * @returns {void}
*/
Game.prototype.submitMove = function(token) { 
  this.lastMovedToken = token;
};

/**
 * Switch to the next player
 * Override to provide more logic on determining the next player
 * @returns {void}
*/
Game.prototype.nextPlayer = function() { 
  this.currentPlayer = (this.currentPlayer + 1) % this.players.length;
};

/**
 * Set the destination for a proposed move
 * @param {Tile} tile - the tile to move to
 * @returns {void}
*/
Game.prototype.setProposedMoveDestination = function(tile) { 
  this.proposedMove.destination = tile;
};

/**
 * Set the token for a proposed move
 * @param {Token} token - the token to move
 * @returns {void}
*/
Game.prototype.setProposedMoveToken = function(token) { 
  this.proposedMove.token = token;
};

/**
 * A proposed move exists and it is valid
 * @returns {boolean}
*/
Game.prototype.hasValidMove = function() { 
  
  if (this.moveType == c.moveTypeManual &&  (!this.proposedMove.token || !this.proposedMove.destination)) {
    return false;
  }
  else if (this.moveType == c.moveTypePlaceToken && !this.proposedMove.destination) {
    return false;
  } 

  var token = this.proposedMove.token;
  var tile = token ? token.tile : null;
  var destination = this.proposedMove.destination;

  return this.isValidMove(token, 
                          tile, 
                          destination);
}; 

/**
 * Determines if it is valid to move the given token to the new tile
 * @param {Token} token - token to place or move
 * @param {Tile} oldTile - the previous token location (could be null if token not on board yet)
 * @param {Tile} newTile - the new token location
 * @abstract 
 * @returns {boolean}
*/
Game.prototype.isValidMove = function(token, oldTile, newTile) { 
  console.warn("isValidMove should be implemented by the subclass");
  return true;
};

/**
 * Evaluates the current state of the game and determines if a player won
 * @param {Player} player - the current player
 * @abstract
 * @returns {boolean}
*/
Game.prototype.playerDidWin = function(player) {
  console.warn("playerDidWin should be implemented by the subclass");
  return false;
};

/** 
 * Execute the proposed move made by the player
 * @param {Player} player - the current player
 * @abstract
 * @returns {void}
*/
Game.prototype.executeMove = function(player) {
  throw new Error('executeMove must be implemented by the subclass!');
};

/**
 * Event for when a token is clicked on
 * @param {Token} token - the token that was clicked
 * @returns {void}
*/
Game.prototype.tokenClicked = function(token) { 
  if (this.moveType == c.moveTypeManual &&
      this.turnMap.getCurrentState() == "waitingForMove") { 
    this.setProposedMoveToken(token);
  }
};

/**
 * Event for when a tile is clicked on
 * @param {Tile} tile - the tile that was clicked
 * @returns {void}
*/
Game.prototype.tileClicked = function(tile) { 
  /* make sure we're in the right state, 
   a token has been pressed, 
   and we're not a tile with a token on it (if we have > 0
   children, then this click was meant for a token... */
  if (this.moveType == c.moveTypeManual && 
      this.turnMap.getCurrentState() == "waitingForMove" && 
      this.proposedMove.token && 
      this.proposedMove.token.tile != tile) { 

    this.setProposedMoveDestination(tile);
    this.turnMap.updateState("makeMove");

  } else if (this.moveType == c.moveTypePlaceToken &&
      this.turnMap.getCurrentState() == "waitingForMove") {

    this.setProposedMoveDestination(tile);
    this.turnMap.updateState("makeMove");
  }
};


module.exports = Game;