package cecj.app.othello;

import cecj.statistics.ObjectiveFitnessCalculator;
import ec.EvolutionState;
import ec.Individual;
import ec.util.Parameter;
import ec.vector.DoubleVectorIndividual;
import games.BoardGame;
import games.Player;
import games.scenarios.GameScenario;
import games.scenarios.RandomizedTwoPlayersGameScenario;

public abstract class OthelloPlayerFitnessCalculator implements ObjectiveFitnessCalculator {

	protected static final int WPC_LENGTH = 64;
	
	protected static final String P_EVALUATOR_RANDOMNESS = "evaluator-randomness";
	protected static final String P_EVALUATED_RANDOMNESS = "evaluated-randomness";

	private static final String P_PLAY_BOTH = "play-both";
	private static final String P_REPEATS = "repeats";

	protected double evaluatedRandomness;
	protected double evaluatorRandomness;
	private boolean playBoth;
	private int repeats;

	public void setup(EvolutionState state, Parameter base) {
		Parameter randomnessParam = base.push(P_EVALUATED_RANDOMNESS);
		evaluatedRandomness = state.parameters.getDoubleWithDefault(randomnessParam, null, 0);

		randomnessParam = base.push(P_EVALUATOR_RANDOMNESS);
		evaluatorRandomness = state.parameters.getDoubleWithDefault(randomnessParam, null, 0);

		Parameter repetitionsParam = base.push(P_REPEATS);
		repeats = state.parameters.getIntWithDefault(repetitionsParam, null, 1);

		Parameter playBothParam = base.push(P_PLAY_BOTH);
		playBoth = state.parameters.getBoolean(playBothParam, null, false);
	}

	public float calculateObjectiveFitness(EvolutionState state, Individual ind) {
		if (!(ind instanceof DoubleVectorIndividual)) {
			state.output.error("Othello players should be represented by floats vectors\n");
		}

		double[] wpc1 = ((DoubleVectorIndividual) ind).genome;

		if (wpc1.length != WPC_LENGTH) {
			state.output.error("Players WPC vectors length should be 64\n");
		}

		Player player1 = new OthelloPlayer(wpc1);
		Player player2 = getPlayer();
		BoardGame game = new OthelloGame(new OthelloBoard());

		GameScenario scenario1 = new RandomizedTwoPlayersGameScenario(state.random[0], new Player[] { player1,
				player2 }, new double[] { evaluatedRandomness, evaluatorRandomness });
		GameScenario scenario2 = new RandomizedTwoPlayersGameScenario(state.random[0], new Player[] { player2,
				player1 }, new double[] { evaluatorRandomness, evaluatedRandomness });

		float sum = 0;
		for (int r = 0; r < repeats; r++) {
			game.reset();
			sum += ((scenario1.play(game) > 0) ? 1 : 0);
			if (playBoth) {
				game.reset();
				sum += ((scenario2.play(game) < 0) ? 1 : 0);
			}
		}

		if (playBoth) {
			return sum / (repeats * 2);
		} else {
			return sum / repeats;
		}
	}

	protected abstract OthelloPlayer getPlayer();
}
