summaryrefslogtreecommitdiff
path: root/src/main.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/main.c')
-rw-r--r--src/main.c680
1 files changed, 680 insertions, 0 deletions
diff --git a/src/main.c b/src/main.c
new file mode 100644
index 0000000..8447566
--- /dev/null
+++ b/src/main.c
@@ -0,0 +1,680 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+#include <SDL.h>
+#include <SDL_ttf.h>
+#include <gtk/gtk.h>
+#include <pthread.h>
+
+#include "sdlutils.h"
+#include "random.h"
+
+#define CHARS_X 180
+#define CHARS_Y 50
+
+#define WIDTH CHARS_X * CHAR_WIDTH
+#define HEIGHT CHARS_Y * CHAR_HEIGHT
+
+#define NEW_SIMULATION 1
+#define QUIT 2
+#define IMPORT_SAVE 3
+
+#define ASK_RUN_METHOD_MESSAGE "Please enter run method, substituting %s for where the genes should be inserted: "
+
+typedef enum
+{
+ STATE_MENU,
+ STATE_IMPORTING_SAVE,
+ STATE_CREATE_SIMULATION,
+ STATE_SIMULATION_OPTIONS,
+ STATE_SIMULATING
+} state_t;
+
+typedef struct
+{
+ double* genes;
+ double fitness;
+} genes_t;
+
+state_t state;
+char* run_method;
+pthread_t run_thread;
+double** gene_lists;
+genes_t* genes_ts;
+int population = 100;
+int num_genes = 100; // Number of genes or product of generation #
+int are_genes_constant = 1; // Is the number of genes constant?
+int population_change = 100;
+int num_genes_change = 100;
+int generation_survivors = 10; // Top n survivors breed
+int parents_per_child = 2;
+int mutation_rate = 70; // 70% of first mutation, after first, 70% of second mutation, etc.
+int generation_number;
+int was_loaded = 0;
+int starting_generation;
+int generations_to_run = -1;
+int loading = 0; // Is it loading a new generation?
+int stop; // Used to communicate with the simulating thread
+int batch_genes = 0; // Can the program process multiple gene sets (a whole generation) at once?
+
+
+
+void draw_simulation_options()
+{
+ char* popbuffer = malloc(12);
+ char* popchangebuffer = malloc(12);
+ char* ppcbuffer = malloc(12);
+ char* topnbuffer = malloc(12);
+ char* mutratebuffer = malloc(12);
+ char* genesbuffer = malloc(12);
+
+ write_str("Number of genes is ", 1, 3, WHITE, BLACK);
+ write_str("Linear to the generation number (n)", 20, 3, !are_genes_constant ? LIGHTBLUE : WHITE, BLACK);
+ write_str("/", 55, 3, WHITE, BLACK);
+ write_str("Constant (c)", 56, 3, are_genes_constant ? LIGHTBLUE : WHITE, BLACK);
+ char* genes_eq = are_genes_constant ? "Genes = " : "Genes = generation number * ";
+ int len = strlen(genes_eq);
+ write_str(" ", 1, 4, BLACK, BLACK);
+ write_str(genes_eq, 1, 4, WHITE, BLACK);
+ sprintf(genesbuffer, "%12d", num_genes);
+ write_str(genesbuffer, 1+len, 4, WHITE, BLACK);
+ write_str("-1 (1)", 14+len, 4, LIGHTRED, DARKRED);
+ write_str("+1 (2)", 20+len, 4, LIGHTGREEN, DARKGREEN);
+ write_str("/2 (3)", 26+len, 4, LIGHTRED, DARKRED);
+ write_str("*2 (4)", 32+len, 4, LIGHTGREEN, DARKGREEN);
+
+ write_str("Population: ", 1, 5, WHITE, BLACK);
+ sprintf(popbuffer, "%12d", population);
+ write_str(popbuffer, 13, 5, WHITE, BLACK);
+ write_str("- (l)", 26, 5, LIGHTRED, DARKRED);
+ write_str("+ (p)", 32, 5, LIGHTGREEN, DARKGREEN);
+ write_str("(Increase by)", 37, 5, WHITE, BLACK);
+ sprintf(popchangebuffer, "%12d", population_change);
+ write_str(popchangebuffer, 50, 5, WHITE, BLACK);
+ write_str("- (k)", 62, 5, LIGHTRED, DARKRED);
+ write_str("+ (o)", 68, 5, LIGHTGREEN, DARKGREEN);
+
+ write_str("Parents per child: ", 1, 6, WHITE, BLACK);
+ sprintf(ppcbuffer, "%3d", parents_per_child);
+ write_str(ppcbuffer, 20, 6, WHITE, BLACK);
+ write_str("- (f)", 24, 6, LIGHTRED, DARKRED);
+ write_str("+ (r)", 30, 6, LIGHTGREEN, DARKGREEN);
+
+ write_str("Top n algorithms survive: ", 1, 7, WHITE, BLACK);
+ sprintf(topnbuffer, "%12d", generation_survivors);
+ write_str(topnbuffer, 27, 7, WHITE, BLACK);
+ write_str("- (g)", 39, 7, LIGHTRED, DARKRED);
+ write_str("+ (t)", 45, 7, LIGHTGREEN, DARKGREEN);
+ write_str("/2 (v)", 51, 7, RED, DARKRED);
+ write_str("*2 (b)", 57, 7, GREEN, DARKGREEN);
+
+ write_str("Mutation rate: ", 1, 8, WHITE, BLACK);
+ sprintf(mutratebuffer, "%3d", mutation_rate);
+ write_str(mutratebuffer, 16, 8, WHITE, BLACK);
+ write_str("- (h)", 20, 8, LIGHTRED, DARKRED);
+ write_str("+ (y)", 26, 8, LIGHTGREEN, DARKGREEN);
+
+ write_str("Batch genes? ", 1, 9, WHITE, BLACK);
+ write_str("ON (z)", 14, 9, batch_genes ? LIGHTBLUE : WHITE, BLACK);
+ write_str("OFF (x)", 21, 9, !batch_genes ? LIGHTBLUE : WHITE, BLACK);
+ write_str("If you enable batch genes, the input will be provided as semicolon-separated comma-separated lists of genes. This will improve run time.",
+ 1, 10, WHITE, BLACK);
+ write_str("Begin (Return)", 1, 12, LIGHTGREEN, DARKGREEN);
+}
+
+int get_num_genes()
+{
+ return are_genes_constant ? num_genes : (num_genes * generation_number);
+}
+
+void allocate_genes()
+{
+ int i;
+ int num_genes = get_num_genes(generation_number);
+ for (i = 0; i < population; i++)
+ gene_lists[i] = malloc(num_genes * sizeof(double));
+}
+
+double get_fitness(double* genes)
+{
+ int ngenes = get_num_genes();
+ char* csgenes = malloc(ngenes * 16); // Comma-separated genes
+ csgenes[0] = 0;
+ strcat(csgenes, double_to_str(genes[0]));
+ int i;
+ for (i = 1; i < ngenes; i++)
+ {
+ strcat(csgenes, ",");
+ strcat(csgenes, double_to_str(genes[i]));
+ }
+ char* command = malloc(ngenes * 16 + 64);
+ sprintf(command, run_method, csgenes);
+ FILE* fp = popen(command, "r");
+ float fitness;
+ fscanf(fp, "%f", &fitness);
+ pclose(fp);
+ return (double) fitness;
+}
+
+double* get_fitnesses(double** genes)
+{
+ int i, j, ngenes = get_num_genes();
+ char* buffer = malloc(ngenes * population * 12);
+ strcat(buffer, double_to_str(genes[0][0]));
+ for (i = 1; i < ngenes; i++)
+ {
+ strcat(buffer, ",");
+ strcat(buffer, double_to_str(genes[0][i]));
+ }
+ for (j = 1; j < population; j++)
+ {
+ strcat(buffer, ";");
+ strcat(buffer, double_to_str(genes[j][0]));
+ for (i = 1; i < ngenes; i++)
+ {
+ strcat(buffer, ",");
+ strcat(buffer, double_to_str(genes[j][i]));
+ }
+ }
+ char* command = malloc(strlen(buffer) + strlen(run_method) + 50);
+ sprintf(command, run_method, buffer);
+ FILE* fp = popen(command, "r");
+ double* fitnesses;
+ float f;
+ fitnesses = malloc(population * sizeof(double));
+ fscanf(fp, "%f", &f);
+ fitnesses[0] = (double) f;
+ for (i = 1; i < population; i++)
+ {
+ fscanf(fp, ",%f", &f);
+ fitnesses[i] = (double) f;
+ }
+ fclose(fp);
+ return fitnesses;
+}
+
+void mutate(double* genes)
+{
+ int ngenes = get_num_genes();
+ int selected_gene;
+ while (rand01() < ((double) mutation_rate / 100))
+ {
+ selected_gene = randrange_int(0, ngenes);
+ genes[selected_gene] = rand01();
+ }
+}
+
+double* breed(double** genes)
+{
+ int i, newngenes, ngenes = get_num_genes();
+
+ generation_number++;
+ newngenes = get_num_genes();
+ generation_number--;
+
+ double* newgenes = malloc(newngenes*sizeof(double));
+
+ for (i = 0; i < ngenes; i++)
+ {
+ newgenes[i] = genes[randrange_int(0, parents_per_child)][i];
+ }
+ for (; i < newngenes; i++)
+ {
+ newgenes[i] = rand01();
+ }
+ mutate(newgenes);
+ return newgenes;
+}
+
+double** select_parents(genes_t* sorted_genes)
+{
+ int* selected = malloc(parents_per_child*sizeof(int));
+ int i, j, hasBeenSelected, sel;
+ for (i = 0; i < parents_per_child; i++)
+ {
+ do
+ {
+ sel = randrange_int(0, generation_survivors);
+ hasBeenSelected = 0;
+ for (j = 0; j < i; j++)
+ {
+ if (sel == selected[j])
+ hasBeenSelected = 1;
+ }
+ }
+ while (hasBeenSelected);
+ selected[i] = sel;
+ }
+ double** parents = malloc(parents_per_child*sizeof(double*));
+ for (i = 0; i < parents_per_child; i++)
+ {
+ parents[i] = sorted_genes[selected[i]].genes;
+ }
+ return parents;
+}
+
+int compare_genes(const void* a, const void* b)
+{
+ genes_t genes_a = *(genes_t*) a;
+ genes_t genes_b = *(genes_t*) b;
+ double diff = genes_b.fitness - genes_a.fitness;
+ return diff > 0 ? 1 : (diff == 0 ? 0 : -1);
+}
+
+
+
+void* run_generations(void* data)
+{
+ // Runs generations_to_run generations
+ if (loading) return NULL;
+ loading = 1;
+ stop = 0;
+ int i, gn; // Number of local generations (out of n)
+ starting_generation = generation_number;
+ double* fitnesses;
+ for (gn = 0; generations_to_run == -1 ? 1 : (gn < generations_to_run); gn++)
+ {
+ if (batch_genes)
+ {
+ fitnesses = get_fitnesses(gene_lists);
+ for (i = 0; i < population; i++)
+ {
+ genes_ts[i].genes = gene_lists[i];
+ genes_ts[i].fitness = fitnesses[i];
+ }
+ }
+ else
+ {
+ for (i = 0; i < population; i++)
+ {
+ genes_ts[i].genes = gene_lists[i];
+ genes_ts[i].fitness = get_fitness(genes_ts[i].genes);
+ }
+ }
+ qsort(genes_ts, population, sizeof(genes_t), compare_genes);
+ if (stop)
+ {
+ loading = -1;
+ return NULL;
+ }
+ for (i = 0; i < population; i++)
+ {
+ gene_lists[i] = breed(select_parents(genes_ts));
+ }
+ printf("Generation %d; Best fitness: %f\n", generation_number, genes_ts[0].fitness);
+ generation_number++;
+ if (stop)
+ {
+ loading = -1;
+ return NULL;
+ }
+ }
+
+ loading = -1;
+ generations_to_run = -1;
+ return NULL;
+}
+
+void simulate()
+{
+ int i, j, num_genes;
+ if (!was_loaded)
+ {
+ generation_number = 1;
+ gene_lists = malloc(population * sizeof(double*));
+ num_genes = get_num_genes();
+ allocate_genes();
+ for (i = 0; i < population; i++)
+ for (j = 0; j < num_genes; j++)
+ gene_lists[i][j] = rand01();
+ }
+
+ write_str("Run 1 generation (1)", 1, 1, WHITE, DARKBLUE);
+ write_str("Run 10 generations (2)", 23, 1, WHITE, DARKBLUE);
+ write_str("Run 100 generations (3)", 47, 1, WHITE, DARKBLUE);
+ write_str("Run 1000 generations (4)", 72, 1, WHITE, DARKBLUE);
+ write_str("Run 10000 generations (5)", 98, 1, WHITE, DARKBLUE);
+ write_str("Keep running generations (6)", 125, 1, WHITE, DARKBLUE);
+ write_str("Stop (7)", 155, 1, WHITE, DARKBLUE);
+}
+
+void change_state(state_t to)
+{
+ state = to;
+ clear_options();
+ SDL_SetRenderDrawColor(renderer, 0, 0, 0, 255);
+ SDL_RenderClear(renderer);
+ switch (to)
+ {
+ case STATE_IMPORTING_SAVE:
+ write_str("Please enter the path to the save file: ", 1, 1, WHITE, BLACK);
+ setup_entry("", 41, 1);
+ break;
+ case STATE_CREATE_SIMULATION:
+ setup_entry("", strlen(ASK_RUN_METHOD_MESSAGE)+1, 1);
+ write_str(ASK_RUN_METHOD_MESSAGE, 1, 1, WHITE, BLACK);
+ write_str("When this command is run, %s will be replaced with a comma-separated list of genes.", 1, 2, WHITE, BLACK);
+ break;
+ case STATE_SIMULATION_OPTIONS:
+ hcenter_str("Simulation Options", CHARS_X, 1, WHITE, BLACK);
+ draw_simulation_options();
+ break;
+ case STATE_SIMULATING:
+ simulate();
+ break;
+ }
+}
+
+void save(char* filename)
+{
+ int i, j;
+ FILE* fp = fopen(filename, "w");
+ fprintf(fp, "%d\n", generation_number);
+ fprintf(fp, "%s\n", run_method);
+ fprintf(fp, "%d\n", population);
+ fprintf(fp, "%d\n", num_genes);
+ fprintf(fp, "%d\n", are_genes_constant);
+ fprintf(fp, "%d\n", parents_per_child);
+ fprintf(fp, "%d\n", generation_survivors);
+ fprintf(fp, "%d\n", mutation_rate);
+ fprintf(fp, "%d\n", batch_genes);
+ for (i = 0; i < population; i++)
+ {
+ for (j = 0; j < get_num_genes(); j++)
+ fprintf(fp, "%f ", genes_ts[i].genes[j]);
+ fprintf(fp, "\n");
+ }
+ fclose(fp);
+}
+
+void load(char* filename)
+{
+ int i, j;
+ FILE* fp = fopen(filename, "r");
+ run_method = malloc(512);
+ fscanf(fp, "%d\n", &generation_number);
+ fscanf(fp, "%[^\n]s\n", run_method);
+ fscanf(fp, "%d\n", &population);
+ fscanf(fp, "%d\n", &num_genes);
+ fscanf(fp, "%d\n", &are_genes_constant);
+ fscanf(fp, "%d\n", &parents_per_child);
+ fscanf(fp, "%d\n", &generation_survivors);
+ fscanf(fp, "%d\n", &mutation_rate);
+ fscanf(fp, "%d\n", &batch_genes);
+ float f;
+ gene_lists = malloc(population * sizeof(double*));
+ allocate_genes();
+ for (i = 0; i < population; i++)
+ {
+ for (j = 0; j < get_num_genes(); j++)
+ {
+ fscanf(fp, "%f ", &f);
+ gene_lists[i][j] = f;
+ }
+ fscanf(fp, "\n");
+ }
+ fclose(fp);
+ was_loaded = 1;
+ change_state(STATE_SIMULATING);
+}
+
+int main()
+{
+ srand(time(NULL));
+ int end = 0;
+ SDL_Event event;
+
+ sdlutils_init(WIDTH, HEIGHT);
+ sdlutils_set_capacity(256);
+
+ // Title
+ write_str("Genetic Algorithm Framework", 1, 1, WHITE, BLACK);
+ create_option("New simulation", NEW_SIMULATION, 1, 2, LIGHTGREEN, DARKGREEN, rgba(100, 200, 100, 255));
+ create_option("Import from save file", IMPORT_SAVE, 1, 3, LIGHTBLUE, DARKBLUE, rgba(100, 100, 200, 255));
+ create_option("Quit", QUIT, 1, 4, LIGHTRED, DARKRED, rgba(200, 100, 100, 255));
+ draw_options();
+ write_str("See github.com/pommicket/genetic for more information.", 1, 5, WHITE, BLACK);
+
+ int action;
+
+
+ genes_ts = malloc(population * sizeof(genes_t));
+
+ char* filename;
+ int i, j, r1, r2;
+ FILE* fp;
+ char* buffer;
+ char* gen = malloc(256);
+ int lgnum = -1;
+ int lgtrun = -1;
+ while (!end)
+ {
+ if (state == STATE_SIMULATING && (lgnum != generation_number || lgtrun != generations_to_run))
+ {
+ if (generations_to_run > 0)
+ sprintf(gen, " Generation %12d of %12d ", generation_number, starting_generation + generations_to_run);
+ else
+ sprintf(gen, " Generation %12d ", generation_number);
+ lgnum = generation_number;
+ lgtrun = generations_to_run;
+ hcenter_str(gen, CHARS_X, 4, WHITE, BLACK);
+ }
+ if (loading == -1)
+ {
+ // When done loading
+ write_str(" ", 1, 2, BLACK, BLACK);
+ write_str("Export best set of genes (b)", 1, 3, WHITE, DARKGREEN);
+ write_str("Export generation (g)", 31, 3, WHITE, DARKGREEN);
+ write_str("Save progress (s)", 54, 3, WHITE, DARKGREEN);
+ buffer = malloc(32);
+ write_str("Best fitness: ", 1, 4, WHITE, BLACK);
+ sprintf(buffer, "%f", genes_ts[0].fitness);
+ write_str(buffer, 15, 4, WHITE, BLACK);
+ write_str("Genes:", 1, 5, WHITE, BLACK);
+ for (i = 0; i < get_num_genes() && i < CHARS_Y; i++)
+ {
+ sprintf(buffer, "%f", genes_ts[0].genes[i]);
+ write_str(buffer, 1, i + 6, WHITE, BLACK);
+ }
+ loading = 0;
+
+ }
+ SDL_RenderPresent(renderer);
+ while (SDL_PollEvent(&event))
+ {
+
+ switch (event.type)
+ {
+ case SDL_QUIT:
+ end = 1;
+ break;
+ case SDL_KEYDOWN:
+ switch (state)
+ {
+ case STATE_SIMULATION_OPTIONS:
+ switch (event.key.keysym.sym)
+ {
+ case SDLK_p:
+ if (population + population_change > 0) population += population_change;
+ break;
+ case SDLK_l:
+ if (population - population_change > 0) population -= population_change;
+ break;
+ case SDLK_o:
+ if (population_change * 2 > 0) population_change *= 2;
+ break;
+ case SDLK_k:
+ if (population_change / 2 > 0) population_change /= 2;
+ break;
+ case SDLK_n:
+ are_genes_constant = 0;
+ break;
+ case SDLK_c:
+ are_genes_constant = 1;
+ break;
+ case SDLK_f:
+ if (parents_per_child > 1) parents_per_child--;
+ break;
+ case SDLK_r:
+ if (parents_per_child < generation_survivors) parents_per_child++;
+ break;
+ case SDLK_g:
+ if (generation_survivors > 1) generation_survivors--;
+ break;
+ case SDLK_t:
+ if (generation_survivors < population) generation_survivors++;
+ break;
+ case SDLK_v:
+ if (generation_survivors / 2 >= 1) generation_survivors /= 2;
+ break;
+ case SDLK_b:
+ if (generation_survivors * 2 <= population) generation_survivors *= 2;
+ break;
+ case SDLK_h:
+ if (mutation_rate) mutation_rate--;
+ break;
+ case SDLK_y:
+ if (mutation_rate < 100) mutation_rate++;
+ break;
+ case SDLK_z:
+ batch_genes = 1;
+ break;
+ case SDLK_x:
+ batch_genes = 0;
+ break;
+ case SDLK_1:
+ if (num_genes > 0) num_genes--;
+ break;
+ case SDLK_2:
+ num_genes++;
+ break;
+ case SDLK_3:
+ num_genes /= 2;
+ break;
+ case SDLK_4:
+ num_genes *= 2;
+ break;
+ case SDLK_RETURN:
+ change_state(STATE_SIMULATING);
+ break;
+ }
+ if (state == STATE_SIMULATION_OPTIONS) draw_simulation_options();
+ break;
+ case STATE_SIMULATING:
+ generations_to_run = 0;
+ switch (event.key.keysym.sym)
+ {
+ case SDLK_1:
+ generations_to_run = 1;
+ break;
+ case SDLK_2:
+ generations_to_run = 10;
+ break;
+ case SDLK_3:
+ generations_to_run = 100;
+ break;
+ case SDLK_4:
+ generations_to_run = 1000;
+ break;
+ case SDLK_5:
+ generations_to_run = 10000;
+ break;
+ case SDLK_6:
+ generations_to_run = -1;
+ break;
+ case SDLK_7:
+ stop = 1;
+ loading = -1;
+ break;
+ case SDLK_b:
+ if (loading) break;
+ filename = malloc(64);
+ r1 = rand() % 100000;
+ r2 = rand() % 100000;
+ sprintf(filename, "genes%05d%05d.txt", r1, r2);
+ fp = fopen(filename, "w");
+ for (i = 0; i < get_num_genes(); i++)
+ fprintf(fp, "%f\n", genes_ts[0].genes[i]);
+ fclose(fp);
+ write_str("Exported to ", 73, 3, WHITE, BLACK);
+ write_str(filename, 85, 3, WHITE, BLACK);
+ write_str(" ", 104, 3, BLACK, BLACK);
+ break;
+ case SDLK_g:
+ if (loading) break;
+ filename = malloc(64);
+ r1 = rand() % 100000;
+ r2 = rand() % 100000;
+ sprintf(filename, "generation%05d%05d.txt", r1, r2);
+ fp = fopen(filename, "w");
+ for (i = 0; i < population; i++){
+ for (j = 0; j < get_num_genes(); j++)
+ fprintf(fp, "%f ", genes_ts[i].genes[j]);
+ fprintf(fp, "\n");
+ }
+ fclose(fp);
+ write_str("Exported to ", 73, 3, WHITE, BLACK);
+ write_str(filename, 85, 3, WHITE, BLACK);
+ break;
+ case SDLK_s:
+ if (loading) break;
+ filename = malloc(64);
+ r1 = rand() % 10000;
+ r2 = rand() % 10000;
+ sprintf(filename, "save%04d%04d.save", r1, r2);
+ save(filename);
+ write_str("Saved to ", 73, 3, WHITE, BLACK);
+ write_str(filename, 82, 3, WHITE, BLACK);
+ write_str(" ", 99, 3, BLACK, BLACK);
+ break;
+ }
+ if (generations_to_run)
+ {
+ write_str("Loading...", 1, 2, WHITE, BLACK);
+ pthread_create(&run_thread, NULL, run_generations, NULL);
+ }
+ break;
+ }
+ action = sdlutils_process_key(event.key.keysym.sym);
+ if (action)
+ {
+ switch (action)
+ {
+ case NEW_SIMULATION:
+ change_state(STATE_CREATE_SIMULATION);
+ break;
+ case ENTRY_SUBMIT:
+ switch (state)
+ {
+ case STATE_CREATE_SIMULATION:
+ run_method = malloc(512);
+ strcpy(run_method, get_entry_contents());
+ change_state(STATE_SIMULATION_OPTIONS);
+ break;
+ case STATE_IMPORTING_SAVE:
+ buffer = malloc(512);
+ strcpy(buffer, get_entry_contents());
+ load(buffer);
+ break;
+ }
+ break;
+ case QUIT:
+ end = 1;
+ break;
+ case IMPORT_SAVE:
+ change_state(STATE_IMPORTING_SAVE);
+ break;
+ }
+ }
+ break;
+ case SDL_TEXTINPUT:
+ handle_text(event.text.text);
+ }
+
+ }
+ }
+
+ quit();
+ return 0;
+}