//***********************************************************
//*
//* File:           Sarge.java
//* Author:         Project 3 Group 3
//* Contact:        mkb24@cs.columbia.edu
//* Update:         10.22.2003
//*
//* Description:    Plays somewhat erratically
//*                 
//*
//***********************************************************

package Cluedo.g3;

import Cluedo.*;
import ui.*;
import java.util.*;
import java.io.*;
import java.awt.Color;

public final class Group3Player4 implements IFCPlayer {

    static Random _random;
    static final String _CNAME = "I voted for Kodos";
    static final Color  _CCOLOR = new Color(0.0f, 0.0f, 0.8f);

    static final char PRESENT = '1';
    static final char ABSENT = '0';
    static final char MAYBE = '?';
    static final boolean IN_DEBUG_MODE = false;

    int MAXTURNS;
    int N;
    int k;
    int h;
    int p;
    int currturn = 0;
    int myIndex;

    char[] hidden;
    char[][] pcards;
    char[][] shownCards;
    Vector[] partials;
    boolean[] playerExited;
    
    int[] mylist;
    Cluedo engine;

    public void register(Cluedo inEngine) throws Exception {

	engine = inEngine;
	N = inEngine.numCards();
	k = inEngine.numCardsPerPlayer();
	h = inEngine.numHiddenCards();
	p = inEngine.numPlayers();
	myIndex = inEngine.indexOf(this);


	_random = new Random();
	MAXTURNS = 1 + _random.nextInt(10);

	hidden = new char[N];
	pcards = new char[p][];
	shownCards = new char[p][];
	partials = new Vector[p];
	playerExited = new boolean[p];

	for(int i=0;i < p;i++) {
	    playerExited[i] = false;
	    pcards[i] = new char[N];
	    shownCards[i] = new char[N];
	    partials[i] = new Vector();
	}

	for(int i=0;i < N;i++)
	    {
		hidden[i] = MAYBE;
		for(int t=0;t < p;t++) {
		    pcards[t][i] = MAYBE;
		    shownCards[t][i] = ABSENT;
		}
		pcards[myIndex][i] = ABSENT;
	    }

	mylist = inEngine.getPlayerList(myIndex, this);

	for(int i=0;i<mylist.length;i++)
	    mylist[i] = mylist[i] - 1;

	for(int j=0;j < mylist.length;j++)
	    {
		int value = mylist[j];
		for(int t=0;t < p;t++)
		    pcards[t][value] = ABSENT;
		pcards[myIndex][value] = PRESENT;
		hidden[value] = ABSENT;
	    }
    }

    public Move move() throws Exception {
	if (engine.currRound() > currturn) {
	    for(int i=currturn+1;i<engine.currRound();i++) {
		processTurn(i);
		//	updateTables();
	    }
	    currturn = engine.currRound();
	}
	
	updateTables();
	showGameStatus();

	if (IN_DEBUG_MODE)
	    System.out.println("getHiddenCount(PRESENT) = " + getHiddenCount(PRESENT));

	Move RET;
	if (getHiddenCount(PRESENT) == h) {
	    int[] hiddenList = getHiddenList(PRESENT);
	    for(int i=0;i<hiddenList.length;i++)
		hiddenList[i] = hiddenList[i] + 1;
	    RET = new Move(_CGUESS, hiddenList);

	    if (IN_DEBUG_MODE) {
		System.out.print("Guessing ");
		printIntArray(hiddenList);
		System.out.println();
	    }
	    
	} else {
	    
	    int numUnknown = 0;
	    int targetPlayer = 0;
	    
	    for(int i=0;i<p;i++) {
		if ( (getUnknownCardCount(i) > numUnknown) && 
		     (!playerExited[i]) &&
		     (i != myIndex) ) {
		    numUnknown = getUnknownCardCount(i);
		    targetPlayer = i;
		}
	    }
	    
	    if (numUnknown == 0) {
		if(IN_DEBUG_MODE)
		    System.out.println("what for not know hidden?");
		int[] queryList = new int[1];
		queryList[0] = 1;
		RET = new Move(_CINTERROGATION, queryList, targetPlayer);
	    } else {
		int[] unknownCardList = getUnknownCardList(targetPlayer);
		int[] knownBluffList = getAbsentCardList(targetPlayer);
		int[] myCardList = mylist;
		int numBluffs = knownBluffList.length / 2 + 1;
		int numMine = myCardList.length / 2 + 1;
		
		int[] queryList = new int[unknownCardList.length + numBluffs + numMine];
		int i;
		for(i=0;i<unknownCardList.length;i++)
		    queryList[i] = unknownCardList[i];
		for(;i<(unknownCardList.length + numBluffs);i++)
		    queryList[i] = knownBluffList[_random.nextInt(knownBluffList.length)];
		for(;i<(unknownCardList.length + numBluffs + numMine);i++)
		    queryList[i] = myCardList[_random.nextInt(myCardList.length)];
		java.util.Arrays.sort(queryList);

		for(i=0;i<queryList.length;i++)
		    queryList[i] = queryList[i] + 1;
		RET = new Move(_CINTERROGATION, queryList, targetPlayer);
		if (IN_DEBUG_MODE) {
		    System.out.print("Asking player " + targetPlayer + ": ");
		    printIntArray(queryList);
		    System.out.println();
		}
	    }
	}
	return RET;
    }
    
