NEAT C main
#ifdef _WIN32
#include <windows.h>
#endif
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <signal.h>
#include <time.h>
#include <math.h>
#include <GL/glut.h>
#include "neat.h"
#include "map.h"
#include "unit.h"
#include "avg.h"
#include "list.h"
#include "error.h"
#define _PI 3.14159265358979323846
#define _2PI (2 * _PI)
#define _NDIST 6
#define _MDIST 1000
#define _NPOPS 4
#define _NUNITS 50
#define _TIME_ALIVE_MIN 2000
#define _TIME_ALIVE_MAX 40000
#define _NINPUTS (5 + _NDIST)
#define _NOUTPUTS 2
#define _GENEPROB 1
#define _NTRACES 100
#define _UNIT_ANGLE (16 * _PI / 180)
#define _LIGHT_MOVERAYS 16
#define _MAPW (16 * 16)
#define _MAPH (16 * 10)
#define _FILENAME_MAP "world.map"
#define _FILENAME_POPS "pops.gnm"
#define _MAX(A, B) ((A) > (B) ? (A) : (B))
#define _MIN(A, B) ((A) < (B) ? (A) : (B))
#define _COL(C) ((C) >> 16 & 0xFF) / 255.,\
((C) >> 8 & 0xFF) / 255., \
((C) & 0xFF) / 255.
#define _DBGSTRLEN 1024
int time_alive_min = _TIME_ALIVE_MIN;
int time_evolve_step = _MAX(1, _TIME_ALIVE_MIN / (.5 * _NUNITS));
int oclock = 0, tickspersecs = 0, ticks = 0;
int clear = 0;
int spawno = 0, spawnr = 0, pause = 0, wolf = 1;
int plot_champ = 1, plot_traces = 1, plot_radar = 0, plot_dbg = 1;
struct avg *avgcrash;
int mapx, mapy, mapu, mapv, tx, ty, move;
double width, height, mapz = 1;
double lightx = _MAPW / 2;
double lighty = _MAPH / 2;
double langle = 0;
int lightmove = 0, lightauto = 0;
char dbgstr[_DBGSTRLEN];
int nests[] = { 24, 24, _MAPW - 24, 24,
_MAPW - 24, _MAPH - 24, 24, _MAPH - 24 };
int cols[] = { 0x00FFFF, 0xFF00FF, 0x00FF00, 0xFFFF00 };
struct unit *units[_NPOPS][_NUNITS];
struct map *map;
//+------------------------------------------------------------------+
//|
//+------------------------------------------------------------------+
void _free_world(void);
//+------------------------------------------------------------------+
//|
//+------------------------------------------------------------------+
void _signal(int sig)
{
if (sig == SIGINT) {
_free_world();
exit(0);
}
}
//+------------------------------------------------------------------+
//|
//+------------------------------------------------------------------+
void _screen_to_map(int x, int y, double *mx, double *my)
{
double min, dx, dy;
min = _MIN(width / map->width, height / map->height);
dx = x - width / 2 - mapx;
dy = y - height / 2 - mapy;
dx = dx / (mapz * width);
dy = dy / (mapz * height);
dx = (dx * (width / map->width) / min + .5) * map->width;
dy = (dy * (height / map->height) / min + .5) * map->height;
*mx = dx;
*my = dy;
}
//+------------------------------------------------------------------+
//|
//+------------------------------------------------------------------+
void _cartesian_to_polar(double x, double y, double *angle, double *radius)
{
*angle = atan2(y, x);
*radius = sqrt(x * x + y * y);
}
//+------------------------------------------------------------------+
//|
//+------------------------------------------------------------------+
int _get_champ(int pop)
{
int u, champ = -1;
double fit, maxfit = -1;
for (u = 0; u < _NUNITS; u++) {
fit = neat_fitness_get(pop, u);
if (fit >= maxfit) {
maxfit = fit;
champ = u;
}
}
return champ;
}
//+------------------------------------------------------------------+
//|
//+------------------------------------------------------------------+
void _eval_light(void)
{
int i, n;
double dx, dy;
if (lightauto && (ticks % 100 == 0)) {
for (n = 0; n < 100; n++) {
langle += ((rand() % 8) - 4) * _PI / 180;
if (langle < 0)
langle += _2PI;
if (langle > _2PI)
langle -= _2PI;
dx = lightx + .2 * cos(langle);
dy = lighty + .2 * sin(langle);
for (i = 0; i < _LIGHT_MOVERAYS; i++)
if (map_collisioni(map, NULL, 3, dx, dy,
i * _2PI / _LIGHT_MOVERAYS))
break;
if (i == _LIGHT_MOVERAYS) {
lightx = dx;
lighty = dy;
break;
}
}
}
}
//+------------------------------------------------------------------+
//|
//+------------------------------------------------------------------+
void _eval_unit(int pop, int org, int *ncrashes)
{
int i, nturns, nest;
double dist, fit, inputs[_NINPUTS], fx, fy, ox, oy, movedist;
double lightdist, lightangle, dx, dy;
const double *outputs;
struct unit *unit;
unit = units[pop][org];
fit = neat_fitness_get(pop, org);
if (unit->age++ == _TIME_ALIVE_MAX)
goto _spawn;
_cartesian_to_polar(unit->xpos - lightx, unit->ypos - lighty,
&lightangle, &lightdist);
inputs[0] = 1;
inputs[1] = unit->tangle;
inputs[2] = unit->tangle - unit->mangle;
inputs[3] = unit->tangle - lightangle;
inputs[4] = unit->mspeed;
for (i = 0; i < _NDIST; i++)
map_collisioni(map, &inputs[5 + i], _MDIST,
unit->xpos, unit->ypos,
unit->tangle + _2PI * i / _NDIST);
outputs = neat_activate(pop, org, inputs);
unit->thrust = outputs[0];
unit->tangle += (outputs[1] - .5) * _UNIT_ANGLE;
if (unit->tangle >= _PI)
unit->tangle -= _2PI;
if (unit->tangle < - _PI)
unit->tangle += _2PI;
fx = unit->mspeed * cos(unit->mangle) +
.03 * unit->thrust * cos(unit->tangle);
fy = unit->mspeed * sin(unit->mangle) +
.03 * unit->thrust * sin(unit->tangle) + .01;
_cartesian_to_polar(fx, fy, &unit->mangle, &unit->mspeed);
/*unit->mspeed -= unit->mspeed * unit->mspeed / 100;
unit->mspeed = _MAX(0, unit->mspeed);*/
if (map_collisioni(map, &dist, unit->mspeed,
unit->xpos, unit->ypos, unit->mangle)) {
(*ncrashes)++;
fit *= .1;
_spawn:
nest = (pop + spawno + spawnr * rand()) % _NPOPS;
unit_spawn(unit, nests[2 * nest], nests[2 * nest + 1]);
neat_flush(pop, org);
goto _done;
}
ox = unit->xpos;
oy = unit->ypos;
unit_move(unit);
movedist = sqrt((unit->xpos - ox) * (unit->xpos - ox) +
(unit->ypos - oy) * (unit->ypos - oy));
fit *= .999;
fit += 1 + movedist + 100 * exp(-lightdist / 5);
_done:
neat_fitness_set(pop, org, fit);
}
//+------------------------------------------------------------------+
//|
//+------------------------------------------------------------------+
void _eval(void)
{
static int oticks = 0;
int ncrashes, clock;
struct unit *unit;
int w, t, u, i, n, worstid, nest, champ, dbgoff;
double fit, dx, dy, dist;
for (w = 0; w < wolf; w++) {
ticks++;
ncrashes = 0;
for (t = 0; t < _NPOPS; t++) {
for (u = 0; u < _NUNITS; u++)
_eval_unit(t, u, &ncrashes);
if ((ticks % time_evolve_step) == 0) {
worstid = neat_evolve(t);
if (worstid >= 0) {
unit = units[t][worstid];
nest = (t + spawno + spawnr * rand()) %
_NPOPS;
unit_spawn(unit, nests[2 * nest],
nests[2 * nest + 1]);
}
}
}
clock = time(NULL);
if (clock - oclock >= 5) {
tickspersecs = (ticks - oticks) / (clock - oclock);
oclock = clock;
oticks = ticks;
}
/* debug info */
avg_add(avgcrash, ncrashes, ticks);
snprintf(dbgstr, _DBGSTRLEN, "%8d %4d %.2f : %.2f : %.2f\n",
ticks, tickspersecs,
avgcrash->savg0, avgcrash->savg1, avgcrash->savg2);
_eval_light();
}
}
//+------------------------------------------------------------------+
//|
//+------------------------------------------------------------------+
void _gl_eval(void)
{
_eval();
if (pause)
glutIdleFunc(NULL);
glutPostRedisplay();
}
//+------------------------------------------------------------------+
//|
//+------------------------------------------------------------------+
void _draw_light(void)
{
glPushMatrix();
glTranslatef(lightx, lighty, 0);
glColor3f(0, 0, 1);
glBegin(GL_QUADS);
glVertex2f(-1, -1);
glVertex2f(1, -1);
glVertex2f(1, 1);
glVertex2f(-1, 1);
glEnd();
glPopMatrix();
}
//+------------------------------------------------------------------+
//|
//+------------------------------------------------------------------+
void _draw_dbg(void)
{
int i = 0;
int slen = strlen(dbgstr);
glRasterPos2d(0, 0);
while (i++ < slen)
glutBitmapCharacter(GLUT_BITMAP_TIMES_ROMAN_24, dbgstr[i]);
}
//+------------------------------------------------------------------+
//|
//+------------------------------------------------------------------+
void _draw_champ(void)
{
int i, t, champ;
struct unit *unit;
double xpos, ypos, dist, angle;
for (t = 0; t < _NPOPS; t++) {
champ = _get_champ(t);
ASSERT(champ >= 0);
unit = units[t][champ];
ASSERT(unit);
xpos = unit->xpos;
ypos = unit->ypos;
/* circle */
glColor3f(_COL(cols[t]));
glBegin(GL_LINE_LOOP);
for (i = 0; i < 16; i++) {
angle = _2PI * i / 16;
glVertex2d(xpos + 6 * cos(angle),
ypos + 6 * sin(angle));
}
glEnd();
/* radar */
if (plot_radar) {
for (i = 0; i < _NDIST; i++) {
angle = unit->tangle + _2PI * i / _NDIST;
map_collisioni(map, &dist, _MDIST,
xpos, ypos, angle);
glBegin(GL_LINES);
glVertex2d(xpos + 6 * cos(angle),
ypos + 6 * sin(angle));
glVertex2d(xpos + dist * cos(angle),
ypos + dist * sin(angle));
glEnd();
}
}
}
}
//+------------------------------------------------------------------+
//|
//+------------------------------------------------------------------+
void _display(void)
{
int u, t;
double min;
if (clear) {
clear = 0;
glClearColor(0, 0, 0, 0);
glClear(GL_COLOR_BUFFER_BIT);
}
min = _MIN(width / map->width, height / map->height);
glMatrixMode(GL_TEXTURE);
glPushMatrix();
glScalef((double)map->width / map->texw,
(double)map->height / map->texh, 1);
glTranslatef(.5, .5, 0);
glScalef(width / (mapz * min * map->width),
height / (mapz * min * map->height), 1);
glTranslatef(-.5 - (double)mapx / width,
-.5 + (double)mapy / height, 0);
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
glTranslatef(-1, -1, 0);
glScalef(2, 2, 1);
map_draw(map);
if (plot_dbg)
_draw_dbg();
glPopMatrix();
glPushMatrix();
glTranslatef(2 * (double)mapx / width, -2 * (double)mapy / height, 0);
glScalef(2 * mapz * min / width, -2 * mapz * min / height, 1);
glTranslatef(-map->width / 2, -map->height / 2, 0);
_draw_light();
for (t = 0; t < _NPOPS; t++)
for (u = 0; u < _NUNITS; u++)
unit_draw(units[t][u], plot_traces, _COL(cols[t]));
if (plot_champ)
_draw_champ();
if (map->wall)
map_wall(map);
glPopMatrix();
glMatrixMode(GL_TEXTURE);
glPopMatrix();
glutSwapBuffers();
}
//+------------------------------------------------------------------+
//|
//+------------------------------------------------------------------+
void _reshape(int w, int h)
{
width = w;
height = h;
glViewport(0, 0, width, height);
}
//+------------------------------------------------------------------+
//|
//+------------------------------------------------------------------+
void _motion(int x, int y)
{
double mx, my;
if (map->wall) {
_screen_to_map(x, y, &mx, &my);
_cartesian_to_polar(mx - map->wx, my - map->wy,
&map->wa, &map->wr);
} else if (move) {
mapx = mapu + x - tx;
mapy = mapv + y - ty;
} else if (lightmove)
_screen_to_map(x, y, &lightx, &lighty);
glutPostRedisplay();
}
//+------------------------------------------------------------------+
//|
//+------------------------------------------------------------------+
void _mouse(int button, int state, int x, int y)
{
int mod;
mod = glutGetModifiers();
if (state == GLUT_UP) {
switch (button) {
case GLUT_MIDDLE_BUTTON:
break;
case GLUT_RIGHT_BUTTON:
if (!(mod & GLUT_ACTIVE_SHIFT)) {
lightmove = 0;
break;
}
/* FALL */
case GLUT_LEFT_BUTTON:
if (mod & GLUT_ACTIVE_SHIFT) {
map->wall = 2;
glutPostRedisplay();
} else {
mapu = mapx;
mapv = mapy;
move = 0;
}
break;
}
}
if (state == GLUT_DOWN) {
switch (button) {
case GLUT_MIDDLE_BUTTON:
break;
case GLUT_RIGHT_BUTTON:
if (mod & GLUT_ACTIVE_SHIFT)
map->erase = 1;
else {
_screen_to_map(x, y, &lightx, &lighty);
lightmove = 1;
glutPostRedisplay();
break;
}
/* FALL */
case GLUT_LEFT_BUTTON:
if (mod & GLUT_ACTIVE_SHIFT) {
map->wall = 1;
_screen_to_map(x, y, &map->wx, &map->wy);
map->wa = 0;
map->wr = 0;
glutPostRedisplay();
} else {
tx = x;
ty = y;
move = 1;
}
break;
case 3:
/* WHEEL_UP */
mapz = _MIN(5, mapz + .1);
glutPostRedisplay();
break;
case 4:
/* WHEEL_DOWN */
clear = 1;
mapz = _MAX(.3, mapz - .1);
glutPostRedisplay();
break;
default:
TRACE("%x", button);
break;
}
}
}
//+------------------------------------------------------------------+
//|
//+------------------------------------------------------------------+
void _keyboard(unsigned char key, int x, int y)
{
FILE *file;
int t, u;
switch (key) {
case ' ':
if (pause)
glutIdleFunc(_gl_eval);
break;
case '+':
mapz = _MIN(5, mapz + .1);
glutPostRedisplay();
break;
case '-':
clear = 1;
mapz = _MAX(.3, mapz - .1);
glutPostRedisplay();
break;
case 'c':
plot_champ = !plot_champ;
glutPostRedisplay();
break;
case 'd':
plot_dbg = !plot_dbg;
glutPostRedisplay();
break;
case 'f':
mapz = 1;
mapx = 0;
mapy = 0;
mapu = 0;
mapv = 0;
glutPostRedisplay();
break;
case 'm':
lightauto = !lightauto;
break;
case 'p':
pause = !pause;
if (pause)
glutIdleFunc(NULL);
else
glutIdleFunc(_gl_eval);
break;
case 'q':
_free_world();
exit(0);
break;
case 'r':
plot_radar = !plot_radar;
glutPostRedisplay();
break;
case 's':
INFO("Saving %s", _FILENAME_MAP);
file = fopen(_FILENAME_MAP, "w");
ASSERT(file);
map_write(map, file);
fclose(file);
INFO("Saving %s", _FILENAME_POPS);
file = fopen(_FILENAME_POPS, "w");
ASSERT(file);
for (t = 0; t < _NPOPS; t++) {
for (u = 0; u < _NUNITS; u++)
neat_print(t, u, file);
}
fclose(file);
break;
case 't':
plot_traces = !plot_traces;
glutPostRedisplay();
break;
case 'u':
map->erase = 1;
map->wall = 2;
map->dirty = 1;
glutPostRedisplay();
break;
case 'v':
spawnr = !spawnr;
break;
case 'w':
switch (wolf) {
case 100:
wolf = 10;
break;
case 10:
wolf = 1;
break;
case 1:
wolf = 100;
break;
}
break;
case 'x':
spawno = (spawno + 1) % _NPOPS;
break;
case 'z':
spawno = (spawno - 1 + _NPOPS) % _NPOPS;
break;
}
}
//+------------------------------------------------------------------+
//|
//+------------------------------------------------------------------+
void _free_world(void)
{
int t, u;
for (t = 0; t < _NPOPS; t++) {
for (u = 0; u < _NUNITS; u++)
unit_destroy(units[t][u]);
neat_destroy(t);
}
avg_destroy(avgcrash);
map_destroy(map);
}
//+------------------------------------------------------------------+
//|
//+------------------------------------------------------------------+
void _init_world(void)
{
int i, x, y, u, t;
FILE *file;
map = map_create(_MAPW, _MAPH);
if ((file = fopen(_FILENAME_MAP, "r"))) {
INFO("Loading %s", _FILENAME_MAP);
map_read(map, file);
fclose(file);
}
if ((file = fopen(_FILENAME_POPS, "r"))) {
INFO("Loading %s", _FILENAME_POPS);
for (t = 0; t < _NPOPS; t++)
neat_create_from_file(_NUNITS, _NINPUTS, _NOUTPUTS,
file);
fclose(file);
} else {
for (t = 0; t < _NPOPS; t++)
neat_create(_NUNITS, _NINPUTS, _NOUTPUTS, _GENEPROB);
}
for (t = 0; t < _NPOPS; t++) {
for (u = 0; u < _NUNITS; u++) {
units[t][u] = unit_create(_NTRACES);
unit_spawn(units[t][u], nests[2 * t], nests[2 * t + 1]);
}
}
avgcrash = avg_create(1000, 5000, 15000);
}
//+------------------------------------------------------------------+
//|
//+------------------------------------------------------------------+
int main(int argc, char **argv)
{
signal(SIGINT, _signal);
if (argc == 2) {
wolf = atoi(argv[1]);
_init_world();
_eval();
_free_world();
exit(0);
}
srand(time(NULL));
_init_world();
glutInit(&argc, argv);
glutInitWindowSize(800, 600);
glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGB);
glutCreateWindow("eco");
glutDisplayFunc(_display);
glutReshapeFunc(_reshape);
glutKeyboardFunc(_keyboard);
glutMouseFunc(_mouse);
glutMotionFunc(_motion);
glutIdleFunc(_gl_eval);
glutMainLoop();
return 0;
}