// 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.*;

/**
 * Important note: class T must implement a proper hashCode( ) method override,
 * since GameGraph and related classes depend on using HashSets to eliminate
 * duplicate positions.
 */
public class GameGraph<T extends GamePosition<T>> {
    
    HashSet<T> positions;
    HashSet<T> endPositions;
    HashMap<T, GameGraphNode<T>> nodeMap;

    
    /** Create and solve the game graph from the given starting position.
     */
    public GameGraph ( T startingPosition ) {
	// First, enumerate all the reachable game positions, ignoring duplicates.
	HashSet<T> unexpandedPositions = new HashSet<T>( );
	HashSet<T> expandedPositions = new HashSet<T>( );
	unexpandedPositions.add( startingPosition );
	while ( ! unexpandedPositions.isEmpty( ) )
	    for ( T pos : unexpandedPositions ) {
		for ( T newpos : pos.successorPositions( ) )
		    if ((! expandedPositions.contains( newpos )) &&
			(! unexpandedPositions.contains( newpos )))
			unexpandedPositions.add( newpos );
		unexpandedPositions.remove( pos );
		expandedPositions.add( pos );
		break;    // get a new iterator, since the set has been modified.
	    }
	positions = expandedPositions;

	// Second, create the nodes and edges for the graph.
	nodeMap = new HashMap<T, GameGraphNode<T>>( );
	for ( T pos : positions ) 
	    nodeMap.put( pos, new GameGraphNode<T>( pos ) );
	for ( T pos : positions ) {
	    GameGraphNode<T> node = nodeMap.get( pos );
	    for ( T newpos : pos.successorPositions( ) ) {
		GameGraphNode<T> newnode = nodeMap.get( newpos );
		node.addEdgeTo( newnode );
	    }
	}	    

	// Third, loop through the positions to find the "game over" ones.
	endPositions = new HashSet<T>( );
	for ( T pos : positions )
	    if ( pos.winner( ) != GamePlayer.UNKNOWN )
		endPositions.add( pos );

	// Fourth, make these all notify their predecessors of their status.
	for ( T pos : endPositions ) {
	    nodeMap.get( pos ).notifyInNbrs( );
	}
	
	// Fifth, find any positions that are still marked "unknown".  These are draws.
	for ( T pos : positions )
	    if ( pos.winner( ) == GamePlayer.UNKNOWN ) {
		System.out.println("leftover: " + pos);
		pos.setWinner( GamePlayer.NOONE );
	    }
	
    }

    public static void main( String[ ] args ) {
	/* TicTacToe start = new TicTacToe( );
	System.out.println( "Starting position: \n" + start );
	GameGraph<TicTacToe> gg = new GameGraph<TicTacToe>( start );
	System.out.println( "This position is evaluated as: " + start.winner( ));
	System.out.println( "game graph contains " + gg.positions.size( ) + " distinct board positions." );
	*/
	GameGraph<TicTacToe> gg;
	TicTacToe start2 = new TicTacToe( );
	//start2.board[1][1] = +1;    // X in center.
	//start2.board[0][1] = -1;    // O on side.  Still X to move.
	//start2.board[0][0] = +1;    // X in corner.
	//start2.numMovesMade = 3;
	//start2.whoseMove = GamePlayer.SECOND_PLAYER;
	System.out.println( "Starting position: \n" + start2 );
	gg = new GameGraph<TicTacToe>( start2 );
	System.out.println( "This position is evaluated as: " + start2.winner( ));
	System.out.println( "game graph contains " + gg.positions.size( ) + " distinct board positions." );

	//	for ( TicTacToe pos : gg.positions ) {
	//  System.out.println( pos + "evaluates to " + pos.winner( ) );
	// }

 }

}