    public void processTurn(int turnNum) {
	
	MoveResult tempMove = null;
	
	try {
	    tempMove = engine.history(turnNum);
	} catch(Exception e) {
	    System.out.println("Problem accessing history of round " + turnNum);
	    e.printStackTrace();
	}
	
	if (tempMove == null)
	    return;
	if (tempMove.player == myIndex)
	    return;
	if(tempMove.interrogatee == myIndex)
	    return;
	
	switch (tempMove.type) {
	case _CGUESS:
	case _CINVALID:
	    break;
	case _CINTERROGATION:
	    if(tempMove.response) {
		int[] tempCardList = new int[tempMove.cardlist.length];
		for(int i=0;i<tempCardList.length;i++)
		    tempCardList[i] = tempMove.cardlist[i] - 1;
		partials[tempMove.interrogatee].add(new Clause(tempCardList));
		break;
	    } else {
		for(int i=0;i<tempMove.cardlist.length;i++) 
		    pcards[tempMove.interrogatee][(tempMove.cardlist[i] - 1)] = ABSENT;
	    }
	    break;
	default:
	    break;
	}
    }
    
    public void printIntArray(int[] inArray) {
	for (int i=0;i<inArray.length;i++)
	    System.out.print(inArray[i] + " ");
    }

    public void printCharArray(char[] inArray) {
	for (int i=0;i<inArray.length;i++)
	    System.out.print(inArray[i] + " ");
    }
		 
    public void showGameStatus() {
	try {
	    if(IN_DEBUG_MODE) {
		System.out.println("Round " + engine.currRound());
		System.out.println("pcards:");
		System.out.print("\t");
		for(int i=0;i<N;i++)
		    System.out.print(i%10 + " ");
		System.out.println();
		for(int i=0;i<p;i++) {
		    System.out.print(i + "\t");
		    printCharArray(pcards[i]);
		    System.out.println();
		}
		System.out.print("h\t");
		printCharArray(hidden);
		System.out.println();
		System.out.println("shown");
		System.out.print("\t");
		for(int i=0;i<N;i++)
		    System.out.print(i%10 + " ");
		System.out.println();
		for(int i=0;i<p;i++) {
		    System.out.print(i + "\t");
		    printCharArray(shownCards[i]);
		    System.out.println();
		}
	    }
	} catch (Exception e) {
	    e.printStackTrace();
	}
	
    }
    
