/**
 * $Id: MetaforgeStrategoAI.java,v 1.6 2007/11/28 07:06:51 chip Exp $
 * $Source: /opt/repository/metaforge/dev/java/webstratego/src/com/metaforge/olg/ws/ai/MetaforgeStrategoAI.java,v $
 * $Revision: 1.6 $
 * $Date: 2007/11/28 07:06:51 $
 */

package com.metaforge.olg.ws.ai;

import java.util.Properties;

/**
 * This class defines the interface by which the Metaforge 'bot' client interacts with a third party
 * developed AI module. All interactions are synchronous, meaning the bot will invoke these methods
 * in a single thread, which will then be the thread of execution within the AI.
 * <p>
 * <table>
 * <tr>
 * <td valign="top"> The expected sequence for a bot creating a game is as follows: <br>
 * <ol>
 * <li>Bot logs in.
 * <li>AI instance created.
 * <li>initialize()
 * <li>setAIUsername()
 * <li>setSkillLevel()
 * <li>createGame(); if yes....
 * <li>getCreateGameMode();
 * <li>For Each Option: getCreateGameOption(option)
 * <li>gameCreated() // Bot creates game.....
 * <li>Someone joins.
 * <li>setOpponentUsername()
 * <li>setGameAids()
 * <li>setMoveTimeLimit()
 * <li>setGameMode()
 * <li>if custom mode -> setCustomPieceCount()
 * <li>For Each Option: setGameOption(option, value)
 * <li>initializeGame();
 * <li>createAISetup();
 * <li>For Each (x,y): getAISetupPiece(x,y)
 * <li>For Each (x,y): setOpponentSetupPiece(x,y)
 * <li>startGame();
 * <li>LOOP: getAIMove(); submitAIMoveResult(); submitOpponentMove();
 * <li>endGame();
 * <li>eventually at logout: terminate()
 * </ol>
 * </td>
 * <td valign="top"> The expected sequence for a bot joining a game is as follows: <br>
 * <ol>
 * <li>Bot logs in.
 * <li>AI instance created.
 * <li>initialize()
 * <li>setAIUsername()
 * <li>setSkillLevel()
 * <li>Someone creates a game
 * <li>newGameCreated(owner, mode); if returns <em>true</em>...
 * <li>For Each Option: newGameOption(option, value)
 * <li>joinGame()? -> if yes.... join game... if that succeeds then
 * <li>gameJoined()
 * <li>setOpponentUsername()
 * <li>setGameAids()
 * <li>setMoveTimeLimit()
 * <li>setGameMode()
 * <li>if custom mode -> setCustomPieceCount()
 * <li>For Each Option: setGameOption(option, value)
 * <li>initializeGame();
 * <li>createAISetup();
 * <li>For Each (x,y): getAISetupPiece(x,y) [illegalSetup() if illegal]
 * <li>For Each (x,y): setOpponentSetupPiece(x,y)
 * <li>startGame();
 * <li>LOOP: submitOpponentMove(); getAIMove(); submitAIMoveResult();
 * <li>endGame();
 * <li>eventually at logout: terminate()
 * </ol>
 * </td>
 * </tr>
 * </table>
 * 
 * @author Vincent, ImerSatz, Chip
 */
public interface MetaforgeStrategoAI {

  /* piece colors */
  /** Plays on top setup zone (0,0) -> (9, 3) & goes first. */
  public static final int COLOR_RED = 0;

  /** Plays on bottom setup zone (0,6) -> (9, 9) & goes second. */
  public static final int COLOR_BLUE = 1;

  /* player turn result */
  
  /** Player rejects draw. */
  public static final int TURN_RESULT_PLAYER_REJECTS_DRAW = -5;
  
  /** Bot times out. */
  public static final int TURN_RESULT_BOT_TIMEDOUT = -4;

  /** Player forfeits. */
  public static final int TURN_RESULT_PLAYER_FORFEITS = -3;

  /** Player loses turn. */
  public static final int TURN_RESULT_PLAYER_LOST_TURN = -2;

  /** Player requests draw. */
  public static final int TURN_RESULT_PLAYER_REQUESTS_DRAW = -1;

  /** Used when no coordinate is needed, such as in draw request or forfeit. */
  public static final int COORDINATE_NOT_APPLICABLE = -1;
  
  /** No battle result from move. */
  public static final int MOVE_RESULT_NONE = 0;

  /** Piece that did not move (defender) is killed. */
  public static final int MOVE_RESULT_KILL = 1;

  /** Piece that moved (attacker) is killed. */
  public static final int MOVE_RESULT_KILLED = 2;

