// OthelloBoard Class.  This class handles the board state and updating the board
// when Moves take place.



import java.io.*;
import java.util.*;

public class OthelloBoard implements Cloneable
{
  static final int NOWHERE = -1;
  
  static final int BLACK = 2;
  static final int WHITE = 1;
  static final int EMPTY = 0;
  static final int OKMOVE = 3;
  
  static final int PLAYER1 = BLACK;
  static final int PLAYER2 = WHITE;
  
  static final int CHANGED = 16;
  
  // the zero column is not used, board is 8x8
  
  
  Vector paintPieces;
  
  int board[][] = new int[9][9];
  int weight[][] = new int[9][9]; // NEW ADDITION
  
  
  int GameTime = 0;
  
  
  // has to be three since USER is 2, COMPUTER is 1
  int score[] = new int[3];
  int fit_inc[] = new int[3];
  
  int gameTurn;
  boolean gameIsOver;			
  
  
  //Board Statistics
  
  int num_white;
  int num_black;
  int num_white_edges;
  int num_black_edges;
  int num_white_corners;
  int num_black_corners;
  int num_white_near_corners;
  int num_black_near_corners;

	
  
  /**
	 * Set up the board, load everything in so it's speedy
	 */
	
  public OthelloBoard copy() {
   weight[1][1] = 100;
   weight[1][2] = -20;
   weight[1][3] = 20;
   weight[1][4] = 3; //3
   weight[2][1] = -20;
   weight[2][2] = -20;
   weight[2][3] = 20;
   weight[2][4] = 3; //3
   weight[3][1] = 20;
   weight[3][2] = 20;
   weight[3][3] = 20;
   weight[3][4] = 3; // 3..
   weight[4][1] = 3;
   weight[4][2] = 3;
   weight[4][3] = 3;
   weight[4][4] = 3; // .. 3
    for(int count1=1; count1<=4; count1++) {
     for(int count2=1; count2<=4;count2++) {
       weight[9 - count1][count2] = weight[count1][count2];
       weight[9 - count1][9 - count2] = weight[count1][count2];
       weight[count1][9-count2] = weight[count1][count2];
     }
   }
   for(int i=0;i<9;i++) {
     weight[0][i] = 0;
     weight[i][0] = 0;
   }
    
  
    
    OthelloBoard newBoard = new OthelloBoard();
    for (int i=0; i<9; i++) {
      for (int j=0; j<9; j++) {
	newBoard.board[i][j]=board[i][j];
      }
    }	
    for (int k=0; k<3; k++) {
      newBoard.score[k]=score[k];
      newBoard.fit_inc[k]=fit_inc[k]; 
    }
    newBoard.GameTime = GameTime;
    newBoard.gameTurn = gameTurn;
    newBoard.gameIsOver = gameIsOver;

    return newBoard;
  }

  OthelloBoard()
  {
    // clear the board
    for (int row = 1; row < 9; row++)
      for (int col = 1; col < 9; col++)
	board[col][row] = EMPTY;
    
    // put the first pieces on the board
    board[4][4] = BLACK; board[4][5] = WHITE;
    board[5][4] = WHITE; board[5][5] = BLACK;
    
    score[PLAYER1] = 2;
    score[PLAYER2] = 2;
    fit_inc[PLAYER1] = 0;
    fit_inc[PLAYER2] = 0;
    gameIsOver = false;
    
    
    gameTurn = BLACK;
    GameTime = 0;

    paintPieces = new Vector(10);
    
  }



  public boolean gameOver() {
    return gameIsOver;
  }
  
 


  // The folowing function returns a string  containing 
  // all of the board statistics.  They are the number white,
  // the number black, the number white edges, the number black edges,
  // the number white corners and the number black corners.

