/* Copyright (C) 2001-2004 Stephane Magnenat & Luc-Olivier de Charrière Copyright (C) 2005 Eli Dupree for any question or comment contact us at or This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "AIWarrush.h" #include "Building.h" #include "Unit.h" #include "Building.h" #include "Game.h" #include "GlobalContainer.h" #include "Order.h" #include "Player.h" #include "Brush.h" #include "Utilities.h" #include #include #define BUILDING_DELAY 30 #define AREAS_DELAY 50 using namespace boost; void AIWarrush::init(Player *player) { assert(player); this->player=player; this->team=player->team; this->game=player->game; this->map=player->map; buildingDelay = 0; areaUpdatingDelay = 0; assert(this->team); assert(this->game); assert(this->map); } AIWarrush::AIWarrush(Player *player) { init(player); } AIWarrush::AIWarrush(GAGCore::InputStream *stream, Player *player, Sint32 versionMinor) { init(player); } AIWarrush::~AIWarrush() { } bool AIWarrush::load(GAGCore::InputStream *stream, Player *player, Sint32 versionMinor) { init(player); return true; } void AIWarrush::save(GAGCore::OutputStream *stream) { } int AIWarrush::numberOfUnitsWithSkillGreaterThanValue(const int skill, const int value)const { Unit **myUnits=team->myUnits; int count = 0; for (int i=0; i<1024; i++) { Unit *u=myUnits[i]; if ((u)&&(u->performance[skill]>value)) { count++; } } return count; } int AIWarrush::numberOfUnitsWithSkillEqualToValue(const int skill, const int value)const { Unit **myUnits=team->myUnits; int count = 0; for (int i=0; i<1024; i++) { Unit *u=myUnits[i]; if ((u)&&(u->performance[skill]==value)) { count++; } } return count; } bool AIWarrush::isAnyUnitWithLessThanOneThirdFood()const { Unit **myUnits=team->myUnits; for (int i=0; i<1024; i++) { Unit *u=myUnits[i]; if ((u)&&(u->hungry<(Unit::HUNGRY_MAX/2))) //Yeah, it's a half, not a third. Weird huh? :P { return true; } } return false; } Building *AIWarrush::getSwarmWithoutSettings(const int workerRatio, const int explorerRatio, const int warriorRatio)const { Building **myBuildings=team->myBuildings; for (int i=0; i<1024; i++) { Building *b=myBuildings[i]; if ( (b) && (b->type->shortTypeNum==IntBuildingType::SWARM_BUILDING) && (b->constructionResultState == Building::NO_CONSTRUCTION) && ((b->ratio[0] != workerRatio) || (b->ratio[1] != explorerRatio) || (b->ratio[2] != warriorRatio)) ) { return b; } } return NULL; } Building *AIWarrush::getBuildingWithoutWorkersAssigned(Sint32 shortTypeNum, int num_workers)const { Building **myBuildings=team->myBuildings; for (int i=0; i<1024; i++) { Building *b=myBuildings[i]; if ( (b) && (b->type->shortTypeNum==shortTypeNum) && (b->maxUnitWorking != num_workers) && (b->constructionResultState != Building::NO_CONSTRUCTION || (b->type->shortTypeNum==shortTypeNum != IntBuildingType::ATTACK_BUILDING && b->type->shortTypeNum==shortTypeNum != IntBuildingType::HEAL_BUILDING && b->type->shortTypeNum==shortTypeNum != IntBuildingType::WALKSPEED_BUILDING && b->type->shortTypeNum==shortTypeNum != IntBuildingType::SWIMSPEED_BUILDING && b->type->shortTypeNum==shortTypeNum != IntBuildingType::SCIENCE_BUILDING)) ) { return b; } } return NULL; } Building *AIWarrush::getSwarmAtRandom()const { Building **myBuildings=team->myBuildings; int swarmsfound = 0; Building *chosen_swarm = NULL; for (int i=0; i<1024; i++) { Building *b=myBuildings[i]; if ((b) && (b->type->shortTypeNum==IntBuildingType::SWARM_BUILDING)) { ++swarmsfound; if(syncRand()%swarmsfound == 0) { chosen_swarm = b; } } } return chosen_swarm; } bool AIWarrush::allOfBuildingTypeAreCompleted(Sint32 shortTypeNum)const { Building **myBuildings=team->myBuildings; for (int i=0; i<1024; i++) { Building *b=myBuildings[i]; if ( (b) && (b->type->shortTypeNum==shortTypeNum) && ( b->constructionResultState != Building::NO_CONSTRUCTION || b->buildingState == Building::DEAD ) ) { return false; } } return true; } bool AIWarrush::allOfBuildingTypeAreFull(Sint32 shortTypeNum)const { Building **myBuildings=team->myBuildings; for (int i=0; i<1024; i++) { Building *b=myBuildings[i]; if ( (b) && (b->type->shortTypeNum==shortTypeNum) && ( b->unitsInside.size() < (size_t)b->maxUnitInside ) ) { return false; } } return true; } int AIWarrush::numberOfBuildingsOfType(Sint32 shortTypeNum)const { Building **myBuildings=team->myBuildings; int count = 0; for (int i=0; i<1024; i++) { Building *b=myBuildings[i]; if((b)&&( b->shortTypeNum == shortTypeNum ))++count; } return count; } int AIWarrush::numberOfExtraBuildings()const { Building **myBuildings=team->myBuildings; int count = 0; for (int i=0; i<1024; i++) { Building *b=myBuildings[i]; if((b)&&( b->shortTypeNum == IntBuildingType::HEAL_BUILDING || b->shortTypeNum == IntBuildingType::WALKSPEED_BUILDING || b->shortTypeNum == IntBuildingType::SWIMSPEED_BUILDING || b->shortTypeNum == IntBuildingType::SCIENCE_BUILDING || b->shortTypeNum == IntBuildingType::DEFENSE_BUILDING ))++count; } return count; } bool AIWarrush::allOfBuildingTypeAreFullyWorked(Sint32 shortTypeNum)const { Building **myBuildings=team->myBuildings; for (int i=0; i<1024; i++) { Building *b=myBuildings[i]; if((b)&&(b->shortTypeNum == shortTypeNum)) { if(b->unitsWorking.size() == (size_t)b->maxUnitWorking) { } else { return false; } } } return true; } bool AIWarrush::percentageOfBuildingsAreFullyWorked(int percentage)const { Building **myBuildings=team->myBuildings; int num_buildings = 0; int num_worked_buildings = 0; for (int i=0; i<1024; i++) { Building *b=myBuildings[i]; if((b)&&(b->shortTypeNum != IntBuildingType::WAR_FLAG)&&(b->shortTypeNum != IntBuildingType::EXPLORATION_FLAG)&&(b->shortTypeNum != IntBuildingType::CLEARING_FLAG)) { ++num_buildings; if(b->unitsWorking.size() == (size_t)b->maxUnitWorking) { ++num_worked_buildings; if(verbose)std::cout << "A"; } else if((b->type->shortTypeNum == IntBuildingType::SWARM_BUILDING || b->type->shortTypeNum == IntBuildingType::FOOD_BUILDING) && b->constructionResultState == Building::NO_CONSTRUCTION && (b->ressources[CORN]) > ((b->wishedResources[CORN]) * 2 / 3)) {//heavily worked swarms and inns sometimes are full and have no workers ++num_worked_buildings; if(verbose)std::cout << "C"; } } } if(verbose)std::cout << ": " << num_worked_buildings << " worked out of " << num_buildings << "\n"; return num_worked_buildings * 100 >= num_buildings * percentage; } boost::shared_ptr AIWarrush::getOrder(void) { // reduce delays if (buildingDelay > 0) buildingDelay--; if (areaUpdatingDelay > 0) areaUpdatingDelay--; if(game->stepCounter < 64 && game->stepCounter%2 == 0) { int teamIndex = game->stepCounter / 2; Team *enemy_team = game->teams[teamIndex]; if((enemy_team)&&(team->enemies & enemy_team->me))return setupExploreFlagForTeam(enemy_team); } //keep those areas up to date if(areaUpdatingDelay == AREAS_DELAY*2/3) return pruneGuardAreas(); if(areaUpdatingDelay == AREAS_DELAY/3) return placeGuardAreas(); if(areaUpdatingDelay <= 0) { areaUpdatingDelay = AREAS_DELAY; return farm(); } //assuming we didn't have to mess with areas or explore flags, check if we can build stuff if (buildingDelay <= 0) { bool shouldBuildMore = percentageOfBuildingsAreFullyWorked(70); if(verbose)if(shouldBuildMore)std::cout << "AIWarrush is ready to build more stuff!"; //Build another swarm if all are swarms are working at capacity, and if we have other random stuff we should-have / need if(verbose)std::cout << "Chance to build swarm: "; if(shouldBuildMore && allOfBuildingTypeAreCompleted(IntBuildingType::SWARM_BUILDING) && numberOfExtraBuildings() >= numberOfBuildingsOfType(IntBuildingType::SWARM_BUILDING) && numberOfBuildingsOfType(IntBuildingType::FOOD_BUILDING) >= numberOfBuildingsOfType(IntBuildingType::SWARM_BUILDING) * 2) { if(verbose)std::cout << "TAKEN!\n"; return buildBuildingOfType(IntBuildingType::SWARM_BUILDING); } if(verbose)std::cout << "ignored.\n"; //Silly inns. Don't build them right away and don't build too many at a time and don't build too many. //More limits than it should have, maybe, but the idea of the AI is that it should either win //or lose warriors (so it shouldn't need so many inns.) if(verbose)std::cout << "Chance to build inn: "; if(isAnyUnitWithLessThanOneThirdFood() && shouldBuildMore && numberOfExtraBuildings() >= numberOfBuildingsOfType(IntBuildingType::FOOD_BUILDING) - 1 && ( (allOfBuildingTypeAreCompleted(IntBuildingType::FOOD_BUILDING) && allOfBuildingTypeAreFull(IntBuildingType::FOOD_BUILDING)) || allOfBuildingTypeAreFullyWorked(IntBuildingType::FOOD_BUILDING)) ) { if(verbose)std::cout << "TAKEN!\n"; return buildBuildingOfType(IntBuildingType::FOOD_BUILDING); } if(verbose)std::cout << "ignored.\n"; //if the barracks are all working at capacity, //build more barracks! (this also builds the first barracks...) if(verbose)std::cout << "Chance to build barracks: "; if( allOfBuildingTypeAreCompleted(IntBuildingType::ATTACK_BUILDING) && allOfBuildingTypeAreFull(IntBuildingType::ATTACK_BUILDING) ) { if(verbose)std::cout << "TAKEN!\n"; return buildBuildingOfType(IntBuildingType::ATTACK_BUILDING); } if(verbose)std::cout << "ignored.\n"; //and if we have excess workers, build random other buildings! if(verbose)std::cout << "Chance to build etc: "; if(shouldBuildMore) { if(verbose)std::cout << "TAKEN!\n"; Sint32 type; int random_number = syncRand()%100; if(random_number < 70 || numberOfBuildingsOfType(IntBuildingType::HEAL_BUILDING) == 0)type = IntBuildingType::HEAL_BUILDING; else if(random_number < 80 || numberOfBuildingsOfType(IntBuildingType::WALKSPEED_BUILDING) == 0)type = IntBuildingType::WALKSPEED_BUILDING; else if(random_number < 87)type = IntBuildingType::SWIMSPEED_BUILDING; else if(random_number < 94)type = IntBuildingType::SCIENCE_BUILDING; else type = IntBuildingType::DEFENSE_BUILDING; return buildBuildingOfType(type); } if(verbose)std::cout << "ignored.\n"; } //If we have enough workers, we can switch to dedicated warrushing production. if(numberOfUnitsWithSkillGreaterThanValue(HARVEST,0) >= 6) { //This is basically a way to change all the swarms without bothering to remember //anything. (It can only issue one order per tick, so it has to do it over several //ticks and calculate the orders seperately.) Building *out_of_date_swarm = getSwarmWithoutSettings(4,1,3); if(out_of_date_swarm) { Sint32 settings[3] = {4,1,3}; return shared_ptr(new OrderModifySwarm(out_of_date_swarm->gid, settings)); } } //all swarms should always have 5 workers at them! Building *weak_swarm = getBuildingWithoutWorkersAssigned(IntBuildingType::SWARM_BUILDING, 5); if (weak_swarm) return shared_ptr(new OrderModifyBuilding(weak_swarm->gid, 5)); //all inns should always have 3 workers at them! (best to build fast, make sure they're fed) Building *weak_inn = getBuildingWithoutWorkersAssigned(IntBuildingType::FOOD_BUILDING, 3); if (weak_inn) return shared_ptr(new OrderModifyBuilding(weak_inn->gid, 3)); //work barracks more too. Building *weak_barracks = getBuildingWithoutWorkersAssigned(IntBuildingType::ATTACK_BUILDING, 3); if (weak_barracks && weak_barracks->constructionResultState != Building::NO_CONSTRUCTION) return shared_ptr(new OrderModifyBuilding(weak_barracks->gid, 3)); //nothing at all to do?! return shared_ptr(new NullOrder); } boost::shared_ptr AIWarrush::pruneGuardAreas() { //If we have any guard areas that aren't adjacent to an enemy building, we remove them. BrushAccumulator acc; for(int x=0;xw;x++) { for(int y=0;yh;y++) { if(map->isGuardArea(x,y,team->me)) { bool keep = false; for(int xmod=-1;xmod<=1;xmod++) { for(int ymod=-1;ymod<=1;ymod++) { //if there's a building... if(map->getBuilding(x+xmod,y+ymod)!=NOGBID) { //...AND it's an enemy building... if(team->enemies & game->teams[Building::GIDtoTeam(map->getBuilding(x+xmod,y+ymod))]->me) { //...then we still want it guarded. keep=true; } } } } if(!keep) { acc.applyBrush(map,BrushApplication(x,y,0)); } } } } if(acc.getApplicationCount()) { return shared_ptr(new OrderAlterateGuardArea(team->teamNumber,BrushTool::MODE_DEL,&acc)); } else return shared_ptr(new NullOrder); } boost::shared_ptr AIWarrush::placeGuardAreas() { BrushAccumulator guard_add_acc; //Place guard area on an enemy building if there is one... for(int i=0;i<32;i++) { Team *t = game->teams[i]; if((t)&&(team->enemies & t->me)) { for(int j=0;j<1024;j++) { Building *b = t->myBuildings[j]; if ((b)&&(b->buildingState != Building::DEAD)&&(b->hp != 1 || b->constructionResultState == Building::NO_CONSTRUCTION)&&(b->shortTypeNum != IntBuildingType::WAR_FLAG)&&(b->shortTypeNum != IntBuildingType::EXPLORATION_FLAG)&&(b->shortTypeNum != IntBuildingType::CLEARING_FLAG)) { BuildingType *bt = b->type; if( //the area must be discovered to prevent AI cheating. ( map->isFOWDiscovered(b->posX, b->posY, team->me) || map->isFOWDiscovered(b->posX+bt->width - 1, b->posY, team->me) || map->isFOWDiscovered(b->posX+bt->width - 1, b->posY+bt->height-1, team->me) || map->isFOWDiscovered(b->posX, b->posY+bt->height-1, team->me) ) && //do not order a building attacked if the order is already in place. ( !map->isGuardArea(b->posX,b->posY,team->me) ) ) { if((map->getBuilding(b->posX, b->posY)!=NOGBID)&&(team->enemies & game->teams[Building::GIDtoTeam(map->getBuilding(b->posX, b->posY))]->me)) //paranoia { for(int x = 0; x < bt->width; x++) { for(int y = 0; y < bt->height; y++) { guard_add_acc.applyBrush(map,BrushApplication(b->posX+x,b->posY+y,6)); } } } } } } } } if(guard_add_acc.getApplicationCount()) { return shared_ptr(new OrderAlterateGuardArea(team->teamNumber,BrushTool::MODE_ADD,&guard_add_acc)); } else return shared_ptr(new NullOrder); } boost::shared_ptr AIWarrush::farm() { // Algorithm initially stolen from Nicowar. DynamicGradientMapArray water_gradient(map->w,map->h); for(int x=0;xw;x++) { for(int y=0;yh;y++) { if (map->isWater(x,y)) { water_gradient(x, y) = 255; } else { water_gradient(x, y) = 1; } } } map->updateGlobalGradientSlow(water_gradient.c_array()); BrushAccumulator del_acc; BrushAccumulator add_acc; BrushAccumulator clr_del_acc; BrushAccumulator clr_add_acc; for(int x=0;xw;x++) { for(int y=0;yh;y++) { if((!map->isRessourceTakeable(x, y, WOOD) && !map->isRessourceTakeable(x, y, CORN))) { if(map->isForbidden(x, y, team->me)) { if( //Make sure we're not deleting buildings' forbidden area! !map->isForbidden (x + 1,y,team->me) && !map->isForbidden (x - 1,y,team->me) && !map->isForbidden (x,y + 1,team->me) && !map->isForbidden (x,y - 1,team->me) //Or fruits'! && !map->isRessourceTakeable(x, y, CHERRY) && !map->isRessourceTakeable(x, y, ORANGE) && !map->isRessourceTakeable(x, y, PRUNE) ) { del_acc.applyBrush(map, BrushApplication(x, y, 0)); } } } if(map->isForbidden(x, y, team->me) && map->isClearArea(x, y, team->me)) { del_acc.applyBrush(map, BrushApplication(x, y, 0)); } //we never clear anything but wood if(!map->isRessourceTakeable(x, y, WOOD)) { if(map->isClearArea(x, y, team->me)) { clr_del_acc.applyBrush(map, BrushApplication(x, y, 0)); } } //we clear wood if it's next to nice stuff like wheat or buildings if(map->isRessourceTakeable(x, y, WOOD)) { if(!map->isClearArea(x, y, team->me) && map->isMapDiscovered(x, y, team->me)) { for(int xmod=-1;xmod<=1;xmod++) { for(int ymod=-1;ymod<=1;ymod++) { if(map->isRessourceTakeable(x+xmod, y+ymod, CORN) || (map->getBuilding(x+xmod,y+ymod)!=NOGBID && (team->me & game->teams[Building::GIDtoTeam(map->getBuilding(x+xmod,y+ymod))]->me))) { clr_add_acc.applyBrush(map, BrushApplication(x, y, 0)); goto doublebreak; } } } doublebreak: /*statement for label to point to*/; } } if(x%2==1 && ((y%2==1 && x%4==1) || (y%2==0 && x%4==3))) { if(map->isRessourceTakeable(x, y, WOOD)) { if(!map->isForbidden(x, y, team->me) && !map->isClearArea(x, y, team->me) && map->isMapDiscovered(x, y, team->me) && water_gradient(x, y) > (255 - 15)) { add_acc.applyBrush(map, BrushApplication(x, y, 0)); } } } if(x%2==y%2) { if(map->isRessourceTakeable(x, y, CORN)) { if(!map->isForbidden(x, y, team->me) && map->isMapDiscovered(x, y, team->me) && water_gradient(x, y) > (255 - 15)) { add_acc.applyBrush(map, BrushApplication(x, y, 0)); } } } //FORBID FRUITS!!! They're horrible for our warriors and we hate converting. if( ( map->isRessourceTakeable(x, y, CHERRY) || map->isRessourceTakeable(x, y, ORANGE) || map->isRessourceTakeable(x, y, PRUNE) ) && !map->isForbidden(x, y, team->me) && map->isMapDiscovered(x, y, team->me) ) { add_acc.applyBrush(map, BrushApplication(x, y, 0)); } } } if(del_acc.getApplicationCount()>0) return shared_ptr(new OrderAlterateForbidden(team->teamNumber, BrushTool::MODE_DEL, &del_acc)); if(add_acc.getApplicationCount()>0) return shared_ptr(new OrderAlterateForbidden(team->teamNumber, BrushTool::MODE_ADD, &add_acc)); if(clr_del_acc.getApplicationCount()>0) return shared_ptr(new OrderAlterateClearArea(team->teamNumber, BrushTool::MODE_DEL, &clr_del_acc)); if(clr_add_acc.getApplicationCount()>0) return shared_ptr(new OrderAlterateClearArea(team->teamNumber, BrushTool::MODE_ADD, &clr_add_acc)); //nothing to do... return shared_ptr(new NullOrder()); } //Simple hack to place explore flags on opponents' starting swarms. boost::shared_ptr AIWarrush::setupExploreFlagForTeam(Team *enemy_team) { if(verbose)std::cout << "looking for swarms:\n"; for(int j=0;j<1024;j++) { Building *b = enemy_team->myBuildings[j]; if((b)&&(b->type->shortTypeNum == IntBuildingType::SWARM_BUILDING)&&(b->constructionResultState == Building::NO_CONSTRUCTION)) { Sint32 typeNum=globalContainer->buildingsTypes.getTypeNum("explorationflag", 0, false); return shared_ptr(new OrderCreate(team->teamNumber, b->posX, b->posY, typeNum, 1, 1)); } } if(verbose)std::cout << "No swarms found\n"; //what, they have no swarm? o_O Find any building: for(int j=0;j<1024;j++) { Building *b = enemy_team->myBuildings[j]; if(b) { Sint32 typeNum=globalContainer->buildingsTypes.getTypeNum("explorationflag", 0, false); return shared_ptr(new OrderCreate(team->teamNumber, b->posX, b->posY, typeNum, 1, 1)); } } if(verbose)std::cout << "No buildings found\n"; //what, they have no buildings? o_O Find any unit: for(int j=0;j<1024;j++) { Unit *u = enemy_team->myUnits[j]; if(u) { Sint32 typeNum=globalContainer->buildingsTypes.getTypeNum("explorationflag", 0, false); return shared_ptr(new OrderCreate(team->teamNumber, u->posX, u->posY, typeNum, 1, 1)); } } //what, enemy has no buildings or units at the beginning of the game? o_O O_o o_O if(verbose)std::cout << "No buildings found, no units found o_O\n"; return shared_ptr(new NullOrder); } bool AIWarrush::locationIsAvailableForBuilding(int x, int y, int width, int height) { /*if(map->isHardSpaceForBuilding(x,y,width,height)) {*/ if( map->isMapDiscovered(x, y, team->me) || map->isMapDiscovered(x+width-1, y, team->me) || map->isMapDiscovered(x+width-1, y+height-1, team->me) || map->isMapDiscovered(x, y+height-1, team->me) ) { return true; } /*}*/ return false; } void AIWarrush::initializeGradientWithResource(DynamicGradientMapArray &gradient, Uint8 resource_type) { for(int x=0;xw;x++) { for(int y=0;yh;y++) { Case c=map->getCase(x,y); if (c.ressource.type==resource_type) { gradient(x, y) = 255; } else if (c.ressource.type!=NO_RES_TYPE) { gradient(x, y) = 0; } else if (c.building!=NOGBID) { gradient(x, y) = 0; } else if (map->isWater(x,y)) { gradient(x, y) = 0; } else { //has to be desert or grass with no buildings or resources, at this point. gradient(x, y) = 1; } } } map->updateGlobalGradientSlow(gradient.c_array()); for(int x=0;xw;x++) { for(int y=0;yh;y++) { if (gradient(x, y) == 255) gradient(x, y) = 0; else gradient(x, y)++; } } } boost::shared_ptr AIWarrush::buildBuildingOfType(Sint32 shortTypeNum) { // set delay // now doing this first in order to avoid repeated failed builds // WARNING THIS IS A HACK FIX // in reality, if it fails to build, it should go on and get another order. buildingDelay = BUILDING_DELAY; DynamicGradientMapArray wood_gradient(map->w,map->h); DynamicGradientMapArray wheat_gradient(map->w,map->h); initializeGradientWithResource(wood_gradient, WOOD); initializeGradientWithResource(wheat_gradient, CORN); BuildingType *bt=globalContainer->buildingsTypes.getByType(IntBuildingType::typeFromShortNumber(shortTypeNum), 0, true); DynamicGradientMapArray availability_gradient(map->w,map->h); for(int x=0;xw;x++) { for(int y=0;yh;y++) { Case c=map->getCase(x,y); if (c.ressource.type!=NO_RES_TYPE) { availability_gradient(x, y) = 0; } else if (c.building!=NOGBID) { availability_gradient(x, y) = 0; } else if (map->isWater(x,y)) { availability_gradient(x, y) = 0; } else if(map->isHardSpaceForBuilding(x-(bt->width / 2),y-(bt->width / 2),bt->width*2,bt->height*2) && locationIsAvailableForBuilding(x,y,bt->width,bt->height)) //the extra numbers at the ends expand the building { availability_gradient(x, y) = 255; } else availability_gradient(x, y) = 1; } } map->updateGlobalGradientSlow(availability_gradient.c_array()); Building *swarm = getSwarmAtRandom(); if (!swarm) { if(verbose)std::cout << "No swarm found!\n"; return shared_ptr(new NullOrder); } Sint32 destination_x,destination_y; { Sint32 x,y; Sint32 x_temp,y_temp; //map->dumpGradient(gradient.c_array(), "gradient.pgm"); //map->dumpGradient(availability_gradient.c_array(), "availability_gradient.pgm"); x = swarm->posX; y = swarm->posY; if(shortTypeNum==IntBuildingType::ATTACK_BUILDING || shortTypeNum==IntBuildingType::HEAL_BUILDING || shortTypeNum==IntBuildingType::SCIENCE_BUILDING || shortTypeNum==IntBuildingType::WALKSPEED_BUILDING || shortTypeNum==IntBuildingType::SWIMSPEED_BUILDING || shortTypeNum==IntBuildingType::DEFENSE_BUILDING) { map->getGlobalGradientDestination(wood_gradient.c_array(), x, y, &x_temp, &y_temp); x = x_temp; y = y_temp; } if(shortTypeNum==IntBuildingType::SWARM_BUILDING || shortTypeNum==IntBuildingType::FOOD_BUILDING) { map->getGlobalGradientDestination(wheat_gradient.c_array(), x, y, &x_temp, &y_temp); x = x_temp; y = y_temp; } bool result = map->getGlobalGradientDestination(availability_gradient.c_array(), x, y, &destination_x, &destination_y); if(verbose)std::cout << "Trying to build " << shortTypeNum << "(" << bt->width << " x " << bt->height << ")" << " at " << destination_x << "," << destination_y << " from " << x << "," << y << " swarm is " << swarm->posX << "," << swarm->posY << ", found = " << result << std::endl; if(verbose)if((int)availability_gradient(destination_x, destination_y) != 255)std::cout << "Could not find valid location for building! Best spot: " << destination_x << "," << destination_y << " (" << (int)availability_gradient(destination_x, destination_y) << ")\n"; } // create and return order Sint32 typeNum=globalContainer->buildingsTypes.getTypeNum(IntBuildingType::typeFromShortNumber(shortTypeNum), 0, true); return shared_ptr(new OrderCreate(team->teamNumber, destination_x, destination_y, typeNum, 1, 1)); }