public class GrammaticalEvolution extends Algorithm<Variable<Integer>> {
  
  public static final Logger logger = Logger.getLogger(NSGAII.class.getName());
  private boolean NEUTRALMUTATION = false;
  private static final boolean jecoPopulationMerge = false; // true if you want to mix old and new generations and then select best individuals

  /////////////////////////////////////////////////////////////////////////
  protected int maxGenerations;
  protected int maxPopulationSize;
  /////////////////////////////////////////////////////////////////////////
  protected Comparator<Solution<Variable<Integer>>> dominance;
  protected int currentGeneration;
  protected Solutions<Variable<Integer>> population;
  public Solutions<Variable<Integer>> getPopulation() { return population; }
  protected MutationOperator<Variable<Integer>> mutationOperator;
  protected NeutralMutation<Variable<Integer>> neutralMutation;
  protected CrossoverOperator<Variable<Integer>> crossoverOperator;
  protected SelectionOperator<Variable<Integer>> selectionOperator;
  /////////////////////////////////////////////////////////////////////////
  public Solution<Variable<Integer>> absoluteBest;
  public ArrayList<ArrayList<Double>> absoluteBestObjetives;
  public ArrayList<ArrayList<Double>> bestObjetives;
  public ArrayList<ArrayList<Double>> averageObjetives;
  public ArrayList<ArrayList<Double>> worstObjetives;
  ////////////////////////////////////////////////////////////////////////
  private final int eliteSize;

  public GrammaticalEvolution(Problem<Variable<Integer>> problem, int maxPopulationSize, int maxGenerations, double probMutation, double probCrossover, int eliteSize) {
      super(problem);
      this.maxPopulationSize = maxPopulationSize;
      this.maxGenerations = maxGenerations;
      this.mutationOperator = new IntegerFlipMutation<Variable<Integer>>(problem, probMutation);
      this.neutralMutation = new NeutralMutation<Variable<Integer>>(problem, probMutation);
      this.crossoverOperator = new SinglePointCrossover<Variable<Integer>>(problem, SinglePointCrossover.DEFAULT_FIXED_CROSSOVER_POINT, probCrossover, SinglePointCrossover.ALLOW_REPETITION);
      this.selectionOperator = new BinaryTournamentNSGAII<Variable<Integer>>();
      
      this.absoluteBestObjetives = new ArrayList<>(this.maxGenerations);
      this.bestObjetives = new ArrayList<>(this.maxGenerations);
      this.averageObjetives = new ArrayList<>(this.maxGenerations);
      this.worstObjetives = new ArrayList<>(this.maxGenerations);
      
      this.eliteSize = eliteSize;
  }

    public GrammaticalEvolution(Problem<Variable<Integer>> problem, int maxPopulationSize, int maxGenerations, double probMutation, double probCrossover, int eliteSize, boolean neutralMutation) {
      this(problem, maxPopulationSize, maxGenerations, probMutation, probCrossover, eliteSize);
      this.NEUTRALMUTATION = neutralMutation;
    }

  public GrammaticalEvolution(Problem<Variable<Integer>> problem, int maxPopulationSize, int maxGenerations) {
    this(problem, maxPopulationSize, maxGenerations, 1.0/problem.getNumberOfVariables(), SinglePointCrossover.DEFAULT_PROBABILITY, EliteSelectorOperator.DEFAULT_ELITE_SIZE);
  }

  @Override
  public void initialize() {
      dominance = new SolutionDominance<Variable<Integer>>();
      // Create the initial solutionSet
      population = problem.newRandomSetOfSolutions(maxPopulationSize);
      problem.evaluate(population);
      // Compute crowding distance
      CrowdingDistance<Variable<Integer>> assigner = new CrowdingDistance<Variable<Integer>>(problem.getNumberOfObjectives());
      assigner.execute(population);
      currentGeneration = 0;
  }

  @Override
  public Solutions<Variable<Integer>> execute() {
      int nextPercentageReport = 10;
      this.notifyStart();
      
      while (!this.stop && currentGeneration < maxGenerations) {
          step();
          int percentage = Math.round((currentGeneration * 100) / maxGenerations);
          if (percentage == nextPercentageReport) {
              logger.info(percentage + "% performed ...");
              logger.info("@ # Gen. "+currentGeneration+", objective values:");
              // Print current population
              Solutions<Variable<Integer>> pop = this.getPopulation();
              for (Solution<Variable<Integer>> s : pop) {
                  for (int i=0; i<s.getObjectives().size();i++) {
                      logger.fine(s.getObjective(i)+";");
                  }
              }
              nextPercentageReport += 10;
          }
          
          this.collectStatistics();
          
          // Notify observers about current generation (object can be a map with more data)
          this.setChanged();
          this.notifyObservers(currentGeneration);
      }
      this.notifyEnd();
      return this.getCurrentSolution();
  }

  public Solutions<Variable<Integer>> getCurrentSolution() {
      population.reduceToNonDominated(dominance);
      return population;
  }

