浏览代码

AI goals refactoring (#524)

Andrii Danylchenko 6 年之前
父节点
当前提交
e1b922f7b8
共有 62 个文件被更改,包括 3236 次插入2380 次删除
  1. 1 0
      AI/VCAI/AIUtility.cpp
  2. 43 2
      AI/VCAI/CMakeLists.txt
  3. 1 0
      AI/VCAI/FuzzyEngines.cpp
  4. 1 1
      AI/VCAI/FuzzyEngines.h
  5. 1 1
      AI/VCAI/FuzzyHelper.cpp
  6. 0 1663
      AI/VCAI/Goals.cpp
  7. 0 698
      AI/VCAI/Goals.h
  8. 192 0
      AI/VCAI/Goals/AbstractGoal.cpp
  9. 193 0
      AI/VCAI/Goals/AbstractGoal.h
  10. 93 0
      AI/VCAI/Goals/Build.cpp
  11. 37 0
      AI/VCAI/Goals/Build.h
  12. 84 0
      AI/VCAI/Goals/BuildBoat.cpp
  13. 37 0
      AI/VCAI/Goals/BuildBoat.h
  14. 74 0
      AI/VCAI/Goals/BuildThis.cpp
  15. 48 0
      AI/VCAI/Goals/BuildThis.h
  16. 45 0
      AI/VCAI/Goals/BuyArmy.cpp
  17. 41 0
      AI/VCAI/Goals/BuyArmy.h
  18. 87 0
      AI/VCAI/Goals/CGoal.h
  19. 85 0
      AI/VCAI/Goals/ClearWayTo.cpp
  20. 45 0
      AI/VCAI/Goals/ClearWayTo.h
  21. 208 0
      AI/VCAI/Goals/CollectRes.cpp
  22. 40 0
      AI/VCAI/Goals/CollectRes.h
  23. 88 0
      AI/VCAI/Goals/Conquer.cpp
  24. 32 0
      AI/VCAI/Goals/Conquer.h
  25. 39 0
      AI/VCAI/Goals/DigAtTile.cpp
  26. 41 0
      AI/VCAI/Goals/DigAtTile.h
  27. 181 0
      AI/VCAI/Goals/Explore.cpp
  28. 40 0
      AI/VCAI/Goals/Explore.h
  29. 70 0
      AI/VCAI/Goals/FindObj.cpp
  30. 47 0
      AI/VCAI/Goals/FindObj.h
  31. 196 0
      AI/VCAI/Goals/GatherArmy.cpp
  32. 38 0
      AI/VCAI/Goals/GatherArmy.h
  33. 139 0
      AI/VCAI/Goals/GatherTroops.cpp
  34. 43 0
      AI/VCAI/Goals/GatherTroops.h
  35. 31 0
      AI/VCAI/Goals/GetArtOfType.cpp
  36. 40 0
      AI/VCAI/Goals/GetArtOfType.h
  37. 32 0
      AI/VCAI/Goals/Goals.h
  38. 41 0
      AI/VCAI/Goals/Invalid.h
  39. 38 0
      AI/VCAI/Goals/RecruitHero.cpp
  40. 41 0
      AI/VCAI/Goals/RecruitHero.h
  41. 23 0
      AI/VCAI/Goals/Trade.cpp
  42. 38 0
      AI/VCAI/Goals/Trade.h
  43. 74 0
      AI/VCAI/Goals/VisitHero.cpp
  44. 42 0
      AI/VCAI/Goals/VisitHero.h
  45. 112 0
      AI/VCAI/Goals/VisitObj.cpp
  46. 32 0
      AI/VCAI/Goals/VisitObj.h
  47. 98 0
      AI/VCAI/Goals/VisitTile.cpp
  48. 37 0
      AI/VCAI/Goals/VisitTile.h
  49. 191 0
      AI/VCAI/Goals/Win.cpp
  50. 39 0
      AI/VCAI/Goals/Win.h
  51. 1 1
      AI/VCAI/Pathfinding/AINodeStorage.cpp
  52. 2 2
      AI/VCAI/Pathfinding/AINodeStorage.h
  53. 2 2
      AI/VCAI/Pathfinding/AIPathfinder.cpp
  54. 1 1
      AI/VCAI/Pathfinding/AIPathfinder.h
  55. 2 1
      AI/VCAI/Pathfinding/AIPathfinderConfig.cpp
  56. 2 2
      AI/VCAI/Pathfinding/AIPathfinderConfig.h
  57. 3 2
      AI/VCAI/Pathfinding/PathfindingManager.cpp
  58. 1 1
      AI/VCAI/Pathfinding/PathfindingManager.h
  59. 1 0
      AI/VCAI/ResourceManager.cpp
  60. 0 1
      AI/VCAI/ResourceManager.h
  61. 1 0
      AI/VCAI/VCAI.cpp
  62. 1 2
      AI/VCAI/VCAI.h

+ 1 - 0
AI/VCAI/AIUtility.cpp

@@ -11,6 +11,7 @@
 #include "AIUtility.h"
 #include "VCAI.h"
 #include "FuzzyHelper.h"
+#include "Goals/Goals.h"
 
 #include "../../lib/UnlockGuard.h"
 #include "../../lib/CConfigHandler.h"

+ 43 - 2
AI/VCAI/CMakeLists.txt

@@ -21,7 +21,26 @@ set(VCAI_SRCS
 		MapObjectsEvaluator.cpp
 		FuzzyEngines.cpp
 		FuzzyHelper.cpp
-		Goals.cpp
+		Goals/AbstractGoal.cpp
+		Goals/BuildBoat.cpp
+		Goals/Build.cpp
+		Goals/BuildThis.cpp
+		Goals/Explore.cpp
+		Goals/GatherArmy.cpp
+		Goals/GatherTroops.cpp
+		Goals/BuyArmy.cpp
+		Goals/Win.cpp
+		Goals/VisitTile.cpp
+		Goals/VisitObj.cpp
+		Goals/VisitHero.cpp
+		Goals/CollectRes.cpp
+		Goals/Trade.cpp
+		Goals/RecruitHero.cpp
+		Goals/Conquer.cpp
+		Goals/ClearWayTo.cpp
+		Goals/DigAtTile.cpp
+		Goals/GetArtOfType.cpp
+		Goals/FindObj.cpp
 		main.cpp
 		VCAI.cpp
 )
@@ -42,7 +61,29 @@ set(VCAI_HEADERS
 		MapObjectsEvaluator.h
 		FuzzyEngines.h
 		FuzzyHelper.h
-		Goals.h
+		Goals/AbstractGoal.h
+		Goals/CGoal.h
+		Goals/Invalid.h
+		Goals/BuildBoat.h
+		Goals/Build.h
+		Goals/BuildThis.h
+		Goals/Explore.h
+		Goals/GatherArmy.h
+		Goals/GatherTroops.h
+		Goals/BuyArmy.h
+		Goals/Win.h
+		Goals/VisitTile.h
+		Goals/VisitObj.h
+		Goals/VisitHero.h
+		Goals/CollectRes.h
+		Goals/Trade.h
+		Goals/RecruitHero.h
+		Goals/Conquer.h
+		Goals/ClearWayTo.h
+		Goals/DigAtTile.h
+		Goals/GetArtOfType.h
+		Goals/FindObj.h
+		Goals/Goals.h
 		VCAI.h
 )
 

+ 1 - 0
AI/VCAI/FuzzyEngines.cpp

@@ -9,6 +9,7 @@
 */
 #include "StdInc.h"
 #include "FuzzyEngines.h"
+#include "Goals/Goals.h"
 
 #include "../../lib/mapObjects/MapObjects.h"
 #include "VCAI.h"

+ 1 - 1
AI/VCAI/FuzzyEngines.h

@@ -9,7 +9,7 @@
 */
 #pragma once
 #include "fl/Headers.h"
-#include "Goals.h"
+#include "Goals/AbstractGoal.h"
 
 class CArmedInstance;
 

+ 1 - 1
AI/VCAI/FuzzyHelper.cpp

@@ -11,7 +11,7 @@
 #include "FuzzyHelper.h"
 
 #include "../../lib/mapObjects/CommonConstructors.h"
-#include "Goals.h"
+#include "Goals/Goals.h"
 #include "VCAI.h"
 
 FuzzyHelper * fh;

+ 0 - 1663
AI/VCAI/Goals.cpp

@@ -1,1663 +0,0 @@
-/*
- * Goals.cpp, part of VCMI engine
- *
- * Authors: listed in file AUTHORS in main folder
- *
- * License: GNU General Public License v2.0 or later
- * Full text of license available in license.txt file, in main folder
- *
- */
-#include "StdInc.h"
-#include "Goals.h"
-#include "VCAI.h"
-#include "FuzzyHelper.h"
-#include "ResourceManager.h"
-#include "BuildingManager.h"
-#include "../../lib/mapping/CMap.h" //for victory conditions
-#include "../../lib/CPathfinder.h"
-#include "../../lib/StringConstants.h"
-
-#include "AIhelper.h"
-
-extern boost::thread_specific_ptr<CCallback> cb;
-extern boost::thread_specific_ptr<VCAI> ai;
-extern FuzzyHelper * fh;
-
-using namespace Goals;
-
-TSubgoal Goals::sptr(const AbstractGoal & tmp)
-{
-	TSubgoal ptr;
-	ptr.reset(tmp.clone());
-	return ptr;
-}
-
-std::string Goals::AbstractGoal::name() const //TODO: virtualize
-{
-	std::string desc;
-	switch(goalType)
-	{
-	case INVALID:
-		return "INVALID";
-	case WIN:
-		return "WIN";
-	case DO_NOT_LOSE:
-		return "DO NOT LOOSE";
-	case CONQUER:
-		return "CONQUER";
-	case BUILD:
-		return "BUILD";
-	case EXPLORE:
-		desc = "EXPLORE";
-		break;
-	case GATHER_ARMY:
-		desc = "GATHER ARMY";
-		break;
-	case BUY_ARMY:
-		return "BUY ARMY";
-		break;
-	case BOOST_HERO:
-		desc = "BOOST_HERO (unsupported)";
-		break;
-	case RECRUIT_HERO:
-		return "RECRUIT HERO";
-	case BUILD_STRUCTURE:
-		return "BUILD STRUCTURE";
-	case COLLECT_RES:
-		desc = "COLLECT RESOURCE " + GameConstants::RESOURCE_NAMES[resID] + " (" + boost::lexical_cast<std::string>(value) + ")";
-		break;
-	case TRADE:
-	{
-		auto obj = cb->getObjInstance(ObjectInstanceID(objid));
-		if (obj)
-			desc = (boost::format("TRADE %d of %s at %s") % value % GameConstants::RESOURCE_NAMES[resID] % obj->getObjectName()).str();
-	}
-	break;
-	case GATHER_TROOPS:
-		desc = "GATHER TROOPS";
-		break;
-	case VISIT_OBJ:
-	{
-		auto obj = cb->getObjInstance(ObjectInstanceID(objid));
-		if(obj)
-			desc = "GET OBJ " + obj->getObjectName();
-	}
-	break;
-	case FIND_OBJ:
-		desc = "FIND OBJ " + boost::lexical_cast<std::string>(objid);
-		break;
-	case VISIT_HERO:
-	{
-		auto obj = cb->getObjInstance(ObjectInstanceID(objid));
-		if(obj)
-			desc = "VISIT HERO " + obj->getObjectName();
-	}
-	break;
-	case GET_ART_TYPE:
-		desc = "GET ARTIFACT OF TYPE " + VLC->arth->artifacts[aid]->Name();
-		break;
-	case ISSUE_COMMAND:
-		return "ISSUE COMMAND (unsupported)";
-	case VISIT_TILE:
-		desc = "VISIT TILE " + tile.toString();
-		break;
-	case CLEAR_WAY_TO:
-		desc = "CLEAR WAY TO " + tile.toString();
-		break;
-	case DIG_AT_TILE:
-		desc = "DIG AT TILE " + tile.toString();
-		break;
-	default:
-		return boost::lexical_cast<std::string>(goalType);
-	}
-	if(hero.get(true)) //FIXME: used to crash when we lost hero and failed goal
-		desc += " (" + hero->name + ")";
-	return desc;
-}
-
-bool Goals::BuildBoat::operator==(const BuildBoat & other) const
-{
-	return shipyard->o->id == other.shipyard->o->id;
-}
-
-bool Goals::Explore::operator==(const Explore & other) const
-{
-	return other.hero.h == hero.h;
-}
-
-bool Goals::Conquer::operator==(const Conquer & other) const
-{
-	return other.hero.h == hero.h;
-}
-
-bool Goals::GatherArmy::operator==(const GatherArmy & other) const
-{
-	return other.hero.h == hero.h || town == other.town;
-}
-
-bool Goals::BuyArmy::operator==(const BuyArmy & other) const
-{
-	return town == other.town && objid == other.objid;
-}
-
-bool Goals::BoostHero::operator==(const BoostHero & other) const
-{
-	return other.hero.h == hero.h;
-}
-
-bool Goals::BuildThis::operator==(const BuildThis & other) const
-{
-	return town == other.town && bid == other.bid;
-}
-
-bool Goals::CollectRes::operator==(const CollectRes & other) const
-{
-	return resID == other.resID;
-}
-
-bool Goals::Trade::operator==(const Trade & other) const
-{
-	return resID == other.resID;
-}
-
-bool Goals::GatherTroops::operator==(const GatherTroops & other) const
-{
-	return objid == other.objid;
-}
-
-bool Goals::VisitObj::operator==(const VisitObj & other) const
-{
-	return other.hero.h == hero.h && other.objid == objid;
-}
-
-bool Goals::FindObj::operator==(const FindObj & other) const
-{
-	return other.hero.h == hero.h && other.objid == objid;
-}
-
-bool Goals::VisitHero::operator==(const VisitHero & other) const
-{
-	return other.hero.h == hero.h && other.objid == objid;
-}
-
-bool Goals::GetArtOfType::operator==(const GetArtOfType & other) const
-{
-	return other.hero.h == hero.h && other.objid == objid;
-}
-
-bool Goals::VisitTile::operator==(const VisitTile & other) const
-{
-	return other.hero.h == hero.h && other.tile == tile;
-}
-
-bool Goals::ClearWayTo::operator==(const ClearWayTo & other) const
-{
-	return other.hero.h == hero.h && other.tile == tile;
-}
-
-bool Goals::DigAtTile::operator==(const DigAtTile & other) const
-{
-	return other.hero.h == hero.h && other.tile == tile;
-}
-
-bool Goals::AbstractGoal::operator==(const AbstractGoal & g) const
-{
-	return false;
-}
-
-bool Goals::AbstractGoal::operator<(AbstractGoal & g) //for std::unique
-{
-	//TODO: make sure it gets goals consistent with == operator
-	if (goalType < g.goalType)
-		return true;
-	if (goalType > g.goalType)
-		return false;
-	if (hero < g.hero)
-		return true;
-	if (hero > g.hero)
-		return false;
-	if (tile < g.tile)
-		return true;
-	if (g.tile < tile)
-		return false;
-	if (objid < g.objid)
-		return true;
-	if (objid > g.objid)
-		return false;
-	if (town < g.town)
-		return true;
-	if (town > g.town)
-		return false;
-	if (value < g.value)
-		return true;
-	if (value > g.value)
-		return false;
-	if (priority < g.priority)
-		return true;
-	if (priority > g.priority)
-		return false;
-	if (resID < g.resID)
-		return true;
-	if (resID > g.resID)
-		return false;
-	if (bid < g.bid)
-		return true;
-	if (bid > g.bid)
-		return false;
-	if (aid < g.aid)
-		return true;
-	if (aid > g.aid)
-		return false;
-	return false;
-}
-
-//TODO: find out why the following are not generated automatically on MVS?
-
-namespace Goals
-{
-	template<>
-	void CGoal<Win>::accept(VCAI * ai)
-	{
-		ai->tryRealize(static_cast<Win &>(*this));
-	}
-
-	template<>
-	void CGoal<Build>::accept(VCAI * ai)
-	{
-		ai->tryRealize(static_cast<Build &>(*this));
-	}
-	template<>
-	float CGoal<Win>::accept(FuzzyHelper * f)
-	{
-		return f->evaluate(static_cast<Win &>(*this));
-	}
-
-	template<>
-	float CGoal<Build>::accept(FuzzyHelper * f)
-	{
-		return f->evaluate(static_cast<Build &>(*this));
-	}
-	bool TSubgoal::operator==(const TSubgoal & rhs) const
-	{
-		return *get() == *rhs.get(); //comparison for Goals is overloaded, so they don't need to be identical to match
-	}
-
-	bool TSubgoal::operator<(const TSubgoal & rhs) const
-	{
-		return get() < rhs.get(); //compae by value
-	}
-
-	bool BuyArmy::fulfillsMe(TSubgoal goal)
-	{
-		//if (hero && hero != goal->hero)
-		//	return false;
-		return town == goal->town && goal->value >= value; //can always buy more army
-	}
-	TSubgoal BuyArmy::whatToDoToAchieve()
-	{
-		//TODO: calculate the actual cost of units instead
-		TResources price;
-		price[Res::GOLD] = value * 0.4f; //some approximate value
-		return ai->ah->whatToDo(price, iAmElementar()); //buy right now or gather resources
-	}
-	std::string BuyArmy::completeMessage() const
-	{
-		return boost::format("Bought army of value %d in town of %s") % value, town->name;
-	}
-}
-
-TSubgoal Trade::whatToDoToAchieve()
-{
-	return iAmElementar();
-}
-
-//TSubgoal AbstractGoal::whatToDoToAchieve()
-//{
-//	logAi->debug("Decomposing goal of type %s",name());
-//	return sptr (Goals::Explore());
-//}
-
-TSubgoal Win::whatToDoToAchieve()
-{
-	auto toBool = [=](const EventCondition &)
-	{
-		// TODO: proper implementation
-		// Right now even already fulfilled goals will be included into generated list
-		// Proper check should test if event condition is already fulfilled
-		// Easiest way to do this is to call CGameState::checkForVictory but this function should not be
-		// used on client side or in AI code
-		return false;
-	};
-
-	std::vector<EventCondition> goals;
-
-	for(const TriggeredEvent & event : cb->getMapHeader()->triggeredEvents)
-	{
-		//TODO: try to eliminate human player(s) using loss conditions that have isHuman element
-
-		if(event.effect.type == EventEffect::VICTORY)
-		{
-			boost::range::copy(event.trigger.getFulfillmentCandidates(toBool), std::back_inserter(goals));
-		}
-	}
-
-	//TODO: instead of returning first encountered goal AI should generate list of possible subgoals
-	for(const EventCondition & goal : goals)
-	{
-		switch(goal.condition)
-		{
-		case EventCondition::HAVE_ARTIFACT:
-			return sptr(Goals::GetArtOfType(goal.objectType));
-		case EventCondition::DESTROY:
-		{
-			if(goal.object)
-			{
-				auto obj = cb->getObj(goal.object->id);
-				if(obj)
-					if(obj->getOwner() == ai->playerID) //we can't capture our own object
-						return sptr(Goals::Conquer());
-
-
-				return sptr(Goals::VisitObj(goal.object->id.getNum()));
-			}
-			else
-			{
-				// TODO: destroy all objects of type goal.objectType
-				// This situation represents "kill all creatures" condition from H3
-				break;
-			}
-		}
-		case EventCondition::HAVE_BUILDING:
-		{
-			// TODO build other buildings apart from Grail
-			// goal.objectType = buidingID to build
-			// goal.object = optional, town in which building should be built
-			// Represents "Improve town" condition from H3 (but unlike H3 it consists from 2 separate conditions)
-
-			if(goal.objectType == BuildingID::GRAIL)
-			{
-				if(auto h = ai->getHeroWithGrail())
-				{
-					//hero is in a town that can host Grail
-					if(h->visitedTown && !vstd::contains(h->visitedTown->forbiddenBuildings, BuildingID::GRAIL))
-					{
-						const CGTownInstance * t = h->visitedTown;
-						return sptr(Goals::BuildThis(BuildingID::GRAIL, t).setpriority(10));
-					}
-					else
-					{
-						auto towns = cb->getTownsInfo();
-						towns.erase(boost::remove_if(towns,
-									     [](const CGTownInstance * t) -> bool
-							{
-								return vstd::contains(t->forbiddenBuildings, BuildingID::GRAIL);
-							}),
-							    towns.end());
-						boost::sort(towns, CDistanceSorter(h.get()));
-						if(towns.size())
-						{
-							return sptr(Goals::VisitTile(towns.front()->visitablePos()).sethero(h));
-						}
-					}
-				}
-				double ratio = 0;
-				// maybe make this check a bit more complex? For example:
-				// 0.75 -> dig randomly within 3 tiles radius
-				// 0.85 -> radius now 2 tiles
-				// 0.95 -> 1 tile radius, position is fully known
-				// AFAIK H3 AI does something like this
-				int3 grailPos = cb->getGrailPos(&ratio);
-				if(ratio > 0.99)
-				{
-					return sptr(Goals::DigAtTile(grailPos));
-				} //TODO: use FIND_OBJ
-				else if(const CGObjectInstance * obj = ai->getUnvisitedObj(objWithID<Obj::OBELISK>)) //there are unvisited Obelisks
-					return sptr(Goals::VisitObj(obj->id.getNum()));
-				else
-					return sptr(Goals::Explore());
-			}
-			break;
-		}
-		case EventCondition::CONTROL:
-		{
-			if(goal.object)
-			{
-				auto objRelations = cb->getPlayerRelations(ai->playerID, goal.object->tempOwner);
-				
-				if(objRelations == PlayerRelations::ENEMIES)
-				{
-					return sptr(Goals::VisitObj(goal.object->id.getNum()));
-				}
-				else
-				{
-					// TODO: Defance
-					break;
-				}
-			}
-			else
-			{
-				//TODO: control all objects of type "goal.objectType"
-				// Represents H3 condition "Flag all mines"
-				break;
-			}
-		}
-
-		case EventCondition::HAVE_RESOURCES:
-			//TODO mines? piles? marketplace?
-			//save?
-			return sptr(Goals::CollectRes(static_cast<Res::ERes>(goal.objectType), goal.value));
-		case EventCondition::HAVE_CREATURES:
-			return sptr(Goals::GatherTroops(goal.objectType, goal.value));
-		case EventCondition::TRANSPORT:
-		{
-			//TODO. merge with bring Grail to town? So AI will first dig grail, then transport it using this goal and builds it
-			// Represents "transport artifact" condition:
-			// goal.objectType = type of artifact
-			// goal.object = destination-town where artifact should be transported
-			break;
-		}
-		case EventCondition::STANDARD_WIN:
-			return sptr(Goals::Conquer());
-
-		// Conditions that likely don't need any implementation
-		case EventCondition::DAYS_PASSED:
-			break; // goal.value = number of days for condition to trigger
-		case EventCondition::DAYS_WITHOUT_TOWN:
-			break; // goal.value = number of days to trigger this
-		case EventCondition::IS_HUMAN:
-			break; // Should be only used in calculation of candidates (see toBool lambda)
-		case EventCondition::CONST_VALUE:
-			break;
-
-		case EventCondition::HAVE_0:
-		case EventCondition::HAVE_BUILDING_0:
-		case EventCondition::DESTROY_0:
-			//TODO: support new condition format
-			return sptr(Goals::Conquer());
-		default:
-			assert(0);
-		}
-	}
-	return sptr(Goals::Invalid());
-}
-
-TSubgoal BuildBoat::whatToDoToAchieve()
-{
-	if(cb->getPlayerRelations(ai->playerID, shipyard->o->tempOwner) == PlayerRelations::ENEMIES)
-	{
-		return fh->chooseSolution(ai->ah->howToVisitObj(shipyard->o));
-	}
-
-	if(shipyard->shipyardStatus() != IShipyard::GOOD)
-	{
-		throw cannotFulfillGoalException("Shipyard is busy.");
-	}
-
-	TResources boatCost;
-	shipyard->getBoatCost(boatCost);
-
-	return ai->ah->whatToDo(boatCost, this->iAmElementar());
-}
-
-void BuildBoat::accept(VCAI * ai)
-{
-	TResources boatCost;
-	shipyard->getBoatCost(boatCost);
-
-	if(!cb->getResourceAmount().canAfford(boatCost))
-	{
-		throw cannotFulfillGoalException("Can not afford boat");
-	}
-
-	if(cb->getPlayerRelations(ai->playerID, shipyard->o->tempOwner) == PlayerRelations::ENEMIES)
-	{
-		throw cannotFulfillGoalException("Can not build boat in enemy shipyard");
-	}
-
-	if(shipyard->shipyardStatus() != IShipyard::GOOD)
-	{
-		throw cannotFulfillGoalException("Shipyard is busy.");
-	}
-
-	cb->buildBoat(shipyard);
-}
-
-std::string BuildBoat::name() const
-{
-	return "BuildBoat";
-}
-
-std::string BuildBoat::completeMessage() const
-{
-	return "Boat have been built at " + shipyard->o->visitablePos().toString();
-}
-
-TSubgoal FindObj::whatToDoToAchieve()
-{
-	const CGObjectInstance * o = nullptr;
-	if(resID > -1) //specified
-	{
-		for(const CGObjectInstance * obj : ai->visitableObjs)
-		{
-			if(obj->ID == objid && obj->subID == resID)
-			{
-				o = obj;
-				break; //TODO: consider multiple objects and choose best
-			}
-		}
-	}
-	else
-	{
-		for(const CGObjectInstance * obj : ai->visitableObjs)
-		{
-			if(obj->ID == objid)
-			{
-				o = obj;
-				break; //TODO: consider multiple objects and choose best
-			}
-		}
-	}
-	if(o && ai->isAccessible(o->pos)) //we don't use isAccessibleForHero as we don't know which hero it is
-		return sptr(Goals::VisitObj(o->id.getNum()));
-	else
-		return sptr(Goals::Explore());
-}
-
-bool Goals::FindObj::fulfillsMe(TSubgoal goal)
-{
-	if (goal->goalType == Goals::VISIT_TILE) //visiting tile visits object at same time
-	{
-		if (!hero || hero == goal->hero)
-			for (auto obj : cb->getVisitableObjs(goal->tile)) //check if any object on that tile matches criteria
-				if (obj->visitablePos() == goal->tile) //object could be removed
-					if (obj->ID == objid && obj->subID == resID) //same type and subtype
-						return true;
-	}
-	return false;
-}
-
-std::string VisitObj::completeMessage() const
-{
-	return "hero " + hero.get()->name + " captured Object ID = " + boost::lexical_cast<std::string>(objid);
-}
-
-TGoalVec VisitObj::getAllPossibleSubgoals()
-{
-	TGoalVec goalList;
-	const CGObjectInstance * obj = cb->getObjInstance(ObjectInstanceID(objid));
-	if(!obj)
-	{
-		throw cannotFulfillGoalException("Object is missing - goal is invalid now!");
-	}
-
-	int3 pos = obj->visitablePos();
-	if(hero)
-	{
-		if(ai->isAccessibleForHero(pos, hero))
-		{
-			if(isSafeToVisit(hero, pos))
-				goalList.push_back(sptr(Goals::VisitObj(obj->id.getNum()).sethero(hero)));
-			else
-				goalList.push_back(sptr(Goals::GatherArmy(evaluateDanger(pos, hero.h) * SAFE_ATTACK_CONSTANT).sethero(hero).setisAbstract(true)));
-
-			return goalList;
-		}
-	}
-	else
-	{
-		for(auto potentialVisitor : cb->getHeroesInfo())
-		{
-			if(ai->isAccessibleForHero(pos, potentialVisitor))
-			{
-				if(isSafeToVisit(potentialVisitor, pos))
-					goalList.push_back(sptr(Goals::VisitObj(obj->id.getNum()).sethero(potentialVisitor)));
-				else
-					goalList.push_back(sptr(Goals::GatherArmy(evaluateDanger(pos, potentialVisitor) * SAFE_ATTACK_CONSTANT).sethero(potentialVisitor).setisAbstract(true)));
-			}
-		}
-		if(!goalList.empty())
-		{
-			return goalList;
-		}
-	}
-
-	goalList.push_back(sptr(Goals::ClearWayTo(pos)));
-	return goalList;
-}
-
-TSubgoal VisitObj::whatToDoToAchieve()
-{
-	auto bestGoal = fh->chooseSolution(getAllPossibleSubgoals());
-
-	if(bestGoal->goalType == Goals::VISIT_OBJ && bestGoal->hero)
-		bestGoal->setisElementar(true);
-
-	return bestGoal;
-}
-
-Goals::VisitObj::VisitObj(int Objid) : CGoal(Goals::VISIT_OBJ)
-{
-	objid = Objid;
-	tile = ai->myCb->getObjInstance(ObjectInstanceID(objid))->visitablePos();
-	priority = 3;
-}
-
-bool VisitObj::fulfillsMe(TSubgoal goal)
-{
-	if(goal->goalType == Goals::VISIT_TILE)
-	{
-		if (!hero || hero == goal->hero)
-		{
-			auto obj = cb->getObjInstance(ObjectInstanceID(objid));
-			if (obj && obj->visitablePos() == goal->tile) //object could be removed
-				return true;
-		}
-	}
-	return false;
-}
-
-std::string VisitHero::completeMessage() const
-{
-	return "hero " + hero.get()->name + " visited hero " + boost::lexical_cast<std::string>(objid);
-}
-
-TSubgoal VisitHero::whatToDoToAchieve()
-{
-	const CGObjectInstance * obj = cb->getObj(ObjectInstanceID(objid));
-	if(!obj)
-		return sptr(Goals::Explore());
-	int3 pos = obj->visitablePos();
-
-	if(hero && ai->isAccessibleForHero(pos, hero, true) && isSafeToVisit(hero, pos)) //enemy heroes can get reinforcements
-	{
-		if(hero->pos == pos)
-			logAi->error("Hero %s tries to visit himself.", hero.name);
-		else
-		{
-			//can't use VISIT_TILE here as tile appears blocked by target hero
-			//FIXME: elementar goal should not be abstract
-			return sptr(Goals::VisitHero(objid).sethero(hero).settile(pos).setisElementar(true));
-		}
-	}
-	return sptr(Goals::Invalid());
-}
-
-bool VisitHero::fulfillsMe(TSubgoal goal)
-{
-	//TODO: VisitObj shoudl not be used for heroes, but...
-	if(goal->goalType == Goals::VISIT_TILE)
-	{
-		auto obj = cb->getObj(ObjectInstanceID(objid));
-		if (!obj)
-		{
-			logAi->error("Hero %s: VisitHero::fulfillsMe at %s: object %d not found", hero.name, goal->tile.toString(), objid);
-			return false;
-		}
-		return obj->visitablePos() == goal->tile;
-	}
-	return false;
-}
-
-TSubgoal GetArtOfType::whatToDoToAchieve()
-{
-	TSubgoal alternativeWay = CGoal::lookForArtSmart(aid); //TODO: use
-	if(alternativeWay->invalid())
-		return sptr(Goals::FindObj(Obj::ARTIFACT, aid));
-	return sptr(Goals::Invalid());
-}
-
-TSubgoal ClearWayTo::whatToDoToAchieve()
-{
-	assert(cb->isInTheMap(tile)); //set tile
-	if(!cb->isVisible(tile))
-	{
-		logAi->error("Clear way should be used with visible tiles!");
-		return sptr(Goals::Explore());
-	}
-
-	return (fh->chooseSolution(getAllPossibleSubgoals()));
-}
-
-bool Goals::ClearWayTo::fulfillsMe(TSubgoal goal)
-{
-	if (goal->goalType == Goals::VISIT_TILE)
-	{
-		if (!hero || hero == goal->hero)
-			return tile == goal->tile;
-	}
-	return false;
-}
-
-TGoalVec ClearWayTo::getAllPossibleSubgoals()
-{
-	TGoalVec ret;
-
-	std::vector<const CGHeroInstance *> heroes;
-	if(hero)
-		heroes.push_back(hero.h);
-	else
-		heroes = cb->getHeroesInfo();
-
-	for(auto h : heroes)
-	{
-		//TODO: handle clearing way to allied heroes that are blocked
-		//if ((hero && hero->visitablePos() == tile && hero == *h) || //we can't free the way ourselves
-		//	h->visitablePos() == tile) //we are already on that tile! what does it mean?
-		//	continue;
-
-		//if our hero is trapped, make sure we request clearing the way from OUR perspective
-
-		vstd::concatenate(ret, ai->ah->howToVisitTile(h, tile));
-	}
-
-	if(ret.empty() && ai->canRecruitAnyHero())
-		ret.push_back(sptr(Goals::RecruitHero()));
-
-	if(ret.empty())
-	{
-		logAi->warn("There is no known way to clear the way to tile %s", tile.toString());
-		throw goalFulfilledException(sptr(Goals::ClearWayTo(tile))); //make sure asigned hero gets unlocked
-	}
-
-	return ret;
-}
-
-std::string Explore::completeMessage() const
-{
-	return "Hero " + hero.get()->name + " completed exploration";
-}
-
-TSubgoal Explore::whatToDoToAchieve()
-{
-	auto ret = fh->chooseSolution(getAllPossibleSubgoals());
-	if(hero) //use best step for this hero
-	{
-		return ret;
-	}
-	else
-	{
-		if(ret->hero.get(true))
-			return sptr(sethero(ret->hero.h).setisAbstract(true)); //choose this hero and then continue with him
-		else
-			return ret; //other solutions, like buying hero from tavern
-	}
-}
-
-TGoalVec Explore::getAllPossibleSubgoals()
-{
-	TGoalVec ret;
-	std::vector<const CGHeroInstance *> heroes;
-
-	if(hero)
-	{
-		heroes.push_back(hero.h);
-	}
-	else
-	{
-		//heroes = ai->getUnblockedHeroes();
-		heroes = cb->getHeroesInfo();
-		vstd::erase_if(heroes, [](const HeroPtr h)
-		{
-			if(ai->getGoal(h)->goalType == Goals::EXPLORE) //do not reassign hero who is already explorer
-				return true;
-
-			if(!ai->isAbleToExplore(h))
-				return true;
-
-			return !h->movement; //saves time, immobile heroes are useless anyway
-		});
-	}
-
-	//try to use buildings that uncover map
-	std::vector<const CGObjectInstance *> objs;
-	for(auto obj : ai->visitableObjs)
-	{
-		if(!vstd::contains(ai->alreadyVisited, obj))
-		{
-			switch(obj->ID.num)
-			{
-			case Obj::REDWOOD_OBSERVATORY:
-			case Obj::PILLAR_OF_FIRE:
-			case Obj::CARTOGRAPHER:
-				objs.push_back(obj);
-				break;
-			case Obj::MONOLITH_ONE_WAY_ENTRANCE:
-			case Obj::MONOLITH_TWO_WAY:
-			case Obj::SUBTERRANEAN_GATE:
-				auto tObj = dynamic_cast<const CGTeleport *>(obj);
-				assert(ai->knownTeleportChannels.find(tObj->channel) != ai->knownTeleportChannels.end());
-				if(TeleportChannel::IMPASSABLE != ai->knownTeleportChannels[tObj->channel]->passability)
-					objs.push_back(obj);
-				break;
-			}
-		}
-		else
-		{
-			switch(obj->ID.num)
-			{
-			case Obj::MONOLITH_TWO_WAY:
-			case Obj::SUBTERRANEAN_GATE:
-				auto tObj = dynamic_cast<const CGTeleport *>(obj);
-				if(TeleportChannel::IMPASSABLE == ai->knownTeleportChannels[tObj->channel]->passability)
-					break;
-				for(auto exit : ai->knownTeleportChannels[tObj->channel]->exits)
-				{
-					if(!cb->getObj(exit))
-					{ // Always attempt to visit two-way teleports if one of channel exits is not visible
-						objs.push_back(obj);
-						break;
-					}
-				}
-				break;
-			}
-		}
-	}
-
-	auto primaryHero = ai->primaryHero().h;
-	for(auto h : heroes)
-	{
-		for(auto obj : objs) //double loop, performance risk?
-		{
-			auto waysToVisitObj = ai->ah->howToVisitObj(h, obj);
-
-			vstd::concatenate(ret, waysToVisitObj);
-		}
-
-		int3 t = whereToExplore(h);
-		if(t.valid())
-		{
-			ret.push_back(sptr(Goals::VisitTile(t).sethero(h)));
-		}
-		else
-		{
-			//FIXME: possible issues when gathering army to break
-			if(hero.h == h || //exporation is assigned to this hero
-				(!hero && h == primaryHero)) //not assigned to specific hero, let our main hero do the job
-			{
-				t = ai->explorationDesperate(h);  //check this only ONCE, high cost
-				if (t.valid()) //don't waste time if we are completely blocked
-				{
-					auto waysToVisitTile = ai->ah->howToVisitTile(h, t);
-
-					vstd::concatenate(ret, waysToVisitTile);
-					continue;
-				}
-			}
-			ai->markHeroUnableToExplore(h); //there is no freely accessible tile, do not poll this hero anymore
-		}
-	}
-	//we either don't have hero yet or none of heroes can explore
-	if((!hero || ret.empty()) && ai->canRecruitAnyHero())
-		ret.push_back(sptr(Goals::RecruitHero()));
-
-	if(ret.empty())
-	{
-		throw goalFulfilledException(sptr(Goals::Explore().sethero(hero)));
-	}
-	//throw cannotFulfillGoalException("Cannot explore - no possible ways found!");
-
-	return ret;
-}
-
-bool Explore::fulfillsMe(TSubgoal goal)
-{
-	if(goal->goalType == Goals::EXPLORE)
-	{
-		if(goal->hero)
-			return hero == goal->hero;
-		else
-			return true; //cancel ALL exploration
-	}
-	return false;
-}
-
-TSubgoal RecruitHero::whatToDoToAchieve()
-{
-	const CGTownInstance * t = ai->findTownWithTavern();
-	if(!t)
-		return sptr(Goals::BuildThis(BuildingID::TAVERN).setpriority(2));
-
-	TResources res;
-	res[Res::GOLD] = GameConstants::HERO_GOLD_COST;
-	return ai->ah->whatToDo(res, iAmElementar()); //either buy immediately, or collect res
-}
-
-std::string VisitTile::completeMessage() const
-{
-	return "Hero " + hero.get()->name + " visited tile " + tile.toString();
-}
-
-TSubgoal VisitTile::whatToDoToAchieve()
-{
-	auto ret = fh->chooseSolution(getAllPossibleSubgoals());
-
-	if(ret->hero)
-	{
-		if(isSafeToVisit(ret->hero, tile) && ai->isAccessibleForHero(tile, ret->hero))
-		{
-			ret->setisElementar(true);
-			return ret;
-		}
-		else
-		{
-			return sptr(Goals::GatherArmy(evaluateDanger(tile, *ret->hero) * SAFE_ATTACK_CONSTANT)
-				    .sethero(ret->hero).setisAbstract(true));
-		}
-	}
-	return ret;
-}
-
-TGoalVec VisitTile::getAllPossibleSubgoals()
-{
-	assert(cb->isInTheMap(tile));
-
-	TGoalVec ret;
-	if(!cb->isVisible(tile))
-		ret.push_back(sptr(Goals::Explore())); //what sense does it make?
-	else
-	{
-		std::vector<const CGHeroInstance *> heroes;
-		if(hero)
-			heroes.push_back(hero.h); //use assigned hero if any
-		else
-			heroes = cb->getHeroesInfo(); //use most convenient hero
-
-		for(auto h : heroes)
-		{
-			if(ai->isAccessibleForHero(tile, h))
-				ret.push_back(sptr(Goals::VisitTile(tile).sethero(h)));
-		}
-		if(ai->canRecruitAnyHero())
-			ret.push_back(sptr(Goals::RecruitHero()));
-	}
-	if(ret.empty())
-	{
-		auto obj = vstd::frontOrNull(cb->getVisitableObjs(tile));
-		if(obj && obj->ID == Obj::HERO && obj->tempOwner == ai->playerID) //our own hero stands on that tile
-		{
-			if(hero.get(true) && hero->id == obj->id) //if it's assigned hero, visit tile. If it's different hero, we can't visit tile now
-				ret.push_back(sptr(Goals::VisitTile(tile).sethero(dynamic_cast<const CGHeroInstance *>(obj)).setisElementar(true)));
-			else
-				throw cannotFulfillGoalException("Tile is already occupied by another hero "); //FIXME: we should give up this tile earlier
-		}
-		else
-			ret.push_back(sptr(Goals::ClearWayTo(tile)));
-	}
-
-	//important - at least one sub-goal must handle case which is impossible to fulfill (unreachable tile)
-	return ret;
-}
-
-TSubgoal DigAtTile::whatToDoToAchieve()
-{
-	const CGObjectInstance * firstObj = vstd::frontOrNull(cb->getVisitableObjs(tile));
-	if(firstObj && firstObj->ID == Obj::HERO && firstObj->tempOwner == ai->playerID) //we have hero at dest
-	{
-		const CGHeroInstance * h = dynamic_cast<const CGHeroInstance *>(firstObj);
-		sethero(h).setisElementar(true);
-		return sptr(*this);
-	}
-
-	return sptr(Goals::VisitTile(tile));
-}
-
-TSubgoal BuildThis::whatToDoToAchieve()
-{
-	auto b = BuildingID(bid);
-
-	// find town if not set
-	if (!town && hero)
-		town = hero->visitedTown;
-
-	if (!town)
-	{
-		for (const CGTownInstance * t : cb->getTownsInfo())
-		{
-			switch (cb->canBuildStructure(town, b))
-			{
-			case EBuildingState::ALLOWED:
-				town = t;
-				break; //TODO: look for prerequisites? this is not our reponsibility
-			default:
-				continue;
-			}
-		}
-	}
-	if (town) //we have specific town to build this
-	{
-		switch (cb->canBuildStructure(town, b))
-		{
-			case EBuildingState::ALLOWED:
-			case EBuildingState::NO_RESOURCES:
-			{
-				auto res = town->town->buildings.at(BuildingID(bid))->resources;
-				return ai->ah->whatToDo(res, iAmElementar()); //realize immediately or gather resources
-			}
-				break;
-			default:
-				throw cannotFulfillGoalException("Not possible to build");
-		}
-	}
-	else
-		throw cannotFulfillGoalException("Cannot find town to build this");
-}
-
-TGoalVec Goals::CollectRes::getAllPossibleSubgoals()
-{
-	TGoalVec ret;
-
-	auto givesResource = [this](const CGObjectInstance * obj) -> bool
-	{
-		//TODO: move this logic to object side
-		//TODO: remember mithril exists
-		//TODO: water objects
-		//TODO: Creature banks
-
-		//return false first from once-visitable, before checking if they were even visited
-		switch (obj->ID.num)
-		{
-		case Obj::TREASURE_CHEST:
-			return resID == Res::GOLD;
-			break;
-		case Obj::RESOURCE:
-			return obj->subID == resID;
-			break;
-		case Obj::MINE:
-			return (obj->subID == resID &&
-				(cb->getPlayerRelations(obj->tempOwner, ai->playerID) == PlayerRelations::ENEMIES)); //don't capture our mines
-			break;
-		case Obj::CAMPFIRE:
-			return true; //contains all resources
-			break;
-		case Obj::WINDMILL:
-			switch (resID)
-			{
-			case Res::GOLD:
-			case Res::WOOD:
-				return false;
-			}
-			break;
-		case Obj::WATER_WHEEL:
-			if (resID != Res::GOLD)
-				return false;
-			break;
-		case Obj::MYSTICAL_GARDEN:
-			if ((resID != Res::GOLD) && (resID != Res::GEMS))
-				return false;
-			break;
-		case Obj::LEAN_TO:
-		case Obj::WAGON:
-			if (resID != Res::GOLD)
-				return false;
-			break;
-		default:
-			return false;
-			break;
-		}
-		return !vstd::contains(ai->alreadyVisited, obj); //for weekly / once visitable
-	};
-
-	std::vector<const CGObjectInstance *> objs;
-	for (auto obj : ai->visitableObjs)
-	{
-		if (givesResource(obj))
-			objs.push_back(obj);
-	}
-	for (auto h : cb->getHeroesInfo())
-	{
-		std::vector<const CGObjectInstance *> ourObjs(objs); //copy common objects
-
-		for (auto obj : ai->reservedHeroesMap[h]) //add objects reserved by this hero
-		{
-			if (givesResource(obj))
-				ourObjs.push_back(obj);
-		}
-
-		for (auto obj : ourObjs)
-		{
-			auto waysToGo = ai->ah->howToVisitObj(h, ObjectIdRef(obj));
-
-			vstd::concatenate(ret, waysToGo);
-		}
-	}
-	return ret;
-}
-
-TSubgoal CollectRes::whatToDoToAchieve()
-{
-	auto goals = getAllPossibleSubgoals();
-	auto trade = whatToDoToTrade();
-	if (!trade->invalid())
-		goals.push_back(trade);
-
-	if (goals.empty())
-		return sptr(Goals::Explore()); //we can always do that
-	else
-		return fh->chooseSolution(goals); //TODO: evaluate trading
-}
-
-TSubgoal Goals::CollectRes::whatToDoToTrade()
-{
-	std::vector<const IMarket *> markets;
-
-	std::vector<const CGObjectInstance *> visObjs;
-	ai->retrieveVisitableObjs(visObjs, true);
-	for (const CGObjectInstance * obj : visObjs)
-	{
-		if (const IMarket * m = IMarket::castFrom(obj, false))
-		{
-			if (obj->ID == Obj::TOWN && obj->tempOwner == ai->playerID && m->allowsTrade(EMarketMode::RESOURCE_RESOURCE))
-				markets.push_back(m);
-			else if (obj->ID == Obj::TRADING_POST)
-				markets.push_back(m);
-		}
-	}
-
-	boost::sort(markets, [](const IMarket * m1, const IMarket * m2) -> bool
-	{
-		return m1->getMarketEfficiency() < m2->getMarketEfficiency();
-	});
-
-	markets.erase(boost::remove_if(markets, [](const IMarket * market) -> bool
-	{
-		if (!(market->o->ID == Obj::TOWN && market->o->tempOwner == ai->playerID))
-		{
-			if (!ai->isAccessible(market->o->visitablePos()))
-				return true;
-		}
-		return false;
-	}), markets.end());
-
-	if (!markets.size())
-	{
-		for (const CGTownInstance * t : cb->getTownsInfo())
-		{
-			if (cb->canBuildStructure(t, BuildingID::MARKETPLACE) == EBuildingState::ALLOWED)
-				return sptr(Goals::BuildThis(BuildingID::MARKETPLACE, t).setpriority(2));
-		}
-	}
-	else
-	{
-		const IMarket * m = markets.back();
-		//attempt trade at back (best prices)
-		int howManyCanWeBuy = 0;
-		for (Res::ERes i = Res::WOOD; i <= Res::GOLD; vstd::advance(i, 1))
-		{
-			if (i == resID)
-				continue;
-			int toGive = -1, toReceive = -1;
-			m->getOffer(i, resID, toGive, toReceive, EMarketMode::RESOURCE_RESOURCE);
-			assert(toGive > 0 && toReceive > 0);
-			howManyCanWeBuy += toReceive * (ai->ah->freeResources()[i] / toGive);
-		}
-
-		if (howManyCanWeBuy >= value)
-		{
-			auto backObj = cb->getTopObj(m->o->visitablePos()); //it'll be a hero if we have one there; otherwise marketplace
-			assert(backObj);
-			auto objid = m->o->id.getNum();
-			if (backObj->tempOwner != ai->playerID) //top object not owned
-			{
-				return sptr(Goals::VisitObj(objid)); //just go there
-			}
-			else //either it's our town, or we have hero there
-			{
-				Goals::Trade trade(resID, value, objid);
-				return sptr(trade.setisElementar(true)); //we can do this immediately
-			}
-		}
-	}
-	return sptr(Goals::Invalid()); //cannot trade
-}
-
-bool CollectRes::fulfillsMe(TSubgoal goal)
-{
-	if (goal->resID == resID)
-		if (goal->value >= value)
-			return true;
-
-	return false;
-}
-
-int GatherTroops::getCreaturesCount(const CArmedInstance * army)
-{
-	int count = 0;
-
-	for(auto stack : army->Slots())
-	{
-		if(objid == stack.second->getCreatureID().num)
-		{
-			count += stack.second->count;
-		}
-	}
-
-	return count;
-}
-
-TSubgoal GatherTroops::whatToDoToAchieve()
-{
-	auto heroes = cb->getHeroesInfo(true);
-
-	for(auto hero : heroes)
-	{
-		if(getCreaturesCount(hero) >= this->value)
-		{
-			logAi->trace("Completing GATHER_TROOPS by hero %s", hero->name);
-
-			throw goalFulfilledException(sptr(*this));
-		}
-	}
-
-	TGoalVec solutions = getAllPossibleSubgoals();
-
-	if(solutions.empty())
-		return sptr(Goals::Explore());
-
-	return fh->chooseSolution(solutions);
-}
-
-
-TGoalVec GatherTroops::getAllPossibleSubgoals()
-{
-	TGoalVec solutions;
-
-	for(const CGTownInstance * t : cb->getTownsInfo())
-	{
-		int count = getCreaturesCount(t->getUpperArmy());
-
-		if(count >= this->value)
-		{
-			vstd::concatenate(solutions, ai->ah->howToVisitObj(t));
-			continue;
-		}
-
-		auto creature = VLC->creh->creatures[objid];
-		if(t->subID == creature->faction) //TODO: how to force AI to build unupgraded creatures? :O
-		{
-			auto creatures = vstd::tryAt(t->town->creatures, creature->level - 1);
-			if(!creatures)
-				continue;
-
-			int upgradeNumber = vstd::find_pos(*creatures, creature->idNumber);
-			if(upgradeNumber < 0)
-				continue;
-
-			BuildingID bid(BuildingID::DWELL_FIRST + creature->level - 1 + upgradeNumber * GameConstants::CREATURES_PER_TOWN);
-			if(t->hasBuilt(bid)) //this assumes only creatures with dwellings are assigned to faction
-			{
-				solutions.push_back(sptr(Goals::BuyArmy(t, creature->AIValue * this->value).setobjid(objid)));
-			}
-			/*else //disable random building requests for now - this code needs to know a lot of town/resource context to do more good than harm
-			{	
-				return sptr(Goals::BuildThis(bid, t).setpriority(priority));
-			}*/
-		}
-	}
-	for(auto obj : ai->visitableObjs)
-	{
-		auto d = dynamic_cast<const CGDwelling *>(obj);
-
-		if(!d || obj->ID == Obj::TOWN)
-			continue;
-
-		for(auto creature : d->creatures)
-		{
-			if(creature.first) //there are more than 0 creatures avaliabe
-			{
-				for(auto type : creature.second)
-				{
-					if(type == objid && ai->ah->freeResources().canAfford(VLC->creh->creatures[type]->cost))
-						vstd::concatenate(solutions, ai->ah->howToVisitObj(obj));
-				}
-			}
-		}
-	}
-
-	return solutions;
-	//TODO: exchange troops between heroes
-}
-
-bool Goals::GatherTroops::fulfillsMe(TSubgoal goal)
-{
-	if (!hero || hero == goal->hero) //we got army for desired hero or any hero
-		if (goal->objid == objid) //same creature type //TODO: consider upgrades?
-			if (goal->value >= value) //notify every time we get resources?
-				return true;
-	return false;
-}
-
-TSubgoal Conquer::whatToDoToAchieve()
-{
-	return fh->chooseSolution(getAllPossibleSubgoals());
-}
-TGoalVec Conquer::getAllPossibleSubgoals()
-{
-	TGoalVec ret;
-
-	auto conquerable = [](const CGObjectInstance * obj) -> bool
-	{
-		if(cb->getPlayerRelations(ai->playerID, obj->tempOwner) == PlayerRelations::ENEMIES)
-		{
-			switch(obj->ID.num)
-			{
-			case Obj::TOWN:
-			case Obj::HERO:
-			case Obj::CREATURE_GENERATOR1:
-			case Obj::MINE: //TODO: check ai->knownSubterraneanGates
-				return true;
-			}
-		}
-		return false;
-	};
-
-	std::vector<const CGObjectInstance *> objs;
-	for(auto obj : ai->visitableObjs)
-	{
-		if(conquerable(obj))
-			objs.push_back(obj);
-	}
-
-	for(auto h : cb->getHeroesInfo())
-	{
-		std::vector<const CGObjectInstance *> ourObjs(objs); //copy common objects
-
-		for(auto obj : ai->reservedHeroesMap[h]) //add objects reserved by this hero
-		{
-			if(conquerable(obj))
-				ourObjs.push_back(obj);
-		}
-		for(auto obj : ourObjs)
-		{
-			auto waysToGo = ai->ah->howToVisitObj(h, ObjectIdRef(obj));
-
-			vstd::concatenate(ret, waysToGo);
-		}
-	}
-	if(!objs.empty() && ai->canRecruitAnyHero()) //probably no point to recruit hero if we see no objects to capture
-		ret.push_back(sptr(Goals::RecruitHero()));
-
-	if(ret.empty())
-		ret.push_back(sptr(Goals::Explore())); //we need to find an enemy
-	return ret;
-}
-
-TGoalVec Goals::Build::getAllPossibleSubgoals()
-{
-	TGoalVec ret;
-
-	for (const CGTownInstance * t : cb->getTownsInfo())
-	{
-		//start fresh with every town
-		ai->ah->getBuildingOptions(t);
-		auto immediateBuilding = ai->ah->immediateBuilding();
-		auto expensiveBuilding = ai->ah->expensiveBuilding();
-
-		//handling for early town development to save money and focus on income
-		if(!t->hasBuilt(ai->ah->getMaxPossibleGoldBuilding(t)) && expensiveBuilding.is_initialized())
-		{		
-			auto potentialBuilding = expensiveBuilding.get();
-			switch(expensiveBuilding.get().bid)
-			{
-			case BuildingID::TOWN_HALL:
-			case BuildingID::CITY_HALL:
-			case BuildingID::CAPITOL:
-			case BuildingID::FORT:
-			case BuildingID::CITADEL:
-			case BuildingID::CASTLE:
-				//If above buildings are next to be bought, but no money... do not buy anything else, try to gather resources for these. Simple but has to suffice for now.
-				auto goal = ai->ah->whatToDo(potentialBuilding.price, sptr(Goals::BuildThis(potentialBuilding.bid, t).setpriority(2.25)));
-				ret.push_back(goal);
-				return ret;
-				break;
-			}
-		}
-
-		if (immediateBuilding.is_initialized())
-		{
-			ret.push_back(sptr(Goals::BuildThis(immediateBuilding.get().bid, t).setpriority(2))); //prioritize buildings we can build quick
-		}
-		else //try build later
-		{
-			if (expensiveBuilding.is_initialized())
-			{
-				auto potentialBuilding = expensiveBuilding.get(); //gather resources for any we can't afford
-				auto goal = ai->ah->whatToDo(potentialBuilding.price, sptr(Goals::BuildThis(potentialBuilding.bid, t).setpriority(0.5)));
-				ret.push_back(goal);
-			}
-		}
-	}
-
-	if (ret.empty())
-		throw cannotFulfillGoalException("BUILD has been realized as much as possible.");
-	else
-		return ret;
-}
-
-TSubgoal Build::whatToDoToAchieve()
-{
-	return fh->chooseSolution(getAllPossibleSubgoals());
-}
-
-bool Goals::Build::fulfillsMe(TSubgoal goal)
-{
-	if (goal->goalType == Goals::BUILD || goal->goalType == Goals::BUILD_STRUCTURE)
-		return (!town || town == goal->town); //building anything will do, in this town if set
-	else
-		return false;
-}
-
-TSubgoal Invalid::whatToDoToAchieve()
-{
-	return iAmElementar();
-}
-
-std::string GatherArmy::completeMessage() const
-{
-	return "Hero " + hero.get()->name + " gathered army of value " + boost::lexical_cast<std::string>(value);
-}
-
-TSubgoal GatherArmy::whatToDoToAchieve()
-{
-	//TODO: find hero if none set
-	assert(hero.h);
-
-	return fh->chooseSolution(getAllPossibleSubgoals()); //find dwelling. use current hero to prevent him from doing nothing.
-}
-
-static const BuildingID unitsSource[] = { BuildingID::DWELL_LVL_1, BuildingID::DWELL_LVL_2, BuildingID::DWELL_LVL_3,
-	BuildingID::DWELL_LVL_4, BuildingID::DWELL_LVL_5, BuildingID::DWELL_LVL_6, BuildingID::DWELL_LVL_7};
-
-TGoalVec GatherArmy::getAllPossibleSubgoals()
-{
-	//get all possible towns, heroes and dwellings we may use
-	TGoalVec ret;
-
-	if(!hero.validAndSet())
-	{
-		return ret;
-	}
-
-	//TODO: include evaluation of monsters gather in calculation
-	for(auto t : cb->getTownsInfo())
-	{
-		auto pos = t->visitablePos();
-		if(ai->isAccessibleForHero(pos, hero))
-		{
-			//grab army from town
-			if(!t->visitingHero && howManyReinforcementsCanGet(hero, t))
-			{
-				if(!vstd::contains(ai->townVisitsThisWeek[hero], t))
-					ret.push_back(sptr(Goals::VisitTile(pos).sethero(hero)));
-			}
-			//buy army in town
-			if (!t->visitingHero || t->visitingHero == hero.get(true))
-			{
-				ui32 val = std::min<ui32>(value, howManyReinforcementsCanBuy(hero, t));
-				if (val)
-				{
-					auto goal = sptr(Goals::BuyArmy(t, val).sethero(hero));
-					if(!ai->ah->containsObjective(goal)) //avoid loops caused by reserving same objective twice
-						ret.push_back(goal);
-					else
-						logAi->debug("Can not buy army, because of ai->ah->containsObjective");
-				}
-			}
-			//build dwelling
-			//TODO: plan building over multiple turns?
-			//auto bid = ah->canBuildAnyStructure(t, std::vector<BuildingID>(unitsSource, unitsSource + ARRAY_COUNT(unitsSource)), 8 - cb->getDate(Date::DAY_OF_WEEK));
-
-			//Do not use below code for now, rely on generic Build. Code below needs to know a lot of town/resource context to do more good than harm
-			/*auto bid = ai->ah->canBuildAnyStructure(t, std::vector<BuildingID>(unitsSource, unitsSource + ARRAY_COUNT(unitsSource)), 1);
-			if (bid.is_initialized())
-			{
-				auto goal = sptr(BuildThis(bid.get(), t).setpriority(priority));
-				if (!ai->ah->containsObjective(goal)) //avoid loops caused by reserving same objective twice
-					ret.push_back(goal);
-				else
-					logAi->debug("Can not build a structure, because of ai->ah->containsObjective");
-			}*/
-		}
-	}
-
-	auto otherHeroes = cb->getHeroesInfo();
-	auto heroDummy = hero;
-	vstd::erase_if(otherHeroes, [heroDummy](const CGHeroInstance * h)
-	{
-		if(h == heroDummy.h)
-			return true;
-		else if(!ai->isAccessibleForHero(heroDummy->visitablePos(), h, true))
-			return true;
-		else if(!ai->canGetArmy(heroDummy.h, h)) //TODO: return actual aiValue
-			return true;
-		else if(ai->getGoal(h)->goalType == Goals::GATHER_ARMY)
-			return true;
-		else
-		   return false;
-	});
-	for(auto h : otherHeroes)
-	{
-		// Go to the other hero if we are faster
-		if (!vstd::contains(ai->visitedHeroes[hero], h)
-			&& ai->isAccessibleForHero(h->visitablePos(), hero, true)) //visit only once each turn //FIXME: this is only bug workaround
-			ret.push_back(sptr(Goals::VisitHero(h->id.getNum()).setisAbstract(true).sethero(hero)));
-		// Let the other hero come to us
-		if (!vstd::contains(ai->visitedHeroes[h], hero))
-			ret.push_back(sptr(Goals::VisitHero(hero->id.getNum()).setisAbstract(true).sethero(h)));
-	}
-
-	std::vector<const CGObjectInstance *> objs;
-	for(auto obj : ai->visitableObjs)
-	{
-		if(obj->ID == Obj::CREATURE_GENERATOR1)
-		{
-			auto relationToOwner = cb->getPlayerRelations(obj->getOwner(), ai->playerID);
-
-			//Use flagged dwellings only when there are available creatures that we can afford
-			if(relationToOwner == PlayerRelations::SAME_PLAYER)
-			{
-				auto dwelling = dynamic_cast<const CGDwelling *>(obj);
-
-				ui32 val = std::min<ui32>(value, howManyReinforcementsCanBuy(hero, dwelling));
-
-				if(val)
-				{
-					for(auto & creLevel : dwelling->creatures)
-					{
-						if(creLevel.first)
-						{
-							for(auto & creatureID : creLevel.second)
-							{
-								auto creature = VLC->creh->creatures[creatureID];
-								if(ai->ah->freeResources().canAfford(creature->cost))
-									objs.push_back(obj); //TODO: reserve resources?
-							}
-						}
-					}
-				}
-			}
-		}
-	}
-	for(auto h : cb->getHeroesInfo())
-	{
-		for(auto obj : objs)
-		{
-			//find safe dwelling
-			if(ai->isGoodForVisit(obj, h))
-			{
-				vstd::concatenate(ret, ai->ah->howToVisitObj(h, obj));
-			}
-		}
-	}
-
-	if(ai->canRecruitAnyHero() && ai->ah->freeGold() > GameConstants::HERO_GOLD_COST) //this is not stupid in early phase of game
-	{
-		if(auto t = ai->findTownWithTavern())
-		{
-			for(auto h : cb->getAvailableHeroes(t)) //we assume that all towns have same set of heroes
-			{
-				if(h && h->getTotalStrength() > 500) //do not buy heroes with single creatures for GatherArmy
-				{
-					ret.push_back(sptr(Goals::RecruitHero()));
-					break;
-				}
-			}
-		}
-	}
-
-	if(ret.empty())
-	{
-		if(hero == ai->primaryHero())
-			ret.push_back(sptr(Goals::Explore()));
-		else
-			throw cannotFulfillGoalException("No ways to gather army");
-	}
-
-	return ret;
-}
-
-//TSubgoal AbstractGoal::whatToDoToAchieve()
-//{
-//	logAi->debug("Decomposing goal of type %s",name());
-//	return sptr (Goals::Explore());
-//}
-
-TSubgoal AbstractGoal::goVisitOrLookFor(const CGObjectInstance * obj)
-{
-	if(obj)
-		return sptr(Goals::VisitObj(obj->id.getNum()));
-	else
-		return sptr(Goals::Explore());
-}
-
-TSubgoal AbstractGoal::lookForArtSmart(int aid)
-{
-	return sptr(Goals::Invalid());
-}
-
-bool AbstractGoal::invalid() const
-{
-	return goalType == INVALID;
-}
-
-void AbstractGoal::accept(VCAI * ai)
-{
-	ai->tryRealize(*this);
-}
-
-
-template<typename T>
-void CGoal<T>::accept(VCAI * ai)
-{
-	ai->tryRealize(static_cast<T &>(*this)); //casting enforces template instantiation
-}
-
-float AbstractGoal::accept(FuzzyHelper * f)
-{
-	return f->evaluate(*this);
-}
-
-template<typename T>
-float CGoal<T>::accept(FuzzyHelper * f)
-{
-	return f->evaluate(static_cast<T &>(*this)); //casting enforces template instantiation
-}

+ 0 - 698
AI/VCAI/Goals.h

@@ -1,698 +0,0 @@
-/*
- * Goals.h, part of VCMI engine
- *
- * Authors: listed in file AUTHORS in main folder
- *
- * License: GNU General Public License v2.0 or later
- * Full text of license available in license.txt file, in main folder
- *
- */
-#pragma once
-
-#include "../../lib/VCMI_Lib.h"
-#include "../../lib/CBuildingHandler.h"
-#include "../../lib/CCreatureHandler.h"
-#include "../../lib/CTownHandler.h"
-#include "AIUtility.h"
-
-struct HeroPtr;
-class VCAI;
-class FuzzyHelper;
-
-namespace Goals
-{
-class AbstractGoal;
-class VisitTile;
-
-class DLL_EXPORT TSubgoal : public std::shared_ptr<Goals::AbstractGoal>
-{
-	public:
-		bool operator==(const TSubgoal & rhs) const;
-		bool operator<(const TSubgoal & rhs) const;
-	//TODO: serialize?
-};
-
-typedef std::vector<TSubgoal> TGoalVec;
-
-enum EGoals
-{
-	INVALID = -1,
-	WIN, DO_NOT_LOSE, CONQUER, BUILD, //build needs to get a real reasoning
-	EXPLORE, GATHER_ARMY,
-	BOOST_HERO,
-	RECRUIT_HERO,
-	BUILD_STRUCTURE, //if hero set, then in visited town
-	COLLECT_RES,
-	GATHER_TROOPS, // val of creatures with objid
-
-	OBJECT_GOALS_BEGIN,
-	VISIT_OBJ, //visit or defeat or collect the object
-	FIND_OBJ, //find and visit any obj with objid + resid //TODO: consider universal subid for various types (aid, bid)
-	VISIT_HERO, //heroes can move around - set goal abstract and track hero every turn
-
-	GET_ART_TYPE,
-
-	//BUILD_STRUCTURE,
-	ISSUE_COMMAND,
-
-	VISIT_TILE, //tile, in conjunction with hero elementar; assumes tile is reachable
-	CLEAR_WAY_TO,
-	DIG_AT_TILE,//elementar with hero on tile
-	BUY_ARMY, //at specific town
-	TRADE, //val resID at object objid
-	BUILD_BOAT
-};
-
-	//method chaining + clone pattern
-#define VSETTER(type, field) virtual AbstractGoal & set ## field(const type &rhs) {field = rhs; return *this;};
-#define OSETTER(type, field) CGoal<T> & set ## field(const type &rhs) override { field = rhs; return *this; };
-
-#if 0
-	#define SETTER
-#endif // _DEBUG
-
-enum {LOW_PR = -1};
-
-DLL_EXPORT TSubgoal sptr(const AbstractGoal & tmp);
-
-struct DLL_EXPORT EvaluationContext
-{
-	uint64_t movementCost;
-	int manaCost;
-	uint64_t danger;
-
-	EvaluationContext()
-		:movementCost(0), danger(0), manaCost(0)
-	{
-	}
-};
-
-class DLL_EXPORT AbstractGoal
-{
-public:
-	bool isElementar; VSETTER(bool, isElementar)
-	bool isAbstract; VSETTER(bool, isAbstract)
-	float priority; VSETTER(float, priority)
-	int value; VSETTER(int, value)
-	int resID; VSETTER(int, resID)
-	int objid; VSETTER(int, objid)
-	int aid; VSETTER(int, aid)
-	int3 tile; VSETTER(int3, tile)
-	HeroPtr hero; VSETTER(HeroPtr, hero)
-	const CGTownInstance *town; VSETTER(CGTownInstance *, town)
-	int bid; VSETTER(int, bid)
-	TSubgoal parent; VSETTER(TSubgoal, parent)
-	EvaluationContext evaluationContext; VSETTER(EvaluationContext, evaluationContext)
-
-	AbstractGoal(EGoals goal = INVALID)
-		: goalType (goal), evaluationContext()
-	{
-		priority = 0;
-		isElementar = false;
-		isAbstract = false;
-		value = 0;
-		aid = -1;
-		resID = -1;
-		objid = -1;
-		tile = int3(-1, -1, -1);
-		town = nullptr;
-		bid = -1;
-	}
-	virtual ~AbstractGoal(){}
-	//FIXME: abstract goal should be abstract, but serializer fails to instantiate subgoals in such case
-	virtual AbstractGoal * clone() const
-	{
-		return const_cast<AbstractGoal *>(this);
-	}
-	virtual TGoalVec getAllPossibleSubgoals()
-	{
-		return TGoalVec();
-	}
-	virtual TSubgoal whatToDoToAchieve()
-	{
-		return sptr(AbstractGoal());
-	}
-
-	EGoals goalType;
-
-	virtual std::string name() const;
-	virtual std::string completeMessage() const
-	{
-		return "This goal is unspecified!";
-	}
-
-	bool invalid() const;
-
-	static TSubgoal goVisitOrLookFor(const CGObjectInstance * obj); //if obj is nullptr, then we'll explore
-	static TSubgoal lookForArtSmart(int aid); //checks non-standard ways of obtaining art (merchants, quests, etc.)
-
-	///Visitor pattern
-	//TODO: make accept work for std::shared_ptr... somehow
-	virtual void accept(VCAI * ai); //unhandled goal will report standard error
-	virtual float accept(FuzzyHelper * f);
-
-	virtual bool operator==(const AbstractGoal & g) const;
-	bool operator<(AbstractGoal & g); //final
-	virtual bool fulfillsMe(Goals::TSubgoal goal) //TODO: multimethod instead of type check
-	{
-		return false; //use this method to check if goal is fulfilled by another (not equal) goal, operator == is handled spearately
-	}
-
-	bool operator!=(const AbstractGoal & g) const
-	{
-		return !(*this == g);
-	}
-
-	template<typename Handler> void serialize(Handler & h, const int version)
-	{
-		h & goalType;
-		h & isElementar;
-		h & isAbstract;
-		h & priority;
-		h & value;
-		h & resID;
-		h & objid;
-		h & aid;
-		h & tile;
-		h & hero;
-		h & town;
-		h & bid;
-	}
-};
-
-template<typename T> class DLL_EXPORT CGoal : public AbstractGoal
-{
-public:
-	CGoal<T>(EGoals goal = INVALID) : AbstractGoal(goal)
-	{
-		priority = 0;
-		isElementar = false;
-		isAbstract = false;
-		value = 0;
-		aid = -1;
-		objid = -1;
-		resID = -1;
-		tile = int3(-1, -1, -1);
-		town = nullptr;
-	}
-
-	OSETTER(bool, isElementar)
-	OSETTER(bool, isAbstract)
-	OSETTER(float, priority)
-	OSETTER(int, value)
-	OSETTER(int, resID)
-	OSETTER(int, objid)
-	OSETTER(int, aid)
-	OSETTER(int3, tile)
-	OSETTER(HeroPtr, hero)
-	OSETTER(CGTownInstance *, town)
-	OSETTER(int, bid)
-
-	void accept(VCAI * ai) override;
-	float accept(FuzzyHelper * f) override;
-
-	CGoal<T> * clone() const override
-	{
-		return new T(static_cast<T const &>(*this)); //casting enforces template instantiation
-	}
-	TSubgoal iAmElementar()
-	{
-		setisElementar(true); //FIXME: it's not const-correct, maybe we shoudl only set returned clone?
-		TSubgoal ptr;
-		ptr.reset(clone());
-		return ptr;
-	}
-	template<typename Handler> void serialize(Handler & h, const int version)
-	{
-		h & static_cast<AbstractGoal &>(*this);
-		//h & goalType & isElementar & isAbstract & priority;
-		//h & value & resID & objid & aid & tile & hero & town & bid;
-	}
-
-	virtual bool operator==(const AbstractGoal & g) const override
-	{
-		if(goalType != g.goalType)
-			return false;
-
-		return (*this) == (dynamic_cast<const T &>(g));
-	}
-
-	virtual bool operator==(const T & other) const = 0;
-};
-
-class DLL_EXPORT Invalid : public CGoal<Invalid>
-{
-public:
-	Invalid()
-		: CGoal(Goals::INVALID)
-	{
-		priority = -1e10;
-	}
-	TGoalVec getAllPossibleSubgoals() override
-	{
-		return TGoalVec();
-	}
-	TSubgoal whatToDoToAchieve() override;
-
-	virtual bool operator==(const Invalid & other) const override
-	{
-		return true;
-	}
-};
-
-class DLL_EXPORT Win : public CGoal<Win>
-{
-public:
-	Win()
-		: CGoal(Goals::WIN)
-	{
-		priority = 100;
-	}
-	TGoalVec getAllPossibleSubgoals() override
-	{
-		return TGoalVec();
-	}
-	TSubgoal whatToDoToAchieve() override;
-
-	virtual bool operator==(const Win & other) const override
-	{
-		return true;
-	}
-};
-
-class DLL_EXPORT NotLose : public CGoal<NotLose>
-{
-public:
-	NotLose()
-		: CGoal(Goals::DO_NOT_LOSE)
-	{
-		priority = 100;
-	}
-	TGoalVec getAllPossibleSubgoals() override
-	{
-		return TGoalVec();
-	}
-	//TSubgoal whatToDoToAchieve() override;
-
-	virtual bool operator==(const NotLose & other) const override
-	{
-		return true;
-	}
-};
-
-class DLL_EXPORT Conquer : public CGoal<Conquer>
-{
-public:
-	Conquer()
-		: CGoal(Goals::CONQUER)
-	{
-		priority = 10;
-	}
-	TGoalVec getAllPossibleSubgoals() override;
-	TSubgoal whatToDoToAchieve() override;
-	virtual bool operator==(const Conquer & other) const override;
-};
-
-class DLL_EXPORT Build : public CGoal<Build>
-{
-public:
-	Build()
-		: CGoal(Goals::BUILD)
-	{
-		priority = 1;
-	}
-	TGoalVec getAllPossibleSubgoals() override;
-	TSubgoal whatToDoToAchieve() override;
-	bool fulfillsMe(TSubgoal goal) override;
-
-	virtual bool operator==(const Build & other) const override
-	{
-		return true;
-	}
-};
-
-class DLL_EXPORT BuildBoat : public CGoal<BuildBoat>
-{
-private:
-	const IShipyard * shipyard;
-
-public:
-	BuildBoat(const IShipyard * shipyard)
-		: CGoal(Goals::BUILD_BOAT), shipyard(shipyard)
-	{
-		priority = 0;
-	}
-	TGoalVec getAllPossibleSubgoals() override
-	{
-		return TGoalVec();
-	}
-	TSubgoal whatToDoToAchieve() override;
-	void accept(VCAI * ai) override;
-	std::string name() const override;
-	std::string completeMessage() const override;
-	virtual bool operator==(const BuildBoat & other) const override;
-};
-
-class DLL_EXPORT Explore : public CGoal<Explore>
-{
-public:
-	Explore()
-		: CGoal(Goals::EXPLORE)
-	{
-		priority = 1;
-	}
-	Explore(HeroPtr h)
-		: CGoal(Goals::EXPLORE)
-	{
-		hero = h;
-		priority = 1;
-	}
-	TGoalVec getAllPossibleSubgoals() override;
-	TSubgoal whatToDoToAchieve() override;
-	std::string completeMessage() const override;
-	bool fulfillsMe(TSubgoal goal) override;
-	virtual bool operator==(const Explore & other) const override;
-};
-
-class DLL_EXPORT GatherArmy : public CGoal<GatherArmy>
-{
-public:
-	GatherArmy()
-		: CGoal(Goals::GATHER_ARMY)
-	{
-	}
-	GatherArmy(int val)
-		: CGoal(Goals::GATHER_ARMY)
-	{
-		value = val;
-		priority = 2.5;
-	}
-	TGoalVec getAllPossibleSubgoals() override;
-	TSubgoal whatToDoToAchieve() override;
-	std::string completeMessage() const override;
-	virtual bool operator==(const GatherArmy & other) const override;
-};
-
-class DLL_EXPORT BuyArmy : public CGoal<BuyArmy>
-{
-private:
-	BuyArmy()
-		: CGoal(Goals::BUY_ARMY)
-	{}
-public:
-	BuyArmy(const CGTownInstance * Town, int val)
-		: CGoal(Goals::BUY_ARMY)
-	{
-		town = Town; //where to buy this army
-		value = val; //expressed in AI unit strength
-		priority = 3;//TODO: evaluate?
-	}
-	bool fulfillsMe(TSubgoal goal) override;
-
-	TSubgoal whatToDoToAchieve() override;
-	std::string completeMessage() const override;
-	virtual bool operator==(const BuyArmy & other) const override;
-};
-
-class DLL_EXPORT BoostHero : public CGoal<BoostHero>
-{
-public:
-	BoostHero()
-		: CGoal(Goals::INVALID)
-	{
-		priority = -1e10; //TODO
-	}
-	TGoalVec getAllPossibleSubgoals() override
-	{
-		return TGoalVec();
-	}
-	virtual bool operator==(const BoostHero & other) const override;
-	//TSubgoal whatToDoToAchieve() override {return sptr(Invalid());};
-};
-
-class DLL_EXPORT RecruitHero : public CGoal<RecruitHero>
-{
-public:
-	RecruitHero()
-		: CGoal(Goals::RECRUIT_HERO)
-	{
-		priority = 1;
-	}
-
-	TGoalVec getAllPossibleSubgoals() override
-	{
-		return TGoalVec();
-	}
-
-	TSubgoal whatToDoToAchieve() override;
-
-	virtual bool operator==(const RecruitHero & other) const override
-	{
-		return true;
-	}
-};
-
-class DLL_EXPORT BuildThis : public CGoal<BuildThis>
-{
-public:
-	BuildThis() //should be private, but unit test uses it
-		: CGoal(Goals::BUILD_STRUCTURE)
-	{}
-	BuildThis(BuildingID Bid, const CGTownInstance * tid)
-		: CGoal(Goals::BUILD_STRUCTURE)
-	{
-		bid = Bid;
-		town = tid;
-		priority = 1;
-	}
-	BuildThis(BuildingID Bid)
-		: CGoal(Goals::BUILD_STRUCTURE)
-	{
-		bid = Bid;
-		priority = 1;
-	}
-	TGoalVec getAllPossibleSubgoals() override
-	{
-		return TGoalVec();
-	}
-	TSubgoal whatToDoToAchieve() override;
-	//bool fulfillsMe(TSubgoal goal) override;
-	virtual bool operator==(const BuildThis & other) const override;
-};
-
-class DLL_EXPORT CollectRes : public CGoal<CollectRes>
-{
-public:
-	CollectRes()
-		: CGoal(Goals::COLLECT_RES)
-	{
-	}
-	CollectRes(int rid, int val)
-		: CGoal(Goals::COLLECT_RES)
-	{
-		resID = rid;
-		value = val;
-		priority = 2;
-	}
-	TGoalVec getAllPossibleSubgoals() override;
-	TSubgoal whatToDoToAchieve() override;
-	TSubgoal whatToDoToTrade();
-	bool fulfillsMe(TSubgoal goal) override; //TODO: Trade
-	virtual bool operator==(const CollectRes & other) const override;
-};
-
-class DLL_EXPORT Trade : public CGoal<Trade>
-{
-public:
-	Trade()
-		: CGoal(Goals::TRADE)
-	{
-	}
-	Trade(int rid, int val, int Objid)
-		: CGoal(Goals::TRADE)
-	{
-		resID = rid;
-		value = val;
-		objid = Objid;
-		priority = 3; //trading is instant, but picking resources is free
-	}
-	TSubgoal whatToDoToAchieve() override;
-	virtual bool operator==(const Trade & other) const override;
-};
-
-class DLL_EXPORT GatherTroops : public CGoal<GatherTroops>
-{
-public:
-	GatherTroops()
-		: CGoal(Goals::GATHER_TROOPS)
-	{
-		priority = 2;
-	}
-	GatherTroops(int type, int val)
-		: CGoal(Goals::GATHER_TROOPS)
-	{
-		objid = type;
-		value = val;
-		priority = 2;
-	}
-	TGoalVec getAllPossibleSubgoals() override;
-	TSubgoal whatToDoToAchieve() override;
-	bool fulfillsMe(TSubgoal goal) override;
-	virtual bool operator==(const GatherTroops & other) const override;
-
-private:
-	int getCreaturesCount(const CArmedInstance * army);
-};
-
-class DLL_EXPORT VisitObj : public CGoal<VisitObj> //this goal was previously known as GetObj
-{
-public:
-	VisitObj() = delete; // empty constructor not allowed
-	VisitObj(int Objid);
-
-	TGoalVec getAllPossibleSubgoals() override;
-	TSubgoal whatToDoToAchieve() override;
-	bool fulfillsMe(TSubgoal goal) override;
-	std::string completeMessage() const override;
-	virtual bool operator==(const VisitObj & other) const override;
-};
-
-class DLL_EXPORT FindObj : public CGoal<FindObj>
-{
-public:
-	FindObj() {} // empty constructor not allowed
-
-	FindObj(int ID)
-		: CGoal(Goals::FIND_OBJ)
-	{
-		objid = ID;
-		resID = -1; //subid unspecified
-		priority = 1;
-	}
-	FindObj(int ID, int subID)
-		: CGoal(Goals::FIND_OBJ)
-	{
-		objid = ID;
-		resID = subID;
-		priority = 1;
-	}
-	TGoalVec getAllPossibleSubgoals() override
-	{
-		return TGoalVec();
-	}
-	TSubgoal whatToDoToAchieve() override;
-	bool fulfillsMe(TSubgoal goal) override;
-	virtual bool operator==(const FindObj & other) const override;
-};
-
-class DLL_EXPORT VisitHero : public CGoal<VisitHero>
-{
-public:
-	VisitHero()
-		: CGoal(Goals::VISIT_HERO)
-	{
-	}
-	VisitHero(int hid)
-		: CGoal(Goals::VISIT_HERO)
-	{
-		objid = hid;
-		priority = 4;
-	}
-	TGoalVec getAllPossibleSubgoals() override
-	{
-		return TGoalVec();
-	}
-	TSubgoal whatToDoToAchieve() override;
-	bool fulfillsMe(TSubgoal goal) override;
-	std::string completeMessage() const override;
-	virtual bool operator==(const VisitHero & other) const override;
-};
-
-class DLL_EXPORT GetArtOfType : public CGoal<GetArtOfType>
-{
-public:
-	GetArtOfType()
-		: CGoal(Goals::GET_ART_TYPE)
-	{
-	}
-	GetArtOfType(int type)
-		: CGoal(Goals::GET_ART_TYPE)
-	{
-		aid = type;
-		priority = 2;
-	}
-	TGoalVec getAllPossibleSubgoals() override
-	{
-		return TGoalVec();
-	}
-	TSubgoal whatToDoToAchieve() override;
-	virtual bool operator==(const GetArtOfType & other) const override;
-};
-
-class DLL_EXPORT VisitTile : public CGoal<VisitTile>
-	//tile, in conjunction with hero elementar; assumes tile is reachable
-{
-public:
-	VisitTile() {} // empty constructor not allowed
-
-	VisitTile(int3 Tile)
-		: CGoal(Goals::VISIT_TILE)
-	{
-		tile = Tile;
-		priority = 5;
-	}
-	TGoalVec getAllPossibleSubgoals() override;
-	TSubgoal whatToDoToAchieve() override;
-	std::string completeMessage() const override;
-	virtual bool operator==(const VisitTile & other) const override;
-};
-
-class DLL_EXPORT ClearWayTo : public CGoal<ClearWayTo>
-{
-public:
-	ClearWayTo()
-		: CGoal(Goals::CLEAR_WAY_TO)
-	{
-	}
-	ClearWayTo(int3 Tile)
-		: CGoal(Goals::CLEAR_WAY_TO)
-	{
-		tile = Tile;
-		priority = 5;
-	}
-	ClearWayTo(int3 Tile, HeroPtr h)
-		: CGoal(Goals::CLEAR_WAY_TO)
-	{
-		tile = Tile;
-		hero = h;
-		priority = 5;
-	}
-	TGoalVec getAllPossibleSubgoals() override;
-	TSubgoal whatToDoToAchieve() override;
-	bool fulfillsMe(TSubgoal goal) override;
-	virtual bool operator==(const ClearWayTo & other) const override;
-};
-
-class DLL_EXPORT DigAtTile : public CGoal<DigAtTile>
-	//elementar with hero on tile
-{
-public:
-	DigAtTile()
-		: CGoal(Goals::DIG_AT_TILE)
-	{
-	}
-	DigAtTile(int3 Tile)
-		: CGoal(Goals::DIG_AT_TILE)
-	{
-		tile = Tile;
-		priority = 20;
-	}
-	TGoalVec getAllPossibleSubgoals() override
-	{
-		return TGoalVec();
-	}
-	TSubgoal whatToDoToAchieve() override;
-	virtual bool operator==(const DigAtTile & other) const override;
-};
-
-}

+ 192 - 0
AI/VCAI/Goals/AbstractGoal.cpp

@@ -0,0 +1,192 @@
+/*
+* AbstractGoal.cpp, part of VCMI engine
+*
+* Authors: listed in file AUTHORS in main folder
+*
+* License: GNU General Public License v2.0 or later
+* Full text of license available in license.txt file, in main folder
+*
+*/
+#include "StdInc.h"
+#include "AbstractGoal.h"
+#include "../VCAI.h"
+#include "../AIhelper.h"
+#include "../FuzzyHelper.h"
+#include "../ResourceManager.h"
+#include "../BuildingManager.h"
+#include "../../../lib/mapping/CMap.h" //for victory conditions
+#include "../../../lib/CPathfinder.h"
+#include "../../../lib/StringConstants.h"
+
+extern boost::thread_specific_ptr<CCallback> cb;
+extern boost::thread_specific_ptr<VCAI> ai;
+extern FuzzyHelper * fh;
+
+using namespace Goals;
+
+TSubgoal Goals::sptr(const AbstractGoal & tmp)
+{
+	TSubgoal ptr;
+	ptr.reset(tmp.clone());
+	return ptr;
+}
+
+std::string AbstractGoal::name() const //TODO: virtualize
+{
+	std::string desc;
+	switch(goalType)
+	{
+	case INVALID:
+		return "INVALID";
+	case WIN:
+		return "WIN";
+	case DO_NOT_LOSE:
+		return "DO NOT LOOSE";
+	case CONQUER:
+		return "CONQUER";
+	case BUILD:
+		return "BUILD";
+	case EXPLORE:
+		desc = "EXPLORE";
+		break;
+	case GATHER_ARMY:
+		desc = "GATHER ARMY";
+		break;
+	case BUY_ARMY:
+		return "BUY ARMY";
+		break;
+	case BOOST_HERO:
+		desc = "BOOST_HERO (unsupported)";
+		break;
+	case RECRUIT_HERO:
+		return "RECRUIT HERO";
+	case BUILD_STRUCTURE:
+		return "BUILD STRUCTURE";
+	case COLLECT_RES:
+		desc = "COLLECT RESOURCE " + GameConstants::RESOURCE_NAMES[resID] + " (" + boost::lexical_cast<std::string>(value) + ")";
+		break;
+	case TRADE:
+	{
+		auto obj = cb->getObjInstance(ObjectInstanceID(objid));
+		if (obj)
+			desc = (boost::format("TRADE %d of %s at %s") % value % GameConstants::RESOURCE_NAMES[resID] % obj->getObjectName()).str();
+	}
+	break;
+	case GATHER_TROOPS:
+		desc = "GATHER TROOPS";
+		break;
+	case VISIT_OBJ:
+	{
+		auto obj = cb->getObjInstance(ObjectInstanceID(objid));
+		if(obj)
+			desc = "GET OBJ " + obj->getObjectName();
+	}
+	break;
+	case FIND_OBJ:
+		desc = "FIND OBJ " + boost::lexical_cast<std::string>(objid);
+		break;
+	case VISIT_HERO:
+	{
+		auto obj = cb->getObjInstance(ObjectInstanceID(objid));
+		if(obj)
+			desc = "VISIT HERO " + obj->getObjectName();
+	}
+	break;
+	case GET_ART_TYPE:
+		desc = "GET ARTIFACT OF TYPE " + VLC->arth->artifacts[aid]->Name();
+		break;
+	case ISSUE_COMMAND:
+		return "ISSUE COMMAND (unsupported)";
+	case VISIT_TILE:
+		desc = "VISIT TILE " + tile.toString();
+		break;
+	case CLEAR_WAY_TO:
+		desc = "CLEAR WAY TO " + tile.toString();
+		break;
+	case DIG_AT_TILE:
+		desc = "DIG AT TILE " + tile.toString();
+		break;
+	default:
+		return boost::lexical_cast<std::string>(goalType);
+	}
+	if(hero.get(true)) //FIXME: used to crash when we lost hero and failed goal
+		desc += " (" + hero->name + ")";
+	return desc;
+}
+
+bool AbstractGoal::operator==(const AbstractGoal & g) const
+{
+	return false;
+}
+
+bool AbstractGoal::operator<(AbstractGoal & g) //for std::unique
+{
+	//TODO: make sure it gets goals consistent with == operator
+	if (goalType < g.goalType)
+		return true;
+	if (goalType > g.goalType)
+		return false;
+	if (hero < g.hero)
+		return true;
+	if (hero > g.hero)
+		return false;
+	if (tile < g.tile)
+		return true;
+	if (g.tile < tile)
+		return false;
+	if (objid < g.objid)
+		return true;
+	if (objid > g.objid)
+		return false;
+	if (town < g.town)
+		return true;
+	if (town > g.town)
+		return false;
+	if (value < g.value)
+		return true;
+	if (value > g.value)
+		return false;
+	if (priority < g.priority)
+		return true;
+	if (priority > g.priority)
+		return false;
+	if (resID < g.resID)
+		return true;
+	if (resID > g.resID)
+		return false;
+	if (bid < g.bid)
+		return true;
+	if (bid > g.bid)
+		return false;
+	if (aid < g.aid)
+		return true;
+	if (aid > g.aid)
+		return false;
+	return false;
+}
+
+//TODO: find out why the following are not generated automatically on MVS?
+bool TSubgoal::operator==(const TSubgoal & rhs) const
+{
+	return *get() == *rhs.get(); //comparison for Goals is overloaded, so they don't need to be identical to match
+}
+
+bool TSubgoal::operator<(const TSubgoal & rhs) const
+{
+	return get() < rhs.get(); //compae by value
+}
+
+bool AbstractGoal::invalid() const
+{
+	return goalType == EGoals::INVALID;
+}
+
+void AbstractGoal::accept(VCAI * ai)
+{
+	ai->tryRealize(*this);
+}
+
+float AbstractGoal::accept(FuzzyHelper * f)
+{
+	return f->evaluate(*this);
+}

+ 193 - 0
AI/VCAI/Goals/AbstractGoal.h

@@ -0,0 +1,193 @@
+/*
+* AbstractGoal.h, part of VCMI engine
+*
+* Authors: listed in file AUTHORS in main folder
+*
+* License: GNU General Public License v2.0 or later
+* Full text of license available in license.txt file, in main folder
+*
+*/
+#pragma once
+
+#include "../../../lib/VCMI_Lib.h"
+#include "../../../lib/CBuildingHandler.h"
+#include "../../../lib/CCreatureHandler.h"
+#include "../../../lib/CTownHandler.h"
+#include "../AIUtility.h"
+
+struct HeroPtr;
+class VCAI;
+class FuzzyHelper;
+
+namespace Goals
+{
+	class AbstractGoal;
+	class Explore;
+	class RecruitHero;
+	class VisitTile;
+	class VisitObj;
+	class VisitHero;
+	class BuildThis;
+	class DigAtTile;
+	class CollectRes;
+	class Build;
+	class BuyArmy;
+	class BuildBoat;
+	class GatherArmy;
+	class ClearWayTo;
+	class Invalid;
+	class Trade;
+
+	class DLL_EXPORT TSubgoal : public std::shared_ptr<AbstractGoal>
+	{
+	public:
+		bool operator==(const TSubgoal & rhs) const;
+		bool operator<(const TSubgoal & rhs) const;
+		//TODO: serialize?
+	};
+
+	typedef std::vector<TSubgoal> TGoalVec;
+
+	enum EGoals
+	{
+		INVALID = -1,
+		WIN, DO_NOT_LOSE, CONQUER, BUILD, //build needs to get a real reasoning
+		EXPLORE, GATHER_ARMY,
+		BOOST_HERO,
+		RECRUIT_HERO,
+		BUILD_STRUCTURE, //if hero set, then in visited town
+		COLLECT_RES,
+		GATHER_TROOPS, // val of creatures with objid
+
+		OBJECT_GOALS_BEGIN,
+		VISIT_OBJ, //visit or defeat or collect the object
+		FIND_OBJ, //find and visit any obj with objid + resid //TODO: consider universal subid for various types (aid, bid)
+		VISIT_HERO, //heroes can move around - set goal abstract and track hero every turn
+
+		GET_ART_TYPE,
+
+		//BUILD_STRUCTURE,
+		ISSUE_COMMAND,
+
+		VISIT_TILE, //tile, in conjunction with hero elementar; assumes tile is reachable
+		CLEAR_WAY_TO,
+		DIG_AT_TILE,//elementar with hero on tile
+		BUY_ARMY, //at specific town
+		TRADE, //val resID at object objid
+		BUILD_BOAT
+	};
+
+	//method chaining + clone pattern
+#define VSETTER(type, field) virtual AbstractGoal & set ## field(const type &rhs) {field = rhs; return *this;};
+#define OSETTER(type, field) CGoal<T> & set ## field(const type &rhs) override { field = rhs; return *this; };
+
+#if 0
+#define SETTER
+#endif // _DEBUG
+
+	enum { LOW_PR = -1 };
+
+	DLL_EXPORT TSubgoal sptr(const AbstractGoal & tmp);
+
+	struct DLL_EXPORT EvaluationContext
+	{
+		uint64_t movementCost;
+		int manaCost;
+		uint64_t danger;
+
+		EvaluationContext()
+			:movementCost(0), danger(0), manaCost(0)
+		{
+		}
+	};
+
+	class DLL_EXPORT AbstractGoal
+	{
+	public:
+		bool isElementar; VSETTER(bool, isElementar)
+			bool isAbstract; VSETTER(bool, isAbstract)
+			float priority; VSETTER(float, priority)
+			int value; VSETTER(int, value)
+			int resID; VSETTER(int, resID)
+			int objid; VSETTER(int, objid)
+			int aid; VSETTER(int, aid)
+			int3 tile; VSETTER(int3, tile)
+			HeroPtr hero; VSETTER(HeroPtr, hero)
+			const CGTownInstance *town; VSETTER(CGTownInstance *, town)
+			int bid; VSETTER(int, bid)
+			TSubgoal parent; VSETTER(TSubgoal, parent)
+			EvaluationContext evaluationContext; VSETTER(EvaluationContext, evaluationContext)
+
+			AbstractGoal(EGoals goal = EGoals::INVALID)
+			: goalType(goal), evaluationContext()
+		{
+			priority = 0;
+			isElementar = false;
+			isAbstract = false;
+			value = 0;
+			aid = -1;
+			resID = -1;
+			objid = -1;
+			tile = int3(-1, -1, -1);
+			town = nullptr;
+			bid = -1;
+		}
+		virtual ~AbstractGoal() {}
+		//FIXME: abstract goal should be abstract, but serializer fails to instantiate subgoals in such case
+		virtual AbstractGoal * clone() const
+		{
+			return const_cast<AbstractGoal *>(this);
+		}
+		virtual TGoalVec getAllPossibleSubgoals()
+		{
+			return TGoalVec();
+		}
+		virtual TSubgoal whatToDoToAchieve()
+		{
+			return sptr(AbstractGoal());
+		}
+
+		EGoals goalType;
+
+		virtual std::string name() const;
+		virtual std::string completeMessage() const
+		{
+			return "This goal is unspecified!";
+		}
+
+		bool invalid() const;
+
+		///Visitor pattern
+		//TODO: make accept work for std::shared_ptr... somehow
+		virtual void accept(VCAI * ai); //unhandled goal will report standard error
+		virtual float accept(FuzzyHelper * f);
+
+		virtual bool operator==(const AbstractGoal & g) const;
+		bool operator<(AbstractGoal & g); //final
+		virtual bool fulfillsMe(Goals::TSubgoal goal) //TODO: multimethod instead of type check
+		{
+			return false; //use this method to check if goal is fulfilled by another (not equal) goal, operator == is handled spearately
+		}
+
+		bool operator!=(const AbstractGoal & g) const
+		{
+			return !(*this == g);
+		}
+
+		template<typename Handler> void serialize(Handler & h, const int version)
+		{
+			h & goalType;
+			h & isElementar;
+			h & isAbstract;
+			h & priority;
+			h & value;
+			h & resID;
+			h & objid;
+			h & aid;
+			h & tile;
+			h & hero;
+			h & town;
+			h & bid;
+		}
+	};
+}

+ 93 - 0
AI/VCAI/Goals/Build.cpp

@@ -0,0 +1,93 @@
+/*
+* Build.cpp, part of VCMI engine
+*
+* Authors: listed in file AUTHORS in main folder
+*
+* License: GNU General Public License v2.0 or later
+* Full text of license available in license.txt file, in main folder
+*
+*/
+#include "StdInc.h"
+#include "Build.h"
+#include "BuildThis.h"
+#include "../VCAI.h"
+#include "../AIUtility.h"
+#include "../AIhelper.h"
+#include "../FuzzyHelper.h"
+#include "../ResourceManager.h"
+#include "../BuildingManager.h"
+#include "../../../lib/mapping/CMap.h" //for victory conditions
+#include "../../../lib/CPathfinder.h"
+#include "../../../lib/StringConstants.h"
+
+
+extern boost::thread_specific_ptr<CCallback> cb;
+extern boost::thread_specific_ptr<VCAI> ai;
+extern FuzzyHelper * fh;
+
+using namespace Goals;
+
+TGoalVec Build::getAllPossibleSubgoals()
+{
+	TGoalVec ret;
+
+	for(const CGTownInstance * t : cb->getTownsInfo())
+	{
+		//start fresh with every town
+		ai->ah->getBuildingOptions(t);
+		auto immediateBuilding = ai->ah->immediateBuilding();
+		auto expensiveBuilding = ai->ah->expensiveBuilding();
+
+		//handling for early town development to save money and focus on income
+		if(!t->hasBuilt(ai->ah->getMaxPossibleGoldBuilding(t)) && expensiveBuilding.is_initialized())
+		{
+			auto potentialBuilding = expensiveBuilding.get();
+			switch(expensiveBuilding.get().bid)
+			{
+			case BuildingID::TOWN_HALL:
+			case BuildingID::CITY_HALL:
+			case BuildingID::CAPITOL:
+			case BuildingID::FORT:
+			case BuildingID::CITADEL:
+			case BuildingID::CASTLE:
+				//If above buildings are next to be bought, but no money... do not buy anything else, try to gather resources for these. Simple but has to suffice for now.
+				auto goal = ai->ah->whatToDo(potentialBuilding.price, sptr(BuildThis(potentialBuilding.bid, t).setpriority(2.25)));
+				ret.push_back(goal);
+				return ret;
+				break;
+			}
+		}
+
+		if(immediateBuilding.is_initialized())
+		{
+			ret.push_back(sptr(BuildThis(immediateBuilding.get().bid, t).setpriority(2))); //prioritize buildings we can build quick
+		}
+		else //try build later
+		{
+			if(expensiveBuilding.is_initialized())
+			{
+				auto potentialBuilding = expensiveBuilding.get(); //gather resources for any we can't afford
+				auto goal = ai->ah->whatToDo(potentialBuilding.price, sptr(BuildThis(potentialBuilding.bid, t).setpriority(0.5)));
+				ret.push_back(goal);
+			}
+		}
+	}
+
+	if(ret.empty())
+		throw cannotFulfillGoalException("BUILD has been realized as much as possible.");
+	else
+		return ret;
+}
+
+TSubgoal Build::whatToDoToAchieve()
+{
+	return fh->chooseSolution(getAllPossibleSubgoals());
+}
+
+bool Build::fulfillsMe(TSubgoal goal)
+{
+	if(goal->goalType == BUILD || goal->goalType == BUILD_STRUCTURE)
+		return (!town || town == goal->town); //building anything will do, in this town if set
+	else
+		return false;
+}

+ 37 - 0
AI/VCAI/Goals/Build.h

@@ -0,0 +1,37 @@
+/*
+* Build.h, part of VCMI engine
+*
+* Authors: listed in file AUTHORS in main folder
+*
+* License: GNU General Public License v2.0 or later
+* Full text of license available in license.txt file, in main folder
+*
+*/
+#pragma once
+
+#include "CGoal.h"
+
+struct HeroPtr;
+class VCAI;
+class FuzzyHelper;
+
+namespace Goals
+{
+	class DLL_EXPORT Build : public CGoal<Build>
+	{
+	public:
+		Build()
+			: CGoal(Goals::BUILD)
+		{
+			priority = 1;
+		}
+		TGoalVec getAllPossibleSubgoals() override;
+		TSubgoal whatToDoToAchieve() override;
+		bool fulfillsMe(TSubgoal goal) override;
+
+		virtual bool operator==(const Build & other) const override
+		{
+			return true;
+		}
+	};
+}

+ 84 - 0
AI/VCAI/Goals/BuildBoat.cpp

@@ -0,0 +1,84 @@
+/*
+* BuildBoat.cpp, part of VCMI engine
+*
+* Authors: listed in file AUTHORS in main folder
+*
+* License: GNU General Public License v2.0 or later
+* Full text of license available in license.txt file, in main folder
+*
+*/
+#include "StdInc.h"
+#include "BuildBoat.h"
+#include "../VCAI.h"
+#include "../FuzzyHelper.h"
+#include "../AIhelper.h"
+#include "../../lib/mapping/CMap.h" //for victory conditions
+#include "../../lib/CPathfinder.h"
+
+extern boost::thread_specific_ptr<CCallback> cb;
+extern boost::thread_specific_ptr<VCAI> ai;
+extern FuzzyHelper * fh;
+
+using namespace Goals;
+
+bool BuildBoat::operator==(const BuildBoat & other) const
+{
+	return shipyard->o->id == other.shipyard->o->id;
+}
+
+TSubgoal BuildBoat::whatToDoToAchieve()
+{
+	if(cb->getPlayerRelations(ai->playerID, shipyard->o->tempOwner) == PlayerRelations::ENEMIES)
+	{
+		return fh->chooseSolution(ai->ah->howToVisitObj(shipyard->o));
+	}
+
+	if(shipyard->shipyardStatus() != IShipyard::GOOD)
+	{
+		throw cannotFulfillGoalException("Shipyard is busy.");
+	}
+
+	TResources boatCost;
+	shipyard->getBoatCost(boatCost);
+
+	return ai->ah->whatToDo(boatCost, this->iAmElementar());
+}
+
+void BuildBoat::accept(VCAI * ai)
+{
+	TResources boatCost;
+	shipyard->getBoatCost(boatCost);
+
+	if(!cb->getResourceAmount().canAfford(boatCost))
+	{
+		throw cannotFulfillGoalException("Can not afford boat");
+	}
+
+	if(cb->getPlayerRelations(ai->playerID, shipyard->o->tempOwner) == PlayerRelations::ENEMIES)
+	{
+		throw cannotFulfillGoalException("Can not build boat in enemy shipyard");
+	}
+
+	if(shipyard->shipyardStatus() != IShipyard::GOOD)
+	{
+		throw cannotFulfillGoalException("Shipyard is busy.");
+	}
+
+	logAi->trace(
+		"Building boat at shipyard %s located at %s, estimated boat position %s", 
+		shipyard->o->getObjectName(),
+		shipyard->o->visitablePos().toString(),
+		shipyard->bestLocation().toString());
+
+	cb->buildBoat(shipyard);
+}
+
+std::string BuildBoat::name() const
+{
+	return "BuildBoat";
+}
+
+std::string BuildBoat::completeMessage() const
+{
+	return "Boat have been built at " + shipyard->o->visitablePos().toString();
+}

+ 37 - 0
AI/VCAI/Goals/BuildBoat.h

@@ -0,0 +1,37 @@
+/*
+* BuildBoat.h, part of VCMI engine
+*
+* Authors: listed in file AUTHORS in main folder
+*
+* License: GNU General Public License v2.0 or later
+* Full text of license available in license.txt file, in main folder
+*
+*/
+#pragma once
+
+#include "CGoal.h"
+
+namespace Goals
+{
+	class DLL_EXPORT BuildBoat : public CGoal<BuildBoat>
+	{
+	private:
+		const IShipyard * shipyard;
+
+	public:
+		BuildBoat(const IShipyard * shipyard)
+			: CGoal(Goals::BUILD_BOAT), shipyard(shipyard)
+		{
+			priority = 0;
+		}
+		TGoalVec getAllPossibleSubgoals() override
+		{
+			return TGoalVec();
+		}
+		TSubgoal whatToDoToAchieve() override;
+		void accept(VCAI * ai) override;
+		std::string name() const override;
+		std::string completeMessage() const override;
+		virtual bool operator==(const BuildBoat & other) const override;
+	};
+}

+ 74 - 0
AI/VCAI/Goals/BuildThis.cpp

@@ -0,0 +1,74 @@
+/*
+* BuildThis.cpp, part of VCMI engine
+*
+* Authors: listed in file AUTHORS in main folder
+*
+* License: GNU General Public License v2.0 or later
+* Full text of license available in license.txt file, in main folder
+*
+*/
+#include "StdInc.h"
+#include "BuildThis.h"
+#include "../VCAI.h"
+#include "../AIUtility.h"
+#include "../AIhelper.h"
+#include "../FuzzyHelper.h"
+#include "../ResourceManager.h"
+#include "../BuildingManager.h"
+#include "../../../lib/mapping/CMap.h" //for victory conditions
+#include "../../../lib/CPathfinder.h"
+#include "../../../lib/StringConstants.h"
+
+
+extern boost::thread_specific_ptr<CCallback> cb;
+extern boost::thread_specific_ptr<VCAI> ai;
+extern FuzzyHelper * fh;
+
+using namespace Goals;
+
+bool BuildThis::operator==(const BuildThis & other) const
+{
+	return town == other.town && bid == other.bid;
+}
+
+TSubgoal BuildThis::whatToDoToAchieve()
+{
+	auto b = BuildingID(bid);
+
+	// find town if not set
+	if(!town && hero)
+		town = hero->visitedTown;
+
+	if(!town)
+	{
+		for(const CGTownInstance * t : cb->getTownsInfo())
+		{
+			switch(cb->canBuildStructure(town, b))
+			{
+			case EBuildingState::ALLOWED:
+				town = t;
+				break; //TODO: look for prerequisites? this is not our reponsibility
+			default:
+				continue;
+			}
+		}
+	}
+
+	if(town) //we have specific town to build this
+	{
+		switch(cb->canBuildStructure(town, b))
+		{
+		case EBuildingState::ALLOWED:
+		case EBuildingState::NO_RESOURCES:
+		{
+			auto res = town->town->buildings.at(BuildingID(bid))->resources;
+			return ai->ah->whatToDo(res, iAmElementar()); //realize immediately or gather resources
+		}
+		break;
+		default:
+			throw cannotFulfillGoalException("Not possible to build");
+		}
+	}
+	else
+		throw cannotFulfillGoalException("Cannot find town to build this");
+}

+ 48 - 0
AI/VCAI/Goals/BuildThis.h

@@ -0,0 +1,48 @@
+/*
+* BuildThis.h, part of VCMI engine
+*
+* Authors: listed in file AUTHORS in main folder
+*
+* License: GNU General Public License v2.0 or later
+* Full text of license available in license.txt file, in main folder
+*
+*/
+#pragma once
+
+#include "CGoal.h"
+
+struct HeroPtr;
+class VCAI;
+class FuzzyHelper;
+
+namespace Goals
+{
+	class DLL_EXPORT BuildThis : public CGoal<BuildThis>
+	{
+	public:
+		BuildThis() //should be private, but unit test uses it
+			: CGoal(Goals::BUILD_STRUCTURE)
+		{
+		}
+		BuildThis(BuildingID Bid, const CGTownInstance * tid)
+			: CGoal(Goals::BUILD_STRUCTURE)
+		{
+			bid = Bid;
+			town = tid;
+			priority = 1;
+		}
+		BuildThis(BuildingID Bid)
+			: CGoal(Goals::BUILD_STRUCTURE)
+		{
+			bid = Bid;
+			priority = 1;
+		}
+		TGoalVec getAllPossibleSubgoals() override
+		{
+			return TGoalVec();
+		}
+		TSubgoal whatToDoToAchieve() override;
+		//bool fulfillsMe(TSubgoal goal) override;
+		virtual bool operator==(const BuildThis & other) const override;
+	};
+}

+ 45 - 0
AI/VCAI/Goals/BuyArmy.cpp

@@ -0,0 +1,45 @@
+/*
+* BuyArmy.cpp, part of VCMI engine
+*
+* Authors: listed in file AUTHORS in main folder
+*
+* License: GNU General Public License v2.0 or later
+* Full text of license available in license.txt file, in main folder
+*
+*/
+#include "StdInc.h"
+#include "BuyArmy.h"
+#include "../FuzzyHelper.h"
+#include "../AIhelper.h"
+#include "../../lib/mapObjects/CGTownInstance.h"
+
+
+extern boost::thread_specific_ptr<CCallback> cb;
+extern boost::thread_specific_ptr<VCAI> ai;
+extern FuzzyHelper * fh;
+
+using namespace Goals;
+
+bool BuyArmy::operator==(const BuyArmy & other) const
+{
+	return town == other.town && objid == other.objid;
+}
+
+bool BuyArmy::fulfillsMe(TSubgoal goal)
+{
+	//if (hero && hero != goal->hero)
+	//	return false;
+	return town == goal->town && goal->value >= value; //can always buy more army
+}
+TSubgoal BuyArmy::whatToDoToAchieve()
+{
+	//TODO: calculate the actual cost of units instead
+	TResources price;
+	price[Res::GOLD] = value * 0.4f; //some approximate value
+	return ai->ah->whatToDo(price, iAmElementar()); //buy right now or gather resources
+}
+
+std::string BuyArmy::completeMessage() const
+{
+	return boost::format("Bought army of value %d in town of %s") % value, town->name;
+}

+ 41 - 0
AI/VCAI/Goals/BuyArmy.h

@@ -0,0 +1,41 @@
+/*
+* BuyArmy.h, part of VCMI engine
+*
+* Authors: listed in file AUTHORS in main folder
+*
+* License: GNU General Public License v2.0 or later
+* Full text of license available in license.txt file, in main folder
+*
+*/
+#pragma once
+
+#include "CGoal.h"
+
+struct HeroPtr;
+class VCAI;
+class FuzzyHelper;
+
+namespace Goals
+{
+	class DLL_EXPORT BuyArmy : public CGoal<BuyArmy>
+	{
+	private:
+		BuyArmy()
+			: CGoal(Goals::BUY_ARMY)
+		{
+		}
+	public:
+		BuyArmy(const CGTownInstance * Town, int val)
+			: CGoal(Goals::BUY_ARMY)
+		{
+			town = Town; //where to buy this army
+			value = val; //expressed in AI unit strength
+			priority = 3;//TODO: evaluate?
+		}
+		bool fulfillsMe(TSubgoal goal) override;
+
+		TSubgoal whatToDoToAchieve() override;
+		std::string completeMessage() const override;
+		virtual bool operator==(const BuyArmy & other) const override;
+	};
+}

+ 87 - 0
AI/VCAI/Goals/CGoal.h

@@ -0,0 +1,87 @@
+/*
+* CGoal.h, part of VCMI engine
+*
+* Authors: listed in file AUTHORS in main folder
+*
+* License: GNU General Public License v2.0 or later
+* Full text of license available in license.txt file, in main folder
+*
+*/
+#pragma once
+
+#include "AbstractGoal.h"
+#include "../FuzzyHelper.h"
+#include "../VCAI.h"
+
+struct HeroPtr;
+class VCAI;
+
+namespace Goals
+{
+	template<typename T> class DLL_EXPORT CGoal : public AbstractGoal
+	{
+	public:
+		CGoal<T>(EGoals goal = INVALID) : AbstractGoal(goal)
+		{
+			priority = 0;
+			isElementar = false;
+			isAbstract = false;
+			value = 0;
+			aid = -1;
+			objid = -1;
+			resID = -1;
+			tile = int3(-1, -1, -1);
+			town = nullptr;
+		}
+
+		OSETTER(bool, isElementar)
+		OSETTER(bool, isAbstract)
+		OSETTER(float, priority)
+		OSETTER(int, value)
+		OSETTER(int, resID)
+		OSETTER(int, objid)
+		OSETTER(int, aid)
+		OSETTER(int3, tile)
+		OSETTER(HeroPtr, hero)
+		OSETTER(CGTownInstance *, town)
+		OSETTER(int, bid)
+
+		void accept(VCAI * ai) override
+		{
+			ai->tryRealize(static_cast<T &>(*this)); //casting enforces template instantiation
+		}
+
+		float accept(FuzzyHelper * f) override
+		{
+			return f->evaluate(static_cast<T &>(*this)); //casting enforces template instantiation
+		}
+
+		CGoal<T> * clone() const override
+		{
+			return new T(static_cast<T const &>(*this)); //casting enforces template instantiation
+		}
+		TSubgoal iAmElementar()
+		{
+			setisElementar(true); //FIXME: it's not const-correct, maybe we shoudl only set returned clone?
+			TSubgoal ptr;
+			ptr.reset(clone());
+			return ptr;
+		}
+		template<typename Handler> void serialize(Handler & h, const int version)
+		{
+			h & static_cast<AbstractGoal &>(*this);
+			//h & goalType & isElementar & isAbstract & priority;
+			//h & value & resID & objid & aid & tile & hero & town & bid;
+		}
+
+		virtual bool operator==(const AbstractGoal & g) const override
+		{
+			if(goalType != g.goalType)
+				return false;
+
+			return (*this) == (dynamic_cast<const T &>(g));
+		}
+
+		virtual bool operator==(const T & other) const = 0;
+	};
+}

+ 85 - 0
AI/VCAI/Goals/ClearWayTo.cpp

@@ -0,0 +1,85 @@
+/*
+* ClearWayTo.cpp, part of VCMI engine
+*
+* Authors: listed in file AUTHORS in main folder
+*
+* License: GNU General Public License v2.0 or later
+* Full text of license available in license.txt file, in main folder
+*
+*/
+#include "StdInc.h"
+#include "ClearWayTo.h"
+#include "Explore.h"
+#include "RecruitHero.h"
+#include "../VCAI.h"
+#include "../AIUtility.h"
+#include "../FuzzyHelper.h"
+#include "../AIhelper.h"
+
+
+extern boost::thread_specific_ptr<CCallback> cb;
+extern boost::thread_specific_ptr<VCAI> ai;
+extern FuzzyHelper * fh;
+
+using namespace Goals;
+
+bool ClearWayTo::operator==(const ClearWayTo & other) const
+{
+	return other.hero.h == hero.h && other.tile == tile;
+}
+
+TSubgoal ClearWayTo::whatToDoToAchieve()
+{
+	assert(cb->isInTheMap(tile)); //set tile
+	if(!cb->isVisible(tile))
+	{
+		logAi->error("Clear way should be used with visible tiles!");
+		return sptr(Explore());
+	}
+
+	return (fh->chooseSolution(getAllPossibleSubgoals()));
+}
+
+bool ClearWayTo::fulfillsMe(TSubgoal goal)
+{
+	if (goal->goalType == VISIT_TILE)
+	{
+		if (!hero || hero == goal->hero)
+			return tile == goal->tile;
+	}
+	return false;
+}
+
+TGoalVec ClearWayTo::getAllPossibleSubgoals()
+{
+	TGoalVec ret;
+
+	std::vector<const CGHeroInstance *> heroes;
+	if(hero)
+		heroes.push_back(hero.h);
+	else
+		heroes = cb->getHeroesInfo();
+
+	for(auto h : heroes)
+	{
+		//TODO: handle clearing way to allied heroes that are blocked
+		//if ((hero && hero->visitablePos() == tile && hero == *h) || //we can't free the way ourselves
+		//	h->visitablePos() == tile) //we are already on that tile! what does it mean?
+		//	continue;
+
+		//if our hero is trapped, make sure we request clearing the way from OUR perspective
+
+		vstd::concatenate(ret, ai->ah->howToVisitTile(h, tile));
+	}
+
+	if(ret.empty() && ai->canRecruitAnyHero())
+		ret.push_back(sptr(RecruitHero()));
+
+	if(ret.empty())
+	{
+		logAi->warn("There is no known way to clear the way to tile %s", tile.toString());
+		throw goalFulfilledException(sptr(ClearWayTo(tile))); //make sure asigned hero gets unlocked
+	}
+
+	return ret;
+}

+ 45 - 0
AI/VCAI/Goals/ClearWayTo.h

@@ -0,0 +1,45 @@
+/*
+* ClearWayTo.h, part of VCMI engine
+*
+* Authors: listed in file AUTHORS in main folder
+*
+* License: GNU General Public License v2.0 or later
+* Full text of license available in license.txt file, in main folder
+*
+*/
+#pragma once
+
+#include "CGoal.h"
+
+struct HeroPtr;
+class VCAI;
+class FuzzyHelper;
+
+namespace Goals
+{
+	class DLL_EXPORT ClearWayTo : public CGoal<ClearWayTo>
+	{
+	public:
+		ClearWayTo()
+			: CGoal(Goals::CLEAR_WAY_TO)
+		{
+		}
+		ClearWayTo(int3 Tile)
+			: CGoal(Goals::CLEAR_WAY_TO)
+		{
+			tile = Tile;
+			priority = 5;
+		}
+		ClearWayTo(int3 Tile, HeroPtr h)
+			: CGoal(Goals::CLEAR_WAY_TO)
+		{
+			tile = Tile;
+			hero = h;
+			priority = 5;
+		}
+		TGoalVec getAllPossibleSubgoals() override;
+		TSubgoal whatToDoToAchieve() override;
+		bool fulfillsMe(TSubgoal goal) override;
+		virtual bool operator==(const ClearWayTo & other) const override;
+	};
+}

+ 208 - 0
AI/VCAI/Goals/CollectRes.cpp

@@ -0,0 +1,208 @@
+/*
+* CollectRes.cpp, part of VCMI engine
+*
+* Authors: listed in file AUTHORS in main folder
+*
+* License: GNU General Public License v2.0 or later
+* Full text of license available in license.txt file, in main folder
+*
+*/
+#include "StdInc.h"
+#include "Goals.h"
+#include "../VCAI.h"
+#include "../AIUtility.h"
+#include "../AIhelper.h"
+#include "../FuzzyHelper.h"
+#include "../ResourceManager.h"
+#include "../BuildingManager.h"
+#include "../../../lib/mapping/CMap.h" //for victory conditions
+#include "../../../lib/CPathfinder.h"
+#include "../../../lib/StringConstants.h"
+
+
+extern boost::thread_specific_ptr<CCallback> cb;
+extern boost::thread_specific_ptr<VCAI> ai;
+extern FuzzyHelper * fh;
+
+using namespace Goals;
+
+bool CollectRes::operator==(const CollectRes & other) const
+{
+	return resID == other.resID;
+}
+
+TGoalVec CollectRes::getAllPossibleSubgoals()
+{
+	TGoalVec ret;
+
+	auto givesResource = [this](const CGObjectInstance * obj) -> bool
+	{
+		//TODO: move this logic to object side
+		//TODO: remember mithril exists
+		//TODO: water objects
+		//TODO: Creature banks
+
+		//return false first from once-visitable, before checking if they were even visited
+		switch (obj->ID.num)
+		{
+		case Obj::TREASURE_CHEST:
+			return resID == Res::GOLD;
+			break;
+		case Obj::RESOURCE:
+			return obj->subID == resID;
+			break;
+		case Obj::MINE:
+			return (obj->subID == resID &&
+				(cb->getPlayerRelations(obj->tempOwner, ai->playerID) == PlayerRelations::ENEMIES)); //don't capture our mines
+			break;
+		case Obj::CAMPFIRE:
+			return true; //contains all resources
+			break;
+		case Obj::WINDMILL:
+			switch (resID)
+			{
+			case Res::GOLD:
+			case Res::WOOD:
+				return false;
+			}
+			break;
+		case Obj::WATER_WHEEL:
+			if (resID != Res::GOLD)
+				return false;
+			break;
+		case Obj::MYSTICAL_GARDEN:
+			if ((resID != Res::GOLD) && (resID != Res::GEMS))
+				return false;
+			break;
+		case Obj::LEAN_TO:
+		case Obj::WAGON:
+			if (resID != Res::GOLD)
+				return false;
+			break;
+		default:
+			return false;
+			break;
+		}
+		return !vstd::contains(ai->alreadyVisited, obj); //for weekly / once visitable
+	};
+
+	std::vector<const CGObjectInstance *> objs;
+	for (auto obj : ai->visitableObjs)
+	{
+		if (givesResource(obj))
+			objs.push_back(obj);
+	}
+	for (auto h : cb->getHeroesInfo())
+	{
+		std::vector<const CGObjectInstance *> ourObjs(objs); //copy common objects
+
+		for (auto obj : ai->reservedHeroesMap[h]) //add objects reserved by this hero
+		{
+			if (givesResource(obj))
+				ourObjs.push_back(obj);
+		}
+
+		for (auto obj : ourObjs)
+		{
+			auto waysToGo = ai->ah->howToVisitObj(h, ObjectIdRef(obj));
+
+			vstd::concatenate(ret, waysToGo);
+		}
+	}
+	return ret;
+}
+
+TSubgoal CollectRes::whatToDoToAchieve()
+{
+	auto goals = getAllPossibleSubgoals();
+	auto trade = whatToDoToTrade();
+	if (!trade->invalid())
+		goals.push_back(trade);
+
+	if (goals.empty())
+		return sptr(Explore()); //we can always do that
+	else
+		return fh->chooseSolution(goals); //TODO: evaluate trading
+}
+
+TSubgoal CollectRes::whatToDoToTrade()
+{
+	std::vector<const IMarket *> markets;
+
+	std::vector<const CGObjectInstance *> visObjs;
+	ai->retrieveVisitableObjs(visObjs, true);
+	for (const CGObjectInstance * obj : visObjs)
+	{
+		if (const IMarket * m = IMarket::castFrom(obj, false))
+		{
+			if (obj->ID == Obj::TOWN && obj->tempOwner == ai->playerID && m->allowsTrade(EMarketMode::RESOURCE_RESOURCE))
+				markets.push_back(m);
+			else if (obj->ID == Obj::TRADING_POST)
+				markets.push_back(m);
+		}
+	}
+
+	boost::sort(markets, [](const IMarket * m1, const IMarket * m2) -> bool
+	{
+		return m1->getMarketEfficiency() < m2->getMarketEfficiency();
+	});
+
+	markets.erase(boost::remove_if(markets, [](const IMarket * market) -> bool
+	{
+		if (!(market->o->ID == Obj::TOWN && market->o->tempOwner == ai->playerID))
+		{
+			if (!ai->isAccessible(market->o->visitablePos()))
+				return true;
+		}
+		return false;
+	}), markets.end());
+
+	if (!markets.size())
+	{
+		for (const CGTownInstance * t : cb->getTownsInfo())
+		{
+			if (cb->canBuildStructure(t, BuildingID::MARKETPLACE) == EBuildingState::ALLOWED)
+				return sptr(BuildThis(BuildingID::MARKETPLACE, t).setpriority(2));
+		}
+	}
+	else
+	{
+		const IMarket * m = markets.back();
+		//attempt trade at back (best prices)
+		int howManyCanWeBuy = 0;
+		for (Res::ERes i = Res::WOOD; i <= Res::GOLD; vstd::advance(i, 1))
+		{
+			if (i == resID)
+				continue;
+			int toGive = -1, toReceive = -1;
+			m->getOffer(i, resID, toGive, toReceive, EMarketMode::RESOURCE_RESOURCE);
+			assert(toGive > 0 && toReceive > 0);
+			howManyCanWeBuy += toReceive * (ai->ah->freeResources()[i] / toGive);
+		}
+
+		if (howManyCanWeBuy >= value)
+		{
+			auto backObj = cb->getTopObj(m->o->visitablePos()); //it'll be a hero if we have one there; otherwise marketplace
+			assert(backObj);
+			auto objid = m->o->id.getNum();
+			if (backObj->tempOwner != ai->playerID) //top object not owned
+			{
+				return sptr(VisitObj(objid)); //just go there
+			}
+			else //either it's our town, or we have hero there
+			{
+				return sptr(Trade(resID, value, objid).setisElementar(true)); //we can do this immediately
+			}
+		}
+	}
+	return sptr(Invalid()); //cannot trade
+}
+
+bool CollectRes::fulfillsMe(TSubgoal goal)
+{
+	if (goal->resID == resID)
+		if (goal->value >= value)
+			return true;
+
+	return false;
+}

+ 40 - 0
AI/VCAI/Goals/CollectRes.h

@@ -0,0 +1,40 @@
+/*
+* CollectRes.h, part of VCMI engine
+*
+* Authors: listed in file AUTHORS in main folder
+*
+* License: GNU General Public License v2.0 or later
+* Full text of license available in license.txt file, in main folder
+*
+*/
+#pragma once
+
+#include "CGoal.h"
+
+struct HeroPtr;
+class VCAI;
+class FuzzyHelper;
+
+namespace Goals
+{
+	class DLL_EXPORT CollectRes : public CGoal<CollectRes>
+	{
+	public:
+		CollectRes()
+			: CGoal(Goals::COLLECT_RES)
+		{
+		}
+		CollectRes(int rid, int val)
+			: CGoal(Goals::COLLECT_RES)
+		{
+			resID = rid;
+			value = val;
+			priority = 2;
+		}
+		TGoalVec getAllPossibleSubgoals() override;
+		TSubgoal whatToDoToAchieve() override;
+		TSubgoal whatToDoToTrade();
+		bool fulfillsMe(TSubgoal goal) override; //TODO: Trade
+		virtual bool operator==(const CollectRes & other) const override;
+	};
+}

+ 88 - 0
AI/VCAI/Goals/Conquer.cpp

@@ -0,0 +1,88 @@
+/*
+* Conquer.cpp, part of VCMI engine
+*
+* Authors: listed in file AUTHORS in main folder
+*
+* License: GNU General Public License v2.0 or later
+* Full text of license available in license.txt file, in main folder
+*
+*/
+#include "StdInc.h"
+#include "Goals.h"
+#include "../VCAI.h"
+#include "../AIUtility.h"
+#include "../AIhelper.h"
+#include "../FuzzyHelper.h"
+#include "../ResourceManager.h"
+#include "../BuildingManager.h"
+#include "../../../lib/mapping/CMap.h" //for victory conditions
+#include "../../../lib/CPathfinder.h"
+#include "../../../lib/StringConstants.h"
+
+
+extern boost::thread_specific_ptr<CCallback> cb;
+extern boost::thread_specific_ptr<VCAI> ai;
+extern FuzzyHelper * fh;
+
+using namespace Goals;
+
+bool Conquer::operator==(const Conquer & other) const
+{
+	return other.hero.h == hero.h;
+}
+
+TSubgoal Conquer::whatToDoToAchieve()
+{
+	return fh->chooseSolution(getAllPossibleSubgoals());
+}
+
+TGoalVec Conquer::getAllPossibleSubgoals()
+{
+	TGoalVec ret;
+
+	auto conquerable = [](const CGObjectInstance * obj) -> bool
+	{
+		if(cb->getPlayerRelations(ai->playerID, obj->tempOwner) == PlayerRelations::ENEMIES)
+		{
+			switch(obj->ID.num)
+			{
+			case Obj::TOWN:
+			case Obj::HERO:
+			case Obj::CREATURE_GENERATOR1:
+			case Obj::MINE: //TODO: check ai->knownSubterraneanGates
+				return true;
+			}
+		}
+		return false;
+	};
+
+	std::vector<const CGObjectInstance *> objs;
+	for(auto obj : ai->visitableObjs)
+	{
+		if(conquerable(obj))
+			objs.push_back(obj);
+	}
+
+	for(auto h : cb->getHeroesInfo())
+	{
+		std::vector<const CGObjectInstance *> ourObjs(objs); //copy common objects
+
+		for(auto obj : ai->reservedHeroesMap[h]) //add objects reserved by this hero
+		{
+			if(conquerable(obj))
+				ourObjs.push_back(obj);
+		}
+		for(auto obj : ourObjs)
+		{
+			auto waysToGo = ai->ah->howToVisitObj(h, ObjectIdRef(obj));
+
+			vstd::concatenate(ret, waysToGo);
+		}
+	}
+	if(!objs.empty() && ai->canRecruitAnyHero()) //probably no point to recruit hero if we see no objects to capture
+		ret.push_back(sptr(RecruitHero()));
+
+	if(ret.empty())
+		ret.push_back(sptr(Explore())); //we need to find an enemy
+	return ret;
+}

+ 32 - 0
AI/VCAI/Goals/Conquer.h

@@ -0,0 +1,32 @@
+/*
+* Conquer.h, part of VCMI engine
+*
+* Authors: listed in file AUTHORS in main folder
+*
+* License: GNU General Public License v2.0 or later
+* Full text of license available in license.txt file, in main folder
+*
+*/
+#pragma once
+
+#include "CGoal.h"
+
+struct HeroPtr;
+class VCAI;
+class FuzzyHelper;
+
+namespace Goals
+{
+	class DLL_EXPORT Conquer : public CGoal<Conquer>
+	{
+	public:
+		Conquer()
+			: CGoal(Goals::CONQUER)
+		{
+			priority = 10;
+		}
+		TGoalVec getAllPossibleSubgoals() override;
+		TSubgoal whatToDoToAchieve() override;
+		virtual bool operator==(const Conquer & other) const override;
+	};
+}

+ 39 - 0
AI/VCAI/Goals/DigAtTile.cpp

@@ -0,0 +1,39 @@
+/*
+* DigAtTile.cpp, part of VCMI engine
+*
+* Authors: listed in file AUTHORS in main folder
+*
+* License: GNU General Public License v2.0 or later
+* Full text of license available in license.txt file, in main folder
+*
+*/
+#include "StdInc.h"
+#include "DigAtTile.h"
+#include "VisitTile.h"
+#include "../VCAI.h"
+#include "../AIUtility.h"
+
+
+extern boost::thread_specific_ptr<CCallback> cb;
+extern boost::thread_specific_ptr<VCAI> ai;
+extern FuzzyHelper * fh;
+
+using namespace Goals;
+
+bool DigAtTile::operator==(const DigAtTile & other) const
+{
+	return other.hero.h == hero.h && other.tile == tile;
+}
+
+TSubgoal DigAtTile::whatToDoToAchieve()
+{
+	const CGObjectInstance * firstObj = vstd::frontOrNull(cb->getVisitableObjs(tile));
+	if(firstObj && firstObj->ID == Obj::HERO && firstObj->tempOwner == ai->playerID) //we have hero at dest
+	{
+		const CGHeroInstance * h = dynamic_cast<const CGHeroInstance *>(firstObj);
+		sethero(h).setisElementar(true);
+		return sptr(*this);
+	}
+
+	return sptr(VisitTile(tile));
+}

+ 41 - 0
AI/VCAI/Goals/DigAtTile.h

@@ -0,0 +1,41 @@
+/*
+* DigAtTile.h, part of VCMI engine
+*
+* Authors: listed in file AUTHORS in main folder
+*
+* License: GNU General Public License v2.0 or later
+* Full text of license available in license.txt file, in main folder
+*
+*/
+#pragma once
+
+#include "CGoal.h"
+
+struct HeroPtr;
+class VCAI;
+class FuzzyHelper;
+
+namespace Goals
+{
+	class DLL_EXPORT DigAtTile : public CGoal<DigAtTile>
+		//elementar with hero on tile
+	{
+	public:
+		DigAtTile()
+			: CGoal(Goals::DIG_AT_TILE)
+		{
+		}
+		DigAtTile(int3 Tile)
+			: CGoal(Goals::DIG_AT_TILE)
+		{
+			tile = Tile;
+			priority = 20;
+		}
+		TGoalVec getAllPossibleSubgoals() override
+		{
+			return TGoalVec();
+		}
+		TSubgoal whatToDoToAchieve() override;
+		virtual bool operator==(const DigAtTile & other) const override;
+	};
+}

+ 181 - 0
AI/VCAI/Goals/Explore.cpp

@@ -0,0 +1,181 @@
+/*
+* Explore.cpp, part of VCMI engine
+*
+* Authors: listed in file AUTHORS in main folder
+*
+* License: GNU General Public License v2.0 or later
+* Full text of license available in license.txt file, in main folder
+*
+*/
+#include "StdInc.h"
+#include "Goals.h"
+#include "../VCAI.h"
+#include "../AIUtility.h"
+#include "../AIhelper.h"
+#include "../FuzzyHelper.h"
+#include "../ResourceManager.h"
+#include "../BuildingManager.h"
+#include "../../../lib/mapping/CMap.h" //for victory conditions
+#include "../../../lib/CPathfinder.h"
+#include "../../../lib/StringConstants.h"
+
+
+extern boost::thread_specific_ptr<CCallback> cb;
+extern boost::thread_specific_ptr<VCAI> ai;
+extern FuzzyHelper * fh;
+
+using namespace Goals;
+
+bool Explore::operator==(const Explore & other) const
+{
+	return other.hero.h == hero.h;
+}
+
+std::string Explore::completeMessage() const
+{
+	return "Hero " + hero.get()->name + " completed exploration";
+}
+
+TSubgoal Explore::whatToDoToAchieve()
+{
+	auto ret = fh->chooseSolution(getAllPossibleSubgoals());
+	if(hero) //use best step for this hero
+	{
+		return ret;
+	}
+	else
+	{
+		if(ret->hero.get(true))
+			return sptr(sethero(ret->hero.h).setisAbstract(true)); //choose this hero and then continue with him
+		else
+			return ret; //other solutions, like buying hero from tavern
+	}
+}
+
+TGoalVec Explore::getAllPossibleSubgoals()
+{
+	TGoalVec ret;
+	std::vector<const CGHeroInstance *> heroes;
+
+	if(hero)
+	{
+		heroes.push_back(hero.h);
+	}
+	else
+	{
+		//heroes = ai->getUnblockedHeroes();
+		heroes = cb->getHeroesInfo();
+		vstd::erase_if(heroes, [](const HeroPtr h)
+		{
+			if(ai->getGoal(h)->goalType == EXPLORE) //do not reassign hero who is already explorer
+				return true;
+
+			if(!ai->isAbleToExplore(h))
+				return true;
+
+			return !h->movement; //saves time, immobile heroes are useless anyway
+		});
+	}
+
+	//try to use buildings that uncover map
+	std::vector<const CGObjectInstance *> objs;
+	for(auto obj : ai->visitableObjs)
+	{
+		if(!vstd::contains(ai->alreadyVisited, obj))
+		{
+			switch(obj->ID.num)
+			{
+			case Obj::REDWOOD_OBSERVATORY:
+			case Obj::PILLAR_OF_FIRE:
+			case Obj::CARTOGRAPHER:
+				objs.push_back(obj);
+				break;
+			case Obj::MONOLITH_ONE_WAY_ENTRANCE:
+			case Obj::MONOLITH_TWO_WAY:
+			case Obj::SUBTERRANEAN_GATE:
+				auto tObj = dynamic_cast<const CGTeleport *>(obj);
+				assert(ai->knownTeleportChannels.find(tObj->channel) != ai->knownTeleportChannels.end());
+				if(TeleportChannel::IMPASSABLE != ai->knownTeleportChannels[tObj->channel]->passability)
+					objs.push_back(obj);
+				break;
+			}
+		}
+		else
+		{
+			switch(obj->ID.num)
+			{
+			case Obj::MONOLITH_TWO_WAY:
+			case Obj::SUBTERRANEAN_GATE:
+				auto tObj = dynamic_cast<const CGTeleport *>(obj);
+				if(TeleportChannel::IMPASSABLE == ai->knownTeleportChannels[tObj->channel]->passability)
+					break;
+				for(auto exit : ai->knownTeleportChannels[tObj->channel]->exits)
+				{
+					if(!cb->getObj(exit))
+					{ // Always attempt to visit two-way teleports if one of channel exits is not visible
+						objs.push_back(obj);
+						break;
+					}
+				}
+				break;
+			}
+		}
+	}
+
+	auto primaryHero = ai->primaryHero().h;
+	for(auto h : heroes)
+	{
+		for(auto obj : objs) //double loop, performance risk?
+		{
+			auto waysToVisitObj = ai->ah->howToVisitObj(h, obj);
+
+			vstd::concatenate(ret, waysToVisitObj);
+		}
+
+		int3 t = whereToExplore(h);
+		if(t.valid())
+		{
+			ret.push_back(sptr(VisitTile(t).sethero(h)));
+		}
+		else
+		{
+			//FIXME: possible issues when gathering army to break
+			if(hero.h == h || //exporation is assigned to this hero
+				(!hero && h == primaryHero)) //not assigned to specific hero, let our main hero do the job
+			{
+				t = ai->explorationDesperate(h);  //check this only ONCE, high cost
+				if (t.valid()) //don't waste time if we are completely blocked
+				{
+					auto waysToVisitTile = ai->ah->howToVisitTile(h, t);
+
+					vstd::concatenate(ret, waysToVisitTile);
+					continue;
+				}
+			}
+			ai->markHeroUnableToExplore(h); //there is no freely accessible tile, do not poll this hero anymore
+		}
+	}
+	//we either don't have hero yet or none of heroes can explore
+	if((!hero || ret.empty()) && ai->canRecruitAnyHero())
+		ret.push_back(sptr(RecruitHero()));
+
+	if(ret.empty())
+	{
+		throw goalFulfilledException(sptr(Explore().sethero(hero)));
+	}
+	//throw cannotFulfillGoalException("Cannot explore - no possible ways found!");
+
+	return ret;
+}
+
+bool Explore::fulfillsMe(TSubgoal goal)
+{
+	if(goal->goalType == EXPLORE)
+	{
+		if(goal->hero)
+			return hero == goal->hero;
+		else
+			return true; //cancel ALL exploration
+	}
+	return false;
+}

+ 40 - 0
AI/VCAI/Goals/Explore.h

@@ -0,0 +1,40 @@
+/*
+* Explore.h, part of VCMI engine
+*
+* Authors: listed in file AUTHORS in main folder
+*
+* License: GNU General Public License v2.0 or later
+* Full text of license available in license.txt file, in main folder
+*
+*/
+#pragma once
+
+#include "CGoal.h"
+
+struct HeroPtr;
+class VCAI;
+class FuzzyHelper;
+
+namespace Goals
+{
+	class DLL_EXPORT Explore : public CGoal<Explore>
+	{
+	public:
+		Explore()
+			: CGoal(Goals::EXPLORE)
+		{
+			priority = 1;
+		}
+		Explore(HeroPtr h)
+			: CGoal(Goals::EXPLORE)
+		{
+			hero = h;
+			priority = 1;
+		}
+		TGoalVec getAllPossibleSubgoals() override;
+		TSubgoal whatToDoToAchieve() override;
+		std::string completeMessage() const override;
+		bool fulfillsMe(TSubgoal goal) override;
+		virtual bool operator==(const Explore & other) const override;
+	};
+}

+ 70 - 0
AI/VCAI/Goals/FindObj.cpp

@@ -0,0 +1,70 @@
+/*
+* FindObj.cpp, part of VCMI engine
+*
+* Authors: listed in file AUTHORS in main folder
+*
+* License: GNU General Public License v2.0 or later
+* Full text of license available in license.txt file, in main folder
+*
+*/
+#include "StdInc.h"
+#include "FindObj.h"
+#include "VisitObj.h"
+#include "Explore.h"
+#include "../VCAI.h"
+#include "../AIUtility.h"
+
+extern boost::thread_specific_ptr<CCallback> cb;
+extern boost::thread_specific_ptr<VCAI> ai;
+extern FuzzyHelper * fh;
+
+using namespace Goals;
+
+bool FindObj::operator==(const FindObj & other) const
+{
+	return other.hero.h == hero.h && other.objid == objid;
+}
+
+TSubgoal FindObj::whatToDoToAchieve()
+{
+	const CGObjectInstance * o = nullptr;
+	if(resID > -1) //specified
+	{
+		for(const CGObjectInstance * obj : ai->visitableObjs)
+		{
+			if(obj->ID == objid && obj->subID == resID)
+			{
+				o = obj;
+				break; //TODO: consider multiple objects and choose best
+			}
+		}
+	}
+	else
+	{
+		for(const CGObjectInstance * obj : ai->visitableObjs)
+		{
+			if(obj->ID == objid)
+			{
+				o = obj;
+				break; //TODO: consider multiple objects and choose best
+			}
+		}
+	}
+	if(o && ai->isAccessible(o->pos)) //we don't use isAccessibleForHero as we don't know which hero it is
+		return sptr(VisitObj(o->id.getNum()));
+	else
+		return sptr(Explore());
+}
+
+bool FindObj::fulfillsMe(TSubgoal goal)
+{
+	if (goal->goalType == VISIT_TILE) //visiting tile visits object at same time
+	{
+		if (!hero || hero == goal->hero)
+			for (auto obj : cb->getVisitableObjs(goal->tile)) //check if any object on that tile matches criteria
+				if (obj->visitablePos() == goal->tile) //object could be removed
+					if (obj->ID == objid && obj->subID == resID) //same type and subtype
+						return true;
+	}
+	return false;
+}

+ 47 - 0
AI/VCAI/Goals/FindObj.h

@@ -0,0 +1,47 @@
+/*
+* FindObj.h, part of VCMI engine
+*
+* Authors: listed in file AUTHORS in main folder
+*
+* License: GNU General Public License v2.0 or later
+* Full text of license available in license.txt file, in main folder
+*
+*/
+#pragma once
+
+#include "CGoal.h"
+
+struct HeroPtr;
+class VCAI;
+class FuzzyHelper;
+
+namespace Goals
+{
+	class DLL_EXPORT FindObj : public CGoal<FindObj>
+	{
+	public:
+		FindObj() {} // empty constructor not allowed
+
+		FindObj(int ID)
+			: CGoal(Goals::FIND_OBJ)
+		{
+			objid = ID;
+			resID = -1; //subid unspecified
+			priority = 1;
+		}
+		FindObj(int ID, int subID)
+			: CGoal(Goals::FIND_OBJ)
+		{
+			objid = ID;
+			resID = subID;
+			priority = 1;
+		}
+		TGoalVec getAllPossibleSubgoals() override
+		{
+			return TGoalVec();
+		}
+		TSubgoal whatToDoToAchieve() override;
+		bool fulfillsMe(TSubgoal goal) override;
+		virtual bool operator==(const FindObj & other) const override;
+	};
+}

+ 196 - 0
AI/VCAI/Goals/GatherArmy.cpp

@@ -0,0 +1,196 @@
+/*
+* GatherArmy.cpp, part of VCMI engine
+*
+* Authors: listed in file AUTHORS in main folder
+*
+* License: GNU General Public License v2.0 or later
+* Full text of license available in license.txt file, in main folder
+*
+*/
+#include "StdInc.h"
+#include "Goals.h"
+#include "../VCAI.h"
+#include "../AIUtility.h"
+#include "../AIhelper.h"
+#include "../FuzzyHelper.h"
+#include "../ResourceManager.h"
+#include "../BuildingManager.h"
+#include "../../../lib/mapping/CMap.h" //for victory conditions
+#include "../../../lib/CPathfinder.h"
+#include "../../../lib/StringConstants.h"
+
+
+extern boost::thread_specific_ptr<CCallback> cb;
+extern boost::thread_specific_ptr<VCAI> ai;
+extern FuzzyHelper * fh;
+
+using namespace Goals;
+
+bool GatherArmy::operator==(const GatherArmy & other) const
+{
+	return other.hero.h == hero.h || town == other.town;
+}
+
+std::string GatherArmy::completeMessage() const
+{
+	return "Hero " + hero.get()->name + " gathered army of value " + boost::lexical_cast<std::string>(value);
+}
+
+TSubgoal GatherArmy::whatToDoToAchieve()
+{
+	//TODO: find hero if none set
+	assert(hero.h);
+
+	return fh->chooseSolution(getAllPossibleSubgoals()); //find dwelling. use current hero to prevent him from doing nothing.
+}
+
+static const BuildingID unitsSource[] = { BuildingID::DWELL_LVL_1, BuildingID::DWELL_LVL_2, BuildingID::DWELL_LVL_3,
+	BuildingID::DWELL_LVL_4, BuildingID::DWELL_LVL_5, BuildingID::DWELL_LVL_6, BuildingID::DWELL_LVL_7};
+
+TGoalVec GatherArmy::getAllPossibleSubgoals()
+{
+	//get all possible towns, heroes and dwellings we may use
+	TGoalVec ret;
+
+	if(!hero.validAndSet())
+	{
+		return ret;
+	}
+
+	//TODO: include evaluation of monsters gather in calculation
+	for(auto t : cb->getTownsInfo())
+	{
+		auto pos = t->visitablePos();
+		if(ai->isAccessibleForHero(pos, hero))
+		{
+			//grab army from town
+			if(!t->visitingHero && howManyReinforcementsCanGet(hero, t))
+			{
+				if(!vstd::contains(ai->townVisitsThisWeek[hero], t))
+					ret.push_back(sptr(VisitTile(pos).sethero(hero)));
+			}
+			//buy army in town
+			if (!t->visitingHero || t->visitingHero == hero.get(true))
+			{
+				ui32 val = std::min<ui32>(value, howManyReinforcementsCanBuy(hero, t));
+				if (val)
+				{
+					auto goal = sptr(BuyArmy(t, val).sethero(hero));
+					if(!ai->ah->containsObjective(goal)) //avoid loops caused by reserving same objective twice
+						ret.push_back(goal);
+					else
+						logAi->debug("Can not buy army, because of ai->ah->containsObjective");
+				}
+			}
+			//build dwelling
+			//TODO: plan building over multiple turns?
+			//auto bid = ah->canBuildAnyStructure(t, std::vector<BuildingID>(unitsSource, unitsSource + ARRAY_COUNT(unitsSource)), 8 - cb->getDate(Date::DAY_OF_WEEK));
+
+			//Do not use below code for now, rely on generic Build. Code below needs to know a lot of town/resource context to do more good than harm
+			/*auto bid = ai->ah->canBuildAnyStructure(t, std::vector<BuildingID>(unitsSource, unitsSource + ARRAY_COUNT(unitsSource)), 1);
+			if (bid.is_initialized())
+			{
+				auto goal = sptr(BuildThis(bid.get(), t).setpriority(priority));
+				if (!ai->ah->containsObjective(goal)) //avoid loops caused by reserving same objective twice
+					ret.push_back(goal);
+				else
+					logAi->debug("Can not build a structure, because of ai->ah->containsObjective");
+			}*/
+		}
+	}
+
+	auto otherHeroes = cb->getHeroesInfo();
+	auto heroDummy = hero;
+	vstd::erase_if(otherHeroes, [heroDummy](const CGHeroInstance * h)
+	{
+		if(h == heroDummy.h)
+			return true;
+		else if(!ai->isAccessibleForHero(heroDummy->visitablePos(), h, true))
+			return true;
+		else if(!ai->canGetArmy(heroDummy.h, h)) //TODO: return actual aiValue
+			return true;
+		else if(ai->getGoal(h)->goalType == GATHER_ARMY)
+			return true;
+		else
+		   return false;
+	});
+	for(auto h : otherHeroes)
+	{
+		// Go to the other hero if we are faster
+		if (!vstd::contains(ai->visitedHeroes[hero], h)
+			&& ai->isAccessibleForHero(h->visitablePos(), hero, true)) //visit only once each turn //FIXME: this is only bug workaround
+			ret.push_back(sptr(VisitHero(h->id.getNum()).setisAbstract(true).sethero(hero)));
+		// Let the other hero come to us
+		if (!vstd::contains(ai->visitedHeroes[h], hero))
+			ret.push_back(sptr(VisitHero(hero->id.getNum()).setisAbstract(true).sethero(h)));
+	}
+
+	std::vector<const CGObjectInstance *> objs;
+	for(auto obj : ai->visitableObjs)
+	{
+		if(obj->ID == Obj::CREATURE_GENERATOR1)
+		{
+			auto relationToOwner = cb->getPlayerRelations(obj->getOwner(), ai->playerID);
+
+			//Use flagged dwellings only when there are available creatures that we can afford
+			if(relationToOwner == PlayerRelations::SAME_PLAYER)
+			{
+				auto dwelling = dynamic_cast<const CGDwelling *>(obj);
+
+				ui32 val = std::min<ui32>(value, howManyReinforcementsCanBuy(hero, dwelling));
+
+				if(val)
+				{
+					for(auto & creLevel : dwelling->creatures)
+					{
+						if(creLevel.first)
+						{
+							for(auto & creatureID : creLevel.second)
+							{
+								auto creature = VLC->creh->creatures[creatureID];
+								if(ai->ah->freeResources().canAfford(creature->cost))
+									objs.push_back(obj); //TODO: reserve resources?
+							}
+						}
+					}
+				}
+			}
+		}
+	}
+	for(auto h : cb->getHeroesInfo())
+	{
+		for(auto obj : objs)
+		{
+			//find safe dwelling
+			if(ai->isGoodForVisit(obj, h))
+			{
+				vstd::concatenate(ret, ai->ah->howToVisitObj(h, obj));
+			}
+		}
+	}
+
+	if(ai->canRecruitAnyHero() && ai->ah->freeGold() > GameConstants::HERO_GOLD_COST) //this is not stupid in early phase of game
+	{
+		if(auto t = ai->findTownWithTavern())
+		{
+			for(auto h : cb->getAvailableHeroes(t)) //we assume that all towns have same set of heroes
+			{
+				if(h && h->getTotalStrength() > 500) //do not buy heroes with single creatures for GatherArmy
+				{
+					ret.push_back(sptr(RecruitHero()));
+					break;
+				}
+			}
+		}
+	}
+
+	if(ret.empty())
+	{
+		if(hero == ai->primaryHero())
+			ret.push_back(sptr(Explore()));
+		else
+			throw cannotFulfillGoalException("No ways to gather army");
+	}
+
+	return ret;
+}

+ 38 - 0
AI/VCAI/Goals/GatherArmy.h

@@ -0,0 +1,38 @@
+/*
+* GatherArmy.h, part of VCMI engine
+*
+* Authors: listed in file AUTHORS in main folder
+*
+* License: GNU General Public License v2.0 or later
+* Full text of license available in license.txt file, in main folder
+*
+*/
+#pragma once
+
+#include "CGoal.h"
+
+struct HeroPtr;
+class VCAI;
+class FuzzyHelper;
+
+namespace Goals
+{
+	class DLL_EXPORT GatherArmy : public CGoal<GatherArmy>
+	{
+	public:
+		GatherArmy()
+			: CGoal(Goals::GATHER_ARMY)
+		{
+		}
+		GatherArmy(int val)
+			: CGoal(Goals::GATHER_ARMY)
+		{
+			value = val;
+			priority = 2.5;
+		}
+		TGoalVec getAllPossibleSubgoals() override;
+		TSubgoal whatToDoToAchieve() override;
+		std::string completeMessage() const override;
+		virtual bool operator==(const GatherArmy & other) const override;
+	};
+}

+ 139 - 0
AI/VCAI/Goals/GatherTroops.cpp

@@ -0,0 +1,139 @@
+/*
+* GatherTroops.cpp, part of VCMI engine
+*
+* Authors: listed in file AUTHORS in main folder
+*
+* License: GNU General Public License v2.0 or later
+* Full text of license available in license.txt file, in main folder
+*
+*/
+#include "StdInc.h"
+#include "Goals.h"
+#include "../VCAI.h"
+#include "../AIUtility.h"
+#include "../AIhelper.h"
+#include "../FuzzyHelper.h"
+#include "../ResourceManager.h"
+#include "../BuildingManager.h"
+#include "../../../lib/mapping/CMap.h" //for victory conditions
+#include "../../../lib/CPathfinder.h"
+#include "../../../lib/StringConstants.h"
+
+
+extern boost::thread_specific_ptr<CCallback> cb;
+extern boost::thread_specific_ptr<VCAI> ai;
+extern FuzzyHelper * fh;
+
+using namespace Goals;
+
+bool GatherTroops::operator==(const GatherTroops & other) const
+{
+	return objid == other.objid;
+}
+
+int GatherTroops::getCreaturesCount(const CArmedInstance * army)
+{
+	int count = 0;
+
+	for(auto stack : army->Slots())
+	{
+		if(objid == stack.second->getCreatureID().num)
+		{
+			count += stack.second->count;
+		}
+	}
+
+	return count;
+}
+
+TSubgoal GatherTroops::whatToDoToAchieve()
+{
+	auto heroes = cb->getHeroesInfo(true);
+
+	for(auto hero : heroes)
+	{
+		if(getCreaturesCount(hero) >= this->value)
+		{
+			logAi->trace("Completing GATHER_TROOPS by hero %s", hero->name);
+
+			throw goalFulfilledException(sptr(*this));
+		}
+	}
+
+	TGoalVec solutions = getAllPossibleSubgoals();
+
+	if(solutions.empty())
+		return sptr(Explore());
+
+	return fh->chooseSolution(solutions);
+}
+
+
+TGoalVec GatherTroops::getAllPossibleSubgoals()
+{
+	TGoalVec solutions;
+
+	for(const CGTownInstance * t : cb->getTownsInfo())
+	{
+		int count = getCreaturesCount(t->getUpperArmy());
+
+		if(count >= this->value)
+		{
+			vstd::concatenate(solutions, ai->ah->howToVisitObj(t));
+			continue;
+		}
+
+		auto creature = VLC->creh->creatures[objid];
+		if(t->subID == creature->faction) //TODO: how to force AI to build unupgraded creatures? :O
+		{
+			auto creatures = vstd::tryAt(t->town->creatures, creature->level - 1);
+			if(!creatures)
+				continue;
+
+			int upgradeNumber = vstd::find_pos(*creatures, creature->idNumber);
+			if(upgradeNumber < 0)
+				continue;
+
+			BuildingID bid(BuildingID::DWELL_FIRST + creature->level - 1 + upgradeNumber * GameConstants::CREATURES_PER_TOWN);
+			if(t->hasBuilt(bid)) //this assumes only creatures with dwellings are assigned to faction
+			{
+				solutions.push_back(sptr(BuyArmy(t, creature->AIValue * this->value).setobjid(objid)));
+			}
+			/*else //disable random building requests for now - this code needs to know a lot of town/resource context to do more good than harm
+			{	
+				return sptr(BuildThis(bid, t).setpriority(priority));
+			}*/
+		}
+	}
+	for(auto obj : ai->visitableObjs)
+	{
+		auto d = dynamic_cast<const CGDwelling *>(obj);
+
+		if(!d || obj->ID == Obj::TOWN)
+			continue;
+
+		for(auto creature : d->creatures)
+		{
+			if(creature.first) //there are more than 0 creatures avaliabe
+			{
+				for(auto type : creature.second)
+				{
+					if(type == objid && ai->ah->freeResources().canAfford(VLC->creh->creatures[type]->cost))
+						vstd::concatenate(solutions, ai->ah->howToVisitObj(obj));
+				}
+			}
+		}
+	}
+
+	return solutions;
+	//TODO: exchange troops between heroes
+}
+
+bool GatherTroops::fulfillsMe(TSubgoal goal)
+{
+	if (!hero || hero == goal->hero) //we got army for desired hero or any hero
+		if (goal->objid == objid) //same creature type //TODO: consider upgrades?
+			if (goal->value >= value) //notify every time we get resources?
+				return true;
+	return false;
+}

+ 43 - 0
AI/VCAI/Goals/GatherTroops.h

@@ -0,0 +1,43 @@
+/*
+* GatherTroops.h, part of VCMI engine
+*
+* Authors: listed in file AUTHORS in main folder
+*
+* License: GNU General Public License v2.0 or later
+* Full text of license available in license.txt file, in main folder
+*
+*/
+#pragma once
+
+#include "CGoal.h"
+
+struct HeroPtr;
+class VCAI;
+class FuzzyHelper;
+
+namespace Goals
+{
+	class DLL_EXPORT GatherTroops : public CGoal<GatherTroops>
+	{
+	public:
+		GatherTroops()
+			: CGoal(Goals::GATHER_TROOPS)
+		{
+			priority = 2;
+		}
+		GatherTroops(int type, int val)
+			: CGoal(Goals::GATHER_TROOPS)
+		{
+			objid = type;
+			value = val;
+			priority = 2;
+		}
+		TGoalVec getAllPossibleSubgoals() override;
+		TSubgoal whatToDoToAchieve() override;
+		bool fulfillsMe(TSubgoal goal) override;
+		virtual bool operator==(const GatherTroops & other) const override;
+
+	private:
+		int getCreaturesCount(const CArmedInstance * army);
+	};
+}

+ 31 - 0
AI/VCAI/Goals/GetArtOfType.cpp

@@ -0,0 +1,31 @@
+/*
+* GetArtOfType.cpp, part of VCMI engine
+*
+* Authors: listed in file AUTHORS in main folder
+*
+* License: GNU General Public License v2.0 or later
+* Full text of license available in license.txt file, in main folder
+*
+*/
+#include "StdInc.h"
+#include "GetArtOfType.h"
+#include "FindObj.h"
+#include "../VCAI.h"
+#include "../AIUtility.h"
+
+
+extern boost::thread_specific_ptr<CCallback> cb;
+extern boost::thread_specific_ptr<VCAI> ai;
+extern FuzzyHelper * fh;
+
+using namespace Goals;
+
+bool GetArtOfType::operator==(const GetArtOfType & other) const
+{
+	return other.hero.h == hero.h && other.objid == objid;
+}
+
+TSubgoal GetArtOfType::whatToDoToAchieve()
+{
+	return sptr(FindObj(Obj::ARTIFACT, aid));
+}

+ 40 - 0
AI/VCAI/Goals/GetArtOfType.h

@@ -0,0 +1,40 @@
+/*
+* GetArtOfType.h, part of VCMI engine
+*
+* Authors: listed in file AUTHORS in main folder
+*
+* License: GNU General Public License v2.0 or later
+* Full text of license available in license.txt file, in main folder
+*
+*/
+#pragma once
+
+#include "CGoal.h"
+
+struct HeroPtr;
+class VCAI;
+class FuzzyHelper;
+
+namespace Goals
+{
+	class DLL_EXPORT GetArtOfType : public CGoal<GetArtOfType>
+	{
+	public:
+		GetArtOfType()
+			: CGoal(Goals::GET_ART_TYPE)
+		{
+		}
+		GetArtOfType(int type)
+			: CGoal(Goals::GET_ART_TYPE)
+		{
+			aid = type;
+			priority = 2;
+		}
+		TGoalVec getAllPossibleSubgoals() override
+		{
+			return TGoalVec();
+		}
+		TSubgoal whatToDoToAchieve() override;
+		virtual bool operator==(const GetArtOfType & other) const override;
+	};
+}

+ 32 - 0
AI/VCAI/Goals/Goals.h

@@ -0,0 +1,32 @@
+/*
+* Goals.h, part of VCMI engine
+*
+* Authors: listed in file AUTHORS in main folder
+*
+* License: GNU General Public License v2.0 or later
+* Full text of license available in license.txt file, in main folder
+*
+*/
+#pragma once
+
+#include "CGoal.h"
+#include "Invalid.h"
+#include "BuildBoat.h"
+#include "Build.h"
+#include "BuildThis.h"
+#include "Conquer.h"
+#include "GatherArmy.h"
+#include "Win.h"
+#include "VisitObj.h"
+#include "VisitTile.h"
+#include "VisitHero.h"
+#include "Explore.h"
+#include "BuyArmy.h"
+#include "GatherTroops.h"
+#include "Trade.h"
+#include "CollectRes.h"
+#include "RecruitHero.h"
+#include "GetArtOfType.h"
+#include "ClearWayTo.h"
+#include "DigAtTile.h"
+#include "FindObj.h"

+ 41 - 0
AI/VCAI/Goals/Invalid.h

@@ -0,0 +1,41 @@
+/*
+* Invalid.h, part of VCMI engine
+*
+* Authors: listed in file AUTHORS in main folder
+*
+* License: GNU General Public License v2.0 or later
+* Full text of license available in license.txt file, in main folder
+*
+*/
+#pragma once
+
+#include "CGoal.h"
+
+struct HeroPtr;
+class VCAI;
+
+namespace Goals
+{
+	class DLL_EXPORT Invalid : public CGoal<Invalid>
+	{
+	public:
+		Invalid()
+			: CGoal(Goals::INVALID)
+		{
+			priority = -1e10;
+		}
+		TGoalVec getAllPossibleSubgoals() override
+		{
+			return TGoalVec();
+		}
+		TSubgoal whatToDoToAchieve() override
+		{
+			return iAmElementar();
+		}
+
+		virtual bool operator==(const Invalid & other) const override
+		{
+			return true;
+		}
+	};
+}

+ 38 - 0
AI/VCAI/Goals/RecruitHero.cpp

@@ -0,0 +1,38 @@
+/*
+* RecruitHero.cpp, part of VCMI engine
+*
+* Authors: listed in file AUTHORS in main folder
+*
+* License: GNU General Public License v2.0 or later
+* Full text of license available in license.txt file, in main folder
+*
+*/
+#include "StdInc.h"
+#include "Goals.h"
+#include "../VCAI.h"
+#include "../AIUtility.h"
+#include "../AIhelper.h"
+#include "../FuzzyHelper.h"
+#include "../ResourceManager.h"
+#include "../BuildingManager.h"
+#include "../../../lib/mapping/CMap.h" //for victory conditions
+#include "../../../lib/CPathfinder.h"
+#include "../../../lib/StringConstants.h"
+
+
+extern boost::thread_specific_ptr<CCallback> cb;
+extern boost::thread_specific_ptr<VCAI> ai;
+extern FuzzyHelper * fh;
+
+using namespace Goals;
+
+TSubgoal RecruitHero::whatToDoToAchieve()
+{
+	const CGTownInstance * t = ai->findTownWithTavern();
+	if(!t)
+		return sptr(BuildThis(BuildingID::TAVERN).setpriority(2));
+
+	TResources res;
+	res[Res::GOLD] = GameConstants::HERO_GOLD_COST;
+	return ai->ah->whatToDo(res, iAmElementar()); //either buy immediately, or collect res
+}

+ 41 - 0
AI/VCAI/Goals/RecruitHero.h

@@ -0,0 +1,41 @@
+/*
+* RecruitHero.h, part of VCMI engine
+*
+* Authors: listed in file AUTHORS in main folder
+*
+* License: GNU General Public License v2.0 or later
+* Full text of license available in license.txt file, in main folder
+*
+*/
+#pragma once
+
+#include "CGoal.h"
+
+struct HeroPtr;
+class VCAI;
+class FuzzyHelper;
+
+namespace Goals
+{
+	class DLL_EXPORT RecruitHero : public CGoal<RecruitHero>
+	{
+	public:
+		RecruitHero()
+			: CGoal(Goals::RECRUIT_HERO)
+		{
+			priority = 1;
+		}
+
+		TGoalVec getAllPossibleSubgoals() override
+		{
+			return TGoalVec();
+		}
+
+		TSubgoal whatToDoToAchieve() override;
+
+		virtual bool operator==(const RecruitHero & other) const override
+		{
+			return true;
+		}
+	};
+}

+ 23 - 0
AI/VCAI/Goals/Trade.cpp

@@ -0,0 +1,23 @@
+/*
+* Trade.cpp, part of VCMI engine
+*
+* Authors: listed in file AUTHORS in main folder
+*
+* License: GNU General Public License v2.0 or later
+* Full text of license available in license.txt file, in main folder
+*
+*/
+#include "StdInc.h"
+#include "Trade.h"
+
+using namespace Goals;
+
+bool Trade::operator==(const Trade & other) const
+{
+	return resID == other.resID;
+}
+
+TSubgoal Trade::whatToDoToAchieve()
+{
+	return iAmElementar();
+}

+ 38 - 0
AI/VCAI/Goals/Trade.h

@@ -0,0 +1,38 @@
+/*
+* Trade.h, part of VCMI engine
+*
+* Authors: listed in file AUTHORS in main folder
+*
+* License: GNU General Public License v2.0 or later
+* Full text of license available in license.txt file, in main folder
+*
+*/
+#pragma once
+
+#include "CGoal.h"
+
+struct HeroPtr;
+class VCAI;
+class FuzzyHelper;
+
+namespace Goals
+{
+	class DLL_EXPORT Trade : public CGoal<Trade>
+	{
+	public:
+		Trade()
+			: CGoal(Goals::TRADE)
+		{
+		}
+		Trade(int rid, int val, int Objid)
+			: CGoal(Goals::TRADE)
+		{
+			resID = rid;
+			value = val;
+			objid = Objid;
+			priority = 3; //trading is instant, but picking resources is free
+		}
+		TSubgoal whatToDoToAchieve() override;
+		virtual bool operator==(const Trade & other) const override;
+	};
+}

+ 74 - 0
AI/VCAI/Goals/VisitHero.cpp

@@ -0,0 +1,74 @@
+/*
+* VisitHero.cpp, part of VCMI engine
+*
+* Authors: listed in file AUTHORS in main folder
+*
+* License: GNU General Public License v2.0 or later
+* Full text of license available in license.txt file, in main folder
+*
+*/
+#include "StdInc.h"
+#include "VisitHero.h"
+#include "Explore.h"
+#include "Invalid.h"
+#include "../VCAI.h"
+#include "../AIUtility.h"
+#include "../AIhelper.h"
+#include "../FuzzyHelper.h"
+#include "../ResourceManager.h"
+#include "../BuildingManager.h"
+#include "../../../lib/mapping/CMap.h" //for victory conditions
+
+
+extern boost::thread_specific_ptr<CCallback> cb;
+extern boost::thread_specific_ptr<VCAI> ai;
+extern FuzzyHelper * fh;
+
+using namespace Goals;
+
+bool VisitHero::operator==(const VisitHero & other) const
+{
+	return other.hero.h == hero.h && other.objid == objid;
+}
+
+std::string VisitHero::completeMessage() const
+{
+	return "hero " + hero.get()->name + " visited hero " + boost::lexical_cast<std::string>(objid);
+}
+
+TSubgoal VisitHero::whatToDoToAchieve()
+{
+	const CGObjectInstance * obj = cb->getObj(ObjectInstanceID(objid));
+	if(!obj)
+		return sptr(Explore());
+	int3 pos = obj->visitablePos();
+
+	if(hero && ai->isAccessibleForHero(pos, hero, true) && isSafeToVisit(hero, pos)) //enemy heroes can get reinforcements
+	{
+		if(hero->pos == pos)
+			logAi->error("Hero %s tries to visit himself.", hero.name);
+		else
+		{
+			//can't use VISIT_TILE here as tile appears blocked by target hero
+			//FIXME: elementar goal should not be abstract
+			return sptr(VisitHero(objid).sethero(hero).settile(pos).setisElementar(true));
+		}
+	}
+	return sptr(Invalid());
+}
+
+bool VisitHero::fulfillsMe(TSubgoal goal)
+{
+	//TODO: VisitObj shoudl not be used for heroes, but...
+	if(goal->goalType == VISIT_TILE)
+	{
+		auto obj = cb->getObj(ObjectInstanceID(objid));
+		if (!obj)
+		{
+			logAi->error("Hero %s: VisitHero::fulfillsMe at %s: object %d not found", hero.name, goal->tile.toString(), objid);
+			return false;
+		}
+		return obj->visitablePos() == goal->tile;
+	}
+	return false;
+}

+ 42 - 0
AI/VCAI/Goals/VisitHero.h

@@ -0,0 +1,42 @@
+/*
+* VisitHero.h, part of VCMI engine
+*
+* Authors: listed in file AUTHORS in main folder
+*
+* License: GNU General Public License v2.0 or later
+* Full text of license available in license.txt file, in main folder
+*
+*/
+#pragma once
+
+#include "CGoal.h"
+
+struct HeroPtr;
+class VCAI;
+class FuzzyHelper;
+
+namespace Goals
+{
+	class DLL_EXPORT VisitHero : public CGoal<VisitHero>
+	{
+	public:
+		VisitHero()
+			: CGoal(Goals::VISIT_HERO)
+		{
+		}
+		VisitHero(int hid)
+			: CGoal(Goals::VISIT_HERO)
+		{
+			objid = hid;
+			priority = 4;
+		}
+		TGoalVec getAllPossibleSubgoals() override
+		{
+			return TGoalVec();
+		}
+		TSubgoal whatToDoToAchieve() override;
+		bool fulfillsMe(TSubgoal goal) override;
+		std::string completeMessage() const override;
+		virtual bool operator==(const VisitHero & other) const override;
+	};
+}

+ 112 - 0
AI/VCAI/Goals/VisitObj.cpp

@@ -0,0 +1,112 @@
+/*
+* VisitObj.cpp, part of VCMI engine
+*
+* Authors: listed in file AUTHORS in main folder
+*
+* License: GNU General Public License v2.0 or later
+* Full text of license available in license.txt file, in main folder
+*
+*/
+#include "StdInc.h"
+#include "Goals.h"
+#include "../VCAI.h"
+#include "../AIUtility.h"
+#include "../AIhelper.h"
+#include "../FuzzyHelper.h"
+#include "../ResourceManager.h"
+#include "../BuildingManager.h"
+#include "../../../lib/mapping/CMap.h" //for victory conditions
+#include "../../../lib/CPathfinder.h"
+#include "../../../lib/StringConstants.h"
+
+
+extern boost::thread_specific_ptr<CCallback> cb;
+extern boost::thread_specific_ptr<VCAI> ai;
+extern FuzzyHelper * fh;
+
+using namespace Goals;
+
+bool VisitObj::operator==(const VisitObj & other) const
+{
+	return other.hero.h == hero.h && other.objid == objid;
+}
+
+std::string VisitObj::completeMessage() const
+{
+	return "hero " + hero.get()->name + " captured Object ID = " + boost::lexical_cast<std::string>(objid);
+}
+
+TGoalVec VisitObj::getAllPossibleSubgoals()
+{
+	TGoalVec goalList;
+	const CGObjectInstance * obj = cb->getObjInstance(ObjectInstanceID(objid));
+	if(!obj)
+	{
+		throw cannotFulfillGoalException("Object is missing - goal is invalid now!");
+	}
+
+	int3 pos = obj->visitablePos();
+	if(hero)
+	{
+		if(ai->isAccessibleForHero(pos, hero))
+		{
+			if(isSafeToVisit(hero, pos))
+				goalList.push_back(sptr(VisitObj(obj->id.getNum()).sethero(hero)));
+			else
+				goalList.push_back(sptr(GatherArmy(evaluateDanger(pos, hero.h) * SAFE_ATTACK_CONSTANT).sethero(hero).setisAbstract(true)));
+
+			return goalList;
+		}
+	}
+	else
+	{
+		for(auto potentialVisitor : cb->getHeroesInfo())
+		{
+			if(ai->isAccessibleForHero(pos, potentialVisitor))
+			{
+				if(isSafeToVisit(potentialVisitor, pos))
+					goalList.push_back(sptr(VisitObj(obj->id.getNum()).sethero(potentialVisitor)));
+				else
+					goalList.push_back(sptr(GatherArmy(evaluateDanger(pos, potentialVisitor) * SAFE_ATTACK_CONSTANT).sethero(potentialVisitor).setisAbstract(true)));
+			}
+		}
+		if(!goalList.empty())
+		{
+			return goalList;
+		}
+	}
+
+	goalList.push_back(sptr(ClearWayTo(pos)));
+	return goalList;
+}
+
+TSubgoal VisitObj::whatToDoToAchieve()
+{
+	auto bestGoal = fh->chooseSolution(getAllPossibleSubgoals());
+
+	if(bestGoal->goalType == VISIT_OBJ && bestGoal->hero)
+		bestGoal->setisElementar(true);
+
+	return bestGoal;
+}
+
+VisitObj::VisitObj(int Objid) : CGoal(VISIT_OBJ)
+{
+	objid = Objid;
+	tile = ai->myCb->getObjInstance(ObjectInstanceID(objid))->visitablePos();
+	priority = 3;
+}
+
+bool VisitObj::fulfillsMe(TSubgoal goal)
+{
+	if(goal->goalType == VISIT_TILE)
+	{
+		if (!hero || hero == goal->hero)
+		{
+			auto obj = cb->getObjInstance(ObjectInstanceID(objid));
+			if (obj && obj->visitablePos() == goal->tile) //object could be removed
+				return true;
+		}
+	}
+	return false;
+}

+ 32 - 0
AI/VCAI/Goals/VisitObj.h

@@ -0,0 +1,32 @@
+/*
+* VisitObj.h, part of VCMI engine
+*
+* Authors: listed in file AUTHORS in main folder
+*
+* License: GNU General Public License v2.0 or later
+* Full text of license available in license.txt file, in main folder
+*
+*/
+#pragma once
+
+#include "CGoal.h"
+
+struct HeroPtr;
+class VCAI;
+class FuzzyHelper;
+
+namespace Goals
+{
+	class DLL_EXPORT VisitObj : public CGoal<VisitObj> //this goal was previously known as GetObj
+	{
+	public:
+		VisitObj() = delete; // empty constructor not allowed
+		VisitObj(int Objid);
+
+		TGoalVec getAllPossibleSubgoals() override;
+		TSubgoal whatToDoToAchieve() override;
+		bool fulfillsMe(TSubgoal goal) override;
+		std::string completeMessage() const override;
+		virtual bool operator==(const VisitObj & other) const override;
+	};
+}

+ 98 - 0
AI/VCAI/Goals/VisitTile.cpp

@@ -0,0 +1,98 @@
+/*
+* VisitTile.cpp, part of VCMI engine
+*
+* Authors: listed in file AUTHORS in main folder
+*
+* License: GNU General Public License v2.0 or later
+* Full text of license available in license.txt file, in main folder
+*
+*/
+#include "StdInc.h"
+#include "Goals.h"
+#include "../VCAI.h"
+#include "../AIUtility.h"
+#include "../AIhelper.h"
+#include "../FuzzyHelper.h"
+#include "../ResourceManager.h"
+#include "../BuildingManager.h"
+#include "../../../lib/mapping/CMap.h" //for victory conditions
+#include "../../../lib/CPathfinder.h"
+#include "../../../lib/StringConstants.h"
+
+
+extern boost::thread_specific_ptr<CCallback> cb;
+extern boost::thread_specific_ptr<VCAI> ai;
+extern FuzzyHelper * fh;
+
+using namespace Goals;
+
+bool VisitTile::operator==(const VisitTile & other) const
+{
+	return other.hero.h == hero.h && other.tile == tile;
+}
+
+std::string VisitTile::completeMessage() const
+{
+	return "Hero " + hero.get()->name + " visited tile " + tile.toString();
+}
+
+TSubgoal VisitTile::whatToDoToAchieve()
+{
+	auto ret = fh->chooseSolution(getAllPossibleSubgoals());
+
+	if(ret->hero)
+	{
+		if(isSafeToVisit(ret->hero, tile) && ai->isAccessibleForHero(tile, ret->hero))
+		{
+			ret->setisElementar(true);
+			return ret;
+		}
+		else
+		{
+			return sptr(GatherArmy(evaluateDanger(tile, *ret->hero) * SAFE_ATTACK_CONSTANT)
+				    .sethero(ret->hero).setisAbstract(true));
+		}
+	}
+	return ret;
+}
+
+TGoalVec VisitTile::getAllPossibleSubgoals()
+{
+	assert(cb->isInTheMap(tile));
+
+	TGoalVec ret;
+	if(!cb->isVisible(tile))
+		ret.push_back(sptr(Explore())); //what sense does it make?
+	else
+	{
+		std::vector<const CGHeroInstance *> heroes;
+		if(hero)
+			heroes.push_back(hero.h); //use assigned hero if any
+		else
+			heroes = cb->getHeroesInfo(); //use most convenient hero
+
+		for(auto h : heroes)
+		{
+			if(ai->isAccessibleForHero(tile, h))
+				ret.push_back(sptr(VisitTile(tile).sethero(h)));
+		}
+		if(ai->canRecruitAnyHero())
+			ret.push_back(sptr(RecruitHero()));
+	}
+	if(ret.empty())
+	{
+		auto obj = vstd::frontOrNull(cb->getVisitableObjs(tile));
+		if(obj && obj->ID == Obj::HERO && obj->tempOwner == ai->playerID) //our own hero stands on that tile
+		{
+			if(hero.get(true) && hero->id == obj->id) //if it's assigned hero, visit tile. If it's different hero, we can't visit tile now
+				ret.push_back(sptr(VisitTile(tile).sethero(dynamic_cast<const CGHeroInstance *>(obj)).setisElementar(true)));
+			else
+				throw cannotFulfillGoalException("Tile is already occupied by another hero "); //FIXME: we should give up this tile earlier
+		}
+		else
+			ret.push_back(sptr(ClearWayTo(tile)));
+	}
+
+	//important - at least one sub-goal must handle case which is impossible to fulfill (unreachable tile)
+	return ret;
+}

+ 37 - 0
AI/VCAI/Goals/VisitTile.h

@@ -0,0 +1,37 @@
+/*
+* VisitTile.h, part of VCMI engine
+*
+* Authors: listed in file AUTHORS in main folder
+*
+* License: GNU General Public License v2.0 or later
+* Full text of license available in license.txt file, in main folder
+*
+*/
+#pragma once
+
+#include "CGoal.h"
+
+struct HeroPtr;
+class VCAI;
+class FuzzyHelper;
+
+namespace Goals
+{
+	class DLL_EXPORT VisitTile : public CGoal<VisitTile>
+		//tile, in conjunction with hero elementar; assumes tile is reachable
+	{
+	public:
+		VisitTile() {} // empty constructor not allowed
+
+		VisitTile(int3 Tile)
+			: CGoal(Goals::VISIT_TILE)
+		{
+			tile = Tile;
+			priority = 5;
+		}
+		TGoalVec getAllPossibleSubgoals() override;
+		TSubgoal whatToDoToAchieve() override;
+		std::string completeMessage() const override;
+		virtual bool operator==(const VisitTile & other) const override;
+	};
+}

+ 191 - 0
AI/VCAI/Goals/Win.cpp

@@ -0,0 +1,191 @@
+/*
+* Win.cpp, part of VCMI engine
+*
+* Authors: listed in file AUTHORS in main folder
+*
+* License: GNU General Public License v2.0 or later
+* Full text of license available in license.txt file, in main folder
+*
+*/
+#include "StdInc.h"
+#include "Goals.h"
+#include "../VCAI.h"
+#include "../AIUtility.h"
+#include "../AIhelper.h"
+#include "../FuzzyHelper.h"
+#include "../ResourceManager.h"
+#include "../BuildingManager.h"
+#include "../../../lib/mapping/CMap.h" //for victory conditions
+#include "../../../lib/CPathfinder.h"
+#include "../../../lib/StringConstants.h"
+
+
+extern boost::thread_specific_ptr<CCallback> cb;
+extern boost::thread_specific_ptr<VCAI> ai;
+extern FuzzyHelper * fh;
+
+using namespace Goals;
+
+TSubgoal Win::whatToDoToAchieve()
+{
+	auto toBool = [=](const EventCondition &)
+	{
+		// TODO: proper implementation
+		// Right now even already fulfilled goals will be included into generated list
+		// Proper check should test if event condition is already fulfilled
+		// Easiest way to do this is to call CGameState::checkForVictory but this function should not be
+		// used on client side or in AI code
+		return false;
+	};
+
+	std::vector<EventCondition> goals;
+
+	for(const TriggeredEvent & event : cb->getMapHeader()->triggeredEvents)
+	{
+		//TODO: try to eliminate human player(s) using loss conditions that have isHuman element
+
+		if(event.effect.type == EventEffect::VICTORY)
+		{
+			boost::range::copy(event.trigger.getFulfillmentCandidates(toBool), std::back_inserter(goals));
+		}
+	}
+
+	//TODO: instead of returning first encountered goal AI should generate list of possible subgoals
+	for(const EventCondition & goal : goals)
+	{
+		switch(goal.condition)
+		{
+		case EventCondition::HAVE_ARTIFACT:
+			return sptr(GetArtOfType(goal.objectType));
+		case EventCondition::DESTROY:
+		{
+			if(goal.object)
+			{
+				auto obj = cb->getObj(goal.object->id);
+				if(obj)
+					if(obj->getOwner() == ai->playerID) //we can't capture our own object
+						return sptr(Conquer());
+
+
+				return sptr(VisitObj(goal.object->id.getNum()));
+			}
+			else
+			{
+				// TODO: destroy all objects of type goal.objectType
+				// This situation represents "kill all creatures" condition from H3
+				break;
+			}
+		}
+		case EventCondition::HAVE_BUILDING:
+		{
+			// TODO build other buildings apart from Grail
+			// goal.objectType = buidingID to build
+			// goal.object = optional, town in which building should be built
+			// Represents "Improve town" condition from H3 (but unlike H3 it consists from 2 separate conditions)
+
+			if(goal.objectType == BuildingID::GRAIL)
+			{
+				if(auto h = ai->getHeroWithGrail())
+				{
+					//hero is in a town that can host Grail
+					if(h->visitedTown && !vstd::contains(h->visitedTown->forbiddenBuildings, BuildingID::GRAIL))
+					{
+						const CGTownInstance * t = h->visitedTown;
+						return sptr(BuildThis(BuildingID::GRAIL, t).setpriority(10));
+					}
+					else
+					{
+						auto towns = cb->getTownsInfo();
+						towns.erase(boost::remove_if(towns,
+									     [](const CGTownInstance * t) -> bool
+							{
+								return vstd::contains(t->forbiddenBuildings, BuildingID::GRAIL);
+							}),
+							    towns.end());
+						boost::sort(towns, CDistanceSorter(h.get()));
+						if(towns.size())
+						{
+							return sptr(VisitTile(towns.front()->visitablePos()).sethero(h));
+						}
+					}
+				}
+				double ratio = 0;
+				// maybe make this check a bit more complex? For example:
+				// 0.75 -> dig randomly within 3 tiles radius
+				// 0.85 -> radius now 2 tiles
+				// 0.95 -> 1 tile radius, position is fully known
+				// AFAIK H3 AI does something like this
+				int3 grailPos = cb->getGrailPos(&ratio);
+				if(ratio > 0.99)
+				{
+					return sptr(DigAtTile(grailPos));
+				} //TODO: use FIND_OBJ
+				else if(const CGObjectInstance * obj = ai->getUnvisitedObj(objWithID<Obj::OBELISK>)) //there are unvisited Obelisks
+					return sptr(VisitObj(obj->id.getNum()));
+				else
+					return sptr(Explore());
+			}
+			break;
+		}
+		case EventCondition::CONTROL:
+		{
+			if(goal.object)
+			{
+				auto objRelations = cb->getPlayerRelations(ai->playerID, goal.object->tempOwner);
+				
+				if(objRelations == PlayerRelations::ENEMIES)
+				{
+					return sptr(VisitObj(goal.object->id.getNum()));
+				}
+				else
+				{
+					// TODO: Defance
+					break;
+				}
+			}
+			else
+			{
+				//TODO: control all objects of type "goal.objectType"
+				// Represents H3 condition "Flag all mines"
+				break;
+			}
+		}
+
+		case EventCondition::HAVE_RESOURCES:
+			//TODO mines? piles? marketplace?
+			//save?
+			return sptr(CollectRes(static_cast<Res::ERes>(goal.objectType), goal.value));
+		case EventCondition::HAVE_CREATURES:
+			return sptr(GatherTroops(goal.objectType, goal.value));
+		case EventCondition::TRANSPORT:
+		{
+			//TODO. merge with bring Grail to town? So AI will first dig grail, then transport it using this goal and builds it
+			// Represents "transport artifact" condition:
+			// goal.objectType = type of artifact
+			// goal.object = destination-town where artifact should be transported
+			break;
+		}
+		case EventCondition::STANDARD_WIN:
+			return sptr(Conquer());
+
+		// Conditions that likely don't need any implementation
+		case EventCondition::DAYS_PASSED:
+			break; // goal.value = number of days for condition to trigger
+		case EventCondition::DAYS_WITHOUT_TOWN:
+			break; // goal.value = number of days to trigger this
+		case EventCondition::IS_HUMAN:
+			break; // Should be only used in calculation of candidates (see toBool lambda)
+		case EventCondition::CONST_VALUE:
+			break;
+
+		case EventCondition::HAVE_0:
+		case EventCondition::HAVE_BUILDING_0:
+		case EventCondition::DESTROY_0:
+			//TODO: support new condition format
+			return sptr(Conquer());
+		default:
+			assert(0);
+		}
+	}
+	return sptr(Invalid());
+}

+ 39 - 0
AI/VCAI/Goals/Win.h

@@ -0,0 +1,39 @@
+/*
+* Win.h, part of VCMI engine
+*
+* Authors: listed in file AUTHORS in main folder
+*
+* License: GNU General Public License v2.0 or later
+* Full text of license available in license.txt file, in main folder
+*
+*/
+#pragma once
+
+#include "CGoal.h"
+
+struct HeroPtr;
+class VCAI;
+class FuzzyHelper;
+
+namespace Goals
+{
+	class DLL_EXPORT Win : public CGoal<Win>
+	{
+	public:
+		Win()
+			: CGoal(Goals::WIN)
+		{
+			priority = 100;
+		}
+		TGoalVec getAllPossibleSubgoals() override
+		{
+			return TGoalVec();
+		}
+		TSubgoal whatToDoToAchieve() override;
+
+		virtual bool operator==(const Win & other) const override
+		{
+			return true;
+		}
+	};
+}

+ 1 - 1
AI/VCAI/Pathfinding/AINodeStorage.cpp

@@ -1,5 +1,5 @@
 /*
-* AIhelper.h, part of VCMI engine
+* AINodeStorage.cpp, part of VCMI engine
 *
 * Authors: listed in file AUTHORS in main folder
 *

+ 2 - 2
AI/VCAI/Pathfinding/AINodeStorage.h

@@ -1,5 +1,5 @@
 /*
-* AIhelper.h, part of VCMI engine
+* AINodeStorage.h, part of VCMI engine
 *
 * Authors: listed in file AUTHORS in main folder
 *
@@ -13,7 +13,7 @@
 #include "../../../lib/CPathfinder.h"
 #include "../../../lib/mapObjects/CGHeroInstance.h"
 #include "../AIUtility.h"
-#include "../Goals.h"
+#include "../Goals/AbstractGoal.h"
 
 class ISpecialAction
 {

+ 2 - 2
AI/VCAI/Pathfinding/AIPathfinder.cpp

@@ -1,5 +1,5 @@
 /*
-* AIhelper.h, part of VCMI engine
+* AIPathfinder.cpp, part of VCMI engine
 *
 * Authors: listed in file AUTHORS in main folder
 *
@@ -67,4 +67,4 @@ std::vector<AIPath> AIPathfinder::getPathInfo(HeroPtr hero, int3 tile)
 	}
 
 	return nodeStorage->getChainInfo(tile, !tileInfo->isWater());
-}
+}

+ 1 - 1
AI/VCAI/Pathfinding/AIPathfinder.h

@@ -1,5 +1,5 @@
 /*
-* AIhelper.h, part of VCMI engine
+* AIPathfinder.h, part of VCMI engine
 *
 * Authors: listed in file AUTHORS in main folder
 *

+ 2 - 1
AI/VCAI/Pathfinding/AIPathfinderConfig.cpp

@@ -1,5 +1,5 @@
 /*
-* AIhelper.h, part of VCMI engine
+* AIPathfinderConfig.cpp, part of VCMI engine
 *
 * Authors: listed in file AUTHORS in main folder
 *
@@ -9,6 +9,7 @@
 */
 #include "StdInc.h"
 #include "AIPathfinderConfig.h"
+#include "../Goals/Goals.h"
 #include "../../../CCallback.h"
 #include "../../../lib/mapping/CMap.h"
 #include "../../../lib/mapObjects/MapObjects.h"

+ 2 - 2
AI/VCAI/Pathfinding/AIPathfinderConfig.h

@@ -1,5 +1,5 @@
 /*
-* AIhelper.h, part of VCMI engine
+* AIPathfinderConfig.h, part of VCMI engine
 *
 * Authors: listed in file AUTHORS in main folder
 *
@@ -23,4 +23,4 @@ namespace AIPathfinding
 			VCAI * ai,
 			std::shared_ptr<AINodeStorage> nodeStorage);
 	};
-}
+}

+ 3 - 2
AI/VCAI/Pathfinding/PathfindingManager.cpp

@@ -1,5 +1,5 @@
 /*
-* AIhelper.h, part of VCMI engine
+* PathfindingManager.cpp, part of VCMI engine
 *
 * Authors: listed in file AUTHORS in main folder
 *
@@ -11,6 +11,7 @@
 #include "PathfindingManager.h"
 #include "AIPathfinder.h"
 #include "AIPathfinderConfig.h"
+#include "Goals/Goals.h"
 #include "../../../lib/CGameInfoCallback.h"
 #include "../../../lib/mapping/CMap.h"
 
@@ -227,4 +228,4 @@ void PathfindingManager::resetPaths()
 {
 	logAi->debug("AIPathfinder has been reseted.");
 	pathfinder->clear();
-}
+}

+ 1 - 1
AI/VCAI/Pathfinding/PathfindingManager.h

@@ -1,5 +1,5 @@
 /*
-* AIhelper.h, part of VCMI engine
+* PathfindingManager.h, part of VCMI engine
 *
 * Authors: listed in file AUTHORS in main folder
 *

+ 1 - 0
AI/VCAI/ResourceManager.cpp

@@ -9,6 +9,7 @@
 */
 #include "StdInc.h"
 #include "ResourceManager.h"
+#include "Goals/Goals.h"
 
 #include "../../CCallback.h"
 #include "../../lib/mapObjects/MapObjects.h"

+ 0 - 1
AI/VCAI/ResourceManager.h

@@ -10,7 +10,6 @@
 #pragma once
 
 #include "AIUtility.h"
-#include "Goals.h"
 #include "../../lib/GameConstants.h"
 #include "../../lib/VCMI_Lib.h"
 #include "VCAI.h"

+ 1 - 0
AI/VCAI/VCAI.cpp

@@ -12,6 +12,7 @@
 #include "FuzzyHelper.h"
 #include "ResourceManager.h"
 #include "BuildingManager.h"
+#include "Goals/Goals.h"
 
 #include "../../lib/UnlockGuard.h"
 #include "../../lib/mapObjects/MapObjects.h"

+ 1 - 2
AI/VCAI/VCAI.h

@@ -10,8 +10,7 @@
 #pragma once
 
 #include "AIUtility.h"
-#include "SectorMap.h"
-#include "Goals.h"
+#include "Goals/AbstractGoal.h"
 #include "../../lib/AI_Base.h"
 #include "../../CCallback.h"