#ifndef WOLF_H
#define WOLF_H
#ifndef FWORLD_H
# error Include fworld.h before wolf.h so the world can be referenced.
#endif
#ifndef NE_H
# error Include ne.h before wolf.h so the wolves can operate.
#endif
#include <cmath>
#include <vector>
namespace wolf {
struct Wolf {
size_t entity_index;
int member;
ne_Population population;
};
NE_REAL DoWolfStuff(
Wolf* w,
fworld::World* world,
double d ){
auto &ent = world->entities[w->entity_index];
auto &net = w->population[w->member];
long long
x = ent.x + 0.5,
y = ent.y + 0.5;
// For type reasons.
const NE_REAL NH = -0.5, Z = 0.0, H = 0.5, P = 1.0, F = 4.0;
// Proximity values are inverted because larger values stimulate.
NE_REAL inputs[6] = {
static_cast<NE_REAL>( ent.direction ) / F,
-( world->tileBlocking( x, y - 1, true ) ? H : Z ) + P
-( world->tileBlocking( x, y - 2, true ) ? H : Z ), // North
-( world->tileBlocking( x, y + 1, true ) ? H : Z ) + P
-( world->tileBlocking( x, y + 2, true ) ? H : Z ), // South
-( world->tileBlocking( x - 1, y, true ) ? H : Z ) + P
-( world->tileBlocking( x - 2, y, true ) ? H : Z ), // West
-( world->tileBlocking( x + 1, y, true ) ? H : Z ) + P
-( world->tileBlocking( x + 2, y, true ) ? H : Z ), // East
NH // Extra stimulation to stabilize the network.
};
int old_direction = 0;
double speed = 0.0;
auto MoveEnt = [&](){
NE_REAL outputs[4];
ne_GetOutputs( &net, inputs, outputs );
double
moveX = std::max( Z, outputs[3] ) - std::max( Z, outputs[2] ),
moveY = std::max( Z, outputs[1] ) - std::max( Z, outputs[0] );
if( moveX > 1.2 || moveX < -1.2 ){
moveX = 0.0;
}
if( moveY > 1.2 || moveY < -1.2 ){
moveY = 0.0;
}
double
oldX = ent.x,
oldY = ent.y;
old_direction = ent.direction;
world->moveEntity( ent, moveX * 1.0, moveY * -1.0, d );
double
diffX = ent.x - oldX,
diffY = ent.y - oldY;
speed = std::sqrt( diffX * diffX + diffY * diffY ) / d;
};
MoveEnt();
NE_REAL bonus = ( (long long)( ent.x + 0.5 ) != x || (long long)( ent.y + 0.5 ) != y ) ? 0.75 : 0.0;
return net.fitness * 0.5 + speed * ( ent.direction == old_direction ? 1.0 : 0.1 ) + bonus;
}
void AI(
std::vector<Wolf>* wolves,
fworld::World* world,
unsigned int rand_seed,
double d ){
for( int i = 0; i < 2; i++ ){
for( auto &w : *wolves ){
if( w.member >= stb_sb_count( w.population ) ){
ne_Iterate( &w.population, rand_seed, 0.7, 0.003, 0 );
w.member = 0;
}
w.population[w.member].fitness = DoWolfStuff( &w, world, d * 0.5 );
w.member++;
}
}
}
std::vector<Wolf> GetWolves(
fworld::World* world,
unsigned int rand_seed ){
int topovalues[9] = { 6, 50, 25, 25, 25, 25, 25, 16, 4 };
int* topology = NULL;
topology = stb_sb_add( topology, 9 );
for( int i = 0; i < 9; i++ ){
topology[i] = topovalues[i];
};
std::vector<Wolf> wolves;
for( size_t i = 0; i < world->entities.size(); i++ ){
auto &ent = world->entities[i];
if( ent.type == "wolf" ){
Wolf w = {};
w.entity_index = i;
w.member = 0;
w.population = ne_RandomNetworks( topology, 20, rand_seed );
wolves.push_back( w );
}
}
stb_sb_free( topology );
return wolves;
}
} // namespace wolf
#endif // WOLF_H