summary refs log tree commit diff
path: root/src/trainer
diff options
context:
space:
mode:
Diffstat (limited to 'src/trainer')
-rw-r--r--src/trainer/logging.c71
-rw-r--r--src/trainer/logging.h14
-rw-r--r--src/trainer/main.c135
3 files changed, 220 insertions, 0 deletions
diff --git a/src/trainer/logging.c b/src/trainer/logging.c
new file mode 100644
index 0000000..f35ab73
--- /dev/null
+++ b/src/trainer/logging.c
@@ -0,0 +1,71 @@
+// File by "Yangelis" https://github.com/yangelis
+
+#include "../game.h"
+
+FILE *gnuplot_pipe = 0;
+#define nOfGnuplotCmds 14
+char *gnuplotCmds[] = {
+  "reset\n",
+  "set size 1,1\n",
+  "set origin 0,0\n",
+  "set xlabel 'Generations'\n",
+  "set multiplot layout 2, 2 columnsfirst title 'gp live stats'\n",
+  "set ylabel 'Avg Lifetime'\n",
+  "plot 'log.dat' using 1:2 with lines lw 2 lt 10 notitle \n",
+  "set ylabel 'Min lifetime'\n",
+  "plot 'log.dat' using 1:3 with lines lw 2 lt 10 notitle\n",
+  "set ylabel 'Max lifetime'\n",
+  "plot 'log.dat' using 1:4 with lines lw 2 lt 10 notitle\n",
+  "set ylabel 'Food Eaten'\n",
+  "plot 'log.dat' using 1:5 with lines lw 2 lt 10 notitle\n",
+  "unset multiplot\n"
+};
+
+
+float avg_lifetime(Agent *agents) {
+  int result = 0.0;
+  for (size_t i = 0; i < AGENTS_COUNT; ++i) {
+    result += agents[i].lifetime;
+  }
+  return (float)result / AGENTS_COUNT;
+}
+void log_header(FILE *stream) {
+  fprintf(stream, "#Loggind generations\n");
+  fprintf(
+      stream,
+      "#Generation, Avg Lifetime, Min Lifetime, Max Lifetime, Food eaten\n");
+  gnuplot_pipe = popen("gnuplot -p", "w");
+}
+void log_generation(FILE *stream, int gen, Game *game) {
+  float avg_lf = avg_lifetime(game->agents);
+  int max_lifetime = game->agents[0].lifetime;
+  int min_lifetime = game->agents[0].lifetime;
+  for (size_t i = 1; i < AGENTS_COUNT; ++i) {
+    if (game->agents[i].lifetime > max_lifetime) {
+      max_lifetime = game->agents[i].lifetime;
+    }
+    if (game->agents[i].lifetime < min_lifetime) {
+      min_lifetime = game->agents[i].lifetime;
+    }
+  }
+
+  int food_eaten = FOODS_COUNT;
+  for (size_t y = 0; y < BOARD_HEIGHT; ++y) {
+    for (size_t x = 0; x < BOARD_WIDTH; ++x) {
+      if (game->foods_map[y][x]) {
+        food_eaten--;
+      }
+    }
+  }
+
+  /* Writing to log file*/
+  fprintf(stream, "%d, %f, %d, %d, %d\n", gen, avg_lf, min_lifetime,
+          max_lifetime, food_eaten);
+}
+void log_live_update(void) {
+  for (size_t i = 0; i < nOfGnuplotCmds; ++i) {
+    fprintf(gnuplot_pipe, "%s \n", gnuplotCmds[i]);
+  }
+  fflush(gnuplot_pipe);
+}
+void log_close_pipe(void) { pclose(gnuplot_pipe); }
diff --git a/src/trainer/logging.h b/src/trainer/logging.h
new file mode 100644
index 0000000..93cb595
--- /dev/null
+++ b/src/trainer/logging.h
@@ -0,0 +1,14 @@
+#ifndef LOGGING_H
+#define LOGGING_H
+
+#include "../game.h"
+
+void log_header(FILE *stream);
+
+void log_generation(FILE *stream, int gen, Game *game);
+
+void log_close_pipe(void);
+
+void log_live_update(void);
+
+#endif
diff --git a/src/trainer/main.c b/src/trainer/main.c
new file mode 100644
index 0000000..fa4d97f
--- /dev/null
+++ b/src/trainer/main.c
@@ -0,0 +1,135 @@
+#include <assert.h>
+#include <signal.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <time.h>
+
+#include <SDL2/SDL.h>
+
+#include "../game.h"
+#include "logging.h"
+
+Game games[2] = {0};
+
+// Tsoding is now a 3-Star Developer!
+const char *tsoding_shift(int *argc, char ***argv) {
+  assert(*argc > 0);
+  const char* result = **argv;
+  *argc -= 1;
+  *argv += 1;
+  return result;
+}
+
+void usage(FILE *stream) {
+  fprintf(stream, "Usage: ./gp_trainer [OPTIONS]\n");
+  fprintf(stream, "       --gen|g <NUMBER>\n");
+  fprintf(stream, "         Amount of generations (default: 100)\n");
+  fprintf(stream, "       --out|o <FILEPATH>\n");
+  fprintf(stream, "         Output filepath (default: out.bin)\n");
+  fprintf(stream, "       --plot|p\n");
+  fprintf(stream, "         Enable Gnuplot live reporting\n");
+}
+
+FILE* gnulog;
+int gnuplot;
+int current;
+const char* output_filepath;
+
+void ctrlc(int signum) {
+  if (gnuplot) {
+    log_close_pipe();
+    fclose(gnulog);
+  }
+
+  dump_game(output_filepath, &games[current]);
+  exit(1);
+}
+
+int main(int argc, char *argv[]) {
+
+  signal(SIGINT, ctrlc);
+
+  tsoding_shift(&argc, &argv); //Skip Program Name
+
+  srand(time(NULL));
+  int gen_count = 100;
+  output_filepath = "out.bin";
+  gnuplot = 0;
+
+  while (argc > 0) {
+    const char *flag = tsoding_shift(&argc, &argv);
+
+    if (strcmp(flag, "--gen") == 0 || strcmp(flag, "-g") == 0) {
+      if (argc == 0) {
+        usage(stderr);
+        fprintf(stderr, "ERROR: no value provided for flag %s\n", flag);
+        exit(1);
+      }
+      const char *value = tsoding_shift(&argc, &argv);
+      gen_count = atoi(value);
+
+    } else if (strcmp(flag, "--out") == 0 || strcmp(flag, "-o") == 0) {
+      if (argc == 0) {
+        usage(stderr);
+        fprintf(stderr, "ERROR: no value provided for flag %s\n", flag);
+        exit(1);
+      }
+      const char *value = tsoding_shift(&argc, &argv);
+      output_filepath = value;
+
+    } else if (strcmp(flag, "--plot") == 0 || strcmp(flag, "-p") == 0) {
+      gnuplot = 1;
+
+    } else if (strcmp(flag, "--help") == 0 || strcmp(flag, "-h") == 0) {
+      usage(stdout);
+      exit(0);
+    } else {
+      usage(stderr);
+      fprintf(stderr, "ERROR: unknown flag: %s\n", flag);
+      exit(1);
+    }
+  }
+
+  if (gnuplot) {
+    gnulog = fopen("log.dat", "w");
+    log_header(gnulog);
+  }
+
+  current = 0;
+
+  init_game(&games[current]);
+
+  for (int i = 0; i < gen_count; ++i) {
+    fprintf(stderr, "Generation %d... ", i);
+    fflush(stderr);
+
+    clock_t begin = clock();
+    while (!is_everyone_dead(&games[current])) {
+      step_game(&games[current]);
+    }
+
+    printf("%fs\n", (float)(clock() - begin) / (float) CLOCKS_PER_SEC);
+    fflush(stdout);
+
+    if (gnuplot) {
+      log_generation(gnulog, i, &games[current]);
+      fflush(gnulog);
+      log_live_update();
+    }
+
+    int next = 1 -  current;
+    make_next_generation(&games[current], &games[next]);
+    current = next;
+  }
+
+  if (gnuplot) {
+    log_close_pipe();
+    fclose(gnulog);
+  }
+
+  dump_game(output_filepath, &games[current]);
+
+  return 0;
+}