//***********************************************************
//*
//* File:           Group5Player3.java
//*
//***********************************************************

package Organisms2.g5;

import Organisms2.*;
import java.util.*;
import java.io.*;
import java.awt.Color;

public final class Group5Player3 implements IFCPlayer {

    Organisms2 amoeba;
    static final String _CNAME = "Black Plague";
    static final Color _CColor = new Color(0.0f, 0.0f, 0.0f);

    // utility objects
    Random rand;
    GregorianCalendar cal = new GregorianCalendar();

    // game constants in an easier to use format
    int E_STAY, E_MOVE, E_FOOD, MAX_E, MAX_FOOD;
    int P, W, E, N, S, R;

    // personal constants
    int EMPTY = -1;
    // ratios to determine landOfPlenty
    float MEAGER = 0.25f;
    float PLENTIFUL = 0.5f;
    float THANKSGIVING = 0.75f;
    // age markers
    int TEEN = 50;
    int MIDAGE = 200;
    int ELDERLY = 1000;
    // sexDrive for ages.
    // note: we live backwards, reproducing more as we
    // age...  what?!?  Merlin did it!  can't we?
    // Viagra anyone?
    float HORNY = 0.4f;
    float DESPERATE = 0.3f;
    float MARRIAGE = 0.25f;
    float IMPOTENT = 0.2f;
    // time markers
    int MESASOIC = 1000;
    int GOLDENAGE = 2500;
    // time increases
    float MESA_INC = 1.1f;
    float GOLDEN_INC = 1.3f;

    // player variables
    int state;
    int lineage;
    int age;
    int children;
    int foodEaten;
    int movesMade;
    ArrayList history;
    int landOfPlenty;
    int birthplace;
    
    public void register(Organisms2 __amoeba, int key) throws Exception {
	// store game object
	amoeba = __amoeba;
	// store game constants
	E_STAY = amoeba.s();
	E_MOVE = amoeba.v();
	E_FOOD = amoeba.u();
	MAX_E = amoeba.M();
	MAX_FOOD = amoeba.K();
	P = _CSTAYPUT;
	W = _CWEST;
	E = _CEAST;
	N = _CNORTH;
	S = _CSOUTH;
	R = _CREPRODUCE;

	rand = new Random(cal.get(cal.MILLISECOND));
	
	if (key != -1) {
	    parseKey(key);
	    // adjust sex drive...
	    TEEN *= (1 + landOfPlenty);
	    MIDAGE *= (1 + landOfPlenty);
	    ELDERLY *= (1 + landOfPlenty);
	} else {
	    state = 0;
	    landOfPlenty = 0;
	    birthplace = -1;
	}
	age = 0;
	movesMade = 0;
	foodEaten = 0;
	children = 0;
	history = new ArrayList();
    }
    
    public String name() throws Exception {
	return _CNAME;
    }
    
    public Color color() throws Exception {
	return _CColor;
    }
    
    public boolean interactive() throws Exception {
	return false;
    }

    public int externalState() throws Exception {
	return state;
    }

    public void parseKey(int key) {
	/*
	Key structre:
	  -1 = primal ancestor, mother of all mothers...
	  otherwise,
	    bits 31-15 = lineage
	    bits 14-13 = parent direction
	    bit  12-11 = abundance of food
	    bits 10-8  = unused
	    bits 7-0   = state
	*/
	// I am the KEYMASTER! Are you the gatekeeper?
	String keyString = Integer.toBinaryString(key);
	if (keyString.length() < 32) {
	    int iterations = (32 - keyString.length());
	    for (int i = 0; i < iterations; i++) {
		keyString = "0" + keyString;
	    }
	}
	// convert binary strings into necessary values...
	// get the lineage
	lineage = Integer.parseInt( keyString.substring(0,16), 2);
	// get parent direction
	birthplace = 1 + Integer.parseInt( keyString.substring(16, 18), 2);
	// get abundance of food
	landOfPlenty = Integer.parseInt( keyString.substring(18, 20), 2);
	// get the state
	state = Integer.parseInt( keyString.substring(24), 2) + 1;
    }

    public int constructKey(int direction) {
	// construct a new key as defined in parseKey;
	String key = new String();
	String buf = Integer.toBinaryString(lineage + age);
	if (buf.length() < 16) {
	    int iterations = (16 - buf.length());
	    for (int i = 0; i < iterations; i++) {
		buf = "0" + buf;
	    }
	}
	key += buf;
	buf = Integer.toBinaryString(direction - 1);
	if (buf.length() < 2) {
	    int iterations = (2 - buf.length());
	    for (int i = 0; i < iterations; i++) {
		buf = "0" + buf;
	    }
	}
	key += buf;
	buf = Integer.toBinaryString(landOfPlenty);
	if (buf.length() < 2) {
	    int iterations = (2 - buf.length());
	    for (int i = 0; i < iterations; i++) {
		buf = "0" + buf;
	    }
	}
	key += buf;
	key += "000";
	if(state > 255) {
	    state = 0;
	}
	buf = Integer.toBinaryString(state);
	if (buf.length() < 8) {
	    int iterations = (8 - buf.length());
	    for (int i = 0; i < iterations; i++) {
		buf = "0" + buf;
	    }
	}
	key += buf;
	return Integer.parseInt(key, 2);
    }

