package com.company.simulated_annealing_travelling_sales_man;

public class SimulatedAnnealing {
	
	private SingleTour actualState;
	private SingleTour nextState;
	private SingleTour bestState;
	
	public void simulate() {
		
		double temp = Constants.MAX_TEMPERATURE;
		
		actualState = new SingleTour();
		actualState.generateIndividual();
		bestState = new SingleTour(actualState.getTour());
		
		System.out.println("Initial solution distance: " + actualState.getDistance());
		
		while( temp > Constants.MIN_TEMPERATURE) {
			
			nextState = generateNeighborState(actualState);
			
			double currentEnergy = actualState.getDistance();
			double neighborEnergy = nextState.getDistance();
			
			if(acceptanceProbability(currentEnergy, neighborEnergy, temp) > Math.random())
				actualState = new SingleTour(nextState.getTour());
			
			if(actualState.getDistance() < bestState.getDistance())
				bestState = new SingleTour(actualState.getTour());
			
			temp *= 1 - Constants.COOLING_RATE;
		}	
	}

	// we swap 2 cities randomly
	private SingleTour generateNeighborState(SingleTour actualState) {
		
		SingleTour newState = new SingleTour(actualState.getTour());
		
		int randomIndex1 = (int) (Math.random()*newState.getTourSize());
		int randomIndex2 = (int) (Math.random()*newState.getTourSize());
		
		City city1 = newState.getCity(randomIndex1);
		City city2 = newState.getCity(randomIndex2);
		
		newState.setCity(randomIndex1, city2);
		newState.setCity(randomIndex2, city1);
		
		return newState;
	}
	
	public SingleTour getBest() {
		return bestState;
	}
	
	// this is the Metropolis-function
	private double acceptanceProbability(double currentEnergy, double neighbourEnergy, double temp) {
		
		if(neighbourEnergy < currentEnergy ) return 1.0;
		
		return Math.exp((currentEnergy-neighbourEnergy)/temp);
	}
}