// TT4DBoad.java
 
/** 
 *
 * @author  Sean Bridges
 * @version 1.0.1
 *
 *  The TT4D Board represents the game logic for a game of 4d Tic Tac Toe.
 */

import java.util.*;

public class TT4DBoard implements Board 
{

//-----------------------------------------------------
    //class variables
    public static final int NULL_PLAYER_NUMBER = -1;
    public static final int X_PLAYER_NUMBER = 0;
    public static final int O_PLAYER_NUMBER = 1;
    
    public static final String NULL_PLAYER_STRING = " ";
    public static final String X_PLAYER_STRING = "X";
    public static final String O_PLAYER_STRING = "O";

    private static final int NUMBER_OF_SQUARES = 81;
        
//-----------------------------------------------------
    //instance variables
    private TT4DSquare[] squares = new TT4DSquare[NUMBER_OF_SQUARES];
    private TT4DStats stats = new TT4DStats();
    private Vector rows = new Vector();
    private int[] moves = new int[NUMBER_OF_SQUARES]; //1 move possible for each square
    private int moveIndex = 0; //points after the last move.
    
//-----------------------------------------------------
    //constructor
    
    /** Creates new TT4DBoad */
    public TT4DBoard() 
    {
        initSquares();  
    }//end constructor

//-------------------------------------------------
    //instance methods

//-------------------------------------------------
    //initializing

    /**
     * Create the squares and organize them into rows.
     */
    private void initSquares()
    {
    
        //create the squares
        for(int i = 0; i < NUMBER_OF_SQUARES; i++)
        {
            squares[i] = new TT4DSquare( new TT4DPoint(i) );
        }
    
        //create the rows
    
        /*
         * The task is to find all possible lines in the board.
         * Counting each line only once.
         *
         * A line consists of three points, one degree of freedom between points.
         *
         * a line can be thought of as <a,b,c,d> 
         * s.t. a,b,c,d exist in the set {0,1,2,x,-x} and at
         * least one of a,b,c,d = x or -x.  
         * 0,1,2 represent constant terms, while x and -x represent 
         * the variable portion.  
         * to construct the three points. x is substituted for 0,1,2
         * and -x is substititued for 2,1,0 
         * For example <x,-x,1,2> is the line composed of the points
         * <0,2,1,2>, <1,1,1,2> and <2,0,1,2>
         * 
         * The task is to find all possible lines in the board. 
         * Counting each line once.  To do this get each combination 
         * of 0,1,2,x,-x.  
         * To get the unique lines, we select each combination with at least 
         * one x, and with any -x's occuring after the first x.
         *
         * x is represented as 3, -x as 4.
         */ 
    
        int x, y, z, w, count;
        count = 0;
  
        for(x = 0; x <= 4; x++)
        {
            for(y = 0; y <= 4; y ++)
            {
                for(z = 0; z <= 4; z ++)
                {
                    for(w = 0;  w <= 4; w++)
                    {
                        //if we have at least 1 x
                        if( (w == 3) | (z == 3) | (y == 3) | (x == 3) )
                        {
                            //if all -x's occur after at least 1 x
                            if( 
                                (x == 3) || 
                                ((y == 3) & (x != 4)) ||
                                ((z == 3) & (x != 4) & (y != 4) ) ||
                                ((w == 3) & (x != 4) & (y != 4) & (z != 4) ) 
                               )
                            { //we have a valid line.
                                count++;
                                TT4DRow r = createRow(x,y,z,w);
                                rows.addElement(r);
               
              
                            }//end if
                        }//end if
                    }//end for w
                }//end for z   
            }//end for y
        } //end for x
        
        //System.out.println(count); 
    } 

//-------------------------------------------------
    //row construction

    /**
     * Creates and returns a new TT4D row from the given line.
     * x,y,z,w exist in 0,1,2,3,4
     * 3 represents x, 4 represents -x
     */
    private TT4DRow createRow(int x, int y, int z, int w)
    {
        return new TT4DRow( getSquare(x,y,z,w, 0),
                            getSquare(x,y,z,w, 1),
                            getSquare(x,y,z,w, 2),
                            stats );
    }
    
    
    /**
     * Find the square for the given point on the line.
     * x,y,z,w represent the line.
     * 0,1,2 representing constants
     * 3 representing x, and 4 representing -x.
     *
     * The key argument determines which point on the line is returned.
     * 0,1,2 return the first second and third points on the line respectively.
     */
    private TT4DSquare getSquare(int x, int y, int z, int w, int key)
    {
        
        if (x == 3)
        {x = key;}
        if (x == 4)
        {x = 2 - key;}
       
        if (y == 3)
        {y = key;}
        if (y == 4)
        {y = 2 - key;}

        if (z == 3)
        {z = key;}
        if (z == 4)
        {z = 2 - key;}
        
        if (w == 3)
        {w = key;}
        if (w == 4)
        {w = 2 - key;}
       
        return squareAt(x + (y * 3) + (z * 9) + (w * 27));
        
    }

//---------------------------------------------------
    //accessing

