// Arup Guha
// 7/27/2010
// Ported from C version.
// Defines an object with which to play Connect Four.

public class ConnectFour {
	
	// Bunch of constants...
	final public static char PLAYERONE = 'X';
	final public static char PLAYERTWO = 'O';
	final public static char EMPTY = '_';
	final public static int NUM_ROWS = 6;
	final public static int NUM_COLS = 7;
	final public static int WIN_LENGTH = 4;
	
	final public static int INIT_TIME = 300000;
	
	final public static int CATS = 0;
	final public static int X_WINS = 1;
	final public static int O_WINS = 2;
	final public static int NOT_OVER = 3;
	
	private char[][] board;
	private char whoseTurn;
	
	// Initialize a game so it's ready to start.
	public ConnectFour() {
		
		board = new char[NUM_ROWS][NUM_COLS];
		
		for (int i=0; i<NUM_ROWS; i++)
			for (int j=0; j<NUM_COLS; j++)
				board[i][j] = EMPTY;
				
		whoseTurn = PLAYERONE;
	}
	
	// Returns a separate copy of the object g, in its
	// current state.
	public ConnectFour(ConnectFour g) {
		
		board = new char[NUM_ROWS][NUM_COLS];
		
		for (int i=0; i<NUM_ROWS; i++)
			for (int j=0; j<NUM_COLS; j++)
				this.board[i][j] = g.board[i][j];
				
		this.whoseTurn = g.whoseTurn;		
	}
	
	// Returns true iff the column curmove is an invalid place
	// to put the next piece.
	public boolean not_valid(int curmove) {
				
		// These columns don't exist on the board.
     	if (curmove < 0 || curmove >= NUM_COLS)
        	return true;
         
     	// Here we check to see if the column is full.
     	else if (board[NUM_ROWS-1][curmove] != EMPTY)
        	return true;
         
     	// Otherwise, we're okay.
     	return false;   
	}
	
	// Pre-condition: move must be a valid column in which to move.
	// Post-condition: returns the row in which a piece dropped in
	//                 column move would go to.
	public int get_row(int move) {
		
		int i = NUM_ROWS;
		
		// Move through each row, until you hit a piece.
    	while (i > 0 && board[i-1][move] == EMPTY) i--;

    	// Return this row number.
    	return i;    
	}
	
	public void print() {
		    
		int i,j;
    
    	System.out.println("Here is the game board:\n");
		System.out.print("  ");
		
    	// Print the column numbers above the board.
    	for (i=0; i<NUM_COLS; i++)
        	System.out.print(i+" ");
    	System.out.println();
  
    	// Go through each row of the board.
    	for (i=NUM_ROWS-1; i>=0; i--) {
      
      		System.out.print(i+" ");
        	for (j=0; j<NUM_COLS; j++)
            	System.out.print(board[i][j]+" ");
        	System.out.println();
    	}
    
    	// Print the column numbers below the board.
    	System.out.print("  ");
    	for (i=0; i<NUM_COLS; i++)
        	System.out.print(i+" ");
    	System.out.println();
	}
	
	// I finally edited this from 1999 to look a bit better =)
	public int check_status() {
		
		// Check all the ways X and O can win. 
		// If any of these are triggered, we skip the rest of
		// the checks.
    	int status = checkRows('X');
    	
    	if (status == NOT_OVER)
    		status = checkRows('O');
    		
    	if (status == NOT_OVER)
    		status = checkCols('X');

    	if (status == NOT_OVER)
    		status = checkCols('O');

    	if (status == NOT_OVER)
    		status = checkForwardDiag('X');
    
    	if (status == NOT_OVER)
    		status = checkForwardDiag('O');

		if (status == NOT_OVER)
			status = checkBackDiag('X');
			
		if (status == NOT_OVER)
			status = checkBackDiag('O');
			
		if (status != NOT_OVER)
			return status;

		// If we get here, no one has won, so check to see
		// if we have more slots to play in.
		if (hasEmptySlot())
			return NOT_OVER;
            
    	// If we get here, we have a CATS game.
    	return CATS;
	}
	