        public void updateBoardStatistics() {
	  
	  num_white = 0;
	  num_black = 0;
	  num_white_edges = 0;
	  num_black_edges = 0;
	  num_white_corners = 0;
	  num_black_corners = 0;
	  num_white_near_corners = 0;
	  num_black_near_corners = 0;


	  for (int row = 1; row < 9; row++) {
	    for (int col = 1; col <9; col++) {
	      if ((board[col][row] == WHITE) || (board[col][row] == WHITE + CHANGED)){
		num_white++;
		if ((col==1) || (col==8) || (row==1) || (row==8)) {
		  num_white_edges++;
		}
		if (((col==1) || (col==8)) && ((row==1) || (row==8))) {
		  num_white_corners++;
		}
		if (((col==1) && ((row == 2) || (row == 7))) ||
		    ((col == 2) && ((row == 1) || (row == 2) || (row == 7) || (row==8))) ||
		    ((col == 7) && ((row == 1) || (row == 2) || (row == 7) || (row ==8))) || 
		    ((col == 8) && ((row == 2) || (row == 7)))) {
		  num_white_near_corners++;
		}
	      }
	      if ((board[col][row] == BLACK) || (board[col][row] == BLACK + CHANGED)){
		num_black++;
		if ((col==1) || (col==8) || (row==1) || (row==8)) {
		  num_black_edges++;
		}
		if (((col==1) || (col==8)) && ((row==1) || (row==8))) {
		  num_black_corners++;
		}
		if (((col==1) && ((row == 2) || (row == 7))) ||
		    ((col == 2) && ((row == 1) || (row == 2) || (row == 7) || (row==8))) ||
		    ((col == 7) && ((row == 1) || (row == 2) || (row == 7) || (row ==8))) || 
		    ((col == 8) && ((row == 2) || (row == 7)))) {
		  num_black_near_corners++;
		}
	      }
	    }
	  }

	}

        public String printBoardStatistics() {
 
	  updateBoardStatistics();

	  return "" + num_white + " " + num_black + " " + num_white_edges + " "
	    + num_black_edges + " " + num_white_corners + " " + num_black_corners + " " 
            + num_white_near_corners + " " + num_black_near_corners;
	    

	}


        public Vector possibleMoves(int color) {
	  

	  Vector moves = new Vector(8);
	  Move tempMove;

	  for (int col=1; col < 9; col++) {
	    for (int row=1; row <9; row++) {
	      if (isLegalMove(color, col, row)) {
		
		tempMove = new Move(col,row);
		moves.addElement(tempMove);
	      }
	    }
	  }
	  return moves;
	}

       int numPossibleMoves(int color) {
         

	 Vector moves = possibleMoves(color);

	 int numMoves = moves.size();
	 return numMoves;

       }

      Move getnthMove(int nthMove, int color) {

	Vector possibleMoves = possibleMoves(color);
	Move move;
        move = (Move)possibleMoves.elementAt(nthMove);
	return move;
      }

        void PrintBoard() {

	  Vector moves;

	  if (gameTurn==BLACK) 
	    moves = possibleMoves(BLACK);
	  else
	    moves = possibleMoves(WHITE);


	  String output = "\n";
	  for (int row=1; row < 9; row++) {
	    for (int col=1; col < 9; col++) {
	      if ((board[col][row] == BLACK) || (board[col][row] == BLACK+CHANGED))
		output = output + "o";
	      else if ((board[col][row] == WHITE) || (board[col][row] == WHITE+CHANGED))
		output = output + "O";
	      else
		output = output + ".";
	    }

	    if (row == 1) { 
	      output = output + "   Moves for ";
	      if (gameTurn==BLACK) output = output + "BLACK";
	      else
		output = output + "WHITE";
	    }
	    if (row == 2) {
	      output = output + " ";
	      for (int i=0; i<numPossibleMoves(gameTurn); i++) {
		output = output + getnthMove(i,gameTurn);
	      }
	    }
	    output = output + "\n";
	  }
	  System.out.println(output);
	}

	void makeMove (int color, int col, int row)
	{
	  fit_inc[color] += weight[col][row]; // ADDED
		score[color] += 1;
		board[col][row] = color;
		for (int colinc = -1; colinc < 2; colinc++) {
			for (int rowinc = -1; rowinc < 2; rowinc++) {
				flipRow(color, col, row, colinc, rowinc, true,
					true);
			}
		}
		GameTime++;
		if (color==BLACK)
		  if (numPossibleMoves(WHITE)>0) {
		    gameTurn = WHITE;
		  } else if (numPossibleMoves(BLACK)>0) {
		    gameTurn=BLACK;
		  } else {
		    gameIsOver=true;
		  }
		else
		  if (numPossibleMoves(BLACK)>0) {
		    gameTurn = BLACK;
		  } else if (numPossibleMoves(WHITE)>0) {
		    gameTurn = WHITE;
		  } else {
		    gameIsOver=true;
		  }
	}