    private TT4DSquare squareAt(int position)
    {
        return squares[position];
    }

    public TT4DSquare squareAt(TT4DPoint point)
    {
        return squareAt(point.toInt() );
    }

//---------------------------------------------------
 //clone

    /**
     * Create a copy of myself.
     * The copy is totally independent, ie a deep copy.
     */
    public synchronized Object clone()
    {
    
        TT4DBoard copy = null;

        try
        {
            copy = (TT4DBoard) super.clone();
        }
        catch(CloneNotSupportedException e)
        {
            //should never get here
            System.err.println(e);
        }

        copy.squares = (TT4DSquare[]) this.squares.clone();
        copy.moves = (int[]) this.moves.clone();
        copy.stats = new TT4DStats();
        
        copy.initSquares();

        for(int i = 0; i < NUMBER_OF_SQUARES; i++ )
        {
            if(squares[i].getContents() != NULL_PLAYER_STRING)
            {
                copy.squares[i].setContents( this.squares[i].getContents() );
            }
            copy.moves[i] = this.moves[i];
        }
        copy.moveIndex = this.moveIndex;
    
        return copy;
    }

//-----------------------------------------------------
    //Board Methods

    /**
     * Return an object which can be interrogated to discover the current
     * state of the game
     */
    public BoardStats getBoardStats() 
    {
        return stats;
    }

    /**
     * Try and make a move.
     * Returns wether or not the move attempt was successful.
     */
    public boolean move(Move m) 
    {
    
        if( squareAt(m.toInt()).getContents() == NULL_PLAYER_STRING )
        {
            squareAt(m.toInt()).setContents(m.maker().getName());
            moves[moveIndex] = m.toInt();
            moveIndex ++;
            return true;
        }
        else
        {
            return false;
        }
    }

    /** Undo the last move made.
     */
    public void undoLastMove() 
    {
        moveIndex--;
        squareAt(moves[moveIndex]).setContents(NULL_PLAYER_STRING);
    }
    /**
     * Get the list of moves that are possible for the given
     * player
     */
    public Move[] getPossibleMoves(Player aPlayer) 
    {
        Move[] possible = new Move[NUMBER_OF_SQUARES - moveIndex];
        int current = 0;
    
        for(int i = 0; i < NUMBER_OF_SQUARES; i++)
        {
            if(squares[i].getContents() == NULL_PLAYER_STRING)
            {
                possible[current] = new TT4DMove(aPlayer, squares[i].getPosition());
                current++;
            }
        }
    
        return possible;
    }
    
    /** Whether or not the game is over.
     */
    public boolean isGameOver() 
    {
        //we are over if there are no more empty squares
        boolean over = true;
        for(int i = 0; i < NUMBER_OF_SQUARES; i++)
        {
            if(squares[i].getContents().equals(NULL_PLAYER_STRING) )
            {
                over = false;
                break;
            }
        }
        return over;
    }

    /** Called by the game master when the game is started.
     */
    public void gameStarted() 
    {
        clearBoard();
    }

    /** Called by the game master when the game is restarted.
     */
    public void gameRestarted() 
    {
        clearBoard();
    }

    /** Called by the game master when the game is stopped.
    */
    public void gameStoped() 
    {
        clearBoard();
    }

    /**
     * Clear all the squares on the board.
     */
    private void clearBoard()
    {
        //clear the squares
        for(int i = 0; i < NUMBER_OF_SQUARES; i++)
        {
            squares[i].setContents(NULL_PLAYER_STRING);
        }
        moveIndex = 0;
  
    }
    

    public String toString()
    {
        StringBuffer buf = new StringBuffer();
    
        for(int x = 0; x < 3; x++)
        {
            buf.append("\n");
            for(int y = 0; y < 3; y++)
            {
                buf.append("\n");
                for(int z = 0; z < 3; z++)
                {
                    buf.append("  ");
                    for(int w = 0; w < 3; w++)
                    {
                        int v = x + (y * 3) + (z * 9) + (w * 27);
                        buf.append("|");
                        buf.append(squareAt(v).getContents() );
                    }//end for w
                }//end for z
            }//end for y
        }//end for x
        return buf.toString();
    }//end toString

}//end class TT4DBoard