	// Returns the appropriate code for player winning, if the player has
	// won on a row. Otherwise, returns NOT_OVER.
	public int checkRows(char player) {
    	
    	// We go through each row, to look for a horizontal win.
    	for (int j=0; j<NUM_ROWS; j++) {
      
        	// We iterate through the possible column starting positions of four
        	// consecutive winning pieces.
        	for (int i=0; i<NUM_COLS-WIN_LENGTH+1; i++) {
            
            	int score = 0;
            	for (int k=0; k<WIN_LENGTH; k++)
	        		if (board[j][i+k] == player)   
	        			score++;
	            
	            // We found four of the same piece in a row.
	            if (score == WIN_LENGTH)
	            	return player == PLAYERONE ? X_WINS: O_WINS;
	    	}
    	}		
    		
    	return NOT_OVER;
	}
	
	// Returns the appropriate code for player winning, if the player has
	// won on a column. Otherwise, returns NOT_OVER.
	public int checkCols(char player) {
		
    	// We go through each column, to look for a vertical win.
    	for (int j=0; j<NUM_COLS; j++) {
      
        	// We iterate through possible row starting positions of four 
        	// consecutive winning pieces.
        	for (int i=0; i<NUM_ROWS-WIN_LENGTH+1; i++) {
            
            	int score = 0;
            	for (int k=0; k<WIN_LENGTH; k++)
            		if (board[i+k][j] == player)
            			score++;
            			
	        	// We found four of the same piece in a row.
                if (score == WIN_LENGTH)
                	return player == PLAYERONE ? X_WINS: O_WINS;
	    	}
    	}		
    		
    	return NOT_OVER;
	}
	
	// Returns the appropriate code for player winning, if the player has
	// won on a forward diagonal. Otherwise, returns NOT_OVER.
	public int checkForwardDiag(char player) {
		
    	// We start at the possible row positions for a "forward" diagonal.
    	for (int i=0; i<NUM_ROWS-WIN_LENGTH+1; i++) {
        
        	// We start at the possible column positions. 
        	for (int j=0; j<NUM_COLS-WIN_LENGTH+1; j++) {
            
            	int score = 0;
            	for (int k=0; k<WIN_LENGTH; k++)
            		if (board[i+k][j+k] == player)
            			score++;

	        	// We found four of the same piece in a row.
                if (score == WIN_LENGTH)
                	return player == PLAYERONE ? X_WINS: O_WINS;  	            			
	    	}
    	}		
    		
    	return NOT_OVER;
	}
	
	// Returns the appropriate code for player winning, if the player has
	// won on a backward diagonal. Otherwise, returns NOT_OVER.
	public int checkBackDiag(char player) {
		
    	// We start at the possible row positions for a "backward" diagonal.
    	for (int i=0; i<NUM_ROWS-WIN_LENGTH+1; i++) {
        
        	// Here are the possible column positions for a backwards diagonal.
        	for (int j=NUM_COLS-1; j>WIN_LENGTH-2; j--) {
        		
        		int score = 0;
        		for (int k=0; k<WIN_LENGTH; k++)
        			if (board[i+k][j-k] == player)
        				score++;

	        	// We found four of the same piece in a row.
                if (score == WIN_LENGTH)
                	return player == PLAYERONE ? X_WINS: O_WINS;  
	    	}
    	}	
    	
    	return NOT_OVER;	
	}
	
	// Returns true iff there's a place to play on the board.
	public boolean hasEmptySlot() {
		
    	// See if there's an empty slot on the board.
    	for (int i=0; i<NUM_COLS; i++)
        	if (board[NUM_ROWS-1][i] == EMPTY)
            	return true;
            	
        return false;		
	}
	
	// Returns the other player (than the one currently moving.)
	public char other() {
		if (whoseTurn == PLAYERONE)
			return PLAYERTWO;
		else
			return PLAYERONE;
	}
	
	// Accessors
	public char getCurPlayer() {
		return whoseTurn;
	}	
		
	public char[][] getBoard() {
		return board;
	}
		
	// Precondition: move represents a valid column in which to move.
	// Postcondition: Executes this move for the current player and
	//                advances the current player to the other player.
	public void execMove(int move) {
		board[get_row(move)][move] = whoseTurn;
		whoseTurn = other();
	}
}