#include #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"); }