From 9de151d9759d4b358aaa4ff47344c4a7da6ca0c5 Mon Sep 17 00:00:00 2001 From: Mark Vejvoda Date: Fri, 25 Feb 2011 16:32:27 +0000 Subject: [PATCH] - added more things to try to improve pathfinding (especially for the AI) AI players should now detect when units are blocked (surrounded by other units from same faction) and tell the other units to move out of the way --- source/glest_game/ai/ai.cpp | 150 ++++++++++++++++++ source/glest_game/ai/ai.h | 4 + source/glest_game/ai/ai_interface.cpp | 10 +- source/glest_game/ai/ai_interface.h | 3 +- source/glest_game/ai/ai_rule.cpp | 19 +++ source/glest_game/ai/ai_rule.h | 15 ++ source/glest_game/ai/path_finder.cpp | 59 ++++++- source/glest_game/game/game.cpp | 2 +- .../menu/menu_state_connected_game.cpp | 2 + 9 files changed, 259 insertions(+), 5 deletions(-) diff --git a/source/glest_game/ai/ai.cpp b/source/glest_game/ai/ai.cpp index 87f006e0..f647b892 100644 --- a/source/glest_game/ai/ai.cpp +++ b/source/glest_game/ai/ai.cpp @@ -14,6 +14,7 @@ #include "ai_rule.h" #include "unit_type.h" #include "unit.h" +#include "map.h" #include "leak_dumper.h" using namespace Shared::Graphics; @@ -122,6 +123,7 @@ void Ai::init(AiInterface *aiInterface, int useStartLocation) { aiRules.push_back(new AiRuleWorkerHarvest(this)); aiRules.push_back(new AiRuleRefreshHarvester(this)); aiRules.push_back(new AiRuleScoutPatrol(this)); + aiRules.push_back(new AiRuleUnBlock(this)); aiRules.push_back(new AiRuleReturnBase(this)); aiRules.push_back(new AiRuleMassiveAttack(this)); aiRules.push_back(new AiRuleAddTasks(this)); @@ -525,4 +527,152 @@ void Ai::harvest(int unitIndex) { } } +bool Ai::haveBlockedUnits() { + int unitCount = aiInterface->getMyUnitCount(); + Map *map = aiInterface->getMap(); + //If there is no close store + for(int j=0; j < unitCount; ++j) { + const Unit *u= aiInterface->getMyUnit(j); + const UnitType *ut= u->getType(); + + // If this building is a store + if(u->isAlive() && ut->isMobile() && u->getPath() != NULL && (u->getPath()->isBlocked() || u->getPath()->getBlockCount())) { + Vec2i unitPos = u->getPos(); + + //printf("#1 AI found blocked unit [%d - %s]\n",u->getId(),u->getFullName().c_str()); + + int failureCount = 0; + int cellCount = 0; + + for(int i = -1; i <= 1; ++i) { + for(int j = -1; j <= 1; ++j) { + Vec2i pos = unitPos + Vec2i(i, j); + if(map->isInside(pos) && map->isInsideSurface(map->toSurfCoords(pos))) { + if(pos != unitPos) { + bool canUnitMoveToCell = map->aproxCanMove(u, unitPos, pos); + if(canUnitMoveToCell == false) { + failureCount++; + } + cellCount++; + } + } + } + } + bool unitImmediatelyBlocked = (failureCount == cellCount); + //printf("#1 unitImmediatelyBlocked = %d, failureCount = %d, cellCount = %d\n",unitImmediatelyBlocked,failureCount,cellCount); + + if(unitImmediatelyBlocked) { + //printf("#1 AI unit IS BLOCKED [%d - %s]\n",u->getId(),u->getFullName().c_str()); + return true; + } + } + } + + return false; +} + +bool Ai::getAdjacentUnits(std::map > &signalAdjacentUnits, const Unit *unit) { + bool result = false; + Map *map = aiInterface->getMap(); + Vec2i unitPos = unit->getPos(); + for(int i = -1; i <= 1; ++i) { + for(int j = -1; j <= 1; ++j) { + Vec2i pos = unitPos + Vec2i(i, j); + if(map->isInside(pos) && map->isInsideSurface(map->toSurfCoords(pos))) { + if(pos != unitPos) { + Unit *adjacentUnit = map->getCell(pos)->getUnit(unit->getCurrField()); + if(adjacentUnit != NULL && adjacentUnit->getFactionIndex() == unit->getFactionIndex()) { + if(adjacentUnit->getType()->isMobile() && adjacentUnit->getPath() != NULL) { + //signalAdjacentUnits.push_back(adjacentUnit); + float dist = unitPos.dist(adjacentUnit->getPos()); + + std::map >::iterator iterFind1 = signalAdjacentUnits.find(dist); + if(iterFind1 == signalAdjacentUnits.end()) { + signalAdjacentUnits[dist][adjacentUnit->getId()] = adjacentUnit; + + getAdjacentUnits(signalAdjacentUnits, adjacentUnit); + result = true; + } + else { + std::map::iterator iterFind2 = iterFind1->second.find(adjacentUnit->getId()); + if(iterFind2 == iterFind1->second.end()) { + signalAdjacentUnits[dist][adjacentUnit->getId()] = adjacentUnit; + getAdjacentUnits(signalAdjacentUnits, adjacentUnit); + result = true; + } + } + } + } + } + } + } + } + return result; +} + +void Ai::unblockUnits() { + int unitCount = aiInterface->getMyUnitCount(); + Map *map = aiInterface->getMap(); + // Find blocked units and move surrounding units out of the way + std::map > signalAdjacentUnits; + for(int idx=0; idx < unitCount; ++idx) { + const Unit *u= aiInterface->getMyUnit(idx); + const UnitType *ut= u->getType(); + + // If this building is a store + if(u->isAlive() && ut->isMobile() && u->getPath() != NULL && (u->getPath()->isBlocked() || u->getPath()->getBlockCount())) { + Vec2i unitPos = u->getPos(); + + //printf("#2 AI found blocked unit [%d - %s]\n",u->getId(),u->getFullName().c_str()); + + int failureCount = 0; + int cellCount = 0; + + for(int i = -1; i <= 1; ++i) { + for(int j = -1; j <= 1; ++j) { + Vec2i pos = unitPos + Vec2i(i, j); + if(map->isInside(pos) && map->isInsideSurface(map->toSurfCoords(pos))) { + if(pos != unitPos) { + bool canUnitMoveToCell = map->aproxCanMove(u, unitPos, pos); + if(canUnitMoveToCell == false) { + failureCount++; + getAdjacentUnits(signalAdjacentUnits, u); + } + cellCount++; + } + } + } + } + //bool unitImmediatelyBlocked = (failureCount == cellCount); + //printf("#2 unitImmediatelyBlocked = %d, failureCount = %d, cellCount = %d, signalAdjacentUnits.size() = %d\n",unitImmediatelyBlocked,failureCount,cellCount,signalAdjacentUnits.size()); + } + } + + if(signalAdjacentUnits.size() > 0) { + //printf("#2 AI units ARE BLOCKED about to unblock\n"); + + for(std::map >::reverse_iterator iterMap = signalAdjacentUnits.rbegin(); + iterMap != signalAdjacentUnits.rend(); iterMap++) { + + for(std::map::iterator iterMap2 = iterMap->second.begin(); + iterMap2 != iterMap->second.end(); iterMap2++) { + int idx = iterMap2->first; + const Unit *adjacentUnit = iterMap2->second; + + for(int moveAttempt = 1; moveAttempt <= villageRadius; ++moveAttempt) { + Vec2i pos= Vec2i( + random.randRange(-villageRadius*2, villageRadius*2), random.randRange(-villageRadius*2, villageRadius*2)) + + adjacentUnit->getPos(); + + bool canUnitMoveToCell = map->aproxCanMove(adjacentUnit, adjacentUnit->getPos(), pos); + if(canUnitMoveToCell == true) { + const CommandType *ct = adjacentUnit->getType()->getFirstCtOfClass(ccMove); + CommandResult r = aiInterface->giveCommand(adjacentUnit,ct, pos); + } + } + } + } + } +} + }}//end namespace diff --git a/source/glest_game/ai/ai.h b/source/glest_game/ai/ai.h index d7b85e96..d3e1a486 100644 --- a/source/glest_game/ai/ai.h +++ b/source/glest_game/ai/ai.h @@ -145,6 +145,8 @@ private: Positions expansionPositions; RandomGen random; + bool getAdjacentUnits(std::map > &signalAdjacentUnits, const Unit *unit); + public: int minWarriors; ~Ai(); @@ -183,6 +185,8 @@ public: void massiveAttack(const Vec2i &pos, Field field, bool ultraAttack= false); void returnBase(int unitIndex); void harvest(int unitIndex); + bool haveBlockedUnits(); + void unblockUnits(); }; }}//end namespace diff --git a/source/glest_game/ai/ai_interface.cpp b/source/glest_game/ai/ai_interface.cpp index b90087d4..1c6d8e45 100644 --- a/source/glest_game/ai/ai_interface.cpp +++ b/source/glest_game/ai/ai_interface.cpp @@ -132,7 +132,7 @@ CommandResult AiInterface::giveCommand(int unitIndex, CommandClass commandClass, } } -CommandResult AiInterface::giveCommand(Unit *unit, const CommandType *commandType, const Vec2i &pos) { +CommandResult AiInterface::giveCommand(const Unit *unit, const CommandType *commandType, const Vec2i &pos) { assert(this->gameSettings != NULL); if(unit == NULL) { @@ -172,7 +172,9 @@ CommandResult AiInterface::giveCommand(Unit *unit, const CommandType *commandTyp else { SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",__FILE__,__FUNCTION__,__LINE__); - CommandResult result = unit->giveCommand(new Command(commandType, pos)); + Faction *faction = world->getFaction(unit->getFactionIndex()); + Unit *unitToCommand = faction->findUnit(unit->getId()); + CommandResult result = unitToCommand->giveCommand(new Command(commandType, pos)); SystemFlags::OutputDebug(SystemFlags::debugSystem,"In [%s::%s Line: %d]\n",__FILE__,__FUNCTION__,__LINE__); return result; @@ -613,5 +615,9 @@ const Unit *AiInterface::getFirstOnSightEnemyUnit(Vec2i &pos, Field &field, int return NULL; } +Map * AiInterface::getMap() { + Map *map= world->getMap(); + return map; +} }}//end namespace diff --git a/source/glest_game/ai/ai_interface.h b/source/glest_game/ai/ai_interface.h index 0d96201c..511d4e39 100644 --- a/source/glest_game/ai/ai_interface.h +++ b/source/glest_game/ai/ai_interface.h @@ -68,7 +68,7 @@ public: CommandResult giveCommand(int unitIndex, const CommandType *commandType, const Vec2i &pos, const UnitType* unitType); CommandResult giveCommand(int unitIndex, const CommandType *commandType, const Vec2i &pos); CommandResult giveCommand(int unitIndex, const CommandType *commandType, Unit *u= NULL); - CommandResult giveCommand(Unit *unit, const CommandType *commandType, const Vec2i &pos); + CommandResult giveCommand(const Unit *unit, const CommandType *commandType, const Vec2i &pos); //get data const ControlType getControlType(); @@ -94,6 +94,7 @@ public: bool checkCosts(const ProducibleType *pt); bool isFreeCells(const Vec2i &pos, int size, Field field); const Unit *getFirstOnSightEnemyUnit(Vec2i &pos, Field &field, int radius); + Map * getMap(); private: string getLogFilename() const {return "ai"+intToStr(factionIndex)+".log";} diff --git a/source/glest_game/ai/ai_rule.cpp b/source/glest_game/ai/ai_rule.cpp index 1155b719..c4384a27 100644 --- a/source/glest_game/ai/ai_rule.cpp +++ b/source/glest_game/ai/ai_rule.cpp @@ -1156,4 +1156,23 @@ void AiRuleExpand::execute(){ ai->addPriorityTask(new BuildTask(storeType, expandPos)); } + +// ======================================== +// class AiRuleUnBlock +// ======================================== + +AiRuleUnBlock::AiRuleUnBlock(Ai *ai): + AiRule(ai) +{ + +} + +bool AiRuleUnBlock::test() { + return ai->haveBlockedUnits(); +} + +void AiRuleUnBlock::execute(){ + ai->unblockUnits(); +} + }}//end namespace diff --git a/source/glest_game/ai/ai_rule.h b/source/glest_game/ai/ai_rule.h index 4935b252..37458a6a 100644 --- a/source/glest_game/ai/ai_rule.h +++ b/source/glest_game/ai/ai_rule.h @@ -310,6 +310,21 @@ public: virtual void execute(); }; +// ===================================================== +// class AiRuleUnBlock +// ===================================================== + +class AiRuleUnBlock: public AiRule{ +public: + AiRuleUnBlock(Ai *ai); + + virtual int getTestInterval() const {return 3000;} + virtual string getName() const {return "Blocked Units => Move surrounding units";} + + virtual bool test(); + virtual void execute(); +}; + }}//end namespace #endif diff --git a/source/glest_game/ai/path_finder.cpp b/source/glest_game/ai/path_finder.cpp index f061c5c3..dca8b7b3 100644 --- a/source/glest_game/ai/path_finder.cpp +++ b/source/glest_game/ai/path_finder.cpp @@ -526,6 +526,63 @@ TravelState PathFinder::aStar(Unit *unit, const Vec2i &targetPos, bool inBailout if(SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled == true && chrono.getMillis() > 4) SystemFlags::OutputDebug(SystemFlags::debugPerformance,"In [%s::%s Line: %d] took msecs: %lld\n",__FILE__,__FUNCTION__,__LINE__,chrono.getMillis()); + // First check if unit currently blocked + if(inBailout == false && unitPos != finalPos) { + int failureCount = 0; + int cellCount = 0; + + for(int i = -1; i <= 1; ++i) { + for(int j = -1; j <= 1; ++j) { + Vec2i pos = unitPos + Vec2i(i, j); + if(pos != unitPos) { + bool canUnitMoveToCell = map->aproxCanMove(unit, unitPos, pos); + if(canUnitMoveToCell == false) { + failureCount++; + } + cellCount++; + } + } + } + nodeLimitReached = (failureCount == cellCount); + pathFound = !nodeLimitReached; + + if(SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled == true && chrono.getMillis() > 1) SystemFlags::OutputDebug(SystemFlags::debugPerformance,"In [%s::%s Line: %d] **Check if dest blocked, distance for unit [%d - %s] from [%s] to [%s] is %.2f took msecs: %lld nodeLimitReached = %d, failureCount = %d\n",__FILE__,__FUNCTION__,__LINE__,unit->getId(),unit->getFullName().c_str(), unitPos.getString().c_str(), finalPos.getString().c_str(), dist,(long long int)chrono.getMillis(),nodeLimitReached,failureCount); + if(showConsoleDebugInfo && nodeLimitReached) { + //if(showConsoleDebugInfo) { + printf("**Check if src blocked [%d - %d], unit [%d - %s] from [%s] to [%s] distance %.2f took msecs: %lld nodeLimitReached = %d, failureCount = %d [%d]\n", + nodeLimitReached, inBailout, unit->getId(),unit->getFullName().c_str(), unitPos.getString().c_str(), finalPos.getString().c_str(), dist,(long long int)chrono.getMillis(),nodeLimitReached,failureCount,cellCount); + } + + if(nodeLimitReached == false) { + // First check if final destination blocked + failureCount = 0; + cellCount = 0; + + for(int i = -1; i <= 1; ++i) { + for(int j = -1; j <= 1; ++j) { + Vec2i pos = finalPos + Vec2i(i, j); + if(pos != finalPos) { + bool canUnitMoveToCell = map->aproxCanMove(unit, pos, finalPos); + if(canUnitMoveToCell == false) { + failureCount++; + } + cellCount++; + } + } + } + nodeLimitReached = (failureCount == cellCount); + pathFound = !nodeLimitReached; + + if(SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled == true && chrono.getMillis() > 1) SystemFlags::OutputDebug(SystemFlags::debugPerformance,"In [%s::%s Line: %d] **Check if dest blocked, distance for unit [%d - %s] from [%s] to [%s] is %.2f took msecs: %lld nodeLimitReached = %d, failureCount = %d\n",__FILE__,__FUNCTION__,__LINE__,unit->getId(),unit->getFullName().c_str(), unitPos.getString().c_str(), finalPos.getString().c_str(), dist,(long long int)chrono.getMillis(),nodeLimitReached,failureCount); + if(showConsoleDebugInfo && nodeLimitReached) { + //if(showConsoleDebugInfo) { + printf("**Check if dest blocked [%d - %d], unit [%d - %s] from [%s] to [%s] distance %.2f took msecs: %lld nodeLimitReached = %d, failureCount = %d [%d]\n", + nodeLimitReached, inBailout, unit->getId(),unit->getFullName().c_str(), unitPos.getString().c_str(), finalPos.getString().c_str(), dist,(long long int)chrono.getMillis(),nodeLimitReached,failureCount,cellCount); + } + } + } + // + int whileLoopCount = 0; while(nodeLimitReached == false) { whileLoopCount++; @@ -614,7 +671,7 @@ TravelState PathFinder::aStar(Unit *unit, const Vec2i &targetPos, bool inBailout if(SystemFlags::getSystemSettingType(SystemFlags::debugPerformance).enabled == true && chrono.getMillis() > 1) SystemFlags::OutputDebug(SystemFlags::debugPerformance,"In [%s::%s Line: %d] took msecs: %lld nodeLimitReached = %d whileLoopCount = %d nodePoolCount = %d\n",__FILE__,__FUNCTION__,__LINE__,chrono.getMillis(),nodeLimitReached,whileLoopCount,nodePoolCount); if(showConsoleDebugInfo && chrono.getMillis() > 2) { - printf("Distance for unit [%d - %s] to destination is %.2f took msecs: %lld nodeLimitReached = %d whileLoopCount = %d nodePoolCount = %d\n",unit->getId(),unit->getFullName().c_str(), dist,(long long int)chrono.getMillis(),nodeLimitReached,whileLoopCount,nodePoolCount); + printf("Distance for unit [%d - %s] from [%s] to [%s] is %.2f took msecs: %lld nodeLimitReached = %d whileLoopCount = %d nodePoolCount = %d\n",unit->getId(),unit->getFullName().c_str(), unitPos.getString().c_str(), finalPos.getString().c_str(), dist,(long long int)chrono.getMillis(),nodeLimitReached,whileLoopCount,nodePoolCount); } Node *lastNode= node; diff --git a/source/glest_game/game/game.cpp b/source/glest_game/game/game.cpp index ece7b570..1f70a68d 100644 --- a/source/glest_game/game/game.cpp +++ b/source/glest_game/game/game.cpp @@ -1701,7 +1701,7 @@ void Game::render2d(){ str+= "Render FPS: " + intToStr(lastRenderFps) + "[" + intToStr(avgRenderFps) + "]\n"; str+= "Update FPS: " + intToStr(lastUpdateFps) + "[" + intToStr(avgUpdateFps) + "]\n"; str+= "GameCamera pos: " + floatToStr(gameCamera.getPos().x)+","+floatToStr(gameCamera.getPos().y)+","+floatToStr(gameCamera.getPos().z)+"\n"; - str+= "Cached surfacedata: " + intToStr(renderer.getCachedSurfaceDataSize()); + str+= "Cached surfacedata: " + intToStr(renderer.getCachedSurfaceDataSize())+"\n"; str+= "Time: " + floatToStr(world.getTimeFlow()->getTime(),2)+"\n"; if(SystemFlags::getThreadedLoggerRunning() == true) { str+= "Log buffer count: " + intToStr(SystemFlags::getLogEntryBufferCount())+"\n"; diff --git a/source/glest_game/menu/menu_state_connected_game.cpp b/source/glest_game/menu/menu_state_connected_game.cpp index 7a535aed..7ccd1ed9 100644 --- a/source/glest_game/menu/menu_state_connected_game.cpp +++ b/source/glest_game/menu/menu_state_connected_game.cpp @@ -889,6 +889,8 @@ void MenuStateConnectedGame::update() { string file = Map::getMapPath(gameSettings->getMap(),"",false); checksum.addFile(file); int32 mapCRC = checksum.getSum(); + // Test data synch + //mapCRC++; safeMutexFTPProgress.ReleaseLock(); bool dataSynchMismatch = ((mapCRC != 0 && mapCRC != gameSettings->getMapCRC()) ||