Package scotlandyard.engine.impl

Source Code of scotlandyard.engine.impl.Game

package scotlandyard.engine.impl;

import java.util.ArrayList;
import java.util.List;

import scotlandyard.engine.spec.IBMap;
import scotlandyard.engine.spec.IGame;
import scotlandyard.engine.spec.IPlayer;
import scotlandyard.engine.spec.IUser;

/**
* game object implements IGame
*
* @author simon
* @version 3.0
*/
public class Game implements IGame {
  private String id;
  private BMap map;
  private String mapPath;
  private List<IPlayer> players = new ArrayList<IPlayer>();
  private int round = 0;
  private char status = NEW;
  private String whoWon = "false";
  private volatile int turn = -1;

  /**
   * returns a string representing the winner
   * @return string for who won the game
   */
  public String getWhoWon()
  {
    return whoWon;
  }

  /**
   * set who won the game
   */
  public void setWhoWon(String winner)
  {
    whoWon = winner;
  }
 
 
  /**
   * constructs an empty game object
   */
  public Game() {
  }

  /**
   * constructs a game object with an id
   */
  public Game(String id) {
    this.id = id;
  }

  /**
   * construct a game object with an id and a xml mapfile
   * @param id
   * @param mapFile
   * @throws Exception
   */
  public Game(String id, String mapFile) throws Exception {
    this(id);
    this.map = new BMap(mapFile);
  }

  /**
   * looks at an array to see if it contains an item
   * @param array array to test
   * @param item item to look for
   * @return true if the item is contained in the array
   */
  public static boolean contains(Integer[] array, Integer item) {
    for (int i = 0; i < array.length; i++) {
      if (array[i].equals(item)) {
        return true;
      }
    }
    return false;
  }

  /**
   * gets the game status of the current game
   * @param _status
   * @return string representation of the constant
   */
  public static String getStatusDefinition(final char _status) {
    final String result;
    switch (_status) {
    case NEW:
      result = "NEW";
      break;
    case STARTED:
      result = "STARTED";
      break;
    case FINISHED:
      result = "FINISHED";
      break;
    default:
      result = "UNKNOWN";
      break;
    }
    return result;
  }


  /**
   * adds a player to the game
   * @return boolean true if it worked false other wise
   */
  @Override
  public boolean addPlayer(final IUser user, final boolean mrx)
      throws Exception {
    if (mrx && getMrX() != null) {
      throw new Exception("This game already has Mr.X");
    }
    if (this.status != NEW) {
      throw new Exception(
          "This game has already been started, create another game");
    }
    if (getPlayerIndex(user.getEmail()) > -1) {
      throw new Exception("There is already a player with the email ["
          + user.getEmail() + "] in this game");
    }
    final Player p = new Player(user, mrx);
    p.icon = "d.png";
    // initializeTickets(p);
    // p.position = map.getEmptyPosition();
    // map.positions[p.position]=p.email;
    if (mrx) {
      int c = this.players.size();
      p.icon="x.png";
      this.players.add(0, p);
      return this.players.size() == c + 1;
    }
    return this.players.add(p);
  }

 
 
  /**
   * test if player can move or not
   * @param player
   * @return true if player can move, else false
   */
  public boolean canPlayerMove(IPlayer player) {
    if (contains(this.getLegalMoves(player.getEmail()), 1)) {
      return true;
    }
    if (contains(this.getLegalMoves(player.getEmail()), 2)) {
      return true;
    }
    if (contains(this.getLegalMoves(player.getEmail()), 4)) {
      return true;
    }
    if (contains(this.getLegalMoves(player.getEmail()), 8)) {
      return true;
    }
    if (contains(this.getLegalMoves(player.getEmail()), 16)) {
      return true;
    }
    return false;
  }

 
  /**
   * converts a integer into a binary number
   * @return int[] the number as a binary
   */
  @Override
  public int[] decomposeBinary(int binary) {
    int[] result = new int[5];

    result[4] = (binary >= 16) ? 16 : 0;
    result[3] = (binary >= 8) ? 8 : 0;
    result[2] = (binary >= 4) ? 4 : 0;
    result[1] = (binary >= 2) ? 2 : 0;
    result[0] = (binary >= 1) ? 1 : 0;

    return result;
  }

  /**
   * get whos turn it is where the int represents
   * an index in player array
   * @return int
   */
  @Override
  public synchronized int getCurrentTurn() {
    return turn;
  }

  /**
   * get game id
   * @return String id of game
   */
  @Override
  public String getId() {
    return id;
  }