  public void step() {
      currentGeneration++;
      // Create the offSpring solutionSet
      if (population.size() < 2) {
          logger.severe("Generation: " + currentGeneration + ". Population size is less than 2.");
          return;
      }

      Solutions<Variable<Integer>> childPop = new Solutions<Variable<Integer>>();
      Solution<Variable<Integer>> parent1, parent2;
      for (int i = 0; i < (maxPopulationSize / 2); i++) {
          //obtain parents
          parent1 = selectionOperator.execute(population).get(0);
          parent2 = selectionOperator.execute(population).get(0);
          if(NEUTRALMUTATION){
	          neutralMutation.execute(parent1);
	          neutralMutation.execute(parent2);
          }
          Solutions<Variable<Integer>> offSpring = crossoverOperator.execute(parent1, parent2);
          for (Solution<Variable<Integer>> solution : offSpring) {
              childPop.add(solution);
          }
      } // for
      
      
      //For de mutación
      for (Solution<Variable<Integer>> solution : childPop) {
          mutationOperator.execute(solution);
      }
      
      //Eval
      problem.evaluate(childPop);

      if(jecoPopulationMerge) {
    	// Create the solutionSet union of solutionSet and offSpring
	      Solutions<Variable<Integer>> mixedPop = new Solutions<Variable<Integer>>();
	      mixedPop.addAll(population);
	      mixedPop.addAll(childPop);
	      
	      // Reducing the union
	      population = reduce(mixedPop, maxPopulationSize);
      }
      else {
    	  // Own method  	  
    	  population.sort(dominance);
		  for(int i = 0; i < eliteSize; i++) {  			  
			  childPop.add(population.get(i));
		  }
    	  
    	  population = reduce(childPop, maxPopulationSize);
      }

      logger.fine("Generation " + currentGeneration + "/" + maxGenerations + "\n" + population.toString());
  } // step

  public Solutions<Variable<Integer>> reduce(Solutions<Variable<Integer>> pop, int maxSize) {
      FrontsExtractor<Variable<Integer>> extractor = new FrontsExtractor<Variable<Integer>>(dominance);
      ArrayList<Solutions<Variable<Integer>>> fronts = extractor.execute(pop);

      Solutions<Variable<Integer>> reducedPop = new Solutions<Variable<Integer>>();
      CrowdingDistance<Variable<Integer>> assigner = new CrowdingDistance<Variable<Integer>>(problem.getNumberOfObjectives());
      Solutions<Variable<Integer>> front;
      int i = 0;
      while (reducedPop.size() < maxSize && i < fronts.size()) {
          front = fronts.get(i);
          assigner.execute(front);
          reducedPop.addAll(front);
          i++;
      }

      ComparatorNSGAII<Variable<Integer>> comparator = new ComparatorNSGAII<Variable<Integer>>();
      if (reducedPop.size() > maxSize) {
          Collections.sort(reducedPop, comparator);
          while (reducedPop.size() > maxSize) {
              reducedPop.remove(reducedPop.size() - 1);
          }
      }
      return reducedPop;
  }
  
  protected void collectStatistics() {
	  Solution<Variable<Integer>> currentBest = this.population.get(0);
	  Comparator<Solution<Variable<Integer>>> comparator = this.dominance;
	  
	  if (this.absoluteBest == null || comparator.compare(currentBest, this.absoluteBest) < 0) {
		  this.absoluteBestObjetives.add(currentBest.getObjectives());
		  this.absoluteBest = currentBest;
	  }
	  else
		  this.absoluteBestObjetives.add(this.absoluteBest.getObjectives());
	  
	  ArrayList<Double> avg = new ArrayList<>(population.get(0).getObjectives().size());
	  for (int i = 0; i < population.get(0).getObjectives().size(); i++) {
		  avg.add(0.0);
	  }
	  for(Solution<Variable<Integer>> sol : this.population) {
		  for (int i = 0; i < sol.getObjectives().size(); i++) {
			  avg.set(i, avg.get(i) + sol.getObjective(i));
		  }
	  }
	  for (int i = 0; i < avg.size(); i++) {
		  avg.set(i, avg.get(i) / this.maxPopulationSize);
	  }
	  this.averageObjetives.add(avg);
	  
      this.bestObjetives.add(currentBest.getObjectives());
      this.worstObjetives.add(this.population.get(this.maxPopulationSize - 1).getObjectives());
  }

  public void setMutationOperator(MutationOperator<Variable<Integer>> mutationOperator) {
      this.mutationOperator = mutationOperator;
  }

  public void setCrossoverOperator(CrossoverOperator<Variable<Integer>> crossoverOperator) {
      this.crossoverOperator = crossoverOperator;
  }

  public void setSelectionOperator(SelectionOperator<Variable<Integer>> selectionOperator) {
      this.selectionOperator = selectionOperator;
  }

  public void setMaxGenerations(int maxGenerations) {
      this.maxGenerations = maxGenerations;
  }

  public void setMaxPopulationSize(int maxPopulationSize) {
      this.maxPopulationSize = maxPopulationSize;
  }
  