    public void updateTables() {
	for(int i=0;i<p;i++) {
	    if (i != myIndex) {
		if (getKnownCardCount(i) == k)
		    allCardsFound(i);
		//		if (getUnknownCardCount(i) == (N - k))
		//allUnknownCardsFound(i);
	    }
	}
	updateHidden();
	for (int i=0;i<p;i++) {
	    if (i != myIndex) {
		for(int j=0;j<partials[i].size();j++) {
		    Clause tempPartial = (Clause)partials[i].get(j);
		    boolean updateThis = false;
		    for(int a=0;a<tempPartial.elements.length;a++) {
			switch (pcards[i][tempPartial.elements[a]]) {
			case PRESENT:
			    partials[i].removeElementAt(j);
			    j--;
			    updateThis = false;
			    a=tempPartial.elements.length;
			    break;
			case ABSENT:
			    updateThis = true;
			    int[] newPartial = new int[tempPartial.elements.length - 1];
			    for(int b=0;b<tempPartial.elements.length;b++) {
				if (b < a)
				    newPartial[b] = tempPartial.elements[b];
				if (b > a)
 				    newPartial[b-1] = tempPartial.elements[b];
 			    }
			    tempPartial.elements = newPartial;
			    a--;
			    break;
			default:
			    break;
			}
		    }
		    if (updateThis)
			partials[i].set(j, tempPartial);
		}
	    }
	}
	
	for (int i=0;i<p;i++) {
	    if (i != myIndex) {
		for (int j=0;j<partials[i].size();j++) {
		    Clause tempPartial = (Clause)partials[i].get(j);
		    if (tempPartial.elements.length == 1) {
			cardFound(i, tempPartial.elements[0]);
			partials[i].removeElementAt(j);
			j--;
		    }
		} 
	    } 
	} 
    } 
    
    public void updateHidden() {
	for(int i=0;i<N;i++) {
	    switch(checkCard(i)) {
	    case PRESENT:
		hidden[i] = ABSENT;
		break;
	    case ABSENT:
		hidden[i] = PRESENT;
		break;
	    case MAYBE:
	    default:
		break;
	    }
	}
    }

    public int checkCard(int cardNum) {
	int RET = ABSENT;

	for(int i=0;i<p;i++) {
	    switch(pcards[i][cardNum]) {
	    case ABSENT:
		break;
	    case PRESENT:
		if(RET == ABSENT)
		    RET = PRESENT;
		break;
	    case MAYBE:
	    default:
		RET = MAYBE;
		return RET;
	    }
	}
	
	return RET;
    }

    public int[] getHiddenList(char type) {
	
	int[] RET = new int[getHiddenCount(type)];
	int count = 0;

	for(int i=0;i<N;i++) {
	    if(hidden[i] == type) {
		RET[count] = i;
		count++;
	    }
	}

	return RET;
    }

    

    public int getHiddenCount(char type) {
	int found = 0;
	for(int i=0;i<N;i++) {
	    if (hidden[i] == type)
		found++;
	}
	return found;
    }
    
    public int getKnownCardCount(int player) {
	return getCardCount(player, PRESENT);
    }
    
    public int getUnknownCardCount(int player) {
	return getCardCount(player, MAYBE);
    }

    public int getAbsentCardCount(int player) {
	return getCardCount(player, ABSENT);
    }
    
    public int getCardCount(int player, char type) {
	int found = 0;
	for(int i=0;i<N;i++) {
	    if (pcards[player][i] == type)
		found++;
	}
	return found;
    }

    public int[] getKnownCardList(int player) {
	return getCardList(player, PRESENT);
    }

    public int[] getUnknownCardList(int player) {
	return getCardList(player, MAYBE);
    }

    public int[] getAbsentCardList(int player) {
	return getCardList(player, ABSENT);
    }

    public void cardFound(int player, int card) {
	for(int i=0;i<p;i++)
	    pcards[i][card] = ABSENT;
	pcards[player][card] = PRESENT;
	hidden[card] = ABSENT;

    }

    public int[] getCardList(int player, char type) {
	int[] RET = new int[getCardCount(player, type)];
	int count = 0;
	for(int i=0;i<N;i++) {
	    if(pcards[player][i] == type) {
		RET[count] = i;
		count++;
	    }
	}
	if(IN_DEBUG_MODE) {
	    System.out.print("getCardList(" + player + ", " + type + ") returning ");
	    printIntArray(RET);
	    System.out.println();
	}
	return RET;
    }
    


    public void allCardsFound(int player) {
	for(int i=0;i<N;i++) {
	    if (pcards[player][i] == MAYBE)
		pcards[player][i] = ABSENT;
	}
    }

