// Genesaver: copyright 2003 Sam Stafford.
#include <math.h>
#include <GL/glut.h>
#include "globals.h"
#include "Brain.h"
#include "DNA.h"
float light;
Brain::Brain( DNA* dna, float* o )
{
int i, j, k;
char c;
//Initialize input neurons to zero.
for ( i = 0 ; i < 14 ; i++ )
{
input[i] = Neuron();
input[i].axon = 0.0;
input[i].x = ( i+1 ) * 2.0 / 15.0 - 1.0;
input[i].active = true;
}
//Initialize lobes.
for ( i = 0 ; i < 6 ; i++ )
{
lobe[i] = Lobe();
//Remember, top level neurons have ONE dendrite each.
for ( j = 0 ; j < 4 ; j++ )
for ( k = 1 ; k < 4 ; k++ )
{
lobe[i].neur[0][j].dendrite[k] = NULL;
lobe[i].neur[0][j].weight[k] = 0;
}
//Establish input pointers.
//Left half of each lobe first, for historical reasons.
c = dna->hchr.iden[i];
lobe[i].neur[0][0].dendrite[0] = &( input[ abs( (c%7)*2 ) ] );
lobe[i].neur[0][1].dendrite[0] = &( input[ abs( (c%7)*2 )+1 ] );
c = dna->hchr.iwgt[i];
lobe[i].neur[0][0].weight[0] = float( c / 32.0 );
lobe[i].neur[0][1].weight[0] = float( c / 32.0 );
CheckActive( & lobe[i].neur[0][0] );
CheckActive( & lobe[i].neur[0][1] );
//Now the right half.
c = dna->hchr.iden[i+6];
lobe[i].neur[0][2].dendrite[0] = &( input[ abs( (c%7)*2 ) ] );
lobe[i].neur[0][3].dendrite[0] = &( input[ abs( (c%7)*2 )+1 ] );
c = dna->hchr.iwgt[i+6];
lobe[i].neur[0][2].weight[0] = float( c / 32.0 );
lobe[i].neur[0][3].weight[0] = float( c / 32.0 );
CheckActive( & lobe[i].neur[0][2] );
CheckActive( & lobe[i].neur[0][3] );
//Top level axons.
for ( j = 0 ; j < 4 ; j++ )
{
lobe[i].neur[0][j].thres = float( dna->ichr[i].ax0[j] / 255.0 );
}
//Connect layer 1 to layer 0.
for ( j = 0 ; j < 4 ; j++ )
{
for ( k = 0 ; k < 4 ; k++ )
{
lobe[i].neur[1][j].dendrite[k] = &( lobe[i].neur[0][k] );
if ( !lobe[i].neur[0][k].active && RandFloat() < D_M / 255.0 )
dna->ichr[i].wgt[j][k] = 0;
lobe[i].neur[1][j].weight[k] = dna->ichr[i].wgt[j][k] / 32.0;
}
CheckActive( & lobe[i].neur[1][j] );
}
//Bottom level axons.
for ( j = 0 ; j < 4 ; j++ )
{
lobe[i].neur[1][j].thres = float( dna->ichr[i].ax1[j] / 255.0 );
}
lobe[i].neur[0][0].x = ( i * 4 + 1 ) * 2.0 / 25.0 - 1.0;
lobe[i].neur[0][1].x = ( i * 4 + 2 ) * 2.0 / 25.0 - 1.0;
lobe[i].neur[0][2].x = ( i * 4 + 3 ) * 2.0 / 25.0 - 1.0;
lobe[i].neur[0][3].x = ( i * 4 + 4 ) * 2.0 / 25.0 - 1.0;
lobe[i].neur[1][0].x = ( i * 4 + 1 ) * 2.0 / 25.0 - 1.0;
lobe[i].neur[1][1].x = ( i * 4 + 2 ) * 2.0 / 25.0 - 1.0;
lobe[i].neur[1][2].x = ( i * 4 + 3 ) * 2.0 / 25.0 - 1.0;
lobe[i].neur[1][3].x = ( i * 4 + 4 ) * 2.0 / 25.0 - 1.0;
}
//Initialize outputs.
for ( i = 0 ; i < 4 ; i++ ) //output neurons
{
for ( j = 0 ; j < 6 ; j++ ) //lobes
{
for ( k = 0 ; k < 4 ; k++ ) //bottom lobe neurons
{
output[i].dendrite[j*4+k] = &lobe[j].neur[1][k];
if ( !lobe[j].neur[1][k].active && RandFloat() < D_M / 255.0 )
dna->hchr.owgt[i][j*4+k] = 0;
output[i].weight[j*4+k] = float( dna->hchr.owgt[i][j*4+k] / 32.0 );
}
}
}
//Set pointer to "muscles" of animal.
muscles = o;
}
Brain::~Brain(void)
{
}
void Brain::CheckActive( Neuron* n ) //check if a neuron is in use
{
n->active = false;
for ( int i = 0 ; i < 4 ; i++ )
{
if ( n->weight[i] && n->dendrite[i] && n->dendrite[i]->active )
n->active = true;
}
}
void Brain::Clear() //reset inputs
{
for ( int i = 0 ; i < 14 ; i++ ) input[i].axon = 0.0;
}
void Brain::Render()
{
int i, j, k;
//Lobe synapses
for ( i = 0 ; i < 6 ; i++ )
for ( j = 0 ; j < 4 ; j++ )
{
for ( k = 0 ; k < 4 ; k++ )
{
RenderSyn
( lobe[i].neur[1][j].weight[k],
lobe[i].neur[1][j].dendrite[k], -0.4,
lobe[i].neur[1][j].x, -0.6
);
}
RenderSyn
( lobe[i].neur[0][j].weight[0],
lobe[i].neur[0][j].dendrite[0], -0.2,
lobe[i].neur[0][j].x, -0.4
);
}
//Input neurons
for ( i = 0 ; i < 14 ; i++ )
{
RenderNeuron( input[i].axon, input[i].x, -0.2 );
}
//Output synapses and neurons
for ( i = 0 ; i < 4 ; i++ )
{
for ( j = 0 ; j < 24 ; j++ )
{
RenderSyn
( output[i].weight[j],
output[i].dendrite[j], -0.6,
( i + 1 ) * 2.0 / 5.0 - 1.0, -0.8
);
}
RenderNeuron( output[i].axon, ( i + 1 ) * 2.0 / 5.0 - 1.0, -0.8 );
}
//Lobe neurons
for ( i = 0 ; i < 6 ; i++ )
for ( j = 0 ; j < 4 ; j++ )
{
RenderNeuron( lobe[i].neur[0][j].axon, lobe[i].neur[0][j].x, -0.4 );
RenderNeuron( lobe[i].neur[1][j].axon, lobe[i].neur[1][j].x, -0.6 );
}
//Label over visual inputs.
RenderLights();
}
void Brain::RenderLights()
{
int i;
if ( light > 0.6 ) light = -1.0;
else if ( light < -1.0 ) light = 0.6;
else light += 0.03 * /**step*/ 1; //replace with velocity magnitude
float r, g, b, lp, rp;
Color draw;
for ( i = 0 ; i < 10 ; i++ )
{
r = g = b = 0.0;
switch( i )
{
case 0:
case 1:
draw = Grey; break;
case 2:
case 3:
draw = Red; break;
case 4:
case 5:
draw = Green; break;
case 6:
case 7:
draw = Blue; break;
default:
draw = Grey; break;
}
switch( draw )
{
case Grey: r = g = b = 0.5; break;
case Red: r = 1; break;
case Green: g = 1 ; break;
case Blue: b = 1 ; break;
case Magenta: r = b = 1; break;
case Yellow: r = g = 1; break;
}
if ( ( light > input[i].x - N_L ) && ( light < input[i].x + N_L ) )
{
lp = 1.0 - ( light - ( input[i].x - N_L ) ) / N_L;
rp = 1.0 - ( ( input[i].x + N_L ) - light ) / N_L;
glBegin( GL_POLYGON );
SetColor( draw );
glVertex2f( light, -0.14 );
glVertex2f( light, -0.15 );
glColor3f( r * lp, g * lp, b * lp );
glVertex2f( input[i].x - N_L, -0.15 );
glVertex2f( input[i].x - N_L, -0.14 );
glEnd();
glBegin( GL_POLYGON );
SetColor( draw );
glVertex2f( light, -0.14 );
glVertex2f( light, -0.15 );
glColor3f( r * rp, g * rp, b * rp );
glVertex2f( input[i].x + N_L, -0.15 );
glVertex2f( input[i].x + N_L, -0.14 );
glEnd();
}
else
{
glColor3f( r * 0.1, g * 0.1, b * 0.1 );
glBegin( GL_POLYGON );
glVertex2f( input[i].x - N_L, -0.15 );
glVertex2f( input[i].x - N_L, -0.14 );
glVertex2f( input[i].x + N_L, -0.14 );
glVertex2f( input[i].x + N_L, -0.15 );
glEnd();
}
}
}
void Brain::RenderNeuron( float a, float x, float y )
{
float r, g, b;
if ( a >= 0.0 )
{
r = 0.0;
g = 0.0;
b = a;
}
else
{
r = -a;
g = 0.0;
b = 0.0;
}
glColor3f( r, g, b );
glBegin( GL_POLYGON );
glVertex2f( x + N_R, y + N_R * 0.5 );
glVertex2f( x + N_R, y - N_R * 0.5 );
glVertex2f( x + N_R * 0.5, y - N_R );
glVertex2f( x - N_R * 0.5, y - N_R );
glVertex2f( x - N_R, y - N_R * 0.5 );
glVertex2f( x - N_R, y + N_R * 0.5 );
glVertex2f( x - N_R * 0.5, y + N_R );
glVertex2f( x + N_R * 0.5, y + N_R );
glEnd();
}
void Brain::RenderSyn( float w, Neuron* n, float y1, float x2, float y2 )
{
if ( !w || !n->active ) return;
float r, g, b;
float x1 = n->x;
if ( w >= 0.0 )
{
r = 0.0;
g = w / 4.0;
b = w / 4.0;
}
else
{
r = -w / 4.0;
g = -w / 4.0;
b = 0.0;
}
float d = 2.0 - n->axon ;
r /= d;
g /= d;
b /= d;
glBegin( GL_LINE_STRIP );
glColor3f( 0, 0, 0 );
glVertex2f( x1, y1 );
glColor3f( r, g, b );
glVertex2f( ( x1 + x2 ) / 2.0, ( y1 + y2 ) / 2.0 );
glColor3f( 0, 0, 0 );
glVertex2f( x2, y2 );
glEnd();
}
void Brain::Think() //act on inputs previously cleared by Clear() and accumulated by See().
{
int i, j, k;
for ( i = 0; i < 2 ; i++ )
for ( j = 0; j < 6 ; j++ )
for( k = 0; k < 4 ; k++ )
{
Fire( &( lobe[j].neur[i][k] ) );
}
for ( i = 0; i < 4 ; i++ )
Fire( &output[i] );
for ( i = 0 ; i < 4 ; i++ )
{
muscles[i] = output[i].axon;
}
}
void Brain::Fire( Neuron* n )
{
n->axon = 0.0;
float d = 0.0;
int e = n->dendrite[1] ? 4 : 1;
for( int i = 0 ; i < e ; i++ )
{
if ( n->weight[i] )
{
n->axon += n->dendrite[i]->axon * n->weight[i];
d++;
}
}
if ( d ) n->axon /= d;
if ( n->thres > 0 ) n->axon = float( n->axon >= n->thres ? 1.0 : 0.0 );
if ( n->axon > 1.0 ) n->axon = 1.0;
if ( n->axon < -1.0 ) n->axon = -1.0;
}
void Brain::Fire( BigNeuron* n )
{
n->axon = 0.0;
float d = 0.0;
for ( int i = 0 ; i < 24 ; i++ )
{
if ( n->weight[i] )
{
n->axon += float( n->dendrite[i]->axon * n->weight[i] );
d++;
}
}
if ( d ) n->axon /= d;
if ( n->axon > 1.0 ) n->axon = 1.0;
if ( n->axon < -1.0 ) n->axon = -1.0;
}
| # | Change | User | Description | Committed | |
|---|---|---|---|---|---|
| #6 | 4463 | Sam Stafford |
Notes on the genome. In the process of reviewing the neural wiring code I fixed an ancient bug with "boolean" neurons. Oops. |
||
| #5 | 4446 | Sam Stafford |
Finished neural inputs, made size hereditary, auto-rotation of images once a certain amount of diffs have been consumed, saving genomes at finish. |
||
| #4 | 4441 | Sam Stafford | Ported chase-cam view. | ||
| #3 | 4440 | Sam Stafford | Bug fixes, new features, the usual. | ||
| #2 | 4433 | Sam Stafford |
More work on this little project. The AI is still nonexistent. |
||
| #1 | 4430 | Sam Stafford |
Start importing alife/AI code from Genesaver. Much tweaking will need to be done. |
||
| //guest/sam_stafford/genesaver/src/Brain.cpp | |||||
| #1 | 3052 | Sam Stafford |
Add Genesaver to the Public Depot. It's not in any way Perforce-related, but it does share a bit of code with Jamgraph, and it feels strange to have an open-source project that's not in the PD. |
||