  public int getCurrentGeneration(){
	  return this.currentGeneration;
  }
  
} public class PacmanGrammaticalEvolution extends AbstractProblemGE {
	public static final Logger logger = Logger.getLogger(PacmanGrammaticalEvolution.class.getName());
	public static BufferedWriter writer;
	public static Path path = FileSystems.getDefault().getPath("logs", "Registro.log");
	
	//Execution parameters
  	public MOFitnessWrapper fitnessWrapper;
  	public int iterPerIndividual; // games ran per evaluation
  	public int codonUpperBound;
  	Controller<EnumMap<GHOST,MOVE>> ghostController;
  	
  	public PacmanGrammaticalEvolution(Controller<EnumMap<GHOST,MOVE>> ghostController, String pathToBnf, MOFitnessWrapper fitnessWrapper, int iterPerIndividual, int chromosomeLength, int maxCntWrappings, int codonUpperBound) {
  		super(pathToBnf, fitnessWrapper.getNumberOfObjs(), chromosomeLength, maxCntWrappings, codonUpperBound);

		this.fitnessWrapper = fitnessWrapper;
		this.iterPerIndividual = iterPerIndividual;
		//chromosomeLenght == numOfVariables, no need to save
		this.codonUpperBound = codonUpperBound;
		this.ghostController = ghostController;
		
		// Create log
		if(writer == null) {
		  	File dir = new File("logs");
		  	dir.mkdir();
		  	
		  	try {
		  	    Files.delete(path);
		  	} catch (NoSuchFileException x) {
		  	    //System.err.format("%s: no such" + " file or directory%n", path);
		  	} catch (DirectoryNotEmptyException x) {
		  	    System.err.format("%s not empty%n", path);
		  	} catch (IOException x) {
		  	    // File permission problems are caught here.
		  	    System.err.println(x);
		  	}
		  	
		  	try {
				writer = Files.newBufferedWriter(path, StandardCharsets.UTF_8);
			} catch (IOException e) {
				System.err.println("Error opening the log.");
				e.printStackTrace();
			}
		}
  	}
  	
  	public void evaluate(Solution<Variable<Integer>> solution) {
		Phenotype phenotype = generatePhenotype(solution);
		if(correctSol)
			evaluate(solution, phenotype);
		else {
			for(int i=0; i<super.numberOfObjectives; ++i) {
				solution.getObjectives().set(i, this.fitnessWrapper.getWorstFitness(i));
			}
		}
	}

	public void evaluate(Solution<Variable<Integer>> solution, Phenotype phenotype) {
		CustomExecutor exec = new CustomExecutor();
		GrammaticalAdapterController pacman = new GrammaticalAdapterController(phenotype.toString());
		
		GameInfo avgGameInfo = exec.runExecution(pacman, this.ghostController, iterPerIndividual);
		
		ArrayList<Double> MOFitness = fitnessWrapper.evaluate(avgGameInfo);
		
		// Security check
		for (int i = 0; i < MOFitness.size(); i++) {
			Double objFitness = MOFitness.get(i);
			
			if (objFitness < 0) {
				logger.severe("ERROR: NEGATIVE FITNESS");
				System.err.println("FITNESS < 0!!!!!!");
				MOFitness.set(i, this.fitnessWrapper.getWorstFitness(i));
			}
		}
		
		// Return objectives
		for (int i = 0; i < MOFitness.size(); i++) {
			solution.getObjectives().set(i, MOFitness.get(i));
		}
	}	

	@Override
	public PacmanGrammaticalEvolution clone() {
		PacmanGrammaticalEvolution clone = new PacmanGrammaticalEvolution(this.ghostController, this.pathToBnf, this.fitnessWrapper, this.iterPerIndividual, this.numberOfVariables, this.maxCntWrappings, this.codonUpperBound);
		return clone;
	}

	public static void main(String[] args) {
		int populationSize = 100;// = 400;
		int generations = 100;// = 500;
		double mutationProb = 0.02;
	  	double crossProb = 0.6;
	  	MOFitnessWrapper fitnessWrapper = new MOFitnessWrapper(new NaiveFitness());
	  	int iterPerIndividual = 2; // games ran per evaluation
	  	int numberOfVariables = 100;
	  	int codonUpperBound = 256;
	  	int maxCntWrappings = 3;
	  	int elite = 10;
	  	String grammar = "grammar/base.bnf";
	  	Controller<EnumMap<GHOST,MOVE>> ghosts = new StarterGhosts();
	  	
		// First create the problem
		PacmanGrammaticalEvolution problem = new PacmanGrammaticalEvolution(ghosts, grammar, fitnessWrapper, iterPerIndividual, numberOfVariables, maxCntWrappings, codonUpperBound);
		// Second create the algorithm
		GrammaticalEvolution algorithm = new GrammaticalEvolution(problem, populationSize, generations, mutationProb, crossProb, elite);
		
		// We can set operators using
		//algorithm.setSelectionOperator(selectionOperator);
		//algorithm.setCrossoverOperator(crossoverOperator);
		//algorithm.setMutationOperator(mutationOperator);

		/** We can set multithread execution:
		// Set multithreading
		MasterWorkerThreads<Variable<Integer>> masterWorker = new MasterWorkerThreads<Variable<Integer>>(algorithm, problem, avalaibleThreads);
		// Execute algorithm
		Solutions<Variable<Integer>> solutions = masterWorker.execute();

		or single thread: */
		algorithm.initialize();
		Solutions<Variable<Integer>> solutions = algorithm.execute();

		for (Solution<Variable<Integer>> solution : solutions) {
			logger.info("Fitness = " + solution.getObjectives().get(0));
			logger.info("Phenotype = (" + problem.generatePhenotype(solution).toString() + ")");
		}
	}

} public class MOFitnessWrapper {
	public ArrayList<FitnessEvaluatorInterface> funcs;
	
	public MOFitnessWrapper(FitnessEvaluatorInterface... funcs) {
		this.funcs = new ArrayList<>(funcs.length);

		Collections.addAll(this.funcs, funcs);
	}

	public void addObjectiveFunction(FitnessEvaluatorInterface f) {
		if (f != null)
			this.funcs.add(f);
	}
	
	public ArrayList<Double> evaluate(GameInfo gi) {
		ArrayList<Double> fit = new ArrayList<>(this.funcs.size());
		
		for(FitnessEvaluatorInterface f : this.funcs) {
			fit.add(f.evaluate(gi));
		}
		
		return fit;
	}
	
	public Double getWorstFitness(int i) {
		return this.funcs.get(i).worstFitness();
	}
	
	public int getNumberOfObjs() {
		return this.funcs.size();
	}
	
	public String getFuncName(int i) {
		return this.funcs.get(i).getName();
	}
	
	public String[] getFuncNames() {
		String[] ret = new String[this.getNumberOfObjs()];
		
		for (FitnessEvaluatorInterface func : this.funcs) {
			Arrays.fill(ret, func.getName());
		}
		
		return ret;
	}
	
	public void clear() {
		this.funcs.clear();
	}

} public class NaiveFitness implements FitnessEvaluatorInterface {

	@Override
	public double evaluate(GameInfo gi) {
		return 100000 - gi.getAvgScore();
	}
	
	public static double fitnessToPoints(double fitness) {
		return 100000 - fitness;
	}
	
	@Override
	public double worstFitness() {
		return 100000;
	}

	@Override
	public String getName() {
		return "Naive_fitness";
	}

} public class MasterWorkerThreads<V extends Variable<?>> extends Problem<V> {

  private static final Logger logger = Logger.getLogger(MasterWorkerThreads.class.getName());
  protected Algorithm<V> algorithm = null;
  protected Problem<V> problem = null;
  protected LinkedBlockingQueue<Solution<V>> sharedQueue = new LinkedBlockingQueue<Solution<V>>();
  protected ArrayList<Problem<V>> problemClones = new ArrayList<Problem<V>>();
  protected Integer numWorkers = null;

  public MasterWorkerThreads(Algorithm<V> algorithm, Problem<V> problem, Integer numWorkers) {
    super(problem.getNumberOfVariables(), problem.getNumberOfObjectives());
    for (int i = 0; i < numberOfVariables; ++i) {
      super.lowerBound[i] = problem.getLowerBound(i);
      super.upperBound[i] = problem.getUpperBound(i);
    }
    this.algorithm = algorithm;
    this.problem = problem;
    this.numWorkers = numWorkers;
    for (int i = 0; i < numWorkers; ++i) {
      problemClones.add(problem.clone());
    }
  }

  public MasterWorkerThreads(Algorithm<V> algorithm, Problem<V> problem) {
    this(algorithm, problem, Runtime.getRuntime().availableProcessors());
  }

  @Override
  public void evaluate(Solutions<V> solutions) {
    sharedQueue.addAll(solutions);
    LinkedList<Worker<V>> workers = new LinkedList<Worker<V>>();
    for (int i = 0; i < numWorkers; ++i) {
      Worker<V> worker = new Worker<V>(problemClones.get(i), sharedQueue);
      workers.add(worker);
      worker.start();
    }
    for (Worker<V> worker : workers) {
      try {
        worker.join();
      } catch (InterruptedException e) {
        logger.severe(e.getLocalizedMessage());
        logger.severe("Main thread cannot join to: " + worker.getId());
      }
    }
  }
  
  @Override
  public void evaluate(Solution<V> solution) {
    logger.log(Level.SEVERE, this.getClass().getSimpleName() + "::evaluate() - I do not know why I am here, doing nothing to evaluate solution");
  }

  @Override
  public Solutions<V> newRandomSetOfSolutions(int size) {
    return problem.newRandomSetOfSolutions(size);
  }

  public Solutions<V> execute() {
    algorithm.setProblem(this);
    algorithm.initialize();
    return algorithm.execute();
  }
  
  public void stop() {
	this.algorithm.stopExection();
  }

  @Override
  public Problem<V> clone() {
    logger.severe("This master cannot be cloned.");
    return null;
  }

  public static void main(String[] args) {
    long begin = System.currentTimeMillis();
    // First create the problem
    GrammaticalEvolution_example problem = new GrammaticalEvolution_example("test/grammar_example.bnf");
    // Second create the algorithm
    GrammaticalEvolution algorithm = new GrammaticalEvolution(problem, 100, 250);
    // Now the master/worker
    MasterWorkerThreads<Variable<Integer>> masterWorker = new MasterWorkerThreads<Variable<Integer>>(algorithm, problem, 4);
    Solutions<Variable<Integer>> solutions = masterWorker.execute();
    for (Solution<Variable<Integer>> solution : solutions) {
      logger.info("Fitness = (" + solution.getObjectives().get(0) + ", " + solution.getObjectives().get(1) + ")");
      logger.info("Phenotype = (" + problem.generatePhenotype(solution).toString() + ")");
    }
    long end = System.currentTimeMillis();
    logger.info("Time: " + ((end - begin) / 1000.0) + " seconds");
  }
} @SuppressWarnings("serial")
public class Solutions<V extends Variable<?>> extends ArrayList<Solution<V>> {

