def main():
    # creates a list of student and course objects in preparation file
    lists = preparation.main()
    student_list = lists[0]
    course_list = lists[1]
    session_list = lists[2]
    room_list = lists[3]

    score_max = 0

    # searches 5 times for a local maximum and visualizes the best score
    for i in range(5):
        print i
        # executes simulated annealing
        local_max_schedule = simulated_annealing(student_list, course_list, session_list, room_list)
        schedule_room_list = local_max_schedule[0]
        schedule_student_list = local_max_schedule[1]
        score_list = score_function.main(schedule_room_list, schedule_student_list, course_list)
        score_local_max = score_list[0]

        # when new hillclimber score is better
        if score_local_max > score_max:
            max_schedule = local_max_schedule
            score_max = score_local_max

    data = max_schedule[2]
    # writes data of schedule  to csv
    with open("data_simulated_annealing.csv", "wb") as f:
        writer = csv.writer(f)
        writer.writerow(["Iteration", "Score", "malus_conflict", "malus_capacity", "malus_spread", "bonus_spread"])
        for row in data:
            writer.writerow(row)

    # visualizes room schedules
    schedule_room_list = max_schedule[0]
    schedule_student_list = max_schedule[1]
    for schedule_room in schedule_room_list:
        visualize.visualize(schedule_room)

    # visualize students schedules
    for schedule_student in schedule_student_list[151:152]:
        visualize.visualize(schedule_student)

    for schedule_student in schedule_student_list[155:156]:
        visualize.visualize(schedule_student)
def simulated_annealing(student_list, course_list, session_list, room_list):

    """
    Starts at random schedule and climbs the hill.
    Probability of acceptance reduces when temperature cools down
    """

    #  creates schedule with schedules for room and students
    schedule = schedule_maker.main(student_list, course_list, session_list, room_list)
    schedule_room_list = schedule[0]
    schedule_student_list = schedule[1]
    course_list = schedule[2]
    time_slot_list = schedule[3]
    new_score_list = score_function.main(schedule_room_list, schedule_student_list, course_list)
    score = new_score_list[0]
    malus_conflict = new_score_list[1]
    malus_capacity = new_score_list[2]
    malus_spread = new_score_list[3]
    bonus_spread = new_score_list[4]

    # sets simulated annaeling to True
    sim = True

    # initiates temperature (certain acceptance prob of 0.8)
    temperature = 1000

    # initiates cooling_rate
    cooling_rate = 0.0001

    # were tries and scores per step are saved
    data = []
    iteration = 0

    while True:
        # only change score when new_score is higher
        tries = 0

        while True:

            # saves the data for this iteration in data list
            data_iteration = []
            data_iteration.append(iteration)
            data_iteration.append(score)
            # data.append(data_iteration)
            data_iteration.append(malus_conflict)
            data_iteration.append(malus_capacity)
            data_iteration.append(malus_spread)
            data_iteration.append(bonus_spread)
            data.append(data_iteration)

            # select 2 random timeslots from random
            random_1 = random.choice(time_slot_list)
            random_2 = random.choice(time_slot_list)
            tries += 1
            iteration += 1

            # Switches two random timeslots in all relevant schedules
            switch(random_1, random_2, schedule_student_list, schedule_room_list)

            # calculates new score of incremental change
            new_score_list = score_function.main(schedule_room_list, schedule_student_list, course_list)
            new_score = new_score_list[0]

            # calculates probability of acceptance
            prob = acceptance_probability(score, new_score, temperature)

            # if simulated annaeling is on
            if sim:
                # cools the temperature
                temperature *= 1 - cooling_rate
                # when probability of acceptance is higher then random number between 0 and 1 take a step
                if prob > random.random():
                    score = new_score
                    malus_conflict = new_score_list[1]
                    malus_capacity = new_score_list[2]
                    malus_spread = new_score_list[3]
                    bonus_spread = new_score_list[4]
                    break
                # when the score is not better switch back
                else:
                    switch(random_2, random_1, schedule_student_list, schedule_room_list)

            # when simulated annaeling is over start hillclimber
            else:
                # when new score is better take the step
                if new_score > score:
                    score = new_score
                    break
                # when the score is not better switch back
                else:
                    switch(random_2, random_1, schedule_student_list, schedule_room_list)
                    new_score = score_function.main(schedule_room_list, schedule_student_list, course_list)

                # ends function if he tries more then 5000 steps
                if tries > 10000:
                    return [schedule_room_list, schedule_student_list, data]

            # ends function if temperature is below 1
            if temperature < 1:
                sim = False