// Example code for CS 361.  Copyright 2013 by Tom Hayes.
// Important note: This file contains working code for implementing
// retrograde analysis for general games.  It is provided for informational
// purposes only.  You may play around with it and study it to help 
// yourself learn what needs to be done.  However, you may not copy
// any part of the source code to turn in for assignment 5.

import java.util.*;

public class TicTacToe extends GamePosition<TicTacToe> {

    int[][] board;  // 0 if empty.  +1 for player 1.  -1 for player 2.
    int numMovesMade;
    int movesToWin;
    
    
    
    // create a new empty TicTacToe game.
    public TicTacToe( ) {
	board = new int[3][3];
	winner = GamePlayer.UNKNOWN;
	numMovesMade = 0;
	whoseMove = GamePlayer.FIRST_PLAYER;
    }
    
    @Override
    public Set<TicTacToe> successorPositions( ) {
	Set<TicTacToe> rv = new HashSet<TicTacToe>( );
	if (gameOver( ))
	    return rv;
	for (int i=0; i<3; i++)
	    for (int j=0; j<3; j++) 
		if (board[i][j] == 0) {
		    TicTacToe newPos = new TicTacToe( );
		    for (int ii = 0; ii<3; ii++)
			for (int jj=0; jj<3; jj++)
			    newPos.board[ii][jj] = board[ii][jj];
		    newPos.board[i][j] = 1 - 2*(numMovesMade % 2);
		    newPos.numMovesMade = numMovesMade + 1;
		    newPos.whoseMove = whoseMove.negate( );
		    rv.add( newPos );
		}
	return rv;
    }
    
    public boolean gameOver( ) {
	// Step 1: Check for 3 in a row.
	// horizontally or vertically
	for (int i=0; i<3; i++) {
	    if ((board[i][0] == board[i][1] && board[i][0] == board[i][2] && board[i][0] != 0) ||
		(board[0][i] == board[1][i] && board[0][i] == board[2][i] && board[0][i] != 0)) {
		if (board[i][i] == 1)
		    winner = GamePlayer.FIRST_PLAYER;
		else
		    winner = GamePlayer.SECOND_PLAYER;
		movesToWin = 0;
		return true;
	    }
	}
	// diagonally
	if ((board[0][0] == board[1][1] && board[0][0] == board[2][2] && board[1][1] != 0) ||
	    (board[2][0] == board[1][1] && board[0][2] == board[1][1] && board[1][1] != 0)) {
	    if (board[1][1] == 1)
		winner = GamePlayer.FIRST_PLAYER;
	    else
		winner = GamePlayer.SECOND_PLAYER;
	    movesToWin = 0;
	    return true;
	}
	// otherwise, is the board full?
	for (int i=0; i<3; i++)
	    for (int j=0; j<3; j++)
		if (board[i][j] == 0)
		    return false;        // still places to play.
	winner = GamePlayer.NOONE;       // Otherwise, a draw.
	return true;
    }
    
    public GamePlayer winner( ) {
	if (winner == GamePlayer.UNKNOWN)
	    gameOver( );     // Just to ensure this has been tested.
	return winner;	    
    }
    
    @Override
    public String toString( ) {
	StringBuilder rv = new StringBuilder( );
	for (int i=0; i<3; i++) {
	    for (int j=0; j<3; j++)
		if (board[i][j] == 0)
		    rv.append('.');
		else if (board[i][j] == 1)
		    rv.append('X');
		else if (board[i][j] == -1)
		    rv.append('O');
	    rv.append('\n');
	}
	if (gameOver( )) 
	    rv.append("Game over.\n");
	else
	    rv.append(whoseMove + " to move.\n");
	return rv.toString( );
    }

    @Override
	public boolean equals( Object o ) {
	if (this == o)
	    return true;
	if (!(o instanceof TicTacToe))
	    return false;
	if (o.toString( ).equals(this.toString( )))     // a fairly restrictive definition.  Also, not very efficient code.
	    return true;
	return false;
    }

    @Override
    public int hashCode( ) {
	return toString( ).hashCode( );
    }
    
    public static void main( String[ ] args ) {
	TicTacToe game = new TicTacToe( );
	game.board[0][0] = +1;
	game.board[1][1] = -1;
	game.board[2][2] = +1;
	game.board[1][1] = -1;
	game.board[2][2] = +1;
	game.numMovesMade = 3;
	System.out.println( game );
	for (TicTacToe g : game.successorPositions( ) ) {
	    System.out.println( g );
	}
    }
}