From 2466489e132195667f45380457ab77555781d063 Mon Sep 17 00:00:00 2001 From: Andrii Danylchenko Date: Sat, 16 Jun 2018 14:56:49 +0300 Subject: [PATCH 1/2] AI - improve exploration, cancel deadends --- AI/VCAI/AIUtility.cpp | 41 ++++++++++++++++++++++++----------------- AI/VCAI/AIUtility.h | 5 ++--- AI/VCAI/VCAI.cpp | 31 ++++++++++++++++--------------- 3 files changed, 42 insertions(+), 35 deletions(-) diff --git a/AI/VCAI/AIUtility.cpp b/AI/VCAI/AIUtility.cpp index d2b66a224..51232a95d 100644 --- a/AI/VCAI/AIUtility.cpp +++ b/AI/VCAI/AIUtility.cpp @@ -18,7 +18,6 @@ #include "../../lib/mapObjects/CBank.h" #include "../../lib/mapObjects/CGTownInstance.h" #include "../../lib/mapObjects/CQuest.h" -#include "../../lib/CPathfinder.h" #include "../../lib/mapping/CMapDefines.h" extern boost::thread_specific_ptr cb; @@ -431,18 +430,31 @@ bool isBlockVisitObj(const int3 & pos) return false; } -int howManyTilesWillBeDiscovered(const int3 & pos, int radious, CCallback * cbp) +bool hasReachableNeighbor(const int3 &pos, const CPathsInfo* paths, CCallback * cbp) { - //TODO: do not explore dead-end boundaries - int ret = 0; - for(int x = pos.x - radious; x <= pos.x + radious; x++) + for (crint3 dir : int3::getDirs()) { - for(int y = pos.y - radious; y <= pos.y + radious; y++) + int3 tile = pos + dir; + if (cbp->isInTheMap(tile) && paths->getPathInfo(tile)->reachable()) + { + return true; + } + } + + return false; +} + +int howManyTilesWillBeDiscovered(const int3 & pos, int radious, CCallback * cbp, const CPathsInfo* pathsInfo) +{ + int ret = 0; + for (int x = pos.x - radious; x <= pos.x + radious; x++) + { + for (int y = pos.y - radious; y <= pos.y + radious; y++) { int3 npos = int3(x, y, pos.z); - if(cbp->isInTheMap(npos) && pos.dist2d(npos) - 0.5 < radious && !cbp->isVisible(npos)) + if (cbp->isInTheMap(npos) && pos.dist2d(npos) - 0.5 < radious && !cbp->isVisible(npos)) { - if(!boundaryBetweenTwoPoints(pos, npos, cbp)) + if (hasReachableNeighbor(npos, pathsInfo, cbp)) ret++; } } @@ -458,14 +470,14 @@ bool boundaryBetweenTwoPoints(int3 pos1, int3 pos2, CCallback * cbp) //determine int yMin = std::min(pos1.y, pos2.y); int yMax = std::max(pos1.y, pos2.y); - for(int x = xMin; x <= xMax; ++x) + for (int x = xMin; x <= xMax; ++x) { - for(int y = yMin; y <= yMax; ++y) + for (int y = yMin; y <= yMax; ++y) { int3 tile = int3(x, y, pos1.z); //use only on same level, ofc - if(std::abs(pos1.dist2d(tile) - pos2.dist2d(tile)) < 1.5) + if (std::abs(pos1.dist2d(tile) - pos2.dist2d(tile)) < 1.5) { - if(!(cbp->isVisible(tile) && cbp->getTile(tile)->blocked)) //if there's invisible or unblocked tile between, it's good + if (!(cbp->isVisible(tile) && cbp->getTile(tile)->blocked)) //if there's invisible or unblocked tile between, it's good return false; } } @@ -473,11 +485,6 @@ bool boundaryBetweenTwoPoints(int3 pos1, int3 pos2, CCallback * cbp) //determine return true; //if all are visible and blocked, we're at dead end } -int howManyTilesWillBeDiscovered(int radious, int3 pos, crint3 dir) -{ - return howManyTilesWillBeDiscovered(pos + dir, radious, cb.get()); -} - void getVisibleNeighbours(const std::vector & tiles, std::vector & out) { for(const int3 & tile : tiles) diff --git a/AI/VCAI/AIUtility.h b/AI/VCAI/AIUtility.h index ef5392f53..8ed6e5a28 100644 --- a/AI/VCAI/AIUtility.h +++ b/AI/VCAI/AIUtility.h @@ -17,6 +17,7 @@ #include "../../lib/CStopWatch.h" #include "../../lib/mapObjects/CObjectHandler.h" #include "../../lib/mapObjects/CGHeroInstance.h" +#include "../../lib/CPathfinder.h" class CCallback; @@ -141,8 +142,7 @@ void foreach_tile_pos(CCallback * cbp, std::function foo); void foreach_neighbour(CCallback * cbp, const int3 & pos, std::function foo); // avoid costly retrieval of thread-specific pointer -int howManyTilesWillBeDiscovered(const int3 & pos, int radious, CCallback * cbp); -int howManyTilesWillBeDiscovered(int radious, int3 pos, crint3 dir); +int howManyTilesWillBeDiscovered(const int3 & pos, int radious, CCallback * cbp, const CPathsInfo * pathsInfo); void getVisibleNeighbours(const std::vector & tiles, std::vector & out); bool canBeEmbarkmentPoint(const TerrainTile * t, bool fromWater); @@ -155,7 +155,6 @@ bool shouldVisit(HeroPtr h, const CGObjectInstance * obj); ui64 evaluateDanger(const CGObjectInstance * obj); ui64 evaluateDanger(crint3 tile, const CGHeroInstance * visitor); bool isSafeToVisit(HeroPtr h, crint3 tile); -bool boundaryBetweenTwoPoints(int3 pos1, int3 pos2, CCallback * cbp); bool compareMovement(HeroPtr lhs, HeroPtr rhs); bool compareHeroStrength(HeroPtr h1, HeroPtr h2); diff --git a/AI/VCAI/VCAI.cpp b/AI/VCAI/VCAI.cpp index 72b441d69..b29814ff7 100644 --- a/AI/VCAI/VCAI.cpp +++ b/AI/VCAI/VCAI.cpp @@ -2625,22 +2625,21 @@ void VCAI::buildArmyIn(const CGTownInstance * t) int3 VCAI::explorationBestNeighbour(int3 hpos, int radius, HeroPtr h) { - int3 ourPos = h->convertPosition(h->pos, false); std::map dstToRevealedTiles; - for(crint3 dir : int3::getDirs()) + auto pathsInfo = cb->getPathsInfo(h.get()); + + for (crint3 dir : int3::getDirs()) { int3 tile = hpos + dir; - if(cb->isInTheMap(tile)) + if (cb->isInTheMap(tile)) { - if(ourPos != dir) //don't stand in place + if (isBlockVisitObj(tile)) + continue; + + if (isSafeToVisit(h, tile) && isAccessibleForHero(tile, h)) { - if(isSafeToVisit(h, tile) && isAccessibleForHero(tile, h)) - { - if(isBlockVisitObj(tile)) - continue; - else - dstToRevealedTiles[tile] = howManyTilesWillBeDiscovered(radius, hpos, dir); - } + auto distance = hpos.dist2d(tile); // diagonal movement opens more tiles but spends more mp + dstToRevealedTiles[tile] = howManyTilesWillBeDiscovered(tile, radius, cb.get(), pathsInfo) / distance; } } } @@ -2651,7 +2650,7 @@ int3 VCAI::explorationBestNeighbour(int3 hpos, int radius, HeroPtr h) auto best = dstToRevealedTiles.begin(); for(auto i = dstToRevealedTiles.begin(); i != dstToRevealedTiles.end(); i++) { - const CGPathNode * pn = cb->getPathsInfo(h.get())->getPathInfo(i->first); + const CGPathNode * pn = pathsInfo->getPathInfo(i->first); //const TerrainTile *t = cb->getTile(i->first); if(best->second < i->second && pn->reachable() && pn->accessible == CGPathNode::ACCESSIBLE) best = i; @@ -2668,6 +2667,7 @@ int3 VCAI::explorationNewPoint(HeroPtr h) int radius = h->getSightRadius(); CCallback * cbp = cb.get(); const CGHeroInstance * hero = h.get(); + const CPathsInfo * pathsInfo = cbp->getPathsInfo(hero); std::vector> tiles; //tiles[distance_to_fow] tiles.resize(radius); @@ -2695,8 +2695,8 @@ int3 VCAI::explorationNewPoint(HeroPtr h) continue; CGPath path; - cb->getPathsInfo(hero)->getPath(path, tile); - float ourValue = (float)howManyTilesWillBeDiscovered(tile, radius, cbp) / (path.nodes.size() + 1); //+1 prevents erratic jumps + pathsInfo->getPath(path, tile); + float ourValue = (float)howManyTilesWillBeDiscovered(tile, radius, cbp, pathsInfo) / (path.nodes.size() + 1); //+1 prevents erratic jumps if(ourValue > bestValue) //avoid costly checks of tiles that don't reveal much { @@ -2722,6 +2722,7 @@ int3 VCAI::explorationDesperate(HeroPtr h) tiles.resize(radius); CCallback * cbp = cb.get(); + const CPathsInfo * pathsInfo = cbp->getPathsInfo(h.get()); foreach_tile_pos([&](const int3 & pos) { @@ -2741,7 +2742,7 @@ int3 VCAI::explorationDesperate(HeroPtr h) { if(cbp->getTile(tile)->blocked) //does it shorten the time? continue; - if(!howManyTilesWillBeDiscovered(tile, radius, cbp)) //avoid costly checks of tiles that don't reveal much + if(!howManyTilesWillBeDiscovered(tile, radius, cbp, pathsInfo)) //avoid costly checks of tiles that don't reveal much continue; auto t = sm->firstTileToGet(h, tile); From 12a3c7ed33ccd003be36c9db410defe251e1ce73 Mon Sep 17 00:00:00 2001 From: Andrii Danylchenko Date: Sun, 24 Jun 2018 13:30:17 +0300 Subject: [PATCH 2/2] AI - code styles and remove redundant performance optimization for explore --- AI/VCAI/AIUtility.cpp | 38 ++++++++------------------------------ AI/VCAI/AIUtility.h | 2 +- AI/VCAI/VCAI.cpp | 21 +++++++++------------ 3 files changed, 18 insertions(+), 43 deletions(-) diff --git a/AI/VCAI/AIUtility.cpp b/AI/VCAI/AIUtility.cpp index 51232a95d..de3a83efd 100644 --- a/AI/VCAI/AIUtility.cpp +++ b/AI/VCAI/AIUtility.cpp @@ -430,12 +430,12 @@ bool isBlockVisitObj(const int3 & pos) return false; } -bool hasReachableNeighbor(const int3 &pos, const CPathsInfo* paths, CCallback * cbp) +bool hasReachableNeighbor(const int3 &pos, HeroPtr hero, CCallback * cbp) { - for (crint3 dir : int3::getDirs()) + for(crint3 dir : int3::getDirs()) { int3 tile = pos + dir; - if (cbp->isInTheMap(tile) && paths->getPathInfo(tile)->reachable()) + if(cbp->isInTheMap(tile) && cbp->getPathsInfo(hero.get())->getPathInfo(tile)->reachable()) { return true; } @@ -444,17 +444,17 @@ bool hasReachableNeighbor(const int3 &pos, const CPathsInfo* paths, CCallback * return false; } -int howManyTilesWillBeDiscovered(const int3 & pos, int radious, CCallback * cbp, const CPathsInfo* pathsInfo) +int howManyTilesWillBeDiscovered(const int3 & pos, int radious, CCallback * cbp, HeroPtr hero) { int ret = 0; - for (int x = pos.x - radious; x <= pos.x + radious; x++) + for(int x = pos.x - radious; x <= pos.x + radious; x++) { - for (int y = pos.y - radious; y <= pos.y + radious; y++) + for(int y = pos.y - radious; y <= pos.y + radious; y++) { int3 npos = int3(x, y, pos.z); - if (cbp->isInTheMap(npos) && pos.dist2d(npos) - 0.5 < radious && !cbp->isVisible(npos)) + if(cbp->isInTheMap(npos) && pos.dist2d(npos) - 0.5 < radious && !cbp->isVisible(npos)) { - if (hasReachableNeighbor(npos, pathsInfo, cbp)) + if(hasReachableNeighbor(npos, hero, cbp)) ret++; } } @@ -463,28 +463,6 @@ int howManyTilesWillBeDiscovered(const int3 & pos, int radious, CCallback * cbp, return ret; } -bool boundaryBetweenTwoPoints(int3 pos1, int3 pos2, CCallback * cbp) //determines if two points are separated by known barrier -{ - int xMin = std::min(pos1.x, pos2.x); - int xMax = std::max(pos1.x, pos2.x); - int yMin = std::min(pos1.y, pos2.y); - int yMax = std::max(pos1.y, pos2.y); - - for (int x = xMin; x <= xMax; ++x) - { - for (int y = yMin; y <= yMax; ++y) - { - int3 tile = int3(x, y, pos1.z); //use only on same level, ofc - if (std::abs(pos1.dist2d(tile) - pos2.dist2d(tile)) < 1.5) - { - if (!(cbp->isVisible(tile) && cbp->getTile(tile)->blocked)) //if there's invisible or unblocked tile between, it's good - return false; - } - } - } - return true; //if all are visible and blocked, we're at dead end -} - void getVisibleNeighbours(const std::vector & tiles, std::vector & out) { for(const int3 & tile : tiles) diff --git a/AI/VCAI/AIUtility.h b/AI/VCAI/AIUtility.h index 8ed6e5a28..dbf45c84b 100644 --- a/AI/VCAI/AIUtility.h +++ b/AI/VCAI/AIUtility.h @@ -142,7 +142,7 @@ void foreach_tile_pos(CCallback * cbp, std::function foo); void foreach_neighbour(CCallback * cbp, const int3 & pos, std::function foo); // avoid costly retrieval of thread-specific pointer -int howManyTilesWillBeDiscovered(const int3 & pos, int radious, CCallback * cbp, const CPathsInfo * pathsInfo); +int howManyTilesWillBeDiscovered(const int3 & pos, int radious, CCallback * cbp, HeroPtr hero); void getVisibleNeighbours(const std::vector & tiles, std::vector & out); bool canBeEmbarkmentPoint(const TerrainTile * t, bool fromWater); diff --git a/AI/VCAI/VCAI.cpp b/AI/VCAI/VCAI.cpp index b29814ff7..937f46938 100644 --- a/AI/VCAI/VCAI.cpp +++ b/AI/VCAI/VCAI.cpp @@ -2626,20 +2626,19 @@ void VCAI::buildArmyIn(const CGTownInstance * t) int3 VCAI::explorationBestNeighbour(int3 hpos, int radius, HeroPtr h) { std::map dstToRevealedTiles; - auto pathsInfo = cb->getPathsInfo(h.get()); - for (crint3 dir : int3::getDirs()) + for(crint3 dir : int3::getDirs()) { int3 tile = hpos + dir; - if (cb->isInTheMap(tile)) + if(cb->isInTheMap(tile)) { - if (isBlockVisitObj(tile)) + if(isBlockVisitObj(tile)) continue; - if (isSafeToVisit(h, tile) && isAccessibleForHero(tile, h)) + if(isSafeToVisit(h, tile) && isAccessibleForHero(tile, h)) { auto distance = hpos.dist2d(tile); // diagonal movement opens more tiles but spends more mp - dstToRevealedTiles[tile] = howManyTilesWillBeDiscovered(tile, radius, cb.get(), pathsInfo) / distance; + dstToRevealedTiles[tile] = howManyTilesWillBeDiscovered(tile, radius, cb.get(), h) / distance; } } } @@ -2650,7 +2649,7 @@ int3 VCAI::explorationBestNeighbour(int3 hpos, int radius, HeroPtr h) auto best = dstToRevealedTiles.begin(); for(auto i = dstToRevealedTiles.begin(); i != dstToRevealedTiles.end(); i++) { - const CGPathNode * pn = pathsInfo->getPathInfo(i->first); + const CGPathNode * pn = cb->getPathsInfo(h.get())->getPathInfo(i->first); //const TerrainTile *t = cb->getTile(i->first); if(best->second < i->second && pn->reachable() && pn->accessible == CGPathNode::ACCESSIBLE) best = i; @@ -2667,7 +2666,6 @@ int3 VCAI::explorationNewPoint(HeroPtr h) int radius = h->getSightRadius(); CCallback * cbp = cb.get(); const CGHeroInstance * hero = h.get(); - const CPathsInfo * pathsInfo = cbp->getPathsInfo(hero); std::vector> tiles; //tiles[distance_to_fow] tiles.resize(radius); @@ -2695,8 +2693,8 @@ int3 VCAI::explorationNewPoint(HeroPtr h) continue; CGPath path; - pathsInfo->getPath(path, tile); - float ourValue = (float)howManyTilesWillBeDiscovered(tile, radius, cbp, pathsInfo) / (path.nodes.size() + 1); //+1 prevents erratic jumps + cb->getPathsInfo(hero)->getPath(path, tile); + float ourValue = (float)howManyTilesWillBeDiscovered(tile, radius, cbp, h) / (path.nodes.size() + 1); //+1 prevents erratic jumps if(ourValue > bestValue) //avoid costly checks of tiles that don't reveal much { @@ -2722,7 +2720,6 @@ int3 VCAI::explorationDesperate(HeroPtr h) tiles.resize(radius); CCallback * cbp = cb.get(); - const CPathsInfo * pathsInfo = cbp->getPathsInfo(h.get()); foreach_tile_pos([&](const int3 & pos) { @@ -2742,7 +2739,7 @@ int3 VCAI::explorationDesperate(HeroPtr h) { if(cbp->getTile(tile)->blocked) //does it shorten the time? continue; - if(!howManyTilesWillBeDiscovered(tile, radius, cbp, pathsInfo)) //avoid costly checks of tiles that don't reveal much + if(!howManyTilesWillBeDiscovered(tile, radius, cbp, h)) //avoid costly checks of tiles that don't reveal much continue; auto t = sm->firstTileToGet(h, tile);