  /** Both attacker and defender are killed. */
  public static final int MOVE_RESULT_BOTH_KILLED = 3;

  public static final int GAME_RESULT_LOSS = 0;

  public static final int GAME_RESULT_WIN = 1;

  public static final int GAME_RESULT_DRAW = 2;

  /** Game is forced to end, perhaps by admin or system error. */
  public static final int GAME_RESULT_FORCED_END = 3;

  /** End for unknown reason. */
  public static final byte GAME_REASON_UNKNOWN = -1;

  /** End by a capturing flag. */
  public static final byte GAME_REASON_FLAG_CAPTURED = 0;

  /** End by player forfeiting. */
  public static final byte GAME_REASON_FORFEIT = 1;

  /** End by opponent disconnecting. */
  public static final byte GAME_REASON_DISCONNECT = 2;

  /** End by unable to move. */
  public static final byte GAME_REASON_UNABLE_TO_MOVE = 3;

  /** End by timing out. */
  public static final byte GAME_REASON_TIMED_OUT = 4;

  /** Rank for Marshal */
  public static final int RANK_MARSHAL = 0;

  /** Rank for General */
  public static final int RANK_GENERAL = 1;

  /** Rank for Colonel */
  public static final int RANK_COLONEL = 2;

  /** Rank for Major */
  public static final int RANK_MAJOR = 3;

  /** Rank for Captain */
  public static final int RANK_CAPTAIN = 4;

  /** Rank for Lieutenant */
  public static final int RANK_LIEUTENANT = 5;

  /** Rank for Sergeant */
  public static final int RANK_SERGEANT = 6;

  /** Rank for Miner */
  public static final int RANK_MINER = 7;

  /** Rank for Scout */
  public static final int RANK_SCOUT = 8;

  /** Rank for Spy */
  public static final int RANK_SPY = 9;

  /** Rank for Bomb */
  public static final int RANK_BOMB = 10;

  /** Rank for Flag */
  public static final int RANK_FLAG = 11;

  /** Rank is unknown. */
  public static final int RANK_UNKNOWN = 12;

  /** There is no piece at (x,y). */
  public static final int RANK_EMPTY = 13;

  /** The square at (x,y) is impassible, such as a lake. */
  public static final int RANK_IMPASSIBLE = 14;

  // game options

  /** One-time bombs bitflag. */
  public static final int GAMEOPT_ONE_TIME_BOMBS = 0x01;

  /** Radius bombs bitflag. */
  public static final int GAMEOPT_RADIUS_BOMBS = 0x02;

  /** Secrecy bitflag. */
  public static final int GAMEOPT_SECRECY = 0x04;

  /** Complete visibility bitflag. */
  public static final int GAMEOPT_COMPLETE_VISIBILITY = 0x08;

  /** Range attack bitflag. */
  public static final int GAMEOPT_RANGE_ATTACK = 0x10;

  /** Economy bitflag. */
  public static final int GAMEOPT_ECONOMY = 0x20;

  /** Movable flag bitflag. */
  public static final int GAMEOPT_MOVABLE_FLAG = 0x40;

  /** Aggression bitflag. */
  public static final int GAMEOPT_AGGRESSION = 0x80;

  /** Super-spy bitflag. */
  public static final int GAMEOPT_SUPER_SPY = 0x100;

  /** Mobility bitflag. */
  public static final int GAMEOPT_MOBILITY = 0x200;

  /** Blitzkrieg bitflag. */
  public static final int GAMEOPT_BLITZKRIEG = 0x400;

  /** Visible flag bitflag. */
  public static final int GAMEOPT_VISIBLE_FLAG = 0x800;

  /** Remain visible bitflag. */
  public static final int GAMEOPT_REMAIN_VISIBLE = 0x1000;

  /** Rescue bitflag. */
  public static final int GAMEOPT_RESCUE = 0x2000;

  /** Probability kill bitflag. */
  public static final int GAMEOPT_PROBABILITY_KILL = 0x4000;

  public static final int GAMEOPT_ARRAY[] =
      { GAMEOPT_ONE_TIME_BOMBS, GAMEOPT_RADIUS_BOMBS, GAMEOPT_SECRECY, GAMEOPT_COMPLETE_VISIBILITY,
          GAMEOPT_RANGE_ATTACK, GAMEOPT_ECONOMY, GAMEOPT_MOVABLE_FLAG, GAMEOPT_AGGRESSION,
          GAMEOPT_SUPER_SPY, GAMEOPT_MOBILITY, GAMEOPT_BLITZKRIEG, GAMEOPT_VISIBLE_FLAG,
          GAMEOPT_REMAIN_VISIBLE, GAMEOPT_RESCUE, GAMEOPT_PROBABILITY_KILL };

