//
// Schizo is a kind of parasite, since it actually uses
// different classes to determine its own turns. It's only
// feature is a simple yet effective adaption mechanism to
// decide which strategies turn to take...
//
import java.util.Vector;

//
// This is the grand mother of (nearly) all reasonable Schizo's:
//  SchizoF, SchizoJ, SchizoL
// Instances based on other strategy combinations can easily 
// achieved by overloading myClasses and nameList();
class Schizo extends Strategy
{
    	private Strategy Comps[];
	private String CompsName[];

	private double weight[];
	private int trial[];
	private int ltl[], scl[], cOl;

	//---- This defines the set of basic strategies used...
	final static String myClasses[] = 
		{ "NFocusPQ", "TimeWarp", "Focus4",
				"Freberg", "Lehrer", "Opus" };
	String[] nameList() { return myClasses; }
	//----

	void priv_init()
	{
		int num;
		Vector compVec = new Vector();
		Vector compNam = new Vector();
		//
		for ( num = 0; num < nameList().length; num++)
			getAClass(nameList()[num], compVec, compNam);

		//
		// Copy the strategies in an array for efficency
		Comps     = new Strategy[compVec.size()];
		CompsName = new String  [compVec.size()];
		compNam.copyInto(CompsName);
		compVec.copyInto(Comps);
		// Give the Garbage collector a hint
		compVec = compNam = null;

		//
		// Initialize all participating Strategies
		for (num = 0; num < Comps.length; num++)
		{
			try
			{
				System.out.println("   "+this+" init: "+num);
				Comps[num].init(maxSteps,
						maxPlayableNumber,
						stepBonus,
						opponents);
			}
			catch ( Exception e )
			{
				disqualify(0, num, e);
			}
		}
		weight = new double[Comps.length];
		for ( num = 0; num < Comps.length; num++)
			weight[num] = 1.0;
		trial = new int[Comps.length];
		//
		ltl = new int[opponents];
		scl = new int[opponents];
	}

	private final void getAClass(String name,
				Vector compVec, Vector compNam)
	{
		Strategy newComp;
		String s = name.trim();

		try
		{
			newComp=(Strategy)Class.forName(s.trim()).newInstance();
			compVec.addElement(newComp);
			compNam.addElement(s);
			System.out.println("   Schizo loaded: "+s);
		} catch (Exception e)
		{
			System.out.println(" ? Strategy load error: "
					+s);
		}
	}

	int eval(int turns[], int carry, int scores[])
	{
		int i, j, sum, t, parts, t0;

		if ( turns[0] == 0 ) return 0;

		sum = carry + stepBonus;
		parts = 0;
		t0 = 0;
		for (i = 0; i < opponents; i++) if ( scores[i] > 0 )
		{
			if ( turns[i] > 0 )
			{
				sum  += turns[i];
				t = 0; // -1;
				for (j = 0; j < opponents; j++)
				{
					if ( turns[j] > 0 && turns[i] >= turns[j] ) t++;
				}
				if (i == 0) t0 = t;
				parts   += t;
			}
		}
		return (int) ( sum * t0 ) / parts - turns[0];
	}


	public int initial_turn() 
	{ 
		int num, t, tm;

		// Play the highest number suggest by any sub-strategy
		t = tm = 0;
		for (num = 0; num < Comps.length; num++)
		{
			try
			{
				t = Comps[num].initial_turn();
				if ( t > tm ) tm = t;
			}
			catch ( Exception e )
			{
				disqualify(0, num, e);
			}
		}
		return t;
	}

