├── .gitignore ├── CMakeLists.txt ├── GeneticAlgo.png ├── GeneticAlgorithm ├── GeneticAlgorithm.cbp ├── Makefile ├── README.md ├── TSP_Solved.png ├── cmake_install.cmake └── main.c /.gitignore: -------------------------------------------------------------------------------- 1 | *.json 2 | tags 3 | cmake-build-debug/ 4 | CMakeFiles/ 5 | CMakeCache.txt 6 | .idea/ 7 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.8) 2 | project(GeneticAlgorithm) 3 | 4 | set(CMAKE_C_STANDARD 99) 5 | set(CMAKE_EXPORT_COMPILE_COMMANDS ON) 6 | set(SOURCE_FILES main.c) 7 | 8 | add_executable(GeneticAlgorithm ${SOURCE_FILES}) 9 | 10 | target_link_libraries(GeneticAlgorithm m ncurses) 11 | -------------------------------------------------------------------------------- /GeneticAlgo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DubiousCactus/GeneticAlgorithm/e1b995b1854f6c95ce02fd941fc256b400ef8a17/GeneticAlgo.png -------------------------------------------------------------------------------- /GeneticAlgorithm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DubiousCactus/GeneticAlgorithm/e1b995b1854f6c95ce02fd941fc256b400ef8a17/GeneticAlgorithm -------------------------------------------------------------------------------- /GeneticAlgorithm.cbp: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 92 | 93 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | # CMAKE generated file: DO NOT EDIT! 2 | # Generated by "Unix Makefiles" Generator, CMake Version 3.8 3 | 4 | # Default target executed when no arguments are given to make. 5 | default_target: all 6 | 7 | .PHONY : default_target 8 | 9 | # Allow only one "make -f Makefile2" at a time, but pass parallelism. 10 | .NOTPARALLEL: 11 | 12 | 13 | #============================================================================= 14 | # Special targets provided by cmake. 15 | 16 | # Disable implicit rules so canonical targets will work. 17 | .SUFFIXES: 18 | 19 | 20 | # Remove some rules from gmake that .SUFFIXES does not remove. 21 | SUFFIXES = 22 | 23 | .SUFFIXES: .hpux_make_needs_suffix_list 24 | 25 | 26 | # Suppress display of executed commands. 27 | $(VERBOSE).SILENT: 28 | 29 | 30 | # A target that is always out of date. 31 | cmake_force: 32 | 33 | .PHONY : cmake_force 34 | 35 | #============================================================================= 36 | # Set environment variables for the build. 37 | 38 | # The shell in which to execute make rules. 39 | SHELL = /bin/sh 40 | 41 | # The CMake executable. 42 | CMAKE_COMMAND = /opt/clion/bin/cmake/bin/cmake 43 | 44 | # The command to remove a file. 45 | RM = /opt/clion/bin/cmake/bin/cmake -E remove -f 46 | 47 | # Escaping for special characters. 48 | EQUALS = = 49 | 50 | # The top-level source directory on which CMake was run. 51 | CMAKE_SOURCE_DIR = /home/transpalette/Code/GeneticAlgorithm 52 | 53 | # The top-level build directory on which CMake was run. 54 | CMAKE_BINARY_DIR = /home/transpalette/Code/GeneticAlgorithm 55 | 56 | #============================================================================= 57 | # Targets provided globally by CMake. 58 | 59 | # Special rule for the target rebuild_cache 60 | rebuild_cache: 61 | @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "Running CMake to regenerate build system..." 62 | /opt/clion/bin/cmake/bin/cmake -H$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR) 63 | .PHONY : rebuild_cache 64 | 65 | # Special rule for the target rebuild_cache 66 | rebuild_cache/fast: rebuild_cache 67 | 68 | .PHONY : rebuild_cache/fast 69 | 70 | # Special rule for the target edit_cache 71 | edit_cache: 72 | @$(CMAKE_COMMAND) -E cmake_echo_color --switch=$(COLOR) --cyan "No interactive CMake dialog available..." 73 | /opt/clion/bin/cmake/bin/cmake -E echo No\ interactive\ CMake\ dialog\ available. 74 | .PHONY : edit_cache 75 | 76 | # Special rule for the target edit_cache 77 | edit_cache/fast: edit_cache 78 | 79 | .PHONY : edit_cache/fast 80 | 81 | # The main all target 82 | all: cmake_check_build_system 83 | $(CMAKE_COMMAND) -E cmake_progress_start /home/transpalette/Code/GeneticAlgorithm/CMakeFiles /home/transpalette/Code/GeneticAlgorithm/CMakeFiles/progress.marks 84 | $(MAKE) -f CMakeFiles/Makefile2 all 85 | $(CMAKE_COMMAND) -E cmake_progress_start /home/transpalette/Code/GeneticAlgorithm/CMakeFiles 0 86 | .PHONY : all 87 | 88 | # The main clean target 89 | clean: 90 | $(MAKE) -f CMakeFiles/Makefile2 clean 91 | .PHONY : clean 92 | 93 | # The main clean target 94 | clean/fast: clean 95 | 96 | .PHONY : clean/fast 97 | 98 | # Prepare targets for installation. 99 | preinstall: all 100 | $(MAKE) -f CMakeFiles/Makefile2 preinstall 101 | .PHONY : preinstall 102 | 103 | # Prepare targets for installation. 104 | preinstall/fast: 105 | $(MAKE) -f CMakeFiles/Makefile2 preinstall 106 | .PHONY : preinstall/fast 107 | 108 | # clear depends 109 | depend: 110 | $(CMAKE_COMMAND) -H$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR) --check-build-system CMakeFiles/Makefile.cmake 1 111 | .PHONY : depend 112 | 113 | #============================================================================= 114 | # Target rules for targets named GeneticAlgorithm 115 | 116 | # Build rule for target. 117 | GeneticAlgorithm: cmake_check_build_system 118 | $(MAKE) -f CMakeFiles/Makefile2 GeneticAlgorithm 119 | .PHONY : GeneticAlgorithm 120 | 121 | # fast build rule for target. 122 | GeneticAlgorithm/fast: 123 | $(MAKE) -f CMakeFiles/GeneticAlgorithm.dir/build.make CMakeFiles/GeneticAlgorithm.dir/build 124 | .PHONY : GeneticAlgorithm/fast 125 | 126 | main.o: main.c.o 127 | 128 | .PHONY : main.o 129 | 130 | # target to build an object file 131 | main.c.o: 132 | $(MAKE) -f CMakeFiles/GeneticAlgorithm.dir/build.make CMakeFiles/GeneticAlgorithm.dir/main.c.o 133 | .PHONY : main.c.o 134 | 135 | main.i: main.c.i 136 | 137 | .PHONY : main.i 138 | 139 | # target to preprocess a source file 140 | main.c.i: 141 | $(MAKE) -f CMakeFiles/GeneticAlgorithm.dir/build.make CMakeFiles/GeneticAlgorithm.dir/main.c.i 142 | .PHONY : main.c.i 143 | 144 | main.s: main.c.s 145 | 146 | .PHONY : main.s 147 | 148 | # target to generate assembly for a file 149 | main.c.s: 150 | $(MAKE) -f CMakeFiles/GeneticAlgorithm.dir/build.make CMakeFiles/GeneticAlgorithm.dir/main.c.s 151 | .PHONY : main.c.s 152 | 153 | # Help Target 154 | help: 155 | @echo "The following are some of the valid targets for this Makefile:" 156 | @echo "... all (the default if no target is provided)" 157 | @echo "... clean" 158 | @echo "... depend" 159 | @echo "... rebuild_cache" 160 | @echo "... edit_cache" 161 | @echo "... GeneticAlgorithm" 162 | @echo "... main.o" 163 | @echo "... main.i" 164 | @echo "... main.s" 165 | .PHONY : help 166 | 167 | 168 | 169 | #============================================================================= 170 | # Special targets to cleanup operation of make. 171 | 172 | # Special rule to run CMake to check the build system integrity. 173 | # No rule that depends on this can have commands that come from listfiles 174 | # because they might be regenerated. 175 | cmake_check_build_system: 176 | $(CMAKE_COMMAND) -H$(CMAKE_SOURCE_DIR) -B$(CMAKE_BINARY_DIR) --check-build-system CMakeFiles/Makefile.cmake 0 177 | .PHONY : cmake_check_build_system 178 | 179 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GeneticAlgorithm 2 | 3 | This project solves the Traveling Salesman Problem using a genetic algorithm with crossover and mutation of chromosomes. 4 | The data is visualized as a graph, using **ncurses** for the nice *old school* vibe ! B) 5 | 6 | ![The genetic algorithm solving the traveling salesman problem](/GeneticAlgo.png) 7 | ![The genetic algorithm solved the traveling salesman problem](/TSP_Solved.png) 8 | -------------------------------------------------------------------------------- /TSP_Solved.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/DubiousCactus/GeneticAlgorithm/e1b995b1854f6c95ce02fd941fc256b400ef8a17/TSP_Solved.png -------------------------------------------------------------------------------- /cmake_install.cmake: -------------------------------------------------------------------------------- 1 | # Install script for directory: /home/transpalette/Code/GeneticAlgorithm 2 | 3 | # Set the install prefix 4 | if(NOT DEFINED CMAKE_INSTALL_PREFIX) 5 | set(CMAKE_INSTALL_PREFIX "/usr/local") 6 | endif() 7 | string(REGEX REPLACE "/$" "" CMAKE_INSTALL_PREFIX "${CMAKE_INSTALL_PREFIX}") 8 | 9 | # Set the install configuration name. 10 | if(NOT DEFINED CMAKE_INSTALL_CONFIG_NAME) 11 | if(BUILD_TYPE) 12 | string(REGEX REPLACE "^[^A-Za-z0-9_]+" "" 13 | CMAKE_INSTALL_CONFIG_NAME "${BUILD_TYPE}") 14 | else() 15 | set(CMAKE_INSTALL_CONFIG_NAME "Debug") 16 | endif() 17 | message(STATUS "Install configuration: \"${CMAKE_INSTALL_CONFIG_NAME}\"") 18 | endif() 19 | 20 | # Set the component getting installed. 21 | if(NOT CMAKE_INSTALL_COMPONENT) 22 | if(COMPONENT) 23 | message(STATUS "Install component: \"${COMPONENT}\"") 24 | set(CMAKE_INSTALL_COMPONENT "${COMPONENT}") 25 | else() 26 | set(CMAKE_INSTALL_COMPONENT) 27 | endif() 28 | endif() 29 | 30 | # Install shared libraries without execute permission? 31 | if(NOT DEFINED CMAKE_INSTALL_SO_NO_EXE) 32 | set(CMAKE_INSTALL_SO_NO_EXE "0") 33 | endif() 34 | 35 | if(CMAKE_INSTALL_COMPONENT) 36 | set(CMAKE_INSTALL_MANIFEST "install_manifest_${CMAKE_INSTALL_COMPONENT}.txt") 37 | else() 38 | set(CMAKE_INSTALL_MANIFEST "install_manifest.txt") 39 | endif() 40 | 41 | string(REPLACE ";" "\n" CMAKE_INSTALL_MANIFEST_CONTENT 42 | "${CMAKE_INSTALL_MANIFEST_FILES}") 43 | file(WRITE "/home/transpalette/Code/GeneticAlgorithm/${CMAKE_INSTALL_MANIFEST}" 44 | "${CMAKE_INSTALL_MANIFEST_CONTENT}") 45 | -------------------------------------------------------------------------------- /main.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Solving the traveling salesman problem using a genetic algorithm. 3 | * Author: Theo Morales 4 | * 2017 5 | */ 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #define POPULATION_SIZE 1000 16 | #define GENERATION_SIZE 50 17 | #define NB_GENES 9 18 | #define GENE_SIZE 3 19 | #define MUTATION_RATE 0.03 20 | #define NB_ITERATIONS 300 21 | 22 | 23 | typedef struct { 24 | int x; 25 | int y; 26 | int binaryIndex[3]; 27 | char *name; 28 | } city; 29 | 30 | 31 | city cities[8]; 32 | 33 | /* 34 | * Describes the order to visit cities: 35 | * A gene is a 3-bit array, a chromosome contains 9 genes for the 8 cities + the first one 36 | * */ 37 | typedef struct { 38 | int genes[NB_GENES][GENE_SIZE]; 39 | float fitness; 40 | float score; 41 | } chromosome; 42 | 43 | 44 | float maxFitness = 0; 45 | WINDOW *visualization_window, *details_window; 46 | 47 | int bin_to_dec(int bin[GENE_SIZE]) { 48 | 49 | int dec = 0; 50 | 51 | for (int i = 0; i < GENE_SIZE; i ++) 52 | dec += bin[i] * pow(2, (GENE_SIZE - 1 - i)); 53 | 54 | return dec; 55 | } 56 | 57 | 58 | /* Generate bits to fill a gene */ 59 | void make_gene(int *gene) { 60 | 61 | for (int i = 0; i < GENE_SIZE; i++) 62 | gene[i] = rand() % 2; 63 | } 64 | 65 | 66 | /* 67 | * Map a gene from its binary index to the corresponding city 68 | * and return its coordinates 69 | */ 70 | void get_gene_coord(int gene[], int coord[]) { 71 | 72 | int geneValue = bin_to_dec(gene); 73 | 74 | coord[0] = cities[geneValue].x; 75 | coord[1] = cities[geneValue].y; 76 | } 77 | 78 | 79 | int validate_chromosome(chromosome c) { 80 | 81 | int isThereTwice = 0; 82 | 83 | /* Check if a gene is present twice */ 84 | for (int i = 0; i < NB_GENES; i++) { 85 | for (int j = 1; j < NB_GENES - 1; j++) { //Ignore the last one, which SHOULD be equal to the first one ! 86 | if (i != j && c.genes[i] == c.genes[j]) { 87 | isThereTwice = 1; 88 | break; 89 | } 90 | } 91 | } 92 | 93 | return !isThereTwice; 94 | } 95 | 96 | /* Objective function: total length of the trip */ 97 | float score(chromosome c) { 98 | 99 | float score = 0; 100 | 101 | for (int i = 1; i < NB_GENES; i++) { 102 | int geneCoord[2] = {0}, prevGeneCoord[2] = {0}; 103 | 104 | get_gene_coord(c.genes[i], geneCoord); 105 | get_gene_coord(c.genes[i - 1], prevGeneCoord); 106 | 107 | score += sqrt( 108 | pow(fabs(geneCoord[0] - prevGeneCoord[0]), 2) 109 | + pow(fabs(geneCoord[1] - prevGeneCoord[1]), 2) 110 | ); 111 | } 112 | 113 | if (!validate_chromosome(c)) 114 | score = 0.1; 115 | 116 | return score; 117 | } 118 | 119 | /* Mean objective function value over population */ 120 | float mean(chromosome *generation, int size) { 121 | 122 | /* TODO: Optimize by caching the mean for the current generation */ 123 | float mean = 0; 124 | 125 | for (int i = 0; i < size; i++) 126 | mean += score(generation[i]); 127 | 128 | mean /= size; 129 | 130 | return mean; 131 | } 132 | 133 | /* Objective score function: score / maxScore */ 134 | float fitness(chromosome c, float minScore, float maxScore) { 135 | 136 | /* Map [minScore; maxScore] to [0; 1] */ 137 | float mappedFitness = (c.score - minScore) / (maxScore - minScore) * (1 - 0) + 0; 138 | 139 | /* Then "invert": low becomes high, and high becomes low */ 140 | return fabsf((mappedFitness - 1) / 1); 141 | } 142 | 143 | 144 | chromosome fittest(chromosome generation[], int size) { 145 | 146 | chromosome fittest; 147 | fittest.fitness = 0; 148 | 149 | for (int i = 0; i < size; i++) 150 | if (generation[i].fitness > fittest.fitness) 151 | fittest = generation[i]; 152 | 153 | return fittest; 154 | } 155 | 156 | 157 | 158 | float rand_a_b(float a, float b) { 159 | 160 | return (rand() / (float) RAND_MAX) * (b - a) + a; 161 | } 162 | 163 | 164 | void quick_sort(chromosome array[], int left, int right) { 165 | 166 | int i = left, j = right; 167 | chromosome tmp; 168 | chromosome pivot = array[(left + right) / 2]; 169 | 170 | while (i <= j) { 171 | while (array[i].fitness < pivot.fitness) 172 | i++; 173 | while (array[j].fitness > pivot.fitness) 174 | j--; 175 | if (i <= j) { 176 | tmp = array[i]; 177 | array[i] = array[j]; 178 | array[j] = tmp; 179 | i++; j--; 180 | } 181 | } 182 | 183 | if (left < j) 184 | quick_sort(array, left, j); 185 | if (right > i) 186 | quick_sort(array, i, right); 187 | } 188 | 189 | 190 | void select_fittest_chromosomes(chromosome generation[], int toSize, chromosome population[], int fromSize) { 191 | 192 | quick_sort(population, 0, fromSize - 1); 193 | 194 | for (int i = toSize; i > 0; i--) 195 | generation[toSize - i] = population[i]; 196 | } 197 | 198 | 199 | void select_chromosomes(chromosome generation[], int toSize, chromosome population[], int fromSize) { 200 | 201 | int picked = 0, left_at = 0; 202 | 203 | for (int i = 0; i < toSize; i++) { 204 | picked = 0; 205 | 206 | while (!picked) { 207 | if (left_at >= 50) left_at = 0; 208 | for (int j = left_at; j < fromSize; j++, left_at++) { 209 | float wheel = rand_a_b(0, 1); 210 | 211 | if (wheel <= population[j].fitness) { 212 | generation[i] = population[j]; 213 | picked = 1; 214 | left_at++; 215 | break; 216 | } 217 | 218 | if (j >= fromSize) j = left_at = 0; 219 | } 220 | } 221 | } 222 | } 223 | 224 | 225 | /* 226 | * Crossover method based on subsets of parents. 227 | * The offspring will be a valid chromosome. 228 | */ 229 | chromosome crossover(chromosome *candidates, int size) { 230 | 231 | chromosome dad, mom, kid; 232 | kid.fitness = 0; 233 | 234 | int firstPick = (int) random() % size; 235 | int secondPick = firstPick; 236 | 237 | dad = candidates[firstPick]; 238 | 239 | while (secondPick == firstPick) secondPick = (int) random() % size; 240 | 241 | mom = candidates[secondPick]; 242 | 243 | int a = (int) rand_a_b(1, NB_GENES - 1); 244 | int b = (int) rand_a_b(a, NB_GENES - 1); 245 | 246 | while (b < a) b = (int) rand_a_b(a, NB_GENES - 1); 247 | 248 | int dadSubset[b - a + 1]; 249 | int k = 0; 250 | 251 | for (int i = a; i <= b; i++) { 252 | for (int j = 0; j < GENE_SIZE; j++) 253 | kid.genes[i][j] = dad.genes[i][j]; //That's for dad 254 | 255 | dadSubset[k++] = bin_to_dec(dad.genes[i]); //Keep track of the used cities 256 | } 257 | 258 | int skip = 0; 259 | k = 0; 260 | 261 | for (int i = 0; i < NB_GENES; i++) { 262 | if (i >= a && i <= b) continue; 263 | 264 | do { 265 | skip = 0; 266 | 267 | for (int j = 0; j < (b - a + 1); j++) { 268 | if (bin_to_dec(mom.genes[k]) == dadSubset[j]) { 269 | skip = 1; 270 | break; 271 | } 272 | } 273 | 274 | if (skip) k++; 275 | } while (skip); 276 | 277 | for (int j = 0; j < GENE_SIZE; j++) 278 | kid.genes[i][j] = mom.genes[k][j]; //That's for mom 279 | 280 | k++; 281 | } 282 | 283 | return kid; 284 | } 285 | 286 | 287 | /* Alternate mutation method: swap two points at random indexes 288 | * This will ensure a valid mutated chromosome */ 289 | chromosome mutate(chromosome c) { 290 | 291 | /* Spin the wheel to mutate */ 292 | if (rand_a_b(0, 1) > MUTATION_RATE) { 293 | c.score = score(c); 294 | return c; 295 | } 296 | 297 | int a = (int) rand_a_b(1, NB_GENES - 1); //Only swap between the start and end points 298 | int b = a; 299 | int temp[GENE_SIZE] = {0}; 300 | 301 | while (b == a) b = (int) rand_a_b(1, NB_GENES - 1); //Make sure the two indexes are different 302 | 303 | /* Copy gene a into temp, b into a, and a into b*/ 304 | for (int i = 0; i < GENE_SIZE; i++) { 305 | temp[i] = c.genes[a][i]; 306 | c.genes[a][i] = c.genes[b][i]; 307 | c.genes[b][i] = temp[i]; 308 | } 309 | 310 | c.score = score(c); 311 | 312 | return c; 313 | } 314 | 315 | WINDOW *create_newwin(int height, int width, int starty, int startx) 316 | { 317 | WINDOW *local_win; 318 | local_win = newwin(height, width, starty, startx); 319 | box(local_win, 0 , 0); 320 | refresh(); 321 | wrefresh(local_win); 322 | 323 | return local_win; 324 | } 325 | 326 | 327 | /* Draw the TSP graph based on the given chromosome */ 328 | void visualize(chromosome journey) 329 | { 330 | 331 | int prev_coord[2] = {-1}; 332 | int prev_gene[GENE_SIZE]; 333 | for (int j = 0; j < NB_GENES; j++) { 334 | int coord[2] = {0}; 335 | int gene[GENE_SIZE]; 336 | int not_drawing = 1; 337 | 338 | for (int k = 0; k < GENE_SIZE; k++) 339 | gene[k] = journey.genes[j][k]; 340 | 341 | get_gene_coord(gene, coord); 342 | 343 | /* Draw the point (city) */ 344 | wattron(visualization_window, COLOR_PAIR(1)); 345 | mvwprintw(visualization_window, coord[1], coord[0], cities[bin_to_dec(gene)].name); 346 | 347 | if ((mvwinch(visualization_window, coord[1] + 1, coord[0] + (sizeof cities[bin_to_dec(gene)].name / sizeof *cities[bin_to_dec(gene)].name) / 2 - 1) & A_CHARTEXT) == '0') 348 | mvwprintw(visualization_window, coord[1] + 1, coord[0] + (sizeof cities[bin_to_dec(gene)].name / sizeof *cities[bin_to_dec(gene)].name) / 2 - 4, "0 - 8"); 349 | else 350 | mvwprintw(visualization_window, coord[1] + 1, coord[0] + (sizeof cities[bin_to_dec(gene)].name / sizeof *cities[bin_to_dec(gene)].name) / 2 - 1, "%d", j); 351 | 352 | wattroff(visualization_window, COLOR_PAIR(1)); 353 | 354 | if (prev_coord[0] != -1 && prev_coord[1] != -1) { 355 | int from_y, to_y, from_x, to_x, reverse_x = 0; 356 | float slope, intercept, dx, dy; 357 | 358 | from_y = prev_coord[1]; 359 | to_y = coord[1]; 360 | from_x = prev_coord[0]; 361 | to_x = coord[0]; 362 | 363 | if (from_x > to_x) { 364 | from_x = coord[0]; 365 | to_x = prev_coord[0]; 366 | from_y = coord[1]; 367 | to_y = prev_coord[1]; 368 | reverse_x = 1; 369 | } 370 | 371 | dx = to_x - from_x; 372 | dy = to_y - from_y; 373 | slope = dy / dx; 374 | intercept = from_y - slope * from_x; 375 | 376 | int prev_y = -1, y = 0; 377 | 378 | wattron(visualization_window, COLOR_PAIR(2)); 379 | for (int x = from_x; x < to_x; x++) { 380 | y = slope * x + intercept; 381 | if ((mvwinch(visualization_window, y, x) & A_CHARTEXT) == ' ' && y != prev_y) { 382 | mvwprintw(visualization_window, y, x, "o"); 383 | prev_y = y; 384 | } 385 | } 386 | wattroff(visualization_window, COLOR_PAIR(2)); 387 | } 388 | 389 | for (int k = 0; k < GENE_SIZE; k++) 390 | prev_gene[k] = gene[k]; 391 | 392 | prev_coord[0] = coord[0]; 393 | prev_coord[1] = coord[1]; 394 | } 395 | 396 | } 397 | 398 | 399 | 400 | int main() { 401 | 402 | srand((unsigned) time(NULL)); 403 | 404 | /* Init ncurses */ 405 | initscr(); 406 | noecho(); 407 | cbreak(); 408 | 409 | if(has_colors() == FALSE) 410 | { 411 | endwin(); 412 | printf("Your terminal does not support color\n"); 413 | exit(1); 414 | } 415 | 416 | start_color(); 417 | init_pair(1, COLOR_BLUE, COLOR_YELLOW); 418 | init_pair(2, COLOR_GREEN, COLOR_BLACK); 419 | init_pair(3, COLOR_WHITE, COLOR_RED); 420 | init_pair(4, COLOR_RED, COLOR_RED); 421 | int yMax, xMax; 422 | getmaxyx(stdscr, yMax, xMax); 423 | 424 | memcpy(cities, (city[]) { 425 | { .x = xMax * 0.47, .y = yMax * 0.03, .binaryIndex = { 0, 0, 0 }, .name = "Lille" }, 426 | { .x = xMax * 0.51, .y = yMax * 0.15, .binaryIndex = { 0, 0, 1 }, .name = "Paris" }, 427 | { .x = xMax * 0.8, .y = yMax * 0.06, .binaryIndex = { 0, 1, 0 }, .name = "Reims" }, 428 | { .x = xMax * 0.7, .y = yMax * 0.65, .binaryIndex = { 0, 1, 1 }, .name = "Lyon" }, 429 | { .x = xMax * 0.9, .y = yMax * 0.8, .binaryIndex = { 1, 0, 0 }, .name = "Marseille" }, 430 | { .x = xMax * 0.15, .y = yMax * 0.3, .binaryIndex = { 1, 0, 1 }, .name = "Nantes" }, 431 | { .x = xMax * 0.05, .y = yMax * 0.42, .binaryIndex = { 1, 1, 0 }, .name = "La Rochelle" }, 432 | { .x = xMax * 0.2, .y = yMax * 0.59, .binaryIndex = { 1, 1, 1 }, .name = "Bordeaux" } 433 | }, sizeof cities); 434 | 435 | 436 | visualization_window = create_newwin(yMax - 7, xMax - 2, 0, 1); 437 | details_window = create_newwin(7, xMax - 2, yMax - 7, 1); 438 | 439 | chromosome population[POPULATION_SIZE]; 440 | chromosome generation[GENERATION_SIZE]; 441 | 442 | float maxScore = 0, minScore = 1000; 443 | 444 | for (int p = 0; p < POPULATION_SIZE; p++) { 445 | chromosome c; 446 | int invalid = 1; 447 | 448 | /* Start from Lille, return to Lille */ 449 | /* TODO: Make this scalable (if we add new cities) */ 450 | c.genes[0][0] = c.genes[NB_GENES - 1][0] = 0; 451 | c.genes[0][1] = c.genes[NB_GENES - 1][1] = 0; 452 | c.genes[0][2] = c.genes[NB_GENES - 1][2] = 0; 453 | 454 | /* Reset C */ 455 | for (int i = 1; i < NB_GENES - 1; i++) 456 | for (int j = 0; j < GENE_SIZE; j++) 457 | c.genes[i][j] = 0; 458 | 459 | 460 | for (int i = 1; i < NB_GENES - 1; i++) { 461 | 462 | invalid = 1; 463 | 464 | while (invalid) { 465 | invalid = 0; 466 | make_gene(c.genes[i]); 467 | 468 | /* Check validity */ 469 | for (int j = 0; j < i; j++) { 470 | if (j != i 471 | && (c.genes[j][0] == c.genes[i][0]) 472 | && (c.genes[j][1] == c.genes[i][1]) 473 | && (c.genes[j][2] == c.genes[i][2])) { 474 | invalid = 1; 475 | break; 476 | } 477 | } 478 | } 479 | } 480 | 481 | c.score = score(c); 482 | 483 | if (c.score > maxScore) 484 | maxScore = c.score; 485 | if (c.score < minScore) 486 | minScore = c.score; 487 | 488 | population[p] = c; 489 | } 490 | 491 | /* Calculate fitness of population individuals */ 492 | for (int i = 0; i < POPULATION_SIZE; i++) 493 | population[i].fitness = fitness(population[i], minScore, maxScore); 494 | 495 | /* Selecting original candidates */ 496 | select_chromosomes(generation, GENERATION_SIZE, population, POPULATION_SIZE); 497 | 498 | float firstSelectionScore = mean(generation, GENERATION_SIZE); 499 | float scoreOfFittest = score(fittest(generation, GENERATION_SIZE)); 500 | 501 | /*printf("* First selection average score: %.2f\n", firstSelectionScore); 502 | printf("* Generation's fittest chromosome's score: %.2f\n", scoreOfFittest);*/ 503 | 504 | int iteration = 0; 505 | 506 | while(iteration < NB_ITERATIONS) { 507 | 508 | mvwprintw(details_window, 1, 1, "* Generating %d candidates for base population...\n", POPULATION_SIZE); 509 | mvwprintw(details_window, 2, 1, "* First selection average score: %.2f\n", firstSelectionScore); 510 | mvwprintw(details_window, 3, 1, "* Base generation's fittest chromosome's score: %.2f\n", scoreOfFittest); 511 | mvwprintw(details_window, 4, 1, "* Iteration: %d\n", iteration++); 512 | 513 | /* Select GENERATION_SIZE individuals from population, based on their fitness or randomly */ 514 | mvwprintw(details_window, 5, 1, "* Selecting %d individuals from generation %d...\n", GENERATION_SIZE, iteration); 515 | 516 | chromosome nextGeneration[GENERATION_SIZE] = {0}; //The offsprings of the (intermediate) generation 517 | 518 | maxFitness = 0; 519 | minScore = 1000; 520 | maxScore = 0; 521 | 522 | /* Crossover and mutate from the selection */ 523 | for (int i = 0; i < GENERATION_SIZE; i++) { 524 | nextGeneration[i] = mutate(crossover(generation, GENERATION_SIZE)); 525 | 526 | if (nextGeneration[i].score > maxScore) maxScore = nextGeneration[i].score; 527 | if (nextGeneration[i].score < minScore) minScore = nextGeneration[i].score; 528 | } 529 | 530 | /* Calculate fitness of next generation's individuals */ 531 | for (int i = 0; i < GENERATION_SIZE; i++) 532 | nextGeneration[i].fitness = fitness(nextGeneration[i], minScore, maxScore); 533 | 534 | chromosome selection[GENERATION_SIZE] = {0}; //Selection of nextGeneration (offsprings) + base generation 535 | 536 | /* 537 | * Select new generation, based on the offsprings and the parent generation, 538 | * and the fitness of their chromosomes 539 | */ 540 | select_chromosomes(selection, GENERATION_SIZE / 2, nextGeneration, GENERATION_SIZE); 541 | /*select_fittest_chromosomes(&selection[GENERATION_SIZE / 2], GENERATION_SIZE / 2, generation, GENERATION_SIZE);*/ 542 | select_chromosomes(&selection[GENERATION_SIZE / 2], GENERATION_SIZE / 2, generation, GENERATION_SIZE); 543 | 544 | /* Replace generation by the selection -> cross-breed of old generation + next generation */ 545 | for (int i = 0; i < GENERATION_SIZE; i++) { 546 | visualize(selection[i]); 547 | generation[i] = selection[i]; 548 | wrefresh(visualization_window); 549 | refresh(); 550 | werase(visualization_window); 551 | box(visualization_window, 0 , 0); 552 | } 553 | 554 | /* Update final mean score to give feedback */ 555 | mvwprintw(details_window, 1, 100, "* Generation %d -> average score: %.2f", iteration, mean(generation, GENERATION_SIZE)); 556 | mvwprintw(details_window, 2, 100, "* Generation %d -> fittest chromosome's score: %.2f", iteration, score(fittest(generation, GENERATION_SIZE))); 557 | 558 | wrefresh(details_window); 559 | refresh(); 560 | } 561 | 562 | while(1) { 563 | wattron(visualization_window, COLOR_PAIR(3)); 564 | mvwprintw(visualization_window, yMax * 0.3, xMax / 2 - 2, "DONE"); 565 | wrefresh(visualization_window); 566 | refresh(); 567 | usleep(500 * 1000); 568 | wattron(visualization_window, COLOR_PAIR(4)); 569 | mvwprintw(visualization_window, yMax * 0.3, xMax / 2 - 2, "DONE"); 570 | wrefresh(visualization_window); 571 | usleep(500 * 1000); 572 | } 573 | 574 | delwin(visualization_window); 575 | delwin(details_window); 576 | endwin(); 577 | 578 | return 0; 579 | } 580 | --------------------------------------------------------------------------------