  // set game modes

  /** Classic Stratego, played with 40 pieces and standard rules. */
  public static final int GAMEMODE_CLASSIC = 0;

  /** Barrage Stratego, played with 8 pieces and standard rules. */
  public static final int GAMEMODE_BARRAGE = 1;

  /** Ultimate Lightning, played with 20 pieces and modified rules. */
  public static final int GAMEMODE_ULTIMATE_LIGHTNING = 2;

  /** Peloton (Duell) Stratego, played with 10 pieces and standard rules. */
  public static final int GAMEMODE_PELOTON = 3;

  /** RPS Stratego, played with 7s, 8s, and bombs where bombs can move. */
  public static final int GAMEMODE_ROCK_PAPER_SCISSORS = 4;

  /** Custom mode (not yet supported). */
  public static final int GAMEMODE_CUSTOM = 5;

  // special field values for last entry of AI move

  /** No special component to the AI move. */
  public static final byte AI_SPECIAL_MOVE_NOTHING = 0;

  /** AI requests draw. */
  public static final byte AI_SPECIAL_MOVE_REQUEST_DRAW = 1;

  /** AI accepts draw request if one has been offered. */
  public static final byte AI_SPECIAL_MOVE_ACCEPT_DRAW = 2;

  /** AI resigns. */
  public static final byte AI_SPECIAL_MOVE_RESIGN = 3;

  /** AI accepts draw request if one has been offered. */
  public static final byte AI_SPECIAL_MOVE_REJECT_DRAW = 4;

  /** AI passes. This is legal only if the preceding opponent move was a turn timeout. */
  public static final byte AI_SPECIAL_MOVE_PASS_TURN = 5;

  // skill level

  /** Beginner level. */
  public static final int LEVEL_BEGINNER = 0;

  /** Intermediate level. */
  public static final int LEVEL_INTERMEDIATE = 1;

  /** Expert level. */
  public static final int LEVEL_EXPERT = 2;

  /** Tournament (highest) level. */
  public static final int LEVEL_TOURNAMENT = 3;

  /** Constant to denote that an option is off. */
  public static final int OPTION_OFF = 0;

  /** Constant to denote that an option is on. */
  public static final int OPTION_ON = 1;

  /**
   * Called by the bot to tell the AI that it is aborting the game prior to start.
   * 
   * @param reason
   *          the reason for aborting the game.
   */
  public void abortGame(int reason);

  /**
   * Called to initialize the setup process RED sets his pieces on rows 0-3 and BLUE sets his pieces
   * on rows 6-9 RED starts the game.
   * 
   * @param color
   *          the color RED or BLUE which the AI will play as.
   */
  public void createAISetup(int color);

  /**
   * Asks the AI whether it would like to create a game. If so, the AI is then asked for the mode &
   * options of the game.
   * 
   * @return <em>true</em> if AI would like to create a game, <em>false</em> if not.
   */
  public boolean createGame();

  /**
   * Tells the AI that the game it wanted to create has been created.
   */
  public void gameCreated();

  /**
   * Tells the AI that the game it wanted to join has been joined.
   */
  public void gameJoined();

  /**
   * Returns the name by which the AI wishes to be known.
   * 
   * @return the name by which the AI wishes to be known.
   */
  public String getPublicName();

  /**
   * Called for each field of the setup and should return the rank of the piece on field (x,y) in
   * the AI's setup, or EMPTY if no piece.
   * 
   * @param x
   *          x coordinate on board.
   * @param y
   *          y coordinate on board.
   * @return rank of piece located at (x,y) or EMPTY if none.
   */
  public int getAISetupPiece(int x, int y);

  /**
   * Ask the AI what mode game it would like to create.
   * 
   * @return a valid game mode for the game to create.
   * @see #createGame
   * @see #getCreateGameOption
   */
  public int getCreateGameMode();

  /**
   * Ask the AI for the value of the specified option. If the option is one the AI does not
   * recognize, it should return {@link #OPTION_OFF}.
   * 
   * @param option
   *          the option whose value we would like to know.
   * @return AI's desired value for the game option in question.
   * @see #createGame
   * @see #getCreateGameMode
   */
  public int getCreateGameOption(int option);

  /**
   * Asks the AI if it wants to send an in game chat message once the game has entered setup mode.
   * 
   * @return a string to send via public game chat, or null if none.
   */
  public String getPreGameChat();

  /**
   * Asks the AI if it wants to send a public chat message after the game has ended.
   * 
   * @return a string to send via public chat, or null if none.
   */
  public String getPostGameChat();