	public int turn(int last_turns[], int scores[],
					int carryOver, int turn_no)
	{
		int i, num, t;
		double ww, wweight[], eval[];
		int lt2[], sc2[];
		int plus, minus;

		lt2 = new int[opponents];

		// Modify weights according to previous success
		// -- missing yet
		if ( turn_no > 2 ) 
		{
			for ( i = 0; i < opponents; i++) lt2[i] = last_turns[i];
			eval = new double[Comps.length];
			for ( num = 0; num < Comps.length; num++)
			{
				lt2[0] = trial[num];
				eval[num] = eval(lt2,cOl,scl); 
			}
			for ( num = 0; num < Comps.length; num++ )
			{
				plus = minus = 0;
				for (i = 0; i < Comps.length; i++)
				{
					if (eval[i] > eval[num]) minus++;
					if (eval[i] < eval[num]) plus++;
				}
				for (i = 0; i < minus; i++) weight[num] *= 0.7;
				weight[num] += plus;
			}
		}
		//
		// Calculate new turn
		wweight = new double[maxPlayableNumber+1];
		sc2 = new int[opponents];
		for ( i = 0; i <= maxPlayableNumber; i++ ) wweight[i] = 0.0;
		ww = 0.0;
		t = 0;
		for (num = 0; num < Comps.length; num++) if (weight[num] > 0.0)
		{
			for ( i = 0; i < opponents; i++)
			{
				lt2[i] = last_turns[i];
				sc2[i] = scores[i];
			}
			try
			{
				t = Comps[num].turn(lt2,sc2,carryOver,turn_no);
				trial[num] = t;
				wweight[t] += weight[num];
				ww += weight[num];
			}
			catch ( Exception e )
			{
				disqualify(turn_no, num, e);
			}
		}
		// Weighted Median (is this the usual way to do it?)
		ww /= 2.0;
		t = 0;
		while ( t <= maxPlayableNumber && ww > wweight[t] )
		{	
			ww -= wweight[t];
			t++;
		}
		// Remember one step of history for success evaluation
		cOl = carryOver;
		for (i = 0; i< opponents; i++) 
		{
			ltl[i] = last_turns[i];
			scl[i] = scores[i];
		}
		//
		return t;	
	}

	private void disqualify(int t, int no, Exception e)
	{
		System.out.println(t+". Killing "+ no+" e="+e);
		weight[no] = 0.0;
	}
}

//
// SchizoR is considered a dead end of Schizo evolution,
// but maybe someone else can find s.th. interesting in it...
//
class SchizoR extends Strategy
{
    	private Strategy Comps[];
	private String CompsName[];

	private double weight[];
	private int trial[];
	private int ltl[], scl[], cOl;

	private final int metaCount = 2;
	private double metaWeight[];
	private int metaTrial[];

	void priv_init()
	{
		int num;
		Vector compVec = new Vector();
		Vector compNam = new Vector();
		//
		getAClass("NFocusPQ",compVec,compNam);
		getAClass("TimeWarp",compVec,compNam);
		getAClass("Focus4",compVec,compNam);
		getAClass("Freberg",compVec,compNam);
		getAClass("Lehrer",compVec,compNam);
		getAClass("Opus",compVec,compNam);
		// Seems to degrade performance:
		// getAClass("Random11",compVec,compNam);
		//
		// Copy the strategies in an array for efficency
		Comps     = new Strategy[compVec.size()];
		CompsName = new String  [compVec.size()];
		compNam.copyInto(CompsName);
		compVec.copyInto(Comps);
		// Give the Garbage collector a hint
		compVec = compNam = null;

		//
		// Initialize all participating Strategies
		for (num = 0; num < Comps.length; num++)
		{
			try
			{
				System.out.println("   Schizo init: "+num);
				Comps[num].init(maxSteps,
						maxPlayableNumber,
						stepBonus,
						opponents);
			}
			catch ( Exception e )
			{
				disqualify(0, num, e);
			}
		}
		weight = new double[Comps.length];
		for ( num = 0; num < Comps.length; num++)
			weight[num] = 1.0;
		trial = new int[Comps.length];
		//
		ltl = new int[opponents];
		scl = new int[opponents];
		//
		metaWeight = new double[metaCount];
		metaTrial  = new int[metaCount];
		metaWeight[0] = 0;
		metaWeight[1] = 0;
	}

	private final void getAClass(String name,
				Vector compVec, Vector compNam)
	{
		Strategy newComp;
		String s = name.trim();

		try
		{
			newComp=(Strategy)Class.forName(s.trim()).newInstance();
			compVec.addElement(newComp);
			compNam.addElement(s);
			System.out.println("   Schizo loaded: "+s);
		} catch (Exception e)
		{
			System.out.println(" ? Strategy load error: "
					+s);
		}
	}

	int eval(int turns[], int carry, int scores[])
	{
		int i, j, sum, t, parts, t0;

		if ( turns[0] == 0 ) return 0;

		sum = carry + stepBonus;
		parts = 0;
		t0 = 0;
		for (i = 0; i < opponents; i++) if ( scores[i] > 0 )
		{
			if ( turns[i] > 0 )
			{
				sum  += turns[i];
				t = 0; // -1;
				for (j = 0; j < opponents; j++)
				{
					if ( turns[j] > 0 && turns[i] >= turns[j] ) t++;
				}
				if (i == 0) t0 = t;
				parts   += t;
			}
		}
		return (int) ( sum * t0 ) / parts - turns[0];
	}


