PixelWars/behaviors/war_charge_east.cpp

394 lines
13 KiB
C++

#include <cstdlib>
#include "behavior.h"
// g++ -shared war_charge_east.cpp -o war_charge_east.dll -std=c++11 -I../src
#define KING_POS Coord(2, 2)
#define ARMY_POS Coord(12, 4)
#define CHARGE_DIR EAST
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");
}