  /**
   * Tells the AI that the game is over.
   * 
   * @param result
   *          result of the game (WIN, LOSS, DRAW)
   * @param reason
   *          reason for game ending (enumerations to follow... capture flag, resign, can't move,
   *          etc)
   */
  public void endGame(int result, int reason);

  /**
   * Requests the next AI Move. The result is an array with 4 numbers:
   * <ul>
   * <li>result[0] => x coordinate from field</li>
   * <li>result[1] => y coordinate from field</li>
   * <li>result[2] => x coordinate to field</li>
   * <li>result[3] => y coordinate to field</li>
   * <li>result[4] => special (for draw requests or resignations)</li>
   * </ul>
   * 
   * @param move
   *          array passed in by bot which AI populates with its move as specified above.
   */
  public void getAIMove(byte move[]);

  /**
   * Tells AI that the last move submitted was illegal.
   * 
   * @param move
   *          the move array last returned by the AI.
   * @return {@link #AI_SPECIAL_MOVE_RESIGN} if the AI would like to resign. Otherwise the return
   *         value is ignored.
   */
  public byte illegalMove(byte move[]);

  /**
   * Tells AI that the last setup (x,y,rank) returned was illegal. After calling this method, the
   * bot will exit the game.
   * 
   * @param x
   *          x coordinate of last setup piece returned by AI.
   * @param y
   *          y coordinate of last setup piece returned by AI.
   * @param rank
   *          rank of piece from last setup piece returned by AI.
   */
  public void illegalSetup(int x, int y, int rank);

  /**
   * Notifies the AI that it has been constructed and should do any initialization that is required.
   * If the return value is <em>false</em>, the bot will call terminate, logoff of the server,
   * and shutdown the application.
   * 
   * @return <em>true</em> on success, <em>false</em> on failure.
   * @see #terminate
   */
  public boolean initialize(Properties props, String password);

  /**
   * Tells the AI that options are all set and the AI will then be asked for its setup.
   */
  public void initializeGame();

  /**
   * Asks the AI whether it wants to join the most recently described game. This will occur after
   * notification of the game and its options. Note that an AI is expected to not join a game that
   * it is incapable of playing!
   * 
   * @return <em>true</em> if AI wants to join, <em>false</em> if not.
   * @see #newGameCreated
   * @see #newGameOption
   */
  public boolean joinGame();

  /**
   * Tells the AI that a new game has been created. This will be followed by calls to newGameOption
   * and joinGame. AI should return <em>false</em> if it is not interested in joining this game,
   * or <em>true</em> if it wants more information before deciding.
   * 
   * @param owner
   *          owner of the new game.
   * @param mode
   *          mode of the new game.
   * @return <em>false</em> if the AI is not interested in joining, <em>true</em> if the AI
   *         wants more information about the game before deciding.
   * @see #newGameOption
   * @see #joinGame
   */
  public boolean newGameCreated(String owner, int mode);

  /**
   * Tells the AI about the options for the most recently created game. Multiple calls of this
   * method will be made after newGameCreated and before joinGame.
   * 
   * @param option
   *          the option.
   * @param value
   *          its value.
   * @return <em>false</em> if the AI is not interested in joining, <em>true</em> if the AI
   *         wants more information about the game before deciding.
   * @see #newGameCreated
   * @see #joinGame
   */
  public boolean newGameOption(int option, int value);

  /**
   * Called for each field containing an opponent piece after the game completes. This is required
   * for AIs that need complete information regarding original opponent piece ranks in order to
   * create game logs.
   * 
   * @param x
   *          X coordinate
   * @param y
   *          Y coordinate
   * @param rank
   *          rank
   */
  public void postGameLogOriginalOpponentSetup(int x, int y, int rank);

  /**
   * Ends post game logging.
   */
  public void postGameLogClose();

  /**
   * Begins post game logging.
   */
  public void postGameLogStart();

  /**
   * Sets the username which the bot interacting with the AI instance has logged in as.
   * 
   * @param aiUsername
   *          the username which the bot interacting with the AI instance has logged in as.
   */
  public void setAIUsername(String aiUsername);

  /**
   * Tells the AI the username of its opponent, so that if part of the AI's strategy is to keep
   * knowledge of the player in a database, this can be used.
   * 
   * @param opponentName
   *          username of the opponent.
   */
  public void setOpponentUsername(String opponentName);

  /**
   * Turns the game aids option on or off. This may be useful to an AI to tell it whether it can
   * model forgetting which pieces have been moved and/or seen to support a lesser mode of play, and
   * also to let it know if the opponent is going to have the use of game aids.
   */
  public void setGameAids(boolean value);

