394 lines
13 KiB
C++
394 lines
13 KiB
C++
#include <cstdlib>
|
|
#include "behavior.h"
|
|
|
|
// g++ -shared war_charge_west.cpp -o war_charge_west.dll -std=c++11 -I../src
|
|
|
|
#define KING_POS Coord(2, 2)
|
|
#define ARMY_POS Coord(-12, 4)
|
|
#define CHARGE_DIR WEST
|
|
|
|
enum Purpose { NEWBORN, KING, PEASANT, SOLDIER };
|
|
|
|
struct KingMemory{ // 16 bytes
|
|
int nbPeasants;
|
|
int nbSoldiers;
|
|
int date;
|
|
Dir last_audience_dir;
|
|
};
|
|
|
|
struct PeasantMemory{ // 8 bytes
|
|
bool tried_picking_up_food;
|
|
bool brings_food;
|
|
};
|
|
|
|
struct SoldierMemory{ // 16 bytes
|
|
Coord target;
|
|
int charge_countdown;
|
|
bool commander;
|
|
};
|
|
|
|
union JobMemory { // 16 bytes
|
|
KingMemory king;
|
|
PeasantMemory peasant;
|
|
SoldierMemory soldier;
|
|
};
|
|
|
|
struct memory_data{ // 104 bytes
|
|
// 40 bytes
|
|
JobMemory job;
|
|
Coord pos;
|
|
bool new_born;
|
|
int purpose;
|
|
Dir last_dir;
|
|
Action::Type last_action;
|
|
// 64 bytes
|
|
char debugStr[64];
|
|
};
|
|
|
|
const char* PURPOSES_STRINGS[] {
|
|
"Newborn",
|
|
"King",
|
|
"Peasant",
|
|
"Soldier"
|
|
};
|
|
|
|
extern "C" void debugOutput(char *outputString, const char *memory)
|
|
{
|
|
memory_data* data = (memory_data*)memory;
|
|
sprintf(outputString, "%s\n%s", PURPOSES_STRINGS[data->purpose], data->debugStr);
|
|
if(data->purpose == KING)
|
|
{
|
|
sprintf(outputString, "%s\nRuled the kingdom for %d ticks\npopulation :\n%d peasants\n%d soldiers",
|
|
outputString, data->job.king.date, data->job.king.nbPeasants, data->job.king.nbSoldiers);
|
|
}
|
|
}
|
|
|
|
extern "C" void think(Action *action, char *memory, const Info *info)
|
|
{
|
|
int i;
|
|
PixelType type;
|
|
|
|
bool success = info->getSuccess();
|
|
memory_data* data = (memory_data*)memory;
|
|
data->debugStr[0] = '\0';
|
|
|
|
// if freshly spawned, init the position variable by looking in which direction is the spawn
|
|
if(!data->new_born){
|
|
success = false;
|
|
data->new_born = true;
|
|
for(i=0; i<4; i++)
|
|
{
|
|
type = info->getNear(Dir(i));
|
|
if(type == SPAWN)
|
|
{
|
|
data->pos = Coord(0) - Dir(i);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
// update position if last successful action was moving
|
|
if(data->last_action == Action::MOVE){
|
|
if(success)
|
|
data->pos += data->last_dir;
|
|
else
|
|
{
|
|
int blockedCount = 0;
|
|
action->type = Action::MOVE;
|
|
do{
|
|
action->dir = Dir((data->last_dir + rand()%3)%4);
|
|
type = info->getNear(action->dir);
|
|
++blockedCount;
|
|
if(blockedCount > 3)
|
|
{
|
|
action->type = Action::WAIT;
|
|
break;
|
|
}
|
|
}while(!PixelProperty::isWalkable(type));
|
|
data->last_dir = action->dir;
|
|
data->last_action = action->type;
|
|
sprintf(data->debugStr, "moving around obstacle");
|
|
return;
|
|
}
|
|
}
|
|
|
|
// work any workable resource when encountered
|
|
if(data->purpose == SOLDIER && data->job.soldier.charge_countdown == 0)
|
|
{
|
|
action->dir = CHARGE_DIR;
|
|
PixelType front = info->getNear(CHARGE_DIR);
|
|
if(!PixelProperty::isWalkable(front))
|
|
{
|
|
if(PixelProperty::isDestructible(front))
|
|
action->type = Action::ATTACK;
|
|
else if(PixelProperty::isResource(front))
|
|
{
|
|
if(info->getInventory() != EMPTY)
|
|
{
|
|
action->type = Action::PUT;
|
|
action->dir = Dir((CHARGE_DIR+2)%4);
|
|
}
|
|
else
|
|
action->type = Action::PICK;
|
|
}
|
|
else
|
|
action->type = Action::WORK;
|
|
}
|
|
else
|
|
action->type = Action::MOVE;
|
|
data->last_dir = action->dir;
|
|
data->last_action = action->type;
|
|
sprintf(data->debugStr, "CHAAAAAAAAAARGE !");
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
for(i=0; i<4; i++){
|
|
type = info->getNear(Dir(i));
|
|
if(type == BERRIES || type == TREE || type == IRON_ORE || type == ROCK){
|
|
action->type = Action::WORK;
|
|
action->dir = Dir(i);
|
|
data->last_dir = action->dir;
|
|
data->last_action = action->type;
|
|
sprintf(data->debugStr, "working on resource");
|
|
return;
|
|
}else if(type == FOOD && data->purpose == PEASANT
|
|
&& !(data->job.peasant.brings_food) && !(data->job.peasant.tried_picking_up_food)){
|
|
action->type = Action::PICK;
|
|
action->dir = Dir(i);
|
|
data->job.peasant.tried_picking_up_food = true;
|
|
data->last_dir = action->dir;
|
|
data->last_action = action->type;
|
|
sprintf(data->debugStr, "picking up food");
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
// do your job
|
|
switch(data->purpose)
|
|
{
|
|
case NEWBORN :
|
|
{
|
|
const Com* com = info->getCom();
|
|
if(com != nullptr) // no activity
|
|
{
|
|
switch(com->data[0])
|
|
{
|
|
case 'p': data->purpose = PEASANT; break;
|
|
case 's':
|
|
data->purpose = SOLDIER;
|
|
data->job.soldier.target.x = *((int*)(com->data+2));
|
|
data->job.soldier.target.y = *((int*)(com->data+6));
|
|
data->job.soldier.commander = bool(com->data[10]);
|
|
data->job.soldier.charge_countdown = *((int*)(com->data+11));
|
|
break;
|
|
default: break;
|
|
}
|
|
action->type = Action::WAIT;
|
|
data->last_action = action->type;
|
|
sprintf(data->debugStr, "receiving job : %s", com->data);
|
|
return;
|
|
}
|
|
Coord target = data->pos - KING_POS;
|
|
int distance = target.dist();
|
|
if(distance == 0) // i am the new king
|
|
{
|
|
data->job.king.last_audience_dir = NO_DIR;
|
|
data->purpose = KING;
|
|
action->type = Action::WAIT;
|
|
data->last_action = action->type;
|
|
sprintf(data->debugStr, "I AM THE KING, LONG LIVE THE KING");
|
|
return;
|
|
}
|
|
// if adjacent to the king's position, speak to the king if there is one
|
|
if(distance == 1){
|
|
for(i=0; i<4; i++){
|
|
type = info->getNear(Dir(i));
|
|
if(Coord(Dir((i+2)%4)) == target && type == DUDE){ // it is the king
|
|
action->dir = Dir(i);
|
|
action->type = Action::COMMUNICATE;
|
|
action->com_data.data[0] = 'n'; // "give me a purpose"
|
|
data->last_dir = action->dir;
|
|
data->last_action = action->type;
|
|
sprintf(data->debugStr, "asking the king to give me a task");
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
// move to the king's position
|
|
action->type = Action::MOVE;
|
|
do{
|
|
action->dir = Dir( rand() % 4 );
|
|
}while(~(target + action->dir) > distance && distance != 0);
|
|
data->last_dir = action->dir;
|
|
data->last_action = action->type;
|
|
sprintf(data->debugStr, "walking to the king's throne");
|
|
return;
|
|
}
|
|
break;
|
|
case KING :
|
|
{
|
|
data->job.king.date += 1;
|
|
const Com* com = info->getCom();
|
|
if(com == nullptr || data->job.king.last_audience_dir == Dir(com->flag)) // no activity
|
|
{
|
|
data->job.king.last_audience_dir = NO_DIR;
|
|
action->type = Action::WAIT;
|
|
data->last_action = action->type;
|
|
sprintf(data->debugStr, "waiting for a subject to ask for an audience");
|
|
return;
|
|
}
|
|
// someone requests an audience to the king
|
|
char request = com->data[0];
|
|
if(request == 'n')
|
|
{
|
|
int population = data->job.king.nbPeasants + data->job.king.nbSoldiers;
|
|
if(population < 15 || data->job.king.nbPeasants < (2*population)/3)
|
|
{
|
|
action->com_data.data[0] = 'p'; // "thou shall be peasant"
|
|
data->job.king.nbPeasants += 1;
|
|
}
|
|
else
|
|
{
|
|
action->com_data.data[0] = 's'; // "thou shall serve me as a soldier"
|
|
Coord target = ARMY_POS;
|
|
target.y -= data->job.king.nbSoldiers % 10;
|
|
target.x -= data->job.king.nbSoldiers / 10;
|
|
Coord* ptr = (Coord*)(action->com_data.data + 2);
|
|
*ptr = target;
|
|
*((int*)(action->com_data.data+11)) = -1;
|
|
data->job.king.nbSoldiers += 1;
|
|
|
|
if(data->job.king.nbSoldiers % 10 == 9)
|
|
{
|
|
action->com_data.data[10] = 1; // "thou shall command the army to march on the corpses of our foes"
|
|
*((int*)(action->com_data.data+11)) = 10;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
action->com_data.data[0] = 'f'; // "you dare disturb me from my royal occupations ? Go "f" yourself !"
|
|
|
|
data->job.king.last_audience_dir = Dir(com->flag);
|
|
action->dir = Dir(com->flag);
|
|
action->type = Action::COMMUNICATE;
|
|
data->last_dir = action->dir;
|
|
data->last_action = action->type;
|
|
sprintf(data->debugStr, "receiving request : %s\ngiving job : %s", com->data, action->com_data.data);
|
|
return;
|
|
}
|
|
case PEASANT :
|
|
{
|
|
if(data->job.peasant.tried_picking_up_food && success)
|
|
data->job.peasant.brings_food = true;
|
|
data->job.peasant.tried_picking_up_food = false;
|
|
|
|
if(data->job.peasant.brings_food){
|
|
int distance = data->pos.dist();
|
|
if(distance == 1){
|
|
action->type = Action::MOVE;
|
|
action->dir = NORTH;
|
|
|
|
for(i=0; i<4; i++){
|
|
type = info->getNear(Dir(i));
|
|
if(type == SPAWN){
|
|
action->dir = Dir(i);
|
|
action->type = Action::PUT;
|
|
data->job.peasant.brings_food = false;
|
|
break;
|
|
}
|
|
}
|
|
}else{
|
|
action->type = Action::MOVE;
|
|
do{
|
|
action->dir = Dir( rand() % 4 );
|
|
}while(~(data->pos + action->dir) > distance && distance != 0);
|
|
|
|
}
|
|
data->last_dir = action->dir;
|
|
data->last_action = action->type;
|
|
sprintf(data->debugStr, "bringing food back to the spawn");
|
|
return;
|
|
}
|
|
}
|
|
break;
|
|
case SOLDIER :
|
|
{
|
|
Coord target = data->pos - data->job.soldier.target;
|
|
int distance = target.dist();
|
|
const Com* com = info->getCom();
|
|
if(com != nullptr)
|
|
{
|
|
int charge_countdown = *((int*)(com->data));
|
|
data->job.soldier.charge_countdown = charge_countdown;
|
|
*((int*)(action->com_data.data))= charge_countdown - 1;
|
|
action->type = Action::COMMUNICATE;
|
|
action->dir = Dir((com->flag+2)%4);
|
|
data->last_dir = action->dir;
|
|
sprintf(data->debugStr, "receiving orders");
|
|
}
|
|
else if(distance == 0)
|
|
{
|
|
if(data->job.soldier.charge_countdown > 0)
|
|
{
|
|
if(data->job.soldier.commander)
|
|
{
|
|
data->job.soldier.commander = false;
|
|
*((int*)(action->com_data.data))= data->job.soldier.charge_countdown;
|
|
action->type = Action::COMMUNICATE;
|
|
action->dir = Dir(SOUTH);
|
|
data->last_dir = action->dir;
|
|
sprintf(data->debugStr, "Notifying the troops that we are going into battle");
|
|
}
|
|
else
|
|
{
|
|
action->type = Action::WAIT;
|
|
data->job.soldier.charge_countdown -= 1;
|
|
sprintf(data->debugStr, "Charge in %d...", data->job.soldier.charge_countdown);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
action->type = Action::WAIT;
|
|
sprintf(data->debugStr, "waiting orders");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
action->type = Action::MOVE;
|
|
do{
|
|
action->dir = Dir( rand() % 4 );
|
|
}while(~(target + action->dir) > distance && distance != 0);
|
|
data->last_dir = action->dir;
|
|
sprintf(data->debugStr, "going into formation");
|
|
}
|
|
data->last_action = action->type;
|
|
return;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
// wander around
|
|
int blockedCount = 0;
|
|
action->type = Action::MOVE;
|
|
bool ok = false;
|
|
do{
|
|
action->dir = Dir((data->last_dir + rand()%3)%4);
|
|
type = info->getNear(action->dir);
|
|
ok = PixelProperty::isWalkable(type) && !((data->pos + Coord(action->dir)).x < -12); // do not interfere with military formations
|
|
++blockedCount;
|
|
if(blockedCount > 3)
|
|
{
|
|
action->type = Action::WAIT;
|
|
ok = true;
|
|
}
|
|
}while(!ok);
|
|
|
|
data->last_dir = action->dir;
|
|
data->last_action = action->type;
|
|
sprintf(data->debugStr, "wandering around");
|
|
}
|