// $Id: tic_board.C,v 1.5 1995/12/01 05:10:14 hartmans Exp $
#include <game.h>
#include <move.h>
#include <ps5/error_response.h>
#include "tic_board.h"
#include <player.h>

//CLASS tic_move BEGINS
class tic_move: public move {
// Overview:  This abstraction, a subclass of the generic move, represents a
// player 'move' in the game of tic-tac-toe.  A move in the game of tic-tac-toe
// is the placement of either an X or an O in one of the 9 squares that make
// up the playing field.

// Abstraction Fn: tpiece is the tic_board::tic_piece placed (only X and O
// are valid; a player may not place the empty piece); txpos and typos are
// the X and Y coordinates of the placed piece. ([0,0] is the upper-left
// square of the playing board.)

// Rep Invariant:  this must be a valid tic_move as defined by the abstraction
// fn; that is tpiece must be an x_piece or o_piece, and txpos and typos must
// be valid board coordinates. 
public:
  tic_move(tic_board::tic_piece cpiece, int xpos, int ypos) {
  // Overview: The move subclass-specific constructor.
  // Checks: cpiece is an X or an O, 0 < xpos, ypos <= 2
  // Effects: Creates a move corresponding to placing cpiece at xpos, ypos.
    check((0 <= xpos) && (xpos <=2), "tic_move: Invalid xpos!");
    check((0 <= ypos) && (ypos <=2), "tic_move: Invalid ypos!");
    check((cpiece == tic_board::x_piece) || (cpiece == tic_board::o_piece), 
	  "tic_move: Invalid piece!");  // Placing a blank piece isn't a move
    tpiece = cpiece;
    txpos  = xpos;
    typos  = ypos;    // Uh, that's t-ypos, not typos.
  }   

  tic_move(const tic_move &m) {
  // Overview: The tic_move copy constructor. Creates an independent duplicate
  // of m.
    tpiece = m.tpiece;
    txpos = m.txpos;
    typos = m.typos; 
  }    // Copy move. I hope.

  virtual ~tic_move(){};
  
    virtual void display(extensions *, ostream& body) {
  // Effects: Sends a move representation to the ostream body.
    body << "put a " << tic_board::piece_to_name(tpiece) << " at ["
      << txpos << "," << typos << "]"; }

  virtual bool operator==(move &m) {
  // Overview: virtual move equality. If m's subclass is that of this, then
  // compares rep, otherwise returns false.
  // Effects: Returns true if the moves are identical, false otherwise.
    tic_move* ptr = (tic_move*)m.force(tic_move::typeinfo);
    if (!ptr) return false;               // They're not even the same type...
    return ((tpiece == ptr->tpiece) && (txpos == ptr->txpos) && 
	    (typos == ptr->typos));
  }          

  virtual move* copy() {
  // Overview: Dups a tic_move. This allows the copy method to work in the
  // virtual class move.
  // Effects: Returns a heap copy of this move.
    return new tic_move(*this); }

  virtual void* force(typeinfo_t &type) {
    if (type == typeinfo) return this;
    else return 0; }

  tic_move& operator=(const tic_move& m) {
  // Modifies: this
  // Effects: this becomes m
    tpiece = m.tpiece;
    txpos = m.txpos;
    typos = m.typos; 
    return *this; }

  // Overview: Public methods to retrieve tic_move specific information.
  tic_board::tic_piece get_piece() {
    return tpiece; }         // Return piece type

  int get_xpos() {
    return txpos; }          // Return x position

  int get_ypos() {
    return typos; }          // Return y position

  static typeinfo_t typeinfo;

private:
  tic_board::tic_piece tpiece;    // The piece placed
  int txpos, typos;               // The location tpiece is placed
};

typeinfo_t tic_move::typeinfo = new_typeinfo(); 
// Initializes tic_move's typeinfo
//CLASS tic_move ENDS