  /**
   * gets all the legal moves a player can make based on there tickets, position of all players
   * @return Integer[] representing legal moves with numbers representing different transport types and combinations of transports
   */
  @Override
  public Integer[] getLegalMoves(String email) {

    if (!getWhosTurn().equals(email) || this.getStatus() == FINISHED) {
      return new Integer[] {};
    }

    int p = getPlayerIndex(email);
    final IPlayer player = this.players.get(p);
    int[] result = map.getPossibleMoves(player.getPosition());
    final Integer[] copy = new Integer[result.length];

    // used for bitwise comparison, as tickets are encoded in binary
    // BUS : 0001
    // player has : 0110
    // result : 0000
    final boolean no_bus = player.getTickets(IBMap.BUS) < 1;
    final boolean no_taxi = player.getTickets(IBMap.TAXI) < 1;
    final boolean no_water = player.getTickets(IBMap.WATER) < 1;
    final boolean no_ug = player.getTickets(IBMap.UG) < 1;
    final boolean no_double = player.getTickets(IBMap.DOUBLE) < 1;

    // first get all possible moves
    // then filter them according to the number of tickets remaining
    for (int i = 0; i < result.length; i++) {
      // if the position is empty, or occupied by Mr X, then its OK
      copy[i] = 0;
      if (map.positions[i] == null
          || this.getMrX().getEmail().equals(map.positions[i])) {
        copy[i] = result[i];
        int temp = copy[i]; // need temp variable to stop transport
                  // types matching incorrectly

        // double
        if (temp >= 16) { // this condition is not required
          temp -= 16;
          if (no_double) {
            copy[i] -= 16;// but maybe used later, so will leave it
                    // now
          }
        }

        // UG
        if (temp >= 8) {
          temp -= 8;
          if (no_ug) {
            copy[i] -= 8;
          }
        }

        // water
        if (temp >= 4) {
          temp -= 4;
          if (no_water) {
            copy[i] -= 4;
          }
        }

        // taxi
        if (temp >= 2) {
          temp -= 2;
          if (no_taxi) {
            copy[i] -= 2;
          }
        }

        // bus
        if (temp >= 1) {
          temp -= 1;
          if (no_bus) {
            copy[i] -= 1;
          }
        }
      }
    }
    if (player.isMrx() && !no_double) {
      for (int i = 0; i < result.length; i++) {
        if (result[i] != 0) // meaning that there is a connection
        {
          int[] resultTwo = map.getPossibleMoves(i);
          for (int j = 0; j < resultTwo.length; j++) {
            if (resultTwo[j] != 0 && map.positions[j] == null) {
              if (copy[j] < 16) {
                copy[j] += 16;
              }
            }
          }
        }
      }
    }

    return copy;
  }

  /**
   * gets the map being used for this game
   * @return map
   */
  @Override
  public IBMap getMap() {
    return map;
  }

  /**
   * gets the path to the map being used for this game
   * @return String
   */
  public String getMapPath() {
    return mapPath;
  }

  /**
   * gets the player object representing mrx for this game
   * @return IPlayer
   */
  @Override
  public IPlayer getMrX() {
    for (final IPlayer p : players) {
      if (p.isMrx()) {
        return p;
      }
    }
    return null;
  }

  /**
   * get the player with the provided hash
   * @return IPlayer the requested player
   */
  @Override
  public IPlayer getPlayer(final String hash) {
    for (IPlayer player : players) {
      if (player.getHash().equals(hash)) {
        return player;
      }
    }
    return null;
  }

  /**
   * get the player index for the player with this email
   * @return int the players index
   */
  @Override
  public int getPlayerIndex(String email) {
    for (int i = 0; i < players.size(); i++) {
      if (email.equals(this.players.get(i).getEmail())) {
        return i;
      }
    }
    return -1;
  }

  /**
   * get a list of all players in this game
   * @return List<IPlayer> all players
   */
  @Override
  public List<IPlayer> getPlayers() {
    return this.players;
  }

  /**
   * get the round number
   * @return int
   */
  @Override
  public int getRound() {
    return round;
  }

  /**
   * get the game status
   * @return char
   */
  @Override
  public char getStatus() {
    return status;
  }

  /**
   * get the email of the player who is currently having his turn
   * @return String
   */
  @Override
  public String getWhosTurn() {
    return this.players.get(turn).getEmail();
  }

  /**
   * tells if this method has the provided user or not
   * @return boolean true if player is in game false other wise
   */
  @Override
  public boolean hasPlayer(final IUser user) {
    for (IPlayer p : this.players) {
      if (p.getHash().equals(user.getHash())) {
        return true;
      }
    }
    return false;
  }