  public Solutions() {
    super();
  }

  /**
   * Keep this set of solutions non-dominated. Returns the set of dominated
   * solutions.
   *
   * @param comparator Comparator used.
   * @return The set of dominated solutions.
   */
  public Solutions<V> reduceToNonDominated(Comparator<Solution<V>> comparator) {
    Solutions<V> rest = new Solutions<V>();
    int compare;
    Solution<V> solI;
    Solution<V> solJ;
    for (int i = 0; i < size() - 1; i++) {
      solI = get(i);
      for (int j = i + 1; j < size(); j++) {
        solJ = get(j);
        compare = comparator.compare(solI, solJ);
        if (compare < 0) { // i dominates j
          rest.add(solJ);
          remove(j--);
        } else if (compare > 0) { // j dominates i
          rest.add(solI);
          remove(i--);
          j = size();
        } else if (solI.equals(solJ)) { // both are equal, just one copy
          remove(j--);
        }
      }
    }
    return rest;
  }

  @Override
  public String toString() {
    StringBuilder buffer = new StringBuilder();
    for (Solution<V> solution : this) {
      buffer.append(solution.toString());
      buffer.append("\n");
    }
    return buffer.toString();
  }

  /**
   * Function that reads a set of solutions from a file.
   * @param filePath File path
   * @return The set of solutions in the archive.
   * @throws IOException 
   * @throws java.lang.Exception
   */
  public static Solutions<Variable<?>> readFrontFromFile(String filePath) throws IOException {
      Solutions<Variable<?>> solutions = new Solutions<Variable<?>>();
      BufferedReader reader = new BufferedReader(new FileReader(new File(filePath)));
      String line = reader.readLine();
      while (line != null) {
          String[] objectives = line.split(" ");
          if (line.length() > 0 && objectives != null && objectives.length > 0) {
              Solution<Variable<?>> solution = new Solution<Variable<?>>(objectives.length);
              for (int i = 0; i < objectives.length; ++i) {
                  solution.getObjectives().set(i, Double.valueOf(objectives[i]));
              }
              solutions.add(solution);
          }
          line = reader.readLine();
      }
      reader.close();
      return solutions;
  }