	public int initial_turn() 
	{ 
		int num, t, tm;

		// Play the highest number suggest by any sub-strategy
		t = tm = 0;
		for (num = 0; num < Comps.length; num++)
		{
			try
			{
				t = Comps[num].initial_turn();
				if ( t > tm ) tm = t;
			}
			catch ( Exception e )
			{
				disqualify(0, num, e);
			}
		}
		return t;
	}

	public int turn(int last_turns[], int scores[],
					int carryOver, int turn_no)
	{
		int i, num, t;
		double ww, wweight[], eval[], metaEval[];
		int lt2[], sc2[];
		int plus = 0, minus = 0;

		lt2 = new int[opponents];

		if ( turn_no > 2 ) 
		{	
			//
			// Modify weights according to previous success
			//
			for ( i = 0; i < opponents; i++) lt2[i] = last_turns[i];
			eval = new double[Comps.length];
			for ( num = 0; num < Comps.length; num++)
			{
				lt2[0] = trial[num];
				eval[num] = eval(lt2,cOl,scl); 
			}
			for ( num = 0; num < Comps.length; num++ )
			{
				plus = minus = 0;
				for (i = 0; i < Comps.length; i++)
				{
					if (eval[i] > eval[num]) minus++;
					if (eval[i] < eval[num]) plus++;
				}
				for (i = 0; i < minus; i++) weight[num] *= 0.7;
				weight[num] += plus;
			}
			//
			// Modify metaWeights according to success...
			//
			metaEval = new double[metaCount];
			for ( num = 0; num < metaCount; num++)
			{
				lt2[0] = metaTrial[num];
				metaEval[num] = eval(lt2,cOl,scl); 
			}
			for ( num = 0; num < metaCount; num++ )
			{
				plus = minus = 0;
				for (i = 0; i < metaCount; i++)
				{
					if (metaEval[i] > metaEval[num]) minus++;
					if (metaEval[i] < metaEval[num]) plus++;
				}
				for (i = 0; i < minus; i++) metaWeight[num] *= 0.7;
				metaWeight[num] += plus;
			}
if ( plus > 0 || minus > 0 ) System.out.println("* "+turn_no+"*** w="+metaWeight[0]+" : "+metaWeight[1]);
		}
		//
		// Calculate new turn
		wweight = new double[maxPlayableNumber+1];
		sc2 = new int[opponents];
		for ( i = 0; i <= maxPlayableNumber; i++ ) wweight[i] = 0.0;
		ww = 0.0;
		t = 0;
		for (num = 0; num < Comps.length; num++) if (weight[num] > 0.0)
		{
			for ( i = 0; i < opponents; i++)
			{
				lt2[i] = last_turns[i];
				sc2[i] = scores[i];
			}
			try
			{
				t = Comps[num].turn(lt2,sc2,carryOver,turn_no);
				trial[num] = t;
				wweight[t] += weight[num];
				ww += weight[num];
			}
			catch ( Exception e )
			{
				disqualify(turn_no, num, e);
			}
		}
		//
		// Meta selection phase...
		// only 2 choices in the moment...
		t = 0;
		for (i = 0; i <= maxPlayableNumber; i++)
			if ( wweight[i] >= wweight[t] ) t = i;
		metaTrial[0] = t;
		//
		ww /= 2.0;
		t = 0;
		while ( t <= maxPlayableNumber && ww > wweight[t] )
		{	
			ww -= wweight[t];
			t++;
		}
		metaTrial[1] = t;
		//
		// Noch sehr einfach
		t = metaWeight[0] > metaWeight[1] // Biased
			? metaTrial[0]
			: metaTrial[1];
		// Remember one step of history for success evaluation
		cOl = carryOver;
		for (i = 0; i< opponents; i++) 
		{
			ltl[i] = last_turns[i];
			scl[i] = scores[i];
		}
		//
		return t;	
	}

	private void disqualify(int t, int no, Exception e)
	{
		System.out.println(t+". Killing "+ no+" e="+e);
		weight[no] = 0.0;
	}
}

// 
// Variations based on the big mother, just
// switching base strategies
class SchizoF extends Schizo
{
	final static String myClasses[] = 
		{ "NFocusPQ", "TimeWarp", "Focus4" };
	String[] nameList() { return myClasses; }
}

class SchizoJ extends Schizo
{
	final static String myClasses[] = 
		{ "Freberg", "Lehrer", "Opus" };
	String[] nameList() { return myClasses; }
}
class SchizoL extends Schizo
{
	final static String myClasses[] = 
		{ "Random11", "WinningMode", "Pi", "Ip",
				"GentleGiant", "Shark7", "Nihilist" };
	String[] nameList() { return myClasses; }
}
