├── .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 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
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 | 
7 | 
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 |
--------------------------------------------------------------------------------