  /**
   * Function that reads N sets of solutions from a file.
   *
   * @param filePath File path
   * @return The set of solutions in the archive. Each solution set is separated
   * in the file by a blank line.
   */
  public static ArrayList<Solutions<Variable<?>>> readFrontsFromFile(String filePath) throws FileNotFoundException, IOException {
    ArrayList<Solutions<Variable<?>>> result = new ArrayList<Solutions<Variable<?>>>();
    BufferedReader reader = new BufferedReader(new FileReader(new File(filePath)));
    String line = reader.readLine();
    Solutions<Variable<?>> solutions = new Solutions<Variable<?>>();
    while (line != null) {
      String[] objectives = line.split(" ");
      if (line.length() <= 0 || objectives == null || objectives.length == 0) {
        if (solutions.size() > 0) {
          result.add(solutions);
        }
        solutions = new Solutions<Variable<?>>();
      } else {
        Solution<Variable<?>> solution = new Solution<Variable<?>>(objectives.length);
        for (int i = 0; i < objectives.length; ++i) {
          solution.getObjectives().set(i, Double.valueOf(objectives[i]));
        }
        solutions.add(solution);
      }
      line = reader.readLine();
    }
    reader.close();
    return result;
  }
  
  /**
   * Function that normalizes a set of fronts in the given interval.
   * @param setOfSolutions Set of fronts
   * @param dim Number of objectives
   * @param lowerBound lower bound
   * @param upperBound upper bound
   */
  public static void normalize(ArrayList<Solutions<Variable<?>>> setOfSolutions, int dim, double lowerBound, double upperBound) {
      double[] mins = new double[dim];
      double[] maxs = new double[dim];
      for (int i = 0; i < dim; ++i) {
          mins[i] = Double.POSITIVE_INFINITY;
          maxs[i] = Double.NEGATIVE_INFINITY;
      }

      Solutions<Variable<?>> allTheSolutions = new Solutions<Variable<?>>();
      for (Solutions<Variable<?>> solutions : setOfSolutions) {
          allTheSolutions.addAll(solutions);
      }
      // Get the maximum and minimun values:
      for (Solution<Variable<?>> solution : allTheSolutions) {
          for (int i = 0; i < dim; ++i) {
              double objI = solution.getObjectives().get(i);
              if (objI < mins[i]) {
                  mins[i] = objI;
              }
              if (objI > maxs[i]) {
                  maxs[i] = objI;
              }
          }
      }

      // Normalize:
      for (Solution<Variable<?>> solution : allTheSolutions) {
          for (int i = 0; i < dim; ++i) {
              double objI = solution.getObjectives().get(i);
              double newObjI = 1.0 + (objI - mins[i]) / (maxs[i] - mins[i]);
              solution.getObjectives().set(i, newObjI);
          }
      }

  }