  /**
   * Sets the value of a game option. This will be called for all options, typically with value of
   * OPTION_OFF indicating option is off, OPTION_ON indicating option is on. Although there is
   * nothing that restricts the theoretical value to these two values. Thus options can be supported
   * which have any integral value.
   * <p>
   * If an AI receives an option it does not recognize, it should return <em>false</em> unless the
   * value is zero. In that case, it should return <em>true</em>.
   * <p>
   * Returning <em>false</em> will cause the game to terminate prior to start, or to not be joined
   * if not already joined.
   * 
   * @param option
   *          option being set.
   * @param value
   *          value of the option.
   * @return whether the option,value pair is accepted.
   */
  public boolean setGameOption(int option, int value);

  /**
   * Sets the skill level of the AI.
   * 
   * @param level
   *          from BEGINNER to TOURNAMENT.
   */
  public boolean setSkillLevel(int level);

  /**
   * Sets thet turn time limit in seconds.
   * 
   * @param turnTimeLimitSecs
   *          the number of seconds the AI will have to return a move once it is asked for its move.
   * @return <em>true</em> if the turn time limit is acceptable, <em>false</em> if not.
   */
  public boolean setMoveTimeLimit(int turnTimeLimitSecs);

  /**
   * Sets the number of pieces for each rank. This is needed only for custom mode games, not for the
   * standard five modes.
   * 
   * @return <em>true</em> if piece count is supported, <em>false</em> if not.
   */
  public boolean setCustomPieceCount(int marshals, int generals, int colonels, int majors,
      int captains, int lieutenants, int sergeants, int miners, int scouts, int spies, int bombs,
      int flags);

  /**
   * Sets the game mode. Returns false if this mode is not accepted.
   * 
   * @param mode
   *          the mode of the game.
   */
  public boolean setGameMode(int mode);

  //
  // Setup methods: startSetup(c) FOR EACH SETUP FIELD getSetupPiece(x,y) END
  // FOR EACH FOR EACH OPPONENT FIELD setSetupPiece(x,y) END FOR EACH
  //

  /**
   * Called for each field containing an opponent piece this is necessary for game modes with less
   * than 40 pieces or custom setup areas.
   * 
   * @param x
   *          x coordinate on board.
   * @param y
   *          y coordinate on board.
   */
  public void setOpponentSetupPiece(int x, int y);

  /**
   * Informs the AI that both setups have been received, are valid, and the game has now begun.
   */
  public void startGame();

  //
  // Moving methods: LOOP submitOpponentMove(x,y,r) getAIMove()
  // submitAIMoveResult(r) END LOOP
  //

  /**
   * Submits the move of the opponent and if the move is an attack, also the rank of the attacking
   * piece. If the move is not attacking, then rank is UNKNOWN. Also includes the result of any
   * attack. If the opponent move is a draw request, then the return value of this method has
   * meaning: a 1 indicates accept draw, a 0 indicates reject draw.
   * 
   * @param xo
   *          starting x coordinate of opponent piece.
   * @param yo
   *          starting y coordinate of opponent piece.
   * @param xf
   *          final x coordinate of opponent piece.
   * @param yf
   *          final y coordinate of opponent piece.
   * @param rank
   *          rank of opponent piece, if revealed by move, or UNKNOWN.
   * @param result
   *          the result of the move for the opponent (KILL, KILLED, BOTH_KILLED, NONE,
   *          TURN_RESULT_PLAYER_REQUESTS_DRAW, TURN_RESULT_PLAYER_LOST_TURN)
   * @return {@link #AI_SPECIAL_MOVE_RESIGN} if the AI would like to resign. Otherwise the return
   *         value is ignored.
   */
  public byte submitOpponentMove(int xo, int yo, int xf, int yf, int rank, int result);

  /**
   * Submits the result of an AI move back to the AI. This validates that the move was accepted as
   * legal. If the move was an attack, the parameters will describe the result of the attack. If
   * not, the parameters will be set to empty/default values such as RANK_UNKNOWN and
   * MOVE_RESULT_NONE.
   * 
   * @param rank
   *          rank of the defending piece if AI move is an attack.
   * @param result
   *          result of the move if AI move is an attack.
   * @return {@link #AI_SPECIAL_MOVE_RESIGN} if the AI would like to resign. Otherwise the return
   *         value is ignored.
   */
  public byte submitAIMoveResult(int rank, int result);

  /**
   * Tells the AI that it should shutdown permanently. This allows a final chance to release any
   * resources the AI may still hold.
   */
  public void terminate();
}
