summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile11
-rw-r--r--batch.py10
-rwxr-xr-xbin/GENETICbin0 -> 38072 bytes
-rw-r--r--fitness.py8
-rw-r--r--fonts/DejaVuSansMono.ttfbin0 -> 340712 bytes
-rw-r--r--src/colors.h22
-rw-r--r--src/main.c680
-rw-r--r--src/random.h16
-rw-r--r--src/sdlutils.h257
9 files changed, 1004 insertions, 0 deletions
diff --git a/Makefile b/Makefile
new file mode 100644
index 0000000..be12f41
--- /dev/null
+++ b/Makefile
@@ -0,0 +1,11 @@
+CC=gcc
+CFLAGS=`pkg-config --libs --cflags sdl2 SDL2_ttf gtk+-3.0` -lm -lpthread -Wall -Wno-switch -Wno-parentheses
+OUTPUT=bin/GENETIC
+
+default: all
+
+all: src/main.c
+ $(CC) src/main.c $(CFLAGS) -o $(OUTPUT)
+
+run: all
+ ./$(OUTPUT)
diff --git a/batch.py b/batch.py
new file mode 100644
index 0000000..5891695
--- /dev/null
+++ b/batch.py
@@ -0,0 +1,10 @@
+# Sample batch fitness algorithm
+# fitness = sum(genes)
+# Run method: python batch.py "%s"
+
+import sys
+
+gene_lists = sys.argv[1].split(";")
+genes = map(lambda genes: map(float, genes.split(",")), gene_lists)
+fitnesses = map(sum, genes)
+print ",".join(map(str, fitnesses))
diff --git a/bin/GENETIC b/bin/GENETIC
new file mode 100755
index 0000000..5f31b4f
--- /dev/null
+++ b/bin/GENETIC
Binary files differ
diff --git a/fitness.py b/fitness.py
new file mode 100644
index 0000000..a1c5aab
--- /dev/null
+++ b/fitness.py
@@ -0,0 +1,8 @@
+# Sample fitness algorithm (non-batch)
+# fitness = sum(genes)
+# Run method: python fitness.py %s
+
+import sys
+
+genes = map(float, sys.argv[1].split(","))
+print sum(genes)
diff --git a/fonts/DejaVuSansMono.ttf b/fonts/DejaVuSansMono.ttf
new file mode 100644
index 0000000..da5abdd
--- /dev/null
+++ b/fonts/DejaVuSansMono.ttf
Binary files differ
diff --git a/src/colors.h b/src/colors.h
new file mode 100644
index 0000000..fb0fade
--- /dev/null
+++ b/src/colors.h
@@ -0,0 +1,22 @@
+#define WHITE rgba(255, 255, 255, 255)
+#define DARKGRAY rgba(50, 50, 50, 255)
+#define LIGHTGRAY rgba(100, 100, 100, 255)
+#define BLACK rgba(0, 0, 0, 255)
+
+#define RED rgba(255, 0, 0, 255)
+#define DARKRED rgba(150, 0, 0, 255)
+#define LIGHTRED rgba(255, 150, 150, 255)
+
+#define GREEN rgba(0, 255, 0, 255)
+#define DARKGREEN rgba(0, 150, 0, 255)
+#define LIGHTGREEN rgba(150, 255, 150, 255)
+
+#define BLUE rgba(0, 0, 255, 255)
+#define DARKBLUE rgba(0, 0, 150, 255)
+#define LIGHTBLUE rgba(150, 150, 255, 255)
+
+SDL_Color rgba(int r, int g, int b, int a)
+{
+ SDL_Color color = {r, g, b, a};
+ return color;
+}
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;
+}
diff --git a/src/random.h b/src/random.h
new file mode 100644
index 0000000..c3fa755
--- /dev/null
+++ b/src/random.h
@@ -0,0 +1,16 @@
+#include <stdlib.h>
+
+double rand01()
+{
+ return (double) rand() / RAND_MAX;
+}
+
+double randrange(double start, double end)
+{
+ return (end-start) * rand01() + start;
+}
+
+int randrange_int(int start, int end)
+{
+ return (int) randrange((double) start, (double) end);
+}
diff --git a/src/sdlutils.h b/src/sdlutils.h
new file mode 100644
index 0000000..a3dbbaf
--- /dev/null
+++ b/src/sdlutils.h
@@ -0,0 +1,257 @@
+// __-prefixed functions, structs, etc. are not advised to be used by other files
+#include <stdlib.h>
+#include <math.h>
+
+#include <SDL.h>
+#include <SDL_ttf.h>
+#include "colors.h"
+
+#define FONT_WIDTH_FACTOR 0.585 // Width of text / letter / font size
+#define FONT_HEIGHT_FACTOR 1.2 // Height of text / font size
+
+#define FONT_SIZE 12
+#define CHAR_WIDTH FONT_WIDTH_FACTOR * FONT_SIZE
+#define CHAR_HEIGHT FONT_HEIGHT_FACTOR * FONT_SIZE
+
+#define MAX_OPTIONS 1000
+
+#define ENTRY_SUBMIT 1 << 14
+
+SDL_Window* window;
+SDL_Surface* window_surface;
+SDL_Renderer* renderer;
+TTF_Font* font;
+char* editor;
+char* __sdlutils_clear;
+int __sdlutils_entry_capacity;
+int __sdlutils_entry_enabled;
+
+typedef struct
+{
+ char* text;
+ int xpos;
+ int ypos;
+ int action_id;
+ SDL_Color fg;
+ SDL_Color bg;
+ SDL_Color hl; // BG When highlighted
+} __sdlutils_option;
+
+__sdlutils_option** __sdlutils_options;
+int __sdlutils_selected = 0;
+int __sdlutils_option_count = 0;
+int __sdlutils_entry_x, __sdlutils_entry_y;
+char* __sdlutils_entry_contents;
+int __sdlutils_entry_contents_sz; // Length of string in entry
+
+char* int_to_str(int i)
+{
+ char* buffer = malloc((int)(log((double)i)/log(10))+3);
+ sprintf(buffer, "%d", i);
+ return buffer;
+}
+
+char* double_to_str(double d)
+{
+ char* buffer = malloc(32);
+ sprintf(buffer, "%f", d);
+ return buffer;
+}
+
+
+void write_text(char* text, int x, int y, SDL_Color fg, SDL_Color bg)
+{
+ if (!(text && text[0])) return;
+ SDL_Surface* text_surface = TTF_RenderText_Solid(font, text, fg);
+ SDL_Texture* texture = SDL_CreateTextureFromSurface(renderer, text_surface);
+ SDL_Rect rect = {x, y, text_surface->w, text_surface->h};
+ SDL_SetRenderDrawColor(renderer, bg.r, bg.g, bg.b, bg.a);
+ SDL_RenderFillRect(renderer, &rect);
+ SDL_RenderCopy(renderer, texture, NULL, &rect);
+ SDL_FreeSurface(text_surface);
+ SDL_DestroyTexture(texture);
+
+}
+
+void write_str(char* s, int xpos, int ypos, SDL_Color fg, SDL_Color bg)
+{
+ // For grid-like letter placement (for text-based applications1)
+ // Same as write_text, but writes in terms of the letter positions
+ write_text(s, xpos * CHAR_WIDTH, ypos * CHAR_HEIGHT, fg, bg);
+}
+
+void hcenter_str(char* s, int cols, int ypos, SDL_Color fg, SDL_Color bg)
+{
+ // Center string horizontally
+ write_str(s, cols / 2 - strlen(s) / 2, ypos, fg, bg);
+}
+
+void write_char(char c, int xpos, int ypos, SDL_Color fg, SDL_Color bg)
+{
+ char* s = malloc(2);
+ s[0] = c;
+ s[1] = 0;
+ write_str(s, xpos, ypos, fg, bg);
+}
+
+void create_option(char* text, int action_id, int x, int y, SDL_Color fg, SDL_Color bg, SDL_Color highlighted)
+{
+ __sdlutils_option* opt = malloc(sizeof(__sdlutils_option));
+ opt->text = text;
+ opt->action_id = action_id;
+ opt->xpos = x;
+ opt->ypos = y;
+ opt->fg = fg;
+ opt->bg = bg;
+ opt->hl = highlighted;
+ __sdlutils_options[__sdlutils_option_count++] = opt;
+}
+
+void draw_options()
+{
+ int i;
+ __sdlutils_option* opt;
+ for (i = 0; opt = __sdlutils_options[i]; i++)
+ {
+ write_str(opt->text, opt->xpos, opt->ypos, opt->fg, __sdlutils_selected == i ? opt->hl : opt->bg);
+ }
+}
+
+void clear_options()
+{
+ int i;
+ for (i = __sdlutils_option_count = 0; i < MAX_OPTIONS; i++)
+ {
+ __sdlutils_options[i] = NULL;
+ }
+ __sdlutils_selected = 0;
+ for (i = 0; i < __sdlutils_entry_capacity; i++) __sdlutils_entry_contents[i] = 0;
+ __sdlutils_entry_enabled = 0;
+ SDL_StopTextInput();
+}
+
+int mod(int a, int b)
+{
+ a %= b;
+ while (a < 0) a += b;
+ return a;
+}
+
+void draw_entry()
+{
+ if (__sdlutils_entry_enabled)
+ {
+ write_str(__sdlutils_clear, __sdlutils_entry_x, __sdlutils_entry_y, BLACK, BLACK);
+ write_str(__sdlutils_entry_contents, __sdlutils_entry_x, __sdlutils_entry_y, WHITE, BLACK);
+ }
+}
+
+void setup_entry(char* contents, int x, int y)
+{
+ memcpy(__sdlutils_entry_contents, contents, strlen(contents)+1);
+ __sdlutils_entry_x = x;
+ __sdlutils_entry_y = y;
+ __sdlutils_entry_enabled = 1;
+ SDL_StartTextInput();
+ draw_entry();
+}
+
+char* get_entry_contents()
+{
+ return __sdlutils_entry_contents;
+}
+
+
+
+int sdlutils_process_key(SDL_Keycode kc)
+{
+ int len;
+ switch (kc)
+ {
+ case SDLK_UP:
+ if (__sdlutils_options[0])
+ {
+ __sdlutils_selected--;
+ __sdlutils_selected = mod(__sdlutils_selected, __sdlutils_option_count);
+ draw_options();
+ }
+ break;
+ case SDLK_DOWN:
+ if (__sdlutils_options[0])
+ {
+ __sdlutils_selected++;
+ __sdlutils_selected = mod(__sdlutils_selected, __sdlutils_option_count);
+ draw_options();
+ }
+ break;
+ case SDLK_RETURN:
+ if (__sdlutils_entry_enabled) return ENTRY_SUBMIT;
+ if (__sdlutils_options[0]) return __sdlutils_options[__sdlutils_selected]->action_id;
+ break;
+ case SDLK_BACKSPACE:
+ if (!__sdlutils_entry_enabled) break;
+ len = strlen(__sdlutils_entry_contents);
+ __sdlutils_entry_contents[(len ? len : 1)-1] = 0;
+ break;
+ }
+ if (__sdlutils_options[0]) __sdlutils_selected = mod(__sdlutils_selected, __sdlutils_option_count);
+ draw_entry();
+ return 0;
+}
+
+void handle_text(char* text)
+{
+ if (!__sdlutils_entry_enabled) return;
+ strcat(__sdlutils_entry_contents, text);
+ draw_entry();
+}
+
+void quit()
+{
+ SDL_DestroyWindow(window);
+ TTF_CloseFont(font);
+ TTF_Quit();
+ SDL_Quit();
+ exit(0);
+}
+
+void sdlutils_set_capacity(int capacity)
+{
+ __sdlutils_entry_contents = malloc((__sdlutils_entry_capacity = capacity)+1);
+ __sdlutils_clear = malloc(__sdlutils_entry_capacity+1);
+ int i;
+ for (i = 0; i < __sdlutils_entry_capacity; i++)
+ {
+ __sdlutils_clear[i] = 'M'; // Wide letter for non-monospace fonts (although most of this wouldn't work for non-monospace fonts)
+ }
+ __sdlutils_clear[i] = 0;
+}
+
+void sdlutils_init(int width, int height)
+{
+ __sdlutils_options = malloc(MAX_OPTIONS*sizeof(__sdlutils_option*));
+
+ clear_options();
+
+ if (SDL_Init(SDL_INIT_VIDEO) < 0)
+ {
+ fprintf(stderr, "SDL Error: %s\n", SDL_GetError());
+ exit(1);
+ }
+ if (TTF_Init() == -1)
+ {
+ fprintf(stderr, "SDL_ttf could not initialize! SDL_ttf Error: %s\n", TTF_GetError());
+ }
+
+ window = SDL_CreateWindow("Life", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, width, height, SDL_WINDOW_SHOWN);
+ if (window == NULL)
+ {
+ fprintf(stderr, "SDL Error: %s\n", SDL_GetError());
+ exit(1);
+ }
+
+ window_surface = SDL_GetWindowSurface(window);
+ renderer = SDL_CreateRenderer(window, -1, SDL_RENDERER_ACCELERATED);
+ // Font
+ font = TTF_OpenFont("fonts/DejaVuSansMono.ttf", FONT_SIZE);
+}