  /**
   * Function that normalize a set of fronts in the interval [1,2]
   * @param setOfSolutions Set of fronts
   * @param dim Number of objectives
   */
  public static void normalize(ArrayList<Solutions<Variable<?>>> setOfSolutions, int dim) {
      Solutions.normalize(setOfSolutions, dim, 1.0, 2.0);
  }  
} public class Variable<T> {
    protected T value;

    public Variable(T value) {
        this.value = value;
    }

    public T getValue() { return value; }

    public void setValue(T value) { this.value = value; }
    
    @Override
    public Variable<T> clone() {
        return new Variable<T>(value);
    }

    @SuppressWarnings("unchecked")
		@Override
    public boolean equals(Object right) {
        Variable<T> var = (Variable<T>)right;
        return this.value.equals(var.value);
    }
    
    @Override
    public String toString()
    {
    	return value.toString();
    }
} @SuppressWarnings("unused")
public class Executor
{	
	/**
	 * The main method. Several options are listed - simply remove comments to use the option you want.
	 *
	 * @param args the command line arguments
	 */
	public static void main(String[] args)
	{
		Executor exec=new Executor();
		
		Controller<MOVE> pacman = new StarterPacMan();
		Controller<EnumMap<GHOST,MOVE>> ghosts = new StarterGhosts();
		/*
		int numTrials=1;
		exec.runExperiment(pacman, ghosts, numTrials);
		*/
		
		int delay=18;
		boolean visual=true;
		exec.runGame(pacman, ghosts, visual, delay);
		
		/*
		//run multiple games in batch mode - good for testing.
		int numTrials=10;
		exec.runExperiment(new RandomPacMan(),new RandomGhosts(),numTrials);
		 */
		
		/*
		//run a game in synchronous mode: game waits until controllers respond.
		int delay=5;
		boolean visual=true;
		exec.runGame(new RandomPacMan(),new RandomGhosts(),visual,delay);
  		 */
		
		/*
		//run the game in asynchronous mode.
		boolean visual=true;
//		exec.runGameTimed(new NearestPillPacMan(),new AggressiveGhosts(),visual);
		exec.runGameTimed(new StarterPacMan(),new StarterGhosts(),visual);
//		exec.runGameTimed(new HumanController(new KeyBoardInput()),new StarterGhosts(),visual);	
		*/
		
		/*
		//run the game in asynchronous mode but advance as soon as both controllers are ready  - this is the mode of the competition.
		//time limit of DELAY ms still applies.
		boolean visual=true;
		boolean fixedTime=false;
		exec.runGameTimedSpeedOptimised(new RandomPacMan(),new RandomGhosts(),fixedTime,visual);
		*/
		
		/*
		//run game in asynchronous mode and record it to file for replay at a later stage.
		boolean visual=true;
		String fileName="replay.txt";
		exec.runGameTimedRecorded(new HumanController(new KeyBoardInput()),new RandomGhosts(),visual,fileName);
		//exec.replayGame(fileName,visual);
		 */
	}
	
    /**
     * For running multiple games without visuals. This is useful to get a good idea of how well a controller plays
     * against a chosen opponent: the random nature of the game means that performance can vary from game to game. 
     * Running many games and looking at the average score (and standard deviation/error) helps to get a better
     * idea of how well the controller is likely to do in the competition.
     *
     * @param pacManController The Pac-Man controller
     * @param ghostController The Ghosts controller
     * @param trials The number of trials to be executed
     */
    public ArrayList<Double> runExperiment(Controller<MOVE> pacManController,Controller<EnumMap<GHOST,MOVE>> ghostController,int trials)
    {
    	double avgScore=0;
    	double avgTime=0;
    	
    	Random rnd=new Random(0);
		Game game;
		
		for(int i=0;i<trials;i++)
		{
			game=new Game(rnd.nextLong());
			int lastLevel = 0;
			
			while(!game.gameOver())
			{				
		        game.advanceGame(pacManController.getMove(game.copy(),System.currentTimeMillis()+DELAY),
		        		ghostController.getMove(game.copy(),System.currentTimeMillis()+DELAY));
		        lastLevel = game.getCurrentLevel();
			}
			
			avgScore+=game.getScore();
			avgTime+=game.getTotalTime();
			//System.out.println(i+"\t"+game.getScore());
		}
		
		ArrayList<Double> ret = new ArrayList<>(2);
		ret.add(avgScore/trials);
		ret.add(avgTime/trials);
		
		//System.out.println(avgScore/trials);
		return ret;
    }
	
	/**
	 * Run a game in asynchronous mode: the game waits until a move is returned. In order to slow thing down in case
	 * the controllers return very quickly, a time limit can be used. If fasted gameplay is required, this delay
	 * should be put as 0.
	 *
	 * @param pacManController The Pac-Man controller
	 * @param ghostController The Ghosts controller
	 * @param visual Indicates whether or not to use visuals
	 * @param delay The delay between time-steps
	 */
	public void runGame(Controller<MOVE> pacManController,Controller<EnumMap<GHOST,MOVE>> ghostController,boolean visual,int delay)
	{
		Game game=new Game(0);

		GameView gv=null;
		
		if(visual)
			gv=new GameView(game).showGame();
		
		int lastLevel = 0;
		
		while(!game.gameOver())
		{				
	        game.advanceGame(pacManController.getMove(game.copy(),-1),ghostController.getMove(game.copy(),-1));
	        lastLevel = game.getCurrentLevel();
	        
	        try{Thread.sleep(delay);}catch(Exception e){}
	        
	        if(visual)
	        	gv.repaint();
		}
	}
	
