From b0cd4f411737cee63113c3e5383ad1ba9e566285 Mon Sep 17 00:00:00 2001 From: DjWarmonger Date: Sat, 22 Nov 2014 18:57:17 +0100 Subject: [PATCH 1/7] Added limits and assertions which show that fuzzy engine is completely bugged. --- AI/VCAI/AIUtility.cpp | 7 +++++-- AI/VCAI/Fuzzy.cpp | 11 ++++++++++- 2 files changed, 15 insertions(+), 3 deletions(-) diff --git a/AI/VCAI/AIUtility.cpp b/AI/VCAI/AIUtility.cpp index 9065d0e9b..5211daf40 100644 --- a/AI/VCAI/AIUtility.cpp +++ b/AI/VCAI/AIUtility.cpp @@ -234,8 +234,11 @@ ui64 evaluateDanger(crint3 tile, const CGHeroInstance *visitor) { //TODO: don't downcast objects AI shouldn't know about! auto armedObj = dynamic_cast(dangerousObject); - if(armedObj) - objectDanger *= fh->getTacticalAdvantage(visitor, armedObj); //this line tends to go infinite for allied towns (?) + if (armedObj) + { + float tacticalAdvantage = fh->getTacticalAdvantage(visitor, armedObj); + objectDanger *= tacticalAdvantage; //this line tends to go infinite for allied towns (?) + } } if (dangerousObject->ID == Obj::SUBTERRANEAN_GATE) { //check guard on the other side of the gate diff --git a/AI/VCAI/Fuzzy.cpp b/AI/VCAI/Fuzzy.cpp index 676c1ed7f..b5860f9b5 100644 --- a/AI/VCAI/Fuzzy.cpp +++ b/AI/VCAI/Fuzzy.cpp @@ -20,7 +20,7 @@ */ #define MIN_AI_STRENGHT (0.5f) //lower when combat AI gets smarter -#define UNGUARDED_OBJECT (100.0f) //we consider unguarded objects 10 times weaker than us +#define UNGUARDED_OBJECT (100.0f) //we consider unguarded objects 100 times weaker than us struct BankConfig; class Engine; @@ -94,6 +94,7 @@ void FuzzyHelper::initBank() //Trivial bank estimation bankInput = new fl::InputVariable("BankInput"); bankDanger = new fl::OutputVariable("BankDanger"); + bankDanger->setMinimum(0); bankInput->addTerm(new fl::Rectangle("SET", 0.5 - 5 * fl::fuzzylite::macheps(), 0.5 + 5 * fl::fuzzylite::macheps())); @@ -171,6 +172,8 @@ void FuzzyHelper::initTacticalAdvantage() 0.0 + 5.0 * fl::fuzzylite::macheps())); ta.threat = new fl::OutputVariable("Threat"); + ta.threat->setMinimum(0); + ta.threat->setMaximum(3); engine.addOutputVariable(ta.threat); ta.threat->addTerm(new fl::Ramp("LOW", 1, MIN_AI_STRENGHT)); ta.threat->addTerm(new fl::Triangle("MEDIUM", 0.8, 1.2)); @@ -232,6 +235,7 @@ ui64 FuzzyHelper::estimateBankDanger (const CBank * bank) { logAi->errorStream() << "estimateBankDanger " << ": " << fe.getWhat(); } + assert(val >= 0); return val; } @@ -276,6 +280,7 @@ float FuzzyHelper::getTacticalAdvantage (const CArmedInstance *we, const CArmedI { logAi->errorStream() << "getTacticalAdvantage " << ": " << fe.getWhat(); } + assert (output >= 0); return output; } @@ -349,6 +354,8 @@ void FuzzyHelper::initVisitTile() vt.turnDistance = new fl::InputVariable("turnDistance"); //we want to use hero who is near vt.missionImportance = new fl::InputVariable("lockedMissionImportance"); //we may want to preempt hero with low-priority mission vt.value = new fl::OutputVariable("Value"); + vt.value->setMinimum(0); + vt.value->setMaximum(5); std::vector helper = {vt.strengthRatio, vt.heroStrength, vt.turnDistance, vt.missionImportance}; for (auto val : helper) @@ -450,6 +457,7 @@ float FuzzyHelper::evaluate (Goals::VisitTile & g) { logAi->errorStream() << "evaluate VisitTile " << ": " << fe.getWhat(); } + assert (g.priority >= 0); return g.priority; } @@ -493,6 +501,7 @@ float FuzzyHelper::evaluate (Goals::ClearWayTo & g) } else return -1; + } float FuzzyHelper::evaluate (Goals::BuildThis & g) From 9ca991282ac96d97bd5a96f6a8362fc63e813b74 Mon Sep 17 00:00:00 2001 From: DjWarmonger Date: Thu, 27 Nov 2014 21:51:48 +0100 Subject: [PATCH 2/7] Taken JCrada patch as it is. --- AI/VCAI/Fuzzy.cpp | 79 ++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 65 insertions(+), 14 deletions(-) diff --git a/AI/VCAI/Fuzzy.cpp b/AI/VCAI/Fuzzy.cpp index b5860f9b5..a5690c931 100644 --- a/AI/VCAI/Fuzzy.cpp +++ b/AI/VCAI/Fuzzy.cpp @@ -87,17 +87,23 @@ FuzzyHelper::FuzzyHelper() logAi->infoStream() << engine.toString(); } + + void FuzzyHelper::initBank() { try { + //Trivial bank estimation bankInput = new fl::InputVariable("BankInput"); - bankDanger = new fl::OutputVariable("BankDanger"); - bankDanger->setMinimum(0); - bankInput->addTerm(new fl::Rectangle("SET", 0.5 - 5 * fl::fuzzylite::macheps(), + //SingletonTerm (4.0) is a Rectangle (5.0) within 5*Macheps + bankInput->addTerm(new fl::Rectangle("SET", 0.5 - 5 * fl::fuzzylite::macheps(), 0.5 + 5 * fl::fuzzylite::macheps())); - + + bankInput->setRange(0.5 - 5 * fl::fuzzylite::macheps(), 0.5 + 5 * fl::fuzzylite::macheps()); + + bankDanger = new fl::OutputVariable("BankDanger"); + engine.addInputVariable(bankInput); engine.addOutputVariable(bankDanger); engine.addRuleBlock(&bankBlock); @@ -106,6 +112,7 @@ void FuzzyHelper::initBank() bankDanger->addTerm(new fl::Triangle("Bank" + boost::lexical_cast(i), 0, 1)); bankBlock.addRule(fl::Rule::parse("if BankInput is SET then BankDanger is Bank" + boost::lexical_cast(i), &engine)); } + bankDanger->setRange(0.0, 1.0); } catch (fl::Exception & fe) { @@ -136,6 +143,7 @@ void FuzzyHelper::initTacticalAdvantage() engine.addInputVariable(val); val->addTerm(new fl::Ramp("FEW", 0.6, 0.0)); val->addTerm(new fl::Ramp("MANY", 0.4, 1)); + val->setRange(0.0, 1.0); } ta.ourSpeed = new fl::InputVariable("OurSpeed"); @@ -149,35 +157,59 @@ void FuzzyHelper::initTacticalAdvantage() val->addTerm(new fl::Ramp("LOW", 6.5, 3)); val->addTerm(new fl::Triangle("MEDIUM", 5.5, 10.5)); val->addTerm(new fl::Ramp("HIGH", 8.5, 16)); + val->setRange(3.0, 16.0); } ta.castleWalls = new fl::InputVariable("CastleWalls"); engine.addInputVariable(ta.castleWalls); - ta.castleWalls->addTerm(new fl::Rectangle("NONE", CGTownInstance::NONE - 5.0 * fl::fuzzylite::macheps(), - CGTownInstance::NONE + 5.0 * fl::fuzzylite::macheps())); { + fl::Rectangle* none = new fl::Rectangle("NONE", + CGTownInstance::NONE - 5.0 * fl::fuzzylite::macheps(), + CGTownInstance::NONE + 5.0 * fl::fuzzylite::macheps()); + ta.castleWalls->addTerm(none); + fl::scalar a = CGTownInstance::FORT, d = 2.5; fl::scalar b = a + (d - a) * 1.0 / 5.0; fl::scalar c = a + (d - a) * 4.0 / 5.0; - ta.castleWalls->addTerm(new fl::Trapezoid("MEDIUM", a, b, c, d)); + fl::Trapezoid* medium = new fl::Trapezoid("MEDIUM", a, b, c, d); + ta.castleWalls->addTerm(medium); + + fl::Ramp* high = new fl::Ramp("HIGH", CGTownInstance::CITADEL - 0.1, CGTownInstance::CASTLE); + ta.castleWalls->addTerm(high); + + ta.castleWalls->setRange( + std::min({none->getStart(),none->getEnd(), + medium->getVertexA(), medium->getVertexD(), + high->getStart(), high->getEnd()}), + std::max({none->getStart(),none->getEnd(), + medium->getVertexA(), medium->getVertexD(), + high->getStart(), high->getEnd()})); } - ta.castleWalls->addTerm(new fl::Ramp("HIGH", CGTownInstance::CITADEL - 0.1, CGTownInstance::CASTLE)); + ta.bankPresent = new fl::InputVariable("Bank"); engine.addInputVariable(ta.bankPresent); - ta.bankPresent->addTerm(new fl::Rectangle("FALSE", 0.0 - 5.0 * fl::fuzzylite::macheps(), - 0.0 + 5.0 * fl::fuzzylite::macheps())); - ta.bankPresent->addTerm(new fl::Rectangle("TRUE", 1.0 - 5.0 * fl::fuzzylite::macheps(), - 0.0 + 5.0 * fl::fuzzylite::macheps())); + { + fl::Rectangle* termFalse = new fl::Rectangle("FALSE", 0.0 - 5.0 * fl::fuzzylite::macheps(), + 0.0 + 5.0 * fl::fuzzylite::macheps()); + ta.bankPresent->addTerm(termFalse); + fl::Rectangle* termTrue = new fl::Rectangle("TRUE", 1.0 - 5.0 * fl::fuzzylite::macheps(), + 0.0 + 5.0 * fl::fuzzylite::macheps()); + ta.bankPresent->addTerm(termTrue); + ta.bankPresent->setRange( + std::min({termFalse->getStart(),termFalse->getEnd(), + termTrue->getStart(), termTrue->getEnd()}), + std::max({termFalse->getStart(),termFalse->getEnd(), + termTrue->getStart(), termTrue->getEnd()})); + } ta.threat = new fl::OutputVariable("Threat"); - ta.threat->setMinimum(0); - ta.threat->setMaximum(3); engine.addOutputVariable(ta.threat); ta.threat->addTerm(new fl::Ramp("LOW", 1, MIN_AI_STRENGHT)); ta.threat->addTerm(new fl::Triangle("MEDIUM", 0.8, 1.2)); ta.threat->addTerm(new fl::Ramp("HIGH", 1, 1.5)); + ta.threat->setRange(MIN_AI_STRENGHT, 1.5); engine.addRuleBlock(&ta.tacticalAdvantage); @@ -221,6 +253,20 @@ ui64 FuzzyHelper::estimateBankDanger (const CBank * bank) bank0->setVertexC(info->minGuards().totalStrength * 1.5f); bank1->setVertexA(info->maxGuards().totalStrength * 0.5f); bank1->setVertexC(info->maxGuards().totalStrength * 1.5f); + + fl::scalar min = std::min( + {bank0->getVertexA(), bank0->getVertexC(), + bank1->getVertexA(), bank1->getVertexC()}); + fl::scalar max = std::max( + {bank0->getVertexA(), bank0->getVertexC(), + bank1->getVertexA(), bank1->getVertexC()}); + + if (fl::Op::isLt(min, bankDanger->getMinimum())){ + bankDanger->setMinimum(min); + } + if (fl::Op::isGt(max, bankDanger->getMaximum())){ + bankDanger->setMaximum(max); + } } //comparison purposes @@ -366,25 +412,30 @@ void FuzzyHelper::initVisitTile() vt.strengthRatio->addTerm(new fl::Ramp("LOW", SAFE_ATTACK_CONSTANT, 0)); vt.strengthRatio->addTerm(new fl::Ramp("HIGH", SAFE_ATTACK_CONSTANT, SAFE_ATTACK_CONSTANT * 3)); + vt.strengthRatio->setRange(0,SAFE_ATTACK_CONSTANT * 3 ); //strength compared to our main hero vt.heroStrength->addTerm(new fl::Ramp("LOW", 0.2, 0)); vt.heroStrength->addTerm(new fl::Triangle("MEDIUM", 0.2, 0.8)); vt.heroStrength->addTerm(new fl::Ramp("HIGH", 0.5, 1)); + vt.heroStrength->setRange(0.0,1.0); vt.turnDistance->addTerm(new fl::Ramp("SMALL", 0.5, 0)); vt.turnDistance->addTerm(new fl::Triangle("MEDIUM", 0.1, 0.8)); vt.turnDistance->addTerm(new fl::Ramp("LONG", 0.5, 3)); + vt.turnDistance->setRange(0.0, 3.0); vt.missionImportance->addTerm(new fl::Ramp("LOW", 2.5, 0)); vt.missionImportance->addTerm(new fl::Triangle("MEDIUM", 2, 3)); vt.missionImportance->addTerm(new fl::Ramp("HIGH", 2.5, 5)); + vt.missionImportance->setRange(0.0, 5.0); //an issue: in 99% cases this outputs center of mass (2.5) regardless of actual input :/ //should be same as "mission Importance" to keep consistency vt.value->addTerm(new fl::Ramp("LOW", 2.5, 0)); vt.value->addTerm(new fl::Triangle("MEDIUM", 2, 3)); //can't be center of mass :/ vt.value->addTerm(new fl::Ramp("HIGH", 2.5, 5)); + vt.value->setRange(0.0,5.0); engine.addRuleBlock (&vt.rules); From 6463c33c26f3f09f7044b8faaacd5368a6f1d354 Mon Sep 17 00:00:00 2001 From: DjWarmonger Date: Fri, 28 Nov 2014 14:17:17 +0100 Subject: [PATCH 3/7] Rewrite / update terms for TacticalAdvantage. --- AI/VCAI/Fuzzy.cpp | 88 ++++++++++++++++++----------------------------- 1 file changed, 34 insertions(+), 54 deletions(-) diff --git a/AI/VCAI/Fuzzy.cpp b/AI/VCAI/Fuzzy.cpp index a5690c931..dc3d547fe 100644 --- a/AI/VCAI/Fuzzy.cpp +++ b/AI/VCAI/Fuzzy.cpp @@ -143,7 +143,7 @@ void FuzzyHelper::initTacticalAdvantage() engine.addInputVariable(val); val->addTerm(new fl::Ramp("FEW", 0.6, 0.0)); val->addTerm(new fl::Ramp("MANY", 0.4, 1)); - val->setRange(0.0, 1.0); + val->setRange(0.0, 1.0); } ta.ourSpeed = new fl::InputVariable("OurSpeed"); @@ -157,59 +157,43 @@ void FuzzyHelper::initTacticalAdvantage() val->addTerm(new fl::Ramp("LOW", 6.5, 3)); val->addTerm(new fl::Triangle("MEDIUM", 5.5, 10.5)); val->addTerm(new fl::Ramp("HIGH", 8.5, 16)); - val->setRange(3.0, 16.0); + val->setRange(0, 25); } ta.castleWalls = new fl::InputVariable("CastleWalls"); engine.addInputVariable(ta.castleWalls); { - fl::Rectangle* none = new fl::Rectangle("NONE", - CGTownInstance::NONE - 5.0 * fl::fuzzylite::macheps(), - CGTownInstance::NONE + 5.0 * fl::fuzzylite::macheps()); - ta.castleWalls->addTerm(none); + fl::Rectangle* none = new fl::Rectangle("NONE", CGTownInstance::NONE, CGTownInstance::NONE + (CGTownInstance::FORT - CGTownInstance::NONE) * 0.5f); + ta.castleWalls->addTerm(none); - fl::scalar a = CGTownInstance::FORT, d = 2.5; - fl::scalar b = a + (d - a) * 1.0 / 5.0; - fl::scalar c = a + (d - a) * 4.0 / 5.0; - fl::Trapezoid* medium = new fl::Trapezoid("MEDIUM", a, b, c, d); - ta.castleWalls->addTerm(medium); + fl::Trapezoid* medium = new fl::Trapezoid("MEDIUM", (CGTownInstance::FORT - CGTownInstance::NONE) * 0.5f, CGTownInstance::FORT, + CGTownInstance::CITADEL, CGTownInstance::CITADEL + (CGTownInstance::CASTLE - CGTownInstance::CITADEL) * 0.5f); + ta.castleWalls->addTerm(medium); - fl::Ramp* high = new fl::Ramp("HIGH", CGTownInstance::CITADEL - 0.1, CGTownInstance::CASTLE); - ta.castleWalls->addTerm(high); - - ta.castleWalls->setRange( - std::min({none->getStart(),none->getEnd(), - medium->getVertexA(), medium->getVertexD(), - high->getStart(), high->getEnd()}), - std::max({none->getStart(),none->getEnd(), - medium->getVertexA(), medium->getVertexD(), - high->getStart(), high->getEnd()})); + fl::Ramp* high = new fl::Ramp("HIGH", CGTownInstance::CITADEL - 0.1, CGTownInstance::CASTLE); + ta.castleWalls->addTerm(high); + + ta.castleWalls->setRange(CGTownInstance::NONE, CGTownInstance::CASTLE); } ta.bankPresent = new fl::InputVariable("Bank"); engine.addInputVariable(ta.bankPresent); - { - fl::Rectangle* termFalse = new fl::Rectangle("FALSE", 0.0 - 5.0 * fl::fuzzylite::macheps(), - 0.0 + 5.0 * fl::fuzzylite::macheps()); - ta.bankPresent->addTerm(termFalse); - fl::Rectangle* termTrue = new fl::Rectangle("TRUE", 1.0 - 5.0 * fl::fuzzylite::macheps(), - 0.0 + 5.0 * fl::fuzzylite::macheps()); - ta.bankPresent->addTerm(termTrue); - ta.bankPresent->setRange( - std::min({termFalse->getStart(),termFalse->getEnd(), - termTrue->getStart(), termTrue->getEnd()}), - std::max({termFalse->getStart(),termFalse->getEnd(), - termTrue->getStart(), termTrue->getEnd()})); - } + { + fl::Rectangle* termFalse = new fl::Rectangle("FALSE", 0.0, 0.5f); + ta.bankPresent->addTerm(termFalse); + fl::Rectangle* termTrue = new fl::Rectangle("TRUE", 0.5f, 1); + ta.bankPresent->addTerm(termTrue); + ta.bankPresent->setRange(0, 1); + } ta.threat = new fl::OutputVariable("Threat"); engine.addOutputVariable(ta.threat); ta.threat->addTerm(new fl::Ramp("LOW", 1, MIN_AI_STRENGHT)); ta.threat->addTerm(new fl::Triangle("MEDIUM", 0.8, 1.2)); ta.threat->addTerm(new fl::Ramp("HIGH", 1, 1.5)); - ta.threat->setRange(MIN_AI_STRENGHT, 1.5); + ta.threat->setRange(MIN_AI_STRENGHT, 1.5); engine.addRuleBlock(&ta.tacticalAdvantage); @@ -254,19 +238,16 @@ ui64 FuzzyHelper::estimateBankDanger (const CBank * bank) bank1->setVertexA(info->maxGuards().totalStrength * 0.5f); bank1->setVertexC(info->maxGuards().totalStrength * 1.5f); - fl::scalar min = std::min( - {bank0->getVertexA(), bank0->getVertexC(), - bank1->getVertexA(), bank1->getVertexC()}); - fl::scalar max = std::max( - {bank0->getVertexA(), bank0->getVertexC(), - bank1->getVertexA(), bank1->getVertexC()}); - - if (fl::Op::isLt(min, bankDanger->getMinimum())){ - bankDanger->setMinimum(min); - } - if (fl::Op::isGt(max, bankDanger->getMaximum())){ - bankDanger->setMaximum(max); - } + fl::scalar min = std::min({bank0->getVertexA(), bank0->getVertexC(), bank1->getVertexA(), bank1->getVertexC()}); + fl::scalar max = std::max({bank0->getVertexA(), bank0->getVertexC(), bank1->getVertexA(), bank1->getVertexC()}); + if (fl::Op::isLt(min, bankDanger->getMinimum())) + { + bankDanger->setMinimum(min); + } + if (fl::Op::isGt(max, bankDanger->getMaximum())) + { + bankDanger->setMaximum(max); + } } //comparison purposes @@ -394,7 +375,6 @@ void FuzzyHelper::initVisitTile() { try { - vt.strengthRatio = new fl::InputVariable("strengthRatio"); //hero must be strong enough to defeat guards vt.heroStrength = new fl::InputVariable("heroStrength"); //we want to use weakest possible hero vt.turnDistance = new fl::InputVariable("turnDistance"); //we want to use hero who is near @@ -412,30 +392,30 @@ void FuzzyHelper::initVisitTile() vt.strengthRatio->addTerm(new fl::Ramp("LOW", SAFE_ATTACK_CONSTANT, 0)); vt.strengthRatio->addTerm(new fl::Ramp("HIGH", SAFE_ATTACK_CONSTANT, SAFE_ATTACK_CONSTANT * 3)); - vt.strengthRatio->setRange(0,SAFE_ATTACK_CONSTANT * 3 ); + vt.strengthRatio->setRange(0, SAFE_ATTACK_CONSTANT * 3 ); //strength compared to our main hero vt.heroStrength->addTerm(new fl::Ramp("LOW", 0.2, 0)); vt.heroStrength->addTerm(new fl::Triangle("MEDIUM", 0.2, 0.8)); vt.heroStrength->addTerm(new fl::Ramp("HIGH", 0.5, 1)); - vt.heroStrength->setRange(0.0,1.0); + vt.heroStrength->setRange(0.0, 1.0); vt.turnDistance->addTerm(new fl::Ramp("SMALL", 0.5, 0)); vt.turnDistance->addTerm(new fl::Triangle("MEDIUM", 0.1, 0.8)); vt.turnDistance->addTerm(new fl::Ramp("LONG", 0.5, 3)); - vt.turnDistance->setRange(0.0, 3.0); + vt.turnDistance->setRange(0.0, 3.0); vt.missionImportance->addTerm(new fl::Ramp("LOW", 2.5, 0)); vt.missionImportance->addTerm(new fl::Triangle("MEDIUM", 2, 3)); vt.missionImportance->addTerm(new fl::Ramp("HIGH", 2.5, 5)); - vt.missionImportance->setRange(0.0, 5.0); + vt.missionImportance->setRange(0.0, 5.0); //an issue: in 99% cases this outputs center of mass (2.5) regardless of actual input :/ //should be same as "mission Importance" to keep consistency vt.value->addTerm(new fl::Ramp("LOW", 2.5, 0)); vt.value->addTerm(new fl::Triangle("MEDIUM", 2, 3)); //can't be center of mass :/ vt.value->addTerm(new fl::Ramp("HIGH", 2.5, 5)); - vt.value->setRange(0.0,5.0); + vt.value->setRange(0.0,5.0); engine.addRuleBlock (&vt.rules); From 7adb0fd60050d3447a54245b9ffa527631c1b8cc Mon Sep 17 00:00:00 2001 From: DjWarmonger Date: Fri, 5 Dec 2014 09:28:58 +0100 Subject: [PATCH 4/7] Fuzzy engine fix by Jcrada. --- AI/VCAI/Fuzzy.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/AI/VCAI/Fuzzy.cpp b/AI/VCAI/Fuzzy.cpp index dc3d547fe..e431f7db2 100644 --- a/AI/VCAI/Fuzzy.cpp +++ b/AI/VCAI/Fuzzy.cpp @@ -81,8 +81,7 @@ FuzzyHelper::FuzzyHelper() initTacticalAdvantage(); initVisitTile(); - engine.configure("AlgebraicProduct", "AlgebraicSum", - "AlgebraicProduct", "AlgebraicSum", "Centroid"); + engine.configure("Minimum", "Maximum", "Minimum", "AlgebraicSum", "Centroid"); logAi->infoStream() << engine.toString(); } From a1e42caddd9a28dbf5d7f94db22d1d3828511d24 Mon Sep 17 00:00:00 2001 From: DjWarmonger Date: Wed, 10 Dec 2014 12:29:51 +0100 Subject: [PATCH 5/7] Banks won't use fuzzy engine at all, only center of mass. --- AI/VCAI/Fuzzy.cpp | 81 ++++----------------------- AI/VCAI/Fuzzy.h | 5 -- lib/mapObjects/CommonConstructors.cpp | 23 ++++++++ lib/mapObjects/CommonConstructors.h | 8 ++- 4 files changed, 42 insertions(+), 75 deletions(-) diff --git a/AI/VCAI/Fuzzy.cpp b/AI/VCAI/Fuzzy.cpp index e431f7db2..4b8b5fc24 100644 --- a/AI/VCAI/Fuzzy.cpp +++ b/AI/VCAI/Fuzzy.cpp @@ -23,6 +23,8 @@ #define UNGUARDED_OBJECT (100.0f) //we consider unguarded objects 100 times weaker than us struct BankConfig; +class IObjectInfo; +class CBankInfo; class Engine; class InputVariable; class CGTownInstance; @@ -77,7 +79,6 @@ armyStructure evaluateArmyStructure (const CArmedInstance * army) FuzzyHelper::FuzzyHelper() { - initBank(); initTacticalAdvantage(); initVisitTile(); @@ -87,38 +88,6 @@ FuzzyHelper::FuzzyHelper() } - -void FuzzyHelper::initBank() -{ - try - { - - //Trivial bank estimation - bankInput = new fl::InputVariable("BankInput"); - //SingletonTerm (4.0) is a Rectangle (5.0) within 5*Macheps - bankInput->addTerm(new fl::Rectangle("SET", 0.5 - 5 * fl::fuzzylite::macheps(), - 0.5 + 5 * fl::fuzzylite::macheps())); - - bankInput->setRange(0.5 - 5 * fl::fuzzylite::macheps(), 0.5 + 5 * fl::fuzzylite::macheps()); - - bankDanger = new fl::OutputVariable("BankDanger"); - - engine.addInputVariable(bankInput); - engine.addOutputVariable(bankDanger); - engine.addRuleBlock(&bankBlock); - for (int i = 0; i < 4; ++i) - { - bankDanger->addTerm(new fl::Triangle("Bank" + boost::lexical_cast(i), 0, 1)); - bankBlock.addRule(fl::Rule::parse("if BankInput is SET then BankDanger is Bank" + boost::lexical_cast(i), &engine)); - } - bankDanger->setRange(0.0, 1.0); - } - catch (fl::Exception & fe) - { - logAi->errorStream() << "initBank : " << fe.getWhat(); - } -} - void FuzzyHelper::initTacticalAdvantage() { try @@ -223,46 +192,20 @@ void FuzzyHelper::initTacticalAdvantage() ui64 FuzzyHelper::estimateBankDanger (const CBank * bank) { - auto info = VLC->objtypeh->getHandlerFor(bank->ID, bank->subID)->getObjectInfo(bank->appearance); + //this one is not fuzzy anymore, just calculate weighted average - ui64 val = std::numeric_limits::max(); - try - { - fl::Triangle* bank0 = dynamic_cast (bankDanger->getTerm("Bank0")); - fl::Triangle* bank1 = dynamic_cast (bankDanger->getTerm("Bank1")); - if (bank0 && bank1) - { - bank0->setVertexA(info->minGuards().totalStrength * 0.5f); - bank0->setVertexC(info->minGuards().totalStrength * 1.5f); - bank1->setVertexA(info->maxGuards().totalStrength * 0.5f); - bank1->setVertexC(info->maxGuards().totalStrength * 1.5f); - - fl::scalar min = std::min({bank0->getVertexA(), bank0->getVertexC(), bank1->getVertexA(), bank1->getVertexC()}); - fl::scalar max = std::max({bank0->getVertexA(), bank0->getVertexC(), bank1->getVertexA(), bank1->getVertexC()}); - if (fl::Op::isLt(min, bankDanger->getMinimum())) - { - bankDanger->setMinimum(min); - } - if (fl::Op::isGt(max, bankDanger->getMaximum())) - { - bankDanger->setMaximum(max); - } - } + auto objectInfo = VLC->objtypeh->getHandlerFor(bank->ID, bank->subID)->getObjectInfo(bank->appearance); - //comparison purposes - //int averageValue = (evaluateBankConfig (VLC->objh->banksInfo[ID][0]) + evaluateBankConfig (VLC->objh->banksInfo[ID][3])) * 0.5; - //dynamic_cast(bankInput->term("SET"))->setValue(0.5); - bankInput->setInputValue(0.5); - //engine.process(BANK_DANGER);//TODO: Process Bank_Dange only - engine.process(); //TODO: Process Bank_Dange only - val = bankDanger->getOutputValue(); //some expected value of this bank - } - catch (fl::Exception & fe) + CBankInfo * bankInfo = dynamic_cast (objectInfo.get()); + + ui64 totalStrength = 0; + ui8 totalChance = 0; + for (auto config : bankInfo->getPossibleGuards()) { - logAi->errorStream() << "estimateBankDanger " << ": " << fe.getWhat(); + totalStrength += config.second.totalStrength * config.first; + totalChance += config.first; } - assert(val >= 0); - return val; + return totalStrength / totalChance; } diff --git a/AI/VCAI/Fuzzy.h b/AI/VCAI/Fuzzy.h index 89ed68f51..785dd7b55 100644 --- a/AI/VCAI/Fuzzy.h +++ b/AI/VCAI/Fuzzy.h @@ -22,10 +22,6 @@ class FuzzyHelper fl::Engine engine; - fl::InputVariable* bankInput; - fl::OutputVariable* bankDanger; - fl::RuleBlock bankBlock; - class TacticalAdvantage { public: @@ -55,7 +51,6 @@ public: //blocks should be initialized in this order, which may be confusing :/ FuzzyHelper(); - void initBank(); void initTacticalAdvantage(); void initVisitTile(); diff --git a/lib/mapObjects/CommonConstructors.cpp b/lib/mapObjects/CommonConstructors.cpp index 14eb1a11f..482aee331 100644 --- a/lib/mapObjects/CommonConstructors.cpp +++ b/lib/mapObjects/CommonConstructors.cpp @@ -370,6 +370,29 @@ IObjectInfo::CArmyStructure CBankInfo::maxGuards() const return *boost::range::max_element(armies); } +TPossibleGuards CBankInfo::getPossibleGuards() const +{ + TPossibleGuards out; + + for (const JsonNode & configEntry : config) + { + const JsonNode & guardsInfo = configEntry["guards"]; + auto stacks = JsonRandom::evaluateCreatures(configEntry["guards"]); + IObjectInfo::CArmyStructure army; + + + for (auto stack : stacks) + { + army.totalStrength += stack.allowedCreatures.front()->AIValue * (stack.minAmount + stack.maxAmount) / 2; + //TODO: add fields for flyers, walkers etc... + } + + ui8 chance = configEntry["chance"].Float(); + out.push_back(std::make_pair(chance, army)); + } + return out; +} + bool CBankInfo::givesResources() const { for (const JsonNode & node : config) diff --git a/lib/mapObjects/CommonConstructors.h b/lib/mapObjects/CommonConstructors.h index e4f44bd05..99a4e268c 100644 --- a/lib/mapObjects/CommonConstructors.h +++ b/lib/mapObjects/CommonConstructors.h @@ -152,14 +152,20 @@ struct BankConfig } }; -class CBankInfo : public IObjectInfo +typedef std::vector> TPossibleGuards; + +class DLL_LINKAGE CBankInfo : public IObjectInfo { JsonVector config; public: CBankInfo(JsonVector config); + TPossibleGuards getPossibleGuards() const; + + //I have no idea what do these functions do or were supposed to do - War CArmyStructure minGuards() const; CArmyStructure maxGuards() const; + bool givesResources() const; bool givesArtifacts() const; bool givesCreatures() const; From cd060c00c5a4809826b139f02b53b9004cb450bb Mon Sep 17 00:00:00 2001 From: DjWarmonger Date: Fri, 19 Dec 2014 10:52:41 +0100 Subject: [PATCH 6/7] Each fuzzy set of rules will use separate fuzzy engine. --- AI/VCAI/Fuzzy.cpp | 108 ++++++++++++++++++++++++++++------------------ AI/VCAI/Fuzzy.h | 18 +++++--- 2 files changed, 78 insertions(+), 48 deletions(-) diff --git a/AI/VCAI/Fuzzy.cpp b/AI/VCAI/Fuzzy.cpp index 4b8b5fc24..8d93d5403 100644 --- a/AI/VCAI/Fuzzy.cpp +++ b/AI/VCAI/Fuzzy.cpp @@ -37,6 +37,22 @@ FuzzyHelper *fh; extern boost::thread_specific_ptr cb; extern boost::thread_specific_ptr ai; +engineBase::engineBase() +{ + engine.addRuleBlock(&rules); +} + +void engineBase::configure() +{ + engine.configure("Minimum", "Maximum", "Minimum", "AlgebraicSum", "Centroid"); + logAi->infoStream() << engine.toString(); +} + +void engineBase::addRule(const std::string &txt) +{ + rules.addRule(fl::Rule::parse(txt, &engine)); +} + struct armyStructure { float walkers, shooters, flyers; @@ -74,17 +90,16 @@ armyStructure evaluateArmyStructure (const CArmedInstance * army) as.shooters = shootersStrenght / totalStrenght; as.flyers = flyersStrenght / totalStrenght; as.maxSpeed = maxSpeed; + assert(as.walkers || as.flyers || as.shooters); return as; } FuzzyHelper::FuzzyHelper() { initTacticalAdvantage(); + ta.configure(); initVisitTile(); - - engine.configure("Minimum", "Maximum", "Minimum", "AlgebraicSum", "Centroid"); - - logAi->infoStream() << engine.toString(); + vt.configure(); } @@ -108,7 +123,7 @@ void FuzzyHelper::initTacticalAdvantage() for (auto val : helper) { - engine.addInputVariable(val); + ta.engine.addInputVariable(val); val->addTerm(new fl::Ramp("FEW", 0.6, 0.0)); val->addTerm(new fl::Ramp("MANY", 0.4, 1)); val->setRange(0.0, 1.0); @@ -121,7 +136,7 @@ void FuzzyHelper::initTacticalAdvantage() for (auto val : helper) { - engine.addInputVariable(val); + ta.engine.addInputVariable(val); val->addTerm(new fl::Ramp("LOW", 6.5, 3)); val->addTerm(new fl::Triangle("MEDIUM", 5.5, 10.5)); val->addTerm(new fl::Ramp("HIGH", 8.5, 16)); @@ -129,7 +144,7 @@ void FuzzyHelper::initTacticalAdvantage() } ta.castleWalls = new fl::InputVariable("CastleWalls"); - engine.addInputVariable(ta.castleWalls); + ta.engine.addInputVariable(ta.castleWalls); { fl::Rectangle* none = new fl::Rectangle("NONE", CGTownInstance::NONE, CGTownInstance::NONE + (CGTownInstance::FORT - CGTownInstance::NONE) * 0.5f); ta.castleWalls->addTerm(none); @@ -147,7 +162,7 @@ void FuzzyHelper::initTacticalAdvantage() ta.bankPresent = new fl::InputVariable("Bank"); - engine.addInputVariable(ta.bankPresent); + ta.engine.addInputVariable(ta.bankPresent); { fl::Rectangle* termFalse = new fl::Rectangle("FALSE", 0.0, 0.5f); ta.bankPresent->addTerm(termFalse); @@ -157,31 +172,29 @@ void FuzzyHelper::initTacticalAdvantage() } ta.threat = new fl::OutputVariable("Threat"); - engine.addOutputVariable(ta.threat); + ta.engine.addOutputVariable(ta.threat); ta.threat->addTerm(new fl::Ramp("LOW", 1, MIN_AI_STRENGHT)); ta.threat->addTerm(new fl::Triangle("MEDIUM", 0.8, 1.2)); ta.threat->addTerm(new fl::Ramp("HIGH", 1, 1.5)); ta.threat->setRange(MIN_AI_STRENGHT, 1.5); - engine.addRuleBlock(&ta.tacticalAdvantage); + ta.addRule("if OurShooters is MANY and EnemySpeed is LOW then Threat is LOW"); + ta.addRule("if OurShooters is MANY and EnemyShooters is FEW then Threat is LOW"); + ta.addRule("if OurSpeed is LOW and EnemyShooters is MANY then Threat is HIGH"); + ta.addRule("if OurSpeed is HIGH and EnemyShooters is MANY then Threat is LOW"); - ta.tacticalAdvantage.addRule(fl::Rule::parse("if OurShooters is MANY and EnemySpeed is LOW then Threat is LOW", &engine)); - ta.tacticalAdvantage.addRule(fl::Rule::parse("if OurShooters is MANY and EnemyShooters is FEW then Threat is LOW", &engine)); - ta.tacticalAdvantage.addRule(fl::Rule::parse("if OurSpeed is LOW and EnemyShooters is MANY then Threat is HIGH", &engine)); - ta.tacticalAdvantage.addRule(fl::Rule::parse("if OurSpeed is HIGH and EnemyShooters is MANY then Threat is LOW", &engine)); - - ta.tacticalAdvantage.addRule(fl::Rule::parse("if OurWalkers is FEW and EnemyShooters is MANY then Threat is somewhat LOW", &engine)); - ta.tacticalAdvantage.addRule(fl::Rule::parse("if OurShooters is MANY and EnemySpeed is HIGH then Threat is somewhat HIGH", &engine)); + ta.addRule("if OurWalkers is FEW and EnemyShooters is MANY then Threat is somewhat LOW"); + ta.addRule("if OurShooters is MANY and EnemySpeed is HIGH then Threat is somewhat HIGH"); //just to cover all cases - ta.tacticalAdvantage.addRule(fl::Rule::parse("if EnemySpeed is MEDIUM then Threat is MEDIUM", &engine)); - ta.tacticalAdvantage.addRule(fl::Rule::parse("if EnemySpeed is LOW and OurShooters is FEW then Threat is MEDIUM", &engine)); + ta.addRule("if EnemySpeed is MEDIUM then Threat is MEDIUM"); + ta.addRule("if EnemySpeed is LOW and OurShooters is FEW then Threat is MEDIUM"); - ta.tacticalAdvantage.addRule(fl::Rule::parse("if Bank is TRUE and OurShooters is MANY then Threat is somewhat HIGH", &engine)); - ta.tacticalAdvantage.addRule(fl::Rule::parse("if Bank is TRUE and EnemyShooters is MANY then Threat is LOW", &engine)); + ta.addRule("if Bank is TRUE and OurShooters is MANY then Threat is somewhat HIGH"); + ta.addRule("if Bank is TRUE and EnemyShooters is MANY then Threat is LOW"); - ta.tacticalAdvantage.addRule(fl::Rule::parse("if CastleWalls is HIGH and OurWalkers is MANY then Threat is very HIGH", &engine)); - ta.tacticalAdvantage.addRule(fl::Rule::parse("if CastleWalls is HIGH and OurFlyers is MANY and OurShooters is MANY then Threat is MEDIUM", &engine)); - ta.tacticalAdvantage.addRule(fl::Rule::parse("if CastleWalls is MEDIUM and OurShooters is MANY and EnemyWalkers is MANY then Threat is LOW", &engine)); + ta.addRule("if CastleWalls is HIGH and OurWalkers is MANY then Threat is very HIGH"); + ta.addRule("if CastleWalls is HIGH and OurFlyers is MANY and OurShooters is MANY then Threat is MEDIUM"); + ta.addRule("if CastleWalls is MEDIUM and OurShooters is MANY and EnemyWalkers is MANY then Threat is LOW"); } catch (fl::Exception & pe) @@ -242,14 +255,25 @@ float FuzzyHelper::getTacticalAdvantage (const CArmedInstance *we, const CArmedI ta.castleWalls->setInputValue(0); //engine.process(TACTICAL_ADVANTAGE);//TODO: Process only Tactical_Advantage - engine.process(); + ta.engine.process(); output = ta.threat->getOutputValue(); } catch (fl::Exception & fe) { logAi->errorStream() << "getTacticalAdvantage " << ": " << fe.getWhat(); } - assert (output >= 0); + logAi->traceStream() << "getTacticalAdvantage output: " << output; + if (output < 0 || (output != output)) + { + fl::InputVariable* tab[] = {ta.bankPresent, ta.castleWalls, ta.ourWalkers, ta.ourShooters, ta.ourFlyers, ta.ourSpeed, ta.enemyWalkers, ta.enemyShooters, ta.enemyFlyers, ta.enemySpeed}; + std::stringstream log; + + for (auto param : tab) + log << param->getInputValue() << " "; + logAi->errorStream() << log.str(); + assert(false); + } + return output; } @@ -328,9 +352,9 @@ void FuzzyHelper::initVisitTile() std::vector helper = {vt.strengthRatio, vt.heroStrength, vt.turnDistance, vt.missionImportance}; for (auto val : helper) { - engine.addInputVariable(val); + vt.engine.addInputVariable(val); } - engine.addOutputVariable(vt.value); + vt.engine.addOutputVariable(vt.value); vt.strengthRatio->addTerm(new fl::Ramp("LOW", SAFE_ATTACK_CONSTANT, 0)); vt.strengthRatio->addTerm(new fl::Ramp("HIGH", SAFE_ATTACK_CONSTANT, SAFE_ATTACK_CONSTANT * 3)); @@ -359,27 +383,25 @@ void FuzzyHelper::initVisitTile() vt.value->addTerm(new fl::Ramp("HIGH", 2.5, 5)); vt.value->setRange(0.0,5.0); - engine.addRuleBlock (&vt.rules); - //use unarmed scouts if possible - vt.rules.addRule(fl::Rule::parse("if strengthRatio is HIGH and heroStrength is LOW then Value is very HIGH", &engine)); + vt.addRule("if strengthRatio is HIGH and heroStrength is LOW then Value is very HIGH"); //we may want to use secondary hero(es) rather than main hero - vt.rules.addRule(fl::Rule::parse("if strengthRatio is HIGH and heroStrength is MEDIUM then Value is somewhat HIGH", &engine)); - vt.rules.addRule(fl::Rule::parse("if strengthRatio is HIGH and heroStrength is HIGH then Value is somewhat LOW", &engine)); + vt.addRule("if strengthRatio is HIGH and heroStrength is MEDIUM then Value is somewhat HIGH"); + vt.addRule("if strengthRatio is HIGH and heroStrength is HIGH then Value is somewhat LOW"); //don't assign targets to heroes who are too weak, but prefer targets of our main hero (in case we need to gather army) - vt.rules.addRule(fl::Rule::parse("if strengthRatio is LOW and heroStrength is LOW then Value is very LOW", &engine)); + vt.addRule("if strengthRatio is LOW and heroStrength is LOW then Value is very LOW"); //attempt to arm secondary heroes is not stupid - vt.rules.addRule(fl::Rule::parse("if strengthRatio is LOW and heroStrength is MEDIUM then Value is somewhat HIGH", &engine)); - vt.rules.addRule(fl::Rule::parse("if strengthRatio is LOW and heroStrength is HIGH then Value is LOW", &engine)); + vt.addRule("if strengthRatio is LOW and heroStrength is MEDIUM then Value is somewhat HIGH"); + vt.addRule("if strengthRatio is LOW and heroStrength is HIGH then Value is LOW"); //do not cancel important goals - vt.rules.addRule(fl::Rule::parse("if lockedMissionImportance is HIGH then Value is very LOW", &engine)); - vt.rules.addRule(fl::Rule::parse("if lockedMissionImportance is MEDIUM then Value is somewhat LOW", &engine)); - vt.rules.addRule(fl::Rule::parse("if lockedMissionImportance is LOW then Value is HIGH", &engine)); + vt.addRule("if lockedMissionImportance is HIGH then Value is very LOW"); + vt.addRule("if lockedMissionImportance is MEDIUM then Value is somewhat LOW"); + vt.addRule("if lockedMissionImportance is LOW then Value is HIGH"); //pick nearby objects if it's easy, avoid long walks - vt.rules.addRule(fl::Rule::parse("if turnDistance is SMALL then Value is HIGH", &engine)); - vt.rules.addRule(fl::Rule::parse("if turnDistance is MEDIUM then Value is MEDIUM", &engine)); - vt.rules.addRule(fl::Rule::parse("if turnDistance is LONG then Value is LOW", &engine)); + vt.addRule("if turnDistance is SMALL then Value is HIGH"); + vt.addRule("if turnDistance is MEDIUM then Value is MEDIUM"); + vt.addRule("if turnDistance is LONG then Value is LOW"); } catch (fl::Exception & fe) { @@ -422,7 +444,7 @@ float FuzzyHelper::evaluate (Goals::VisitTile & g) vt.turnDistance->setInputValue(turns); vt.missionImportance->setInputValue(missionImportance); - engine.process(); + vt.engine.process(); //engine.process(VISIT_TILE); //TODO: Process only Visit_Tile g.priority = vt.value->getOutputValue(); } diff --git a/AI/VCAI/Fuzzy.h b/AI/VCAI/Fuzzy.h index 785dd7b55..67b77fa58 100644 --- a/AI/VCAI/Fuzzy.h +++ b/AI/VCAI/Fuzzy.h @@ -16,13 +16,22 @@ class VCAI; class CArmedInstance; class CBank; +class engineBase +{ +public: + fl::Engine engine; + fl::RuleBlock rules; + + engineBase(); + void configure(); + void addRule(const std::string &txt); +}; + class FuzzyHelper { friend class VCAI; - fl::Engine engine; - - class TacticalAdvantage + class TacticalAdvantage : public engineBase { public: fl::InputVariable * ourWalkers, * ourShooters, * ourFlyers, * enemyWalkers, * enemyShooters, * enemyFlyers; @@ -30,11 +39,10 @@ class FuzzyHelper fl::InputVariable * bankPresent; fl::InputVariable * castleWalls; fl::OutputVariable * threat; - fl::RuleBlock tacticalAdvantage; ~TacticalAdvantage(); } ta; - class EvalVisitTile + class EvalVisitTile : public engineBase { public: fl::InputVariable * strengthRatio; From a823fae822d1999e162fc6e2ce6ec05d5d985310 Mon Sep 17 00:00:00 2001 From: DjWarmonger Date: Fri, 19 Dec 2014 19:15:41 +0100 Subject: [PATCH 7/7] Fixed NaNs in FuzzyHelper::getTacticalAdvantage --- AI/VCAI/Fuzzy.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/AI/VCAI/Fuzzy.cpp b/AI/VCAI/Fuzzy.cpp index 8d93d5403..bfb7012d6 100644 --- a/AI/VCAI/Fuzzy.cpp +++ b/AI/VCAI/Fuzzy.cpp @@ -186,6 +186,7 @@ void FuzzyHelper::initTacticalAdvantage() ta.addRule("if OurWalkers is FEW and EnemyShooters is MANY then Threat is somewhat LOW"); ta.addRule("if OurShooters is MANY and EnemySpeed is HIGH then Threat is somewhat HIGH"); //just to cover all cases + ta.addRule("if OurShooters is FEW and EnemySpeed is HIGH then Threat is MEDIUM"); ta.addRule("if EnemySpeed is MEDIUM then Threat is MEDIUM"); ta.addRule("if EnemySpeed is LOW and OurShooters is FEW then Threat is MEDIUM"); @@ -266,10 +267,11 @@ float FuzzyHelper::getTacticalAdvantage (const CArmedInstance *we, const CArmedI if (output < 0 || (output != output)) { fl::InputVariable* tab[] = {ta.bankPresent, ta.castleWalls, ta.ourWalkers, ta.ourShooters, ta.ourFlyers, ta.ourSpeed, ta.enemyWalkers, ta.enemyShooters, ta.enemyFlyers, ta.enemySpeed}; - std::stringstream log; + std::string names[] = {"bankPresent", "castleWalls", "ourWalkers", "ourShooters", "ourFlyers", "ourSpeed", "enemyWalkers", "enemyShooters", "enemyFlyers", "enemySpeed" }; + std::stringstream log("Warning! Fuzzy engine doesn't cover this set of parameters: "); - for (auto param : tab) - log << param->getInputValue() << " "; + for (int i = 0; i < boost::size(tab); i++) + log << names[i] << ": " << tab[i]->getInputValue() << " "; logAi->errorStream() << log.str(); assert(false); }