//CLASS tic_move_gen BEGINS
class tic_move_gen : public move_generator {
// Overview: This creates a move_generator that iterates over all valid moves
// for a given player and board. Neither the board nor player may change while
// the generator is in use. The player's position, and the piece corresponding
// to that position may not change, as well.

public:
  tic_move_gen(int p, tic_board *cboard) {
  // Requires: cboard doesn't change while the generator is in use.
  // Effects: constructs a generator that returns the valid moves.
    tx = 0; ty = 0;     // Reset generator
    tboard = cboard;    // Squirrel away our construction data
    ppiece = tic_board::player_to_piece(p);
    tplayer = p; }
  
  virtual bool get(move *&m) {
  // Modifies: this
  // Effects:  If more moves remain, returns true and sets m to be a pointer
  // to the next move, else returns false.
  // FIXME This routine is memory 'leaky'
    if (ty > 2) return false;  // Whoops! That's all, folks!
    m=new tic_move(ppiece,tx,ty);
    while (!tboard->is_legal_move(*m,tplayer)) {
      tx++;                      // Advance to the next move
      if (tx > 2) { tx = 0; ty++; }
      if (ty > 2) return false;  // Whoops! That's all, folks!
      m=new tic_move(ppiece,tx,ty);
    }
    tx++;                      // Advance to the next move
    if (tx > 2) { tx = 0; ty++; } 
    return true; }

private:
  tic_board *tboard;              // The board we are iterating over
  int tx, ty;                     // Our current position on (or off) the board
  int tplayer;                    // The player we are generating moves for
    tic_board::tic_piece ppiece;     // Tplayer's piece type
};
//CLASS tic_move_gen ENDS

tic_board::tic_board(){
// Effects: Initializes the state of tic_board on creation.
  array<tic_piece> atmp;

  for (int i=0; i <=  2; i++) {             // Create the tic_board, fill with
    board_rep.append(atmp);               // no_pieces.
    for (int j=0; j <= 2; j++) 
      board_rep[i].append(no_piece);
  }
  won_player = -1;                        // No player has won
  turn_token = 0;                         // X goes first
}

bool tic_board::is_legal_move(move &m, int p) const {
// Checks: Player must have a position.
  if (won_player != -1) return false;  // The game is over
  tic_move* ptr = (tic_move*)m.force(tic_move::typeinfo);
  if (!ptr) return false;        // I can't use this move here
  if (player_to_piece(p) != ptr->get_piece())
       return false;
  if (p != turn_token) return false;  // It's not p's turn 
  if (board_rep[ptr->get_xpos()][ptr->get_ypos()] == no_piece)
    return true;                // Is the square empty?  
  else return false;            
}

game::move_result tic_board::apply(move &m, int p) {
  tic_move* ptr = (tic_move*)m.force(tic_move::typeinfo);
  if (!ptr) return game::move_invalid; // I can't use this move here
  if (!is_legal_move(m, p))
    return game::move_invalid;         // That move was invalid
  board_rep[ptr->get_xpos()].store(ptr->get_ypos(),ptr->get_piece());
  if (won_player == -1)                // Check and see if p just won
    won_player = this->check_for_winner();
  turn_token=1-turn_token;
  return game::ok;                     // Move applied.
}

bool tic_board::unapply(move &m, int ) {
    tic_move* ptr = (tic_move*)m.force(tic_move::typeinfo);
    if (!ptr) return false;
    won_player = -1;  
    turn_token=1-turn_token;
    board_rep[ptr->get_xpos()].store(ptr->get_ypos(),no_piece);  
  return true; }

int tic_board::winner() {
  return won_player;}

http_response* tic_board::get(string request, extensions *extras){
  return new error_response(http_response::OK,
			    "tic_board::get unimplemented."); }

//FIXME This should probably say whose turn it is
void tic_board::display(extensions *ext, ostream& body, int player_num)const {
  body << "<H2>The current game board:</H2>" << endl;
  for (int i=0; i <= 2; i++) {
      for (int j=0; j <= 2; j++)
      body << piece_to_name(board_rep[j][i]) << " ";
    body << "<P>" << endl;
  }
}