	/**
     * Run the game with time limit (asynchronous mode). This is how it will be done in the competition. 
     * Can be played with and without visual display of game states.
     *
     * @param pacManController The Pac-Man controller
     * @param ghostController The Ghosts controller
	 * @param visual Indicates whether or not to use visuals
     */
    public void runGameTimed(Controller<MOVE> pacManController,Controller<EnumMap<GHOST,MOVE>> ghostController,boolean visual)
	{
		Game game=new Game(0);
		int lastLevel = 0;
		
		GameView gv=null;
		
		if(visual)
			gv=new GameView(game).showGame();
		
		if(pacManController instanceof HumanController)
			gv.getFrame().addKeyListener(((HumanController)pacManController).getKeyboardInput());
				
		new Thread(pacManController).start();
		new Thread(ghostController).start();
		
		while(!game.gameOver())
		{
			pacManController.update(game.copy(),System.currentTimeMillis()+DELAY);
			ghostController.update(game.copy(),System.currentTimeMillis()+DELAY);

			try
			{
				Thread.sleep(DELAY);
			}
			catch(InterruptedException e)
			{
				e.printStackTrace();
			}
			
	        game.advanceGame(pacManController.getMove(),ghostController.getMove());	   
	        lastLevel = game.getCurrentLevel();
	        
	        if(visual)
	        	gv.repaint();
		}
		
		pacManController.terminate();
		ghostController.terminate();
	}
	
    /**
     * Run the game in asynchronous mode but proceed as soon as both controllers replied. The time limit still applies so 
     * so the game will proceed after 40ms regardless of whether the controllers managed to calculate a turn.
     *     
     * @param pacManController The Pac-Man controller
     * @param ghostController The Ghosts controller
     * @param fixedTime Whether or not to wait until 40ms are up even if both controllers already responded
	 * @param visual Indicates whether or not to use visuals
     */
    public void runGameTimedSpeedOptimised(Controller<MOVE> pacManController,Controller<EnumMap<GHOST,MOVE>> ghostController,boolean fixedTime,boolean visual)
 	{
 		Game game=new Game(0);
 		int lastLevel = 0;
 		
 		GameView gv=null;
 		
 		if(visual)
 			gv=new GameView(game).showGame();
 		
 		if(pacManController instanceof HumanController)
 			gv.getFrame().addKeyListener(((HumanController)pacManController).getKeyboardInput());
 				
 		new Thread(pacManController).start();
 		new Thread(ghostController).start();
 		
 		while(!game.gameOver())
 		{
 			pacManController.update(game.copy(),System.currentTimeMillis()+DELAY);
 			ghostController.update(game.copy(),System.currentTimeMillis()+DELAY);

 			try
			{
				int waited=DELAY/INTERVAL_WAIT;
				
				for(int j=0;j<DELAY/INTERVAL_WAIT;j++)
				{
					Thread.sleep(INTERVAL_WAIT);
					
					if(pacManController.hasComputed() && ghostController.hasComputed())
					{
						waited=j;
						break;
					}
				}
				
				if(fixedTime)
					Thread.sleep(((DELAY/INTERVAL_WAIT)-waited)*INTERVAL_WAIT);
				
				game.advanceGame(pacManController.getMove(),ghostController.getMove());	
				lastLevel = game.getCurrentLevel();
			}
			catch(InterruptedException e)
			{
				e.printStackTrace();
			}
 	        
 	        if(visual)
 	        	gv.repaint();
 		}
 		
 		pacManController.terminate();
 		ghostController.terminate();
 	}
    
	/**
	 * Run a game in asynchronous mode and recorded.
	 *
     * @param pacManController The Pac-Man controller
     * @param ghostController The Ghosts controller
     * @param visual Whether to run the game with visuals
	 * @param fileName The file name of the file that saves the replay
	 */
	public void runGameTimedRecorded(Controller<MOVE> pacManController,Controller<EnumMap<GHOST,MOVE>> ghostController,boolean visual,String fileName)
	{
		StringBuilder replay=new StringBuilder();
		
		Game game=new Game(0);
		int lastLevel = 0;
		
		GameView gv=null;
		
		if(visual)
		{
			gv=new GameView(game).showGame();
			
			if(pacManController instanceof HumanController)
				gv.getFrame().addKeyListener(((HumanController)pacManController).getKeyboardInput());
		}		
		
		new Thread(pacManController).start();
		new Thread(ghostController).start();
		
		while(!game.gameOver())
		{
			pacManController.update(game.copy(),System.currentTimeMillis()+DELAY);
			ghostController.update(game.copy(),System.currentTimeMillis()+DELAY);

			try
			{
				Thread.sleep(DELAY);
			}
			catch(InterruptedException e)
			{
				e.printStackTrace();
			}

			game.advanceGame(pacManController.getMove(),ghostController.getMove());	  
			lastLevel = game.getCurrentLevel();
	        
	        if(visual)
	        	gv.repaint();
	        
	        replay.append(game.getGameState()+"\n");
		}
		
		pacManController.terminate();
		ghostController.terminate();
		
		saveToFile(replay.toString(),fileName,false);
	}
	