    public Move move(boolean[] foodpresent, int[] enemies, int foodleft, int energyleft) throws Exception {

	// *sniff* we're growing up so fast *sniff*
	age++;

	// do I come from the land down under where women blow and men thunder?
	int ratio = foodEaten/age;
	if (ratio < MEAGER) {
	    landOfPlenty = 0;
	} else if (ratio < PLENTIFUL) {
	    landOfPlenty = 1;
	} else if (ratio < THANKSGIVING) {
	    landOfPlenty = 2;
	} else {
	    landOfPlenty = 3;
	}


	ArrayList validMoves = new ArrayList();
	for (int i = 1; i < enemies.length; i++) {
	    if (enemies[i] == EMPTY) {
		validMoves.add(new Integer(i));
	    }
	}

	ArrayList foodThere = new ArrayList();
	for (int i = 1; i < foodpresent.length; i++) {
	    if (foodpresent[i]) {
		foodThere.add(new Integer(i));
	    }
	}

	// remember the time... have we been here before?
	boolean repeat = false;
	/*
	int max = 10;
	if (age <= 10) {
	    max = age - 1;
	}
	if (history.size() > 2) {
	    int vert = 0, horiz = 0;
	    for (int i = 0; i < max; i++) {
		Move m = (Move) history.get(history.size() - i - 1);
		int dir = m.type();
		if (dir  == W) {
		    horiz--;
		} else if (dir == E) {
		    horiz++;
		} else if (dir == N) {
		    vert--;
		} else if (dir == S) {
		    vert++;
		}
		if ((horiz == 0) && (vert == 0)) {
		    repeat = true;
		}
	    }
	}
	*/

	// mmm, I'm hungry...
	if (foodpresent[P]) {
	    foodEaten++;
	}


	// figure out how often to hop in the sack.  with myself.
	float sexDrive;
	if (age < TEEN) {
	    sexDrive = IMPOTENT;
	} else if (age < MIDAGE) {
	    sexDrive = MARRIAGE;
	} else if (age < ELDERLY) {
	    sexDrive = DESPERATE;
	} else {
	    sexDrive = HORNY;
	}

	// family planning.
	if (lineage > MESASOIC) {
	    sexDrive *= MESA_INC;
	} else if (lineage > GOLDENAGE) {
	    sexDrive *= GOLDEN_INC;
	}

	// are my boys swimming?
	float pregnancyTest = rand.nextFloat();

	if (pregnancyTest <= sexDrive) {
	    // woohoo!! let's make babies!!
	    
	    // if we have grande amounts of energy, try to reproduce
	    if (energyleft > (3 * MAX_E / 5)) {    
		// if we can go somewhere
		if (validMoves.size() > 0) {
		    // choose randomly between the moves
		    int index = rand.nextInt(validMoves.size());
		    int dir = ((Integer)validMoves.get(index)).intValue();
		    int childDir = -1;
		    // Have you seen my mommy?
		    if (dir == N) {
			childDir = S;
		    } else if (dir == S) {
			childDir = N;
		    } else if (dir == E) {
			childDir = W;
		    } else if (dir == W) {
			childDir = E;
		    }
		    // Lets get it on... (Marvin Gaye)
		    // I'm asexual.  Umm, just thought I'd tell you that.
		    // I ain't Fred Flintstone, but I can sure make your bed rock.
		    // Or rather, my bed.  rock.
		    // (Just clarifying the fact I'm asexual.)
		    Move m = new Move(R, dir, constructKey(childDir));
		    history.add(m);
		    children++;
		    return m;
		} else {
		    // we have nowhere to go, so stay put
		    //Move m = new Move(P);
		    //history.add(m);
		    //return m;
		}
	    } else {
		// I cannut doo it, Captain! I doo nut have the powrr!
		// Abort! Abort!
	    }
	}

	// got milk?
	if(foodpresent[P]) {
	    // doo nut tuch me haggis!!
	    //System.out.println(state + ": on food");
	    Move m = new Move(P);
	    history.add(m);
	    return m;
	}

	// ahh!  a cramp!  sit out a round...
	float val = rand.nextFloat();
	float r1 = (float)E_STAY / (float)E_MOVE;
	int r2 = 4 - landOfPlenty;
	//	System.out.println("stay: " + E_STAY);
	//	System.out.println("move: " + E_MOVE);
	//	System.out.println("stay/move: " + r1 + " mod: " + r2);
	//	System.out.println((r1 * r2)  + " <? " + val);
	if (r1/10 < val) {
	    // try to move to food that nobody's eating!
	    foodThere.retainAll(validMoves);
	    //System.out.println(state + ": Food there: " + foodThere.size());
	    if (foodThere.size() > 0) {
		// choose randomly between the moves
		int index = rand.nextInt(foodThere.size());
		int dir = ((Integer)foodThere.get(index)).intValue();
		// move in that direction
		Move m = new Move(dir);
		history.add(m);
		movesMade++;	    
		return m;
	    } else {
		Move m = new Move(P);
		history.add(m);
		return m;
	    }
	}

	// Autobots, Roll Out!!!
	if (validMoves.size() > 0) {
	    // try to move to food that nobody's eating!
	    foodThere.retainAll(validMoves);
	    if (foodThere.size() > 0) {
		// choose randomly between the moves
		int index = rand.nextInt(foodThere.size());
		int dir = ((Integer)foodThere.get(index)).intValue();
		// move in that direction
		Move m = new Move(dir);
		history.add(m);
		movesMade++;
		return m;
	    } else {
		// choose randomly between the moves
		int index = rand.nextInt(validMoves.size());
		int dir = ((Integer)validMoves.get(index)).intValue();
		// move in that direction
		Move m = new Move(dir);
		history.add(m);
		movesMade++;
		return m;
	    }
	} else {
	    // trapped like a rat!
	    Move m = new Move(P);
	    history.add(m);
	    return m;
	}
	
	
    }
    
}