bool tic_board::valid_move_available(int p) const {
// Effects: returns true if player p has any current legal moves,
// returns true, else returns false.
  move_generator* mg = new tic_move_gen(p, (tic_board *) this); 
move *foo;
  bool retval=mg->get(foo);
  delete mg;
  return retval;
}

move_generator* tic_board::valid_moves(int p) const{
  return new tic_move_gen(p, (tic_board *) this); }

board::value tic_board::eval_move_delta(move &m, int p) const {
  tic_move* ptr = (tic_move*)m.force(tic_move::typeinfo);
  if (!ptr) return 0;          // I can't use this move here
  if (!is_legal_move(m, p))
    return 0;                  // That move was invalid
//FIXME: Write this
// See if this move:
//   Wins the game: 10
//   Allows opponent to win next move: -10
//   Everything else: 0 (We're quite simplistic here.)
  return 0; //Temporary
}

board::value tic_board::evaluate(int p) const {
//FIXME: Write this
// See if:
//   p can win in one move: 10
//   !p can win in one move: -10
//   Else: 0
  return 0; //Temporary
}

board* tic_board::clone() const {
  tic_board *tb = new tic_board();
  board::clone_part(*tb);
  clone_part(*tb);
}

void tic_board::clone_part(tic_board &b) const {
  b.won_player = won_player;
  b.turn_token = turn_token;
  b.board_rep = board_rep;
}

void* tic_board::force(typeinfo_t &t){
  if (t == typeinfo) return this;
  else return 0;
}

char tic_board::piece_to_name(tic_piece cpiece) {
  switch (cpiece) {
  case no_piece:
    return '_';
    
  case x_piece:
    return 'X';
    
  case o_piece:
    return 'O';
  };
  abort();
}

tic_board::tic_piece tic_board::player_to_piece(int p) {
  if (p == 0) return x_piece;
  if (p == 1) return o_piece;
  return no_piece;
}

int tic_board::piece_to_player(tic_piece t) {
  if (t == x_piece) return 0;
  if (t == o_piece) return 1;
  return -1;
}

int tic_board::check_for_winner() {
// Effects: Returns the winner of this if a player has won, -1 if nobody has
//  won, -2 is there is a draw.  The return value is undefined if more than
// one player can be considered to have won.
  for (int i=0; i<=2; i++) {
    tic_piece winpiece=board_rep[i][0];
    bool twin=true;
    for (int j=1; j<=2; j++)
      if (board_rep[i][j] != winpiece)
	twin=false;
    if (twin && (winpiece != no_piece))
      return piece_to_player(winpiece);
  }
  for (int i=0; i<=2; i++) {
    tic_piece winpiece=board_rep[0][i];
    bool twin=true;
    for (int j=1; j<=2; j++)
      if (board_rep[j][i] != winpiece)
	twin=false;
    if (twin && (winpiece != no_piece))
      return piece_to_player(winpiece);
  }
  tic_piece winpiece=board_rep[0][0];
  bool twin=true;
  for (int j=1; j<=2; j++)
    if (board_rep[j][j] != winpiece)
      twin=false;
  if (twin && (winpiece != no_piece))
    return piece_to_player(winpiece);
  winpiece=board_rep[0][2];
  twin=true;
  for (int j=1; j<=2; j++)
    if (board_rep[j][2-j] != winpiece)
      twin=false;
  if (twin && (winpiece != no_piece))
    return piece_to_player(winpiece);
  twin=true;
  for (int i=0; i<=2; i++)
    for (int j=0; j<=2; j++)
      if (board_rep[i][j] == no_piece)
	twin=false;
  if (twin)
    return -2;
  else return -1;
}

void tic_board::associate(game *g) {
  board::associate(g);
  check(g->players() == 2,"tic_board::associate: Yaah! I can't associate that number of players!");
  g->get_player(0)->set_position("X");   // Player 0 is X
  g->get_player(1)->set_position("O");   // Player 1 is O
}

typeinfo_t tic_board::typeinfo = new_typeinfo();

tic_board::~tic_board() {}