	/**
	 * Replay a previously saved game.
	 *
	 * @param fileName The file name of the game to be played
	 * @param visual Indicates whether or not to use visuals
	 */
	public void replayGame(String fileName,boolean visual)
	{
		ArrayList<String> timeSteps=loadReplay(fileName);
		
		Game game=new Game(0);
		
		GameView gv=null;
		
		if(visual)
			gv=new GameView(game).showGame();
		
		for(int j=0;j<timeSteps.size();j++)
		{			
			game.setGameState(timeSteps.get(j));

			try
			{
				Thread.sleep(DELAY);
			}
			catch(InterruptedException e)
			{
				e.printStackTrace();
			}
	        if(visual)
	        	gv.repaint();
		}
	}
	
	//save file for replays
    public static void saveToFile(String data,String name,boolean append)
    {
        try 
        {
            FileOutputStream outS=new FileOutputStream(name,append);
            PrintWriter pw=new PrintWriter(outS);

            pw.println(data);
            pw.flush();
            outS.close();

        } 
        catch (IOException e)
        {
            System.out.println("Could not save data!");	
        }
    }  

    //load a replay
    private static ArrayList<String> loadReplay(String fileName)
	{
    	ArrayList<String> replay=new ArrayList<String>();
		
        try(BufferedReader br=new BufferedReader(new InputStreamReader(new FileInputStream(fileName))))
        {
            String input=br.readLine();		
            
            while(input!=null)
            {
            	if(!input.equals(""))
            		replay.add(input);

            	input=br.readLine();	
            }
        }
        catch(IOException ioe)
        {
            ioe.printStackTrace();
        }
        
        return replay;
	}
} public class GrammaticalAdapterController extends Controller<MOVE>
{
	private String fenotipoStr;
	private NicerTree tree;
	
	public GrammaticalAdapterController(String fenotipo) {
		this.fenotipoStr = fenotipo;
		tree = TreeParser.parseTree(fenotipoStr);
	}

	public MOVE getMove(Game game, long timeDue) {
		return tree.executeAndGetMove(game);
	}	
} public enum GHOST
{
	BLINKY(40),
	PINKY(60),
	INKY(80),
	SUE(100);
		
	public final int initialLairTime;
		
	GHOST(int lairTime)
	{
		this.initialLairTime=lairTime;
	}
}; public enum MOVE 
{
	UP {
		public MOVE opposite() {
			return MOVE.DOWN;
		}

		public MOVE R90() {
			return MOVE.RIGHT;
		}

		public MOVE L90() {
			return MOVE.LEFT;
		};
	},
	RIGHT {
		public MOVE opposite() {
			return MOVE.LEFT;
		}

		public MOVE R90() {
			return MOVE.DOWN;
		}

		public MOVE L90() {
			return MOVE.UP;
		};
	},
	DOWN {
		public MOVE opposite() {
			return MOVE.UP;
		}

		public MOVE R90() {
			return MOVE.LEFT;
		}

		public MOVE L90() {
			return MOVE.RIGHT;
		};
	},
	LEFT {
		public MOVE opposite() {
			return MOVE.RIGHT;
		}

		public MOVE R90() {
			return MOVE.UP;
		}

		public MOVE L90() {
			return MOVE.DOWN;
		};
	},
	NEUTRAL {
		public MOVE opposite() {
			return MOVE.NEUTRAL;
		}

		public MOVE R90() {
			return MOVE.NEUTRAL;
		}

		public MOVE L90() {
			return MOVE.NEUTRAL;
		};
	};

	public abstract MOVE opposite();
	public abstract MOVE R90();
	public abstract MOVE L90();
};
import java.util.ArrayList;
import java.util.EnumMap;
import java.util.Scanner;
import java.util.logging.Logger;
import jeco.core.algorithm.moge.GrammaticalEvolution;
import jeco.core.algorithm.moge.PacmanGrammaticalEvolution;
import jeco.core.operator.evaluator.fitness.MOFitnessWrapper;
import jeco.core.operator.evaluator.fitness.NaiveFitness;
import jeco.core.optimization.threads.MasterWorkerThreads;
import jeco.core.problem.Solution;
import jeco.core.problem.Solutions;
import jeco.core.problem.Variable;
import pacman.Executor;
import pacman.controllers.Controller;
import pacman.controllers.GrammaticalAdapterController;
import pacman.controllers.examples.StarterGhosts;
import pacman.game.Constants.GHOST;
import pacman.game.Constants.MOVE;
package view.cli;



public class CLIView {
	final static int ticks = 19;
	Logger logger;
	GrammaticalEvolution algorithm;
	PacmanGrammaticalEvolution problem;
	MasterWorkerThreads<Variable<Integer>> masterWorker; // if algorithm is multithreading
	static int avalaibleThreads = Runtime.getRuntime().availableProcessors();
	
	public CLIView() {
		// Configure parameters
		int populationSize = 100;// = 400;
		int generations = 100;// = 500;
		double mutationProb = 0.02;
	  	double crossProb = 0.6;
	  	MOFitnessWrapper fitnessWrapper = new MOFitnessWrapper(new NaiveFitness());