	/**
	 * make an actual move, put the new piece on the board
	 * flip the rest of the pieces
	 */
	void FakemakeMove (int color, int col, int row)
	{
		score[color] += 1;
		fit_inc[color] += weight[col][row];
		board[col][row] = color;
		for (int colinc = -1; colinc < 2; colinc++) {
			for (int rowinc = -1; rowinc < 2; rowinc++) {
			    flipRow(color, col, row, colinc, rowinc, true, 
				    false);
			}
		}
	}


	/**
	 * try and flip a row in a particular direction
	 * if really_flipping, then go ahead and flip 'em
	 * return number of pieces flipped
	 */
	int flipRow (int color, int col, int row, int colinc,
				 int rowinc, boolean really_flipping,
		     boolean really_painting)
	{
		int newcol = col + colinc;
		int newrow = row + rowinc;
		int opponent = BLACK + WHITE - color;
		int count = 0;
	
		// if not incrementing (moving in a direction), then forget it
		if ((colinc == 0) && (rowinc == 0))
			return 0;
	
		if (newcol == 0 || newcol > 8 || newrow == 0 || newrow > 8)
			return 0;
			
		// if we aren't flipping an opponent, then forget it 
		// have to include the CHANGED in case the screen update
		// has not happened yet (grr.. this is annoying about java..)
		if ((board[newcol][newrow] != opponent) &&
			(board[newcol][newrow] != opponent+CHANGED))
			return 0;
	
		// try to flip as many as possible
		while ((board[newcol][newrow] == opponent) ||
			   (board[newcol][newrow] == opponent+CHANGED)) {
			newcol += colinc;
			newrow += rowinc;
			count++;
			if (newcol == 0 || newcol > 8 || newrow == 0 || newrow > 8)
				return 0;
		}
	
		// is there a matching piece on the other end?
		if ((board[newcol][newrow] != color) && (board[newcol][newrow] != color + CHANGED))
			return 0;
	
		// if we're really flipping them now, then do it..
		if (really_flipping) {
			while ((col != newcol) || (row != newrow)) 
			  {
			      if (board[col][row] != color)
				board[col][row] = color + CHANGED;
			      if (really_painting) {
				Move tempMove = new Move(col, row);
				paintPieces.addElement(tempMove);
			      }
			      col += colinc;
			      row += rowinc;
			      score[color] += 1;
			      score[opponent] -= 1;
			  }
			// ok, so we got a little over-excited
			score[color] -= 1;
			score[opponent] += 1;
		}
		return count;
	}


	// can you even move there?
	boolean isLegalMove (int color, int col, int row)
	{
		if ((board[col][row] != EMPTY) && (board[col][row] != OKMOVE)) {
			return false;
		}
		for (int colinc = -1; colinc < 2; colinc++) {
			for (int rowinc = -1; rowinc < 2; rowinc++) {
				if (flipRow(color, col, row, colinc, rowinc, 
					    false, false) > 0) {
					return true;
				}
			}
		}
		return false;
	}


	/**
	 * does this color have anywhere to go?
	 */
	boolean hasMove (int color)
	{
		for ( int col = 1; col < 9; col++ )	{
			for ( int row = 1; row < 9; row++) {
				if (numPossibleMoves(color) > 0) {
					return true;
				}
			}
		}
		return false;
	}


        //  This can get overwritten if we want to do something at the end of a game.
	void endGame ()
	{
	}
}



// This class defines a move record.
class Move {
  

  // The column and row of a move.
  private int col;
  private int row;

  // the value of a move.  ie the value of the board possition once it is done.
  // this is initially set to 0 and later change.
  private double value;
  
  Move(int col, int row) {
    this.row = row;
    this.col = col;
    value = 0;
  }
  
  public int col() {
    return col;
  }
  
  public int row() {
    return row;
  }
  

  public double value() {
    return value;
  }

  public void setValue(double val) {
    value = val;
  }

  public String toString() {
    String output = "(" + row + "," + col + ") ";
    return output;
  }

}  
  