    public void allUnknownCardsFound(int player) {
	for(int i=0;i<N;i++) {
	    if (pcards[player][i] == MAYBE)
		pcards[player][i] = PRESENT;
	}
    }

    public String name() throws Exception {
        return _CNAME;
    }

    public Color color() throws Exception {
        return _CCOLOR;
    }

    public boolean interactive() throws Exception {
        return false;
    }        

    public int question(int interrogator, int[] tempcardlist) throws Exception
    {
	int[] cardlist = new int[tempcardlist.length];
	for(int i=0;i<cardlist.length;i++)
	    cardlist[i] = tempcardlist[i] - 1;
	if (IN_DEBUG_MODE) {
	    System.out.print("question called by " + interrogator + ": ");
	    printIntArray(cardlist);
	    System.out.println();
	}

	int i,j;
	Vector myMatches = new Vector();
	
	for(i=0;i<cardlist.length;i++) {
	    for(j=0;j<mylist.length;j++) {
		if (cardlist[i] == mylist[j])
		    myMatches.add(new Integer(cardlist[i]));
	    }
	}

	if (myMatches.size() == 0)
	    return 0;
	
	for(i=0;i<myMatches.size();i++) {
	    Integer J = (Integer)myMatches.get(i);
	    j = J.intValue();
	    if (shownCards[interrogator][j] == PRESENT) 
		return (j + 1);
	}

	Integer tempRET = (Integer)myMatches.get(_random.nextInt(myMatches.size()));
	int RET = tempRET.intValue();
	shownCards[interrogator][RET] = PRESENT;
	if(IN_DEBUG_MODE)
	    System.out.println("question returning " + RET + " + 1");
	return (RET + 1);

    }
    
    public void answer(int interrogatee, int[] mycardlist, int response) throws Exception
    {
	if (mycardlist == null)
	    return;

	if (response == 0) {
	    if(IN_DEBUG_MODE)
		System.out.println("Null response, invalidating cards");
	    for(int i=0;i<mycardlist.length;i++) 
		pcards[interrogatee][mycardlist[i]-1] = ABSENT;
	    return;
	}
	if(IN_DEBUG_MODE) {
	    System.out.print("Answer called from interrogatee " + interrogatee + " with list ");
	    printIntArray(mycardlist);
	    System.out.println("And response " + (response - 1) + "- 1");
	    System.out.println("Card found");
	}
	cardFound(interrogatee, response - 1);
	
    }
    
    public void notifyPlayerExit(int playerexited) throws Exception
    {
	if(IN_DEBUG_MODE)
	    System.out.println("Player " + playerexited + " has exited the game");
	playerExited[playerexited] = true;
	int[] playerCards = engine.getPlayerList(playerexited, null);
	for(int i=0;i<playerCards.length;i++)
	    cardFound(playerexited, playerCards[i] - 1);
	allCardsFound(playerexited);
	updateTables();
	//	showGameStatus();
    }
    
    public int[] forcedGuess() throws Exception
    {
	if(IN_DEBUG_MODE)
	    System.out.println("forcedGuess called");
	//updateTables();
	showGameStatus();
	int[] finallist = new int[h];
	int[] hiddenList = getHiddenList(PRESENT);
	
	int i;
	for(i=0;i<hiddenList.length;i++)
	    finallist[i] = hiddenList[i] + 1;
	
	hiddenList = getHiddenList(MAYBE);
	int count = 0;
	for(;i<h;i++) {
	    finallist[i] = hiddenList[count] + 1;
	    count++;
	}
	if(IN_DEBUG_MODE) {
	    System.out.print("Forced to guess ");
	    printIntArray(finallist);
	    System.out.println();
	}
	
	return finallist;
    }

    public static boolean clausesEqual(Clause a, Clause b) {
	return (java.util.Arrays.equals(a.elements, b.elements));
    }
    
    
    class Clause {
	public int[] elements;

	public Clause() {
	    elements = null;
	}

	public Clause(int[] inElements) {
	    elements = inElements;
	    java.util.Arrays.sort(elements);
	}

	
    }
}