  /**
   * initialise tickets for game use
   * sets tickets with the game rule
   * @param p
   */
  private void initializeTickets(final IPlayer p) {
    final boolean mrx = p.isMrx();
    p.setTickets(IBMap.TAXI, mrx ? MRX_TAXI_TICKETS
        : DETECTIVE_TAXI_TICKETS);
    p.setTickets(IBMap.BUS, mrx ? MRX_BUS_TICKETS : DETECTIVE_BUS_TICKETS);
    p.setTickets(IBMap.UG, mrx ? MRX_UG_TICKETS : DETECTIVE_UG_TICKETS);
    p.setTickets(IBMap.WATER, mrx ? MRX_WATER_TICKETS
        : DETECTIVE_WATER_TICKETS);
    p.setTickets(IBMap.DOUBLE, mrx ? MRX_DOUBLE_TICKETS : 0);
  }

  /**
   * rounds 3,8,13,18 are those where mrx will expose himself to others
   * @param round : the round number
   * @return true if the round shall expose mrx
   */
  @Override
  public boolean isRoundExposingMrX(int round) {
    return round == 3 || round == 8 || round == 13 || round == 18
        || this.getStatus() == FINISHED;
  }

  /**
   * move player from the current position to another with specify transport type
   * @param email - player identifier
   * @param newPosition - position node that this move will take the player to
   * @param transport - transport type the player use for this move
   * @throws Exception
   */
  @Override
  public void movePlayer(String email, int newPosition, int transport)
      throws Exception {
    final String curTurn = getWhosTurn();
    if (!curTurn.equals(email)) {
      throw new Exception("It is " + curTurn + " turn now");
    }

    final int p = getPlayerIndex(email);
    final IPlayer player = this.players.get(p);
    final int current = player.getPosition();

    if (!this.getMrX().getEmail().equals(email) && this.getMrX().getEmail().equals(this.map.positions[newPosition])) {
      this.setWhoWon(player.getName() + " Has Caught Mrx ");
      this.status = FINISHED;
    }else{

      if (this.map.positions[newPosition] != null) {
        throw new Exception("This position is already occupied by ["
            + map.positions[newPosition] + "]");
      }


      if (this.getLegalMoves(email)[newPosition] == 0) {
        throw new Exception("node " + current + " and " + newPosition
            + " are not connected");
      }

      if (player.getTickets(transport) < 1) {
        throw new Exception("not enough tickets to move from " + current
            + " to " + newPosition);
      }
    }


    // update player last activity
    player.updateLastActivity();

    // decrement player's tickets
    player.consumeTicket(transport);

    // make player place empty
    map.positions[player.getPosition()] = null;

    // move to new position
    player.setPosition(newPosition);

    // make player new place on the map
    map.positions[player.getPosition()] = player.getEmail();

    // if the player is not mrx then increment mrx's tokens
    if (!player.isMrx()) {
      this.getMrX().setTickets(transport,
          (this.getMrX().getTickets(transport) + 1));
    }

    // increment the round when MrX plays
    if (turn == 0) {
      round++;
      this.mrxMadeAMove();
    }

    // increment turn
    turn = (turn + 1) % this.players.size();
    if(round > 22)
    {
      this.setWhoWon("Mr X has won the Game");
      this.status = FINISHED;
    }
  }

  /**
   * this method should be declared as abstract, bit we don't want the class
   * to be declared as abstract because of one method. this method is
   * triggered when Mr X makes a move
   */
  @Override
  public void mrxMadeAMove() {
  }

  /**
   * removes player who leaves the game while the game is running
   * @param email
   */
  @Override
  public void removePlayer(String email) {
    final int p = getPlayerIndex(email);
    if (p > -1) {
      this.players.remove(p);
    }
  }

  /**
   * sets the game identifier
   * @param id - this will be the name that the user use for the game when creating the game
   */
  @Override
  public void setId(String id) {
    this.id = id;
  }

  /**
   * sets the map that user selected when creating the game
   * @param map - "palmerston north" or "auckland"
   */
  @Override
  public void setMap(final IBMap map) {
    this.map = (BMap) map;
  }

  /**
   * sets map path with xml document
   * @param mapPath
   */
  @Override
  public void setMapPath(String mapPath) {
    this.mapPath = mapPath;
  }

  /**
   * start the game, change game status to running, initialise players and tokens
   * @param player
   * @throws Exception
   */
  @Override
  public void start(IPlayer player) throws Exception {
    if (!this.players.contains(player)) {
      throw new Exception(
          "You can not start this game unless you join it");
    }
    if (this.players.size() < 2 || this.getMrX() == null) {
      throw new Exception(
          "This game can be started given that it has at least two players, and one of them must be MrX");
    }
    if (this.status != NEW) {
      throw new Exception("This game has already been started");
    }
    this.turn = 0;
    player.updateLastActivity();
    for (IPlayer p : this.players) {
      initializeTickets(p);
      p.setPosition(map.getEmptyPosition());
      map.positions[p.getPosition()] = p.getEmail();
    }
    this.status = STARTED;
  }
}
TOP

Related Classes of scotlandyard.engine.impl.Game

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.