Răsfoiți Sursa

Fuzzy rework

Andrii Danylchenko 2 ani în urmă
părinte
comite
b1ca663eb6

+ 66 - 3
AI/Nullkiller/Engine/PriorityEvaluator.cpp

@@ -49,7 +49,8 @@ EvaluationContext::EvaluationContext(const Nullkiller * ai)
 	turn(0),
 	strategicalValue(0),
 	evaluator(ai),
-	enemyHeroDangerRatio(0)
+	enemyHeroDangerRatio(0),
+	armyGrowth(0)
 {
 }
 
@@ -64,6 +65,7 @@ void PriorityEvaluator::initVisitTile()
 	std::string str = std::string((char *)file.first.get(), file.second);
 	engine = fl::FllImporter().fromString(str);
 	armyLossPersentageVariable = engine->getInputVariable("armyLoss");
+	armyGrowthVariable = engine->getInputVariable("armyGrowth");
 	heroRoleVariable = engine->getInputVariable("heroRole");
 	dangerVariable = engine->getInputVariable("danger");
 	turnVariable = engine->getInputVariable("turn");
@@ -164,7 +166,7 @@ uint64_t getCreatureBankArmyReward(const CGObjectInstance * target, const CGHero
 	return result;
 }
 
-uint64_t getDwellingScore(CCallback * cb, const CGObjectInstance * target, bool checkGold)
+uint64_t getDwellingArmyValue(CCallback * cb, const CGObjectInstance * target, bool checkGold)
 {
 	auto dwelling = dynamic_cast<const CGDwelling *>(target);
 	uint64_t score = 0;
@@ -185,6 +187,27 @@ uint64_t getDwellingScore(CCallback * cb, const CGObjectInstance * target, bool
 	return score;
 }
 
+uint64_t getDwellingArmyGrowth(CCallback * cb, const CGObjectInstance * target, PlayerColor myColor)
+{
+	auto dwelling = dynamic_cast<const CGDwelling *>(target);
+	uint64_t score = 0;
+
+	if(dwelling->getOwner() == myColor)
+		return 0;
+
+	for(auto & creLevel : dwelling->creatures)
+	{
+		if(creLevel.second.size())
+		{
+			auto creature = creLevel.second.back().toCreature();
+
+			score += creature->getAIValue() * creature->getGrowth();
+		}
+	}
+
+	return score;
+}
+
 int getDwellingArmyCost(const CGObjectInstance * target)
 {
 	auto dwelling = dynamic_cast<const CGDwelling *>(target);
@@ -272,7 +295,7 @@ uint64_t RewardEvaluator::getArmyReward(
 	case Obj::CREATURE_GENERATOR2:
 	case Obj::CREATURE_GENERATOR3:
 	case Obj::CREATURE_GENERATOR4:
-		return getDwellingScore(ai->cb.get(), target, checkGold);
+		return getDwellingArmyValue(ai->cb.get(), target, checkGold);
 	case Obj::CRYPT:
 	case Obj::SHIPWRECK:
 	case Obj::SHIPWRECK_SURVIVOR:
@@ -293,6 +316,44 @@ uint64_t RewardEvaluator::getArmyReward(
 	}
 }
 
+uint64_t RewardEvaluator::getArmyGrowth(
+	const CGObjectInstance * target,
+	const CGHeroInstance * hero,
+	const CCreatureSet * army) const
+{
+	const float enemyArmyEliminationRewardRatio = 0.5f;
+
+	if(!target)
+		return 0;
+
+	switch(target->ID)
+	{
+	case Obj::TOWN:
+	{
+		auto town = dynamic_cast<const CGTownInstance *>(target);
+		auto fortLevel = town->fortLevel();
+		auto neutral = !town->getOwner().isValidPlayer();
+		auto booster = isAnotherAi(town, *ai->cb) ||  neutral ? 1 : 2;
+
+		if(fortLevel < CGTownInstance::CITADEL)
+			return town->hasFort() ? booster * 500 : 0;
+		else
+			return booster * (fortLevel == CGTownInstance::CASTLE ? 5000 : 2000);
+	}
+
+	case Obj::CREATURE_GENERATOR1:
+	case Obj::CREATURE_GENERATOR2:
+	case Obj::CREATURE_GENERATOR3:
+	case Obj::CREATURE_GENERATOR4:
+		return getDwellingArmyGrowth(ai->cb.get(), target, hero->getOwner());
+	case Obj::ARTIFACT:
+		// it is not supported now because hero will not sit in town on 7th day but later parts of legion may be counted as army growth as well.
+		return 0;
+	default:
+		return 0;
+	}
+}
+
 int RewardEvaluator::getGoldCost(const CGObjectInstance * target, const CGHeroInstance * hero, const CCreatureSet * army) const
 {
 	if(!target)
@@ -714,6 +775,7 @@ public:
 		{
 			evaluationContext.goldReward += evaluationContext.evaluator.getGoldReward(target, hero);
 			evaluationContext.armyReward += evaluationContext.evaluator.getArmyReward(target, hero, army, checkGold);
+			evaluationContext.armyGrowth += evaluationContext.evaluator.getArmyGrowth(target, hero, army);
 			evaluationContext.skillReward += evaluationContext.evaluator.getSkillReward(target, hero, evaluationContext.heroRole);
 			evaluationContext.strategicalValue += evaluationContext.evaluator.getStrategicalValue(target);
 			evaluationContext.goldCost += evaluationContext.evaluator.getGoldCost(target, hero, army);
@@ -920,6 +982,7 @@ float PriorityEvaluator::evaluate(Goals::TSubgoal task)
 		scoutTurnDistanceVariable->setValue(evaluationContext.movementCostByRole[HeroRole::SCOUT]);
 		goldRewardVariable->setValue(evaluationContext.goldReward);
 		armyRewardVariable->setValue(evaluationContext.armyReward);
+		armyGrowthVariable->setValue(evaluationContext.armyGrowth);
 		skillRewardVariable->setValue(evaluationContext.skillReward);
 		dangerVariable->setValue(evaluationContext.danger);
 		rewardTypeVariable->setValue(rewardType);

+ 3 - 0
AI/Nullkiller/Engine/PriorityEvaluator.h

@@ -33,6 +33,7 @@ public:
 	RewardEvaluator(const Nullkiller * ai) : ai(ai) {}
 
 	uint64_t getArmyReward(const CGObjectInstance * target, const CGHeroInstance * hero, const CCreatureSet * army, bool checkGold) const;
+	uint64_t getArmyGrowth(const CGObjectInstance * target, const CGHeroInstance * hero, const CCreatureSet * army) const;
 	int getGoldCost(const CGObjectInstance * target, const CGHeroInstance * hero, const CCreatureSet * army) const;
 	float getEnemyHeroStrategicalValue(const CGHeroInstance * enemy) const;
 	float getResourceRequirementStrength(int resType) const;
@@ -54,6 +55,7 @@ struct DLL_EXPORT EvaluationContext
 	float closestWayRatio;
 	float armyLossPersentage;
 	float armyReward;
+	uint64_t armyGrowth;
 	int32_t goldReward;
 	int32_t goldCost;
 	float skillReward;
@@ -95,6 +97,7 @@ private:
 	fl::InputVariable * turnVariable;
 	fl::InputVariable * goldRewardVariable;
 	fl::InputVariable * armyRewardVariable;
+	fl::InputVariable * armyGrowthVariable;
 	fl::InputVariable * dangerVariable;
 	fl::InputVariable * skillRewardVariable;
 	fl::InputVariable * strategicalValueVariable;

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

@@ -879,7 +879,7 @@ void AINodeStorage::setHeroes(std::map<const CGHeroInstance *, HeroRole> heroes)
 	for(auto & hero : heroes)
 	{
 		// do not allow our own heroes in garrison to act on map
-		if(hero.first->getOwner() == ai->playerID && hero.first->inTownGarrison && ai->isHeroLocked(hero.first))
+		if(hero.first->getOwner() == ai->playerID && hero.first->inTownGarrison)
 			continue;
 
 		uint64_t mask = FirstActorMask << actors.size();

+ 23 - 90
config/ai/object-priorities.txt

@@ -121,106 +121,39 @@ InputVariable: fear
   term: LOW Triangle 0.000 0.500 1.000
   term: MEDIUM Triangle 0.500 1.000 1.500
   term: HIGH Ramp 1.000 1.800
+InputVariable: armyGrowth
+  enabled: true
+  range: 0.000 20000.000
+  lock-range: false
+  term: NONE Ramp 100.000 0.000
+  term: SMALL Triangle 0.000 1000.000 3000.000
+  term: MEDIUM Triangle 1000.000 3000.000 8000.000
+  term: BIG Triangle 3000.000 8000.000 20000.000
+  term: HUGE Ramp 8000.000 20000.000
 OutputVariable: Value
   enabled: true
-  range: -0.500 1.500
+  range: -1.500 2.000
   lock-range: false
   aggregation: AlgebraicSum
   defuzzifier: Centroid 100
   default: 0.500
   lock-previous: false
-  term: LOWEST Discrete -0.500 0.000 -0.500 1.000 -0.200 1.000 -0.200 0.000 0.200 0.000 0.200 1.000 0.500 1.000 0.500 0.000 0.500
-  term: BITLOW Rectangle -0.010 0.010 0.500
-  term: LOW Discrete -0.150 0.000 -0.150 1.000 -0.050 1.000 -0.050 0.000 0.050 0.000 0.050 1.000 0.150 1.000 0.150 0.000 0.500
-  term: MEDIUM Triangle 0.450 0.500 0.550 0.050
-  term: HIGH Discrete 0.850 0.000 0.850 1.000 0.950 1.000 0.950 0.000 1.050 0.000 1.050 1.000 1.150 1.000 1.150 0.000 0.500
-  term: HIGHEST Discrete 0.500 0.000 0.500 1.000 0.800 1.000 0.800 0.000 1.200 0.000 1.200 1.000 1.500 1.000 1.500 0.000 0.500
-  term: BITHIGH Rectangle 0.990 1.010 0.500
+  term: WORST Binary -1.000 -inf 0.500
+  term: BAD Rectangle -1.000 -0.700 0.500
+  term: BASE Rectangle -0.200 0.200 0.400
+  term: LOW Rectangle 1.110 1.190 0.320
+  term: HIGHEST Discrete 0.300 0.000 0.300 1.000 0.600 1.000 0.600 0.000 1.700 0.000 1.700 1.000 2.000 1.000 2.000 0.000 0.500
+  term: HIGH Discrete 0.600 0.000 0.600 1.000 0.850 1.000 0.850 0.000 1.450 0.000 1.450 1.000 1.700 1.000 1.700 0.000 0.400
+  term: BITHIGH Discrete 0.850 0.000 0.850 1.000 1.000 1.000 1.000 0.000 1.300 0.000 1.300 1.000 1.450 1.000 1.450 0.000 0.350
+  term: MEDIUM Discrete 1.000 0.000 1.000 1.000 1.100 1.000 1.100 0.000 1.200 0.000 1.200 1.000 1.300 1.000 1.300 0.000 0.330
 RuleBlock: gold reward
   enabled: true
   conjunction: AlgebraicProduct
   disjunction: AlgebraicSum
   implication: AlgebraicProduct
   activation: General
-  rule: if turn is NOW and mainTurnDistance is very LONG and heroRole is SCOUT then Value is LOW with 0.5
-  rule: if turn is NOW and mainTurnDistance is LONG and heroRole is SCOUT then Value is LOW with 0.3
-  rule: if turn is NOW and scoutTurnDistance is LONG and heroRole is SCOUT then Value is LOW with 0.3
-  rule: if turn is NOW and mainTurnDistance is LONG and heroRole is MAIN then Value is LOW with 0.3
-  rule: if turn is NEXT and mainTurnDistance is very LONG and heroRole is SCOUT then Value is LOW with 0.8
-  rule: if turn is NEXT and scoutTurnDistance is LONG and heroRole is SCOUT then Value is BITLOW
-  rule: if turn is NEXT and mainTurnDistance is LONG and heroRole is MAIN then Value is LOW with 0.3
-  rule: if turn is NEXT and mainTurnDistance is LONG and heroRole is SCOUT then Value is BITLOW with 0.3
-  rule: if turn is FUTURE and scoutTurnDistance is very LONG and heroRole is SCOUT then Value is LOWEST with 0.3
-  rule: if turn is FUTURE and mainTurnDistance is very LONG and heroRole is SCOUT then Value is LOWEST with 0.5
-  rule: if turn is FUTURE and mainTurnDistance is very LONG and heroRole is MAIN and strategicalValue is NONE then Value is LOWEST with 0.5
-  rule: if turn is FUTURE and mainTurnDistance is very LONG and heroRole is MAIN and strategicalValue is LOW then Value is LOWEST with 0.3
-  rule: if turn is FUTURE and mainTurnDistance is very LONG and heroRole is MAIN and strategicalValue is MEDIUM then Value is LOW with 0.5
-  rule: if turn is FUTURE and mainTurnDistance is very LONG and heroRole is MAIN and strategicalValue is HIGH then Value is BITLOW
-  rule: if turn is FUTURE and scoutTurnDistance is LONG and heroRole is SCOUT then Value is LOW
-  rule: if turn is FUTURE and mainTurnDistance is LONG and heroRole is MAIN then Value is LOW
-  rule: if turn is FUTURE and mainTurnDistance is LONG and heroRole is SCOUT then Value is LOW
-  rule: if scoutTurnDistance is MEDIUM and heroRole is SCOUT then Value is BITLOW
-  rule: if mainTurnDistance is MEDIUM then Value is BITLOW
-  rule: if scoutTurnDistance is LOW and heroRole is SCOUT then Value is MEDIUM
-  rule: if mainTurnDistance is LOW then Value is MEDIUM
-  rule: if goldReward is HIGH and goldPreasure is HIGH and heroRole is SCOUT and danger is not NONE and armyLoss is LOW then Value is BITHIGH
-  rule: if goldReward is HIGH and goldPreasure is HIGH and heroRole is SCOUT and danger is NONE then Value is HIGH with 0.7
-  rule: if goldReward is HIGH and goldPreasure is HIGH and heroRole is MAIN and danger is not NONE and armyLoss is LOW and fear is not HIGH then Value is HIGHEST
-  rule: if goldReward is HIGH and goldPreasure is HIGH and heroRole is MAIN and danger is NONE then Value is BITHIGH
-  rule: if goldReward is MEDIUM and goldPreasure is HIGH and heroRole is SCOUT and danger is NONE then Value is HIGH
-  rule: if goldReward is MEDIUM and goldPreasure is HIGH and armyLoss is LOW and heroRole is SCOUT and danger is not NONE then Value is MEDIUM
-  rule: if goldReward is MEDIUM and heroRole is MAIN and danger is NONE and rewardType is SINGLE then Value is BITLOW
-  rule: if goldReward is MEDIUM and goldPreasure is HIGH and armyLoss is LOW and heroRole is MAIN and danger is not NONE then Value is BITHIGH
-  rule: if goldReward is LOW and goldPreasure is HIGH and heroRole is SCOUT and armyLoss is LOW then Value is BITHIGH
-  rule: if goldReward is LOW and heroRole is MAIN and danger is not NONE and rewardType is SINGLE and armyLoss is LOW then Value is BITLOW
-  rule: if goldReward is LOW and heroRole is MAIN and danger is NONE and rewardType is SINGLE then Value is LOW
-  rule: if goldReward is LOWEST and heroRole is MAIN and danger is NONE and rewardType is SINGLE then Value is LOWEST
-  rule: if armyReward is HIGH and heroRole is SCOUT and danger is not NONE and armyLoss is LOW then Value is HIGH with 0.5
-  rule: if armyReward is HIGH and heroRole is SCOUT and danger is NONE then Value is HIGHEST
-  rule: if armyReward is HIGH and heroRole is MAIN and rewardType is MIXED and armyLoss is LOW and fear is not HIGH then Value is HIGHEST
-  rule: if armyReward is HIGH and heroRole is MAIN and rewardType is SINGLE and mainTurnDistance is LOWEST then Value is HIGHEST
-  rule: if armyReward is HIGH and heroRole is MAIN and rewardType is SINGLE and danger is NONE and fear is not HIGH then Value is HIGH
-  rule: if armyReward is HIGH and heroRole is MAIN and rewardType is SINGLE and danger is not NONE and armyLoss is LOW and fear is not HIGH then Value is HIGHEST
-  rule: if armyReward is MEDIUM and heroRole is MAIN and danger is not NONE and armyLoss is LOW and fear is not HIGH then Value is HIGHEST with 0.5
-  rule: if armyReward is MEDIUM and heroRole is MAIN and danger is NONE then Value is BITHIGH
-  rule: if armyReward is MEDIUM and heroRole is MAIN and danger is NONE and mainTurnDistance is LOWEST then Value is HIGH with 0.2
-  rule: if armyReward is MEDIUM and heroRole is SCOUT and danger is NONE then Value is HIGHEST with 0.5
-  rule: if armyReward is LOW and heroRole is SCOUT and danger is NONE then Value is HIGH
-  rule: if armyReward is LOW and heroRole is MAIN and danger is not NONE and armyLoss is LOW then Value is HIGH
-  rule: if armyReward is LOW and heroRole is MAIN and danger is NONE then Value is BITLOW with 0.5
-  rule: if armyReward is LOW and heroRole is MAIN and danger is NONE and mainTurnDistance is LOWEST then Value is HIGH
-  rule: if skillReward is LOW and heroRole is MAIN and armyLoss is LOW then Value is BITHIGH
-  rule: if skillReward is MEDIUM and heroRole is MAIN and armyLoss is LOW and fear is not HIGH then Value is BITHIGH
-  rule: if skillReward is MEDIUM and heroRole is MAIN and rewardType is MIXED and armyLoss is LOW and fear is not HIGH then Value is HIGH with 0.5
-  rule: if skillReward is HIGH and heroRole is MAIN and armyLoss is LOW and fear is not HIGH then Value is HIGH
-  rule: if skillReward is MEDIUM and heroRole is SCOUT then Value is LOWEST
-  rule: if skillReward is HIGH and heroRole is SCOUT then Value is LOWEST
-  rule: if strategicalValue is LOW and heroRole is MAIN and armyLoss is LOW then Value is BITHIGH
-  rule: if strategicalValue is LOWEST and heroRole is MAIN and armyLoss is LOW then Value is LOW
-  rule: if strategicalValue is LOW and heroRole is SCOUT and armyLoss is LOW and fear is not HIGH then Value is HIGH with 0.5
-  rule: if strategicalValue is MEDIUM and heroRole is SCOUT and danger is NONE and fear is not HIGH then Value is HIGH
-  rule: if strategicalValue is HIGH and heroRole is SCOUT and danger is NONE and fear is not HIGH then Value is HIGHEST with 0.5
-  rule: if strategicalValue is HIGH and heroRole is MAIN and armyLoss is LOW and fear is not HIGH then Value is HIGHEST
-  rule: if strategicalValue is HIGH and heroRole is MAIN and armyLoss is MEDIUM and fear is not HIGH then Value is HIGH
-  rule: if strategicalValue is MEDIUM and heroRole is MAIN and armyLoss is LOW and fear is not HIGH then Value is HIGH
-  rule: if rewardType is NONE then Value is LOWEST
-  rule: if armyLoss is HIGH and strategicalValue is not HIGH and heroRole is MAIN then Value is LOWEST
-  rule: if armyLoss is HIGH and strategicalValue is HIGH and heroRole is MAIN then Value is LOW
-  rule: if armyLoss is HIGH and heroRole is SCOUT then Value is LOWEST
-  rule: if heroRole is SCOUT and closestHeroRatio is LOW then Value is LOW
-  rule: if heroRole is SCOUT and closestHeroRatio is LOWEST then Value is LOWEST
-  rule: if heroRole is MAIN and danger is NONE and skillReward is NONE and rewardType is SINGLE and closestHeroRatio is LOW then Value is LOW
-  rule: if heroRole is MAIN and danger is NONE and skillReward is NONE and rewardType is SINGLE and closestHeroRatio is LOWEST then Value is LOWEST
-  rule: if heroRole is MAIN and danger is not NONE and armyLoss is LOW then Value is BITHIGH with 0.2
-  rule: if heroRole is SCOUT then Value is BITLOW
-  rule: if goldCost is not NONE and goldReward is NONE and goldPreasure is HIGH then Value is LOWEST
-  rule: if turn is NOW then Value is LOW with 0.3
-  rule: if turn is not NOW then Value is LOW with 0.4
-  rule: if goldPreasure is HIGH and goldReward is HIGH and heroRole is MAIN and danger is not NONE and armyLoss is LOW and fear is not HIGH then Value is HIGHEST
-  rule: if goldPreasure is HIGH and goldReward is MEDIUM and heroRole is MAIN and danger is not NONE and armyLoss is LOW and fear is not HIGH then Value is HIGH
-  rule: if goldPreasure is HIGH and goldReward is HIGH and heroRole is SCOUT and danger is NONE and armyLoss is LOW and fear is not HIGH then Value is HIGHEST
-  rule: if goldPreasure is HIGH and goldReward is MEDIUM and heroRole is SCOUT and danger is NONE and armyLoss is LOW and fear is not HIGH then Value is HIGH
-  rule: if goldPreasure is HIGH and goldReward is LOW and heroRole is SCOUT and armyLoss is LOW then Value is BITHIGH
-  rule: if goldPreasure is HIGH and goldReward is LOW and heroRole is SCOUT and scoutTurnDistance is LOW and armyLoss is LOW then Value is HIGH with 0.5
-  rule: if fear is MEDIUM then Value is LOW
-  rule: if fear is HIGH then Value is LOWEST
+  rule: if heroRole is MAIN then Value is BASE
+  rule: if heroRole is SCOUT then Value is BASE
+  rule: if heroRole is MAIN and armyGrowth is HUGE then Value is HIGH
+  rule: if heroRole is MAIN and armyGrowth is BIG then Value is BITHIGH
+  rule: if heroRole is MAIN and strategicalValue is HIGH then Value is HIGHEST