2012-03-03 13:08:01 +03:00
# include "StdInc.h"
# include "Fuzzy.h"
2012-03-12 23:11:46 +03:00
# include <limits>
2012-09-06 13:39:48 +03:00
2014-06-05 19:52:14 +03:00
# include "../../lib/mapObjects/MapObjects.h"
2014-06-22 13:39:40 +03:00
# include "../../lib/mapObjects/CommonConstructors.h"
2012-09-06 13:39:48 +03:00
# include "../../lib/CCreatureHandler.h"
# include "../../lib/VCMI_Lib.h"
2013-12-20 12:43:12 +03:00
# include "../../CCallback.h"
# include "VCAI.h"
2012-03-03 13:08:01 +03:00
/*
* Fuzzy . 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
*
*/
2012-03-04 13:43:46 +03:00
# define MIN_AI_STRENGHT (0.5f) //lower when combat AI gets smarter
2014-02-07 11:23:31 +03:00
# define UNGUARDED_OBJECT (100.0f) //we consider unguarded objects 10 times weaker than us
2012-03-04 13:43:46 +03:00
2012-05-28 22:29:32 +03:00
struct BankConfig ;
2012-03-03 13:08:01 +03:00
class FuzzyEngine ;
class InputLVar ;
2012-03-04 13:43:46 +03:00
class CGTownInstance ;
using namespace boost : : assign ;
using namespace vstd ;
2013-11-24 11:21:51 +03:00
//using namespace Goals;
2012-03-03 13:08:01 +03:00
2012-03-10 22:14:45 +03:00
FuzzyHelper * fh ;
2012-03-03 13:08:01 +03:00
2013-12-20 12:43:12 +03:00
extern boost : : thread_specific_ptr < CCallback > cb ;
extern boost : : thread_specific_ptr < VCAI > ai ;
2012-03-04 13:43:46 +03:00
struct armyStructure
{
float walkers , shooters , flyers ;
ui32 maxSpeed ;
} ;
armyStructure evaluateArmyStructure ( const CArmedInstance * army )
{
ui64 totalStrenght = army - > getArmyStrength ( ) ;
double walkersStrenght = 0 ;
double flyersStrenght = 0 ;
double shootersStrenght = 0 ;
ui32 maxSpeed = 0 ;
2013-06-29 16:05:48 +03:00
for ( auto s : army - > Slots ( ) )
2012-03-04 13:43:46 +03:00
{
bool walker = true ;
if ( s . second - > type - > hasBonusOfType ( Bonus : : SHOOTER ) )
{
shootersStrenght + = s . second - > getPower ( ) ;
walker = false ;
}
if ( s . second - > type - > hasBonusOfType ( Bonus : : FLYING ) )
{
flyersStrenght + = s . second - > getPower ( ) ;
walker = false ;
}
if ( walker )
walkersStrenght + = s . second - > getPower ( ) ;
2012-08-30 18:37:43 +03:00
amax ( maxSpeed , s . second - > type - > valOfBonuses ( Bonus : : STACKS_SPEED ) ) ;
2012-03-04 13:43:46 +03:00
}
armyStructure as ;
as . walkers = walkersStrenght / totalStrenght ;
as . shooters = shootersStrenght / totalStrenght ;
as . flyers = flyersStrenght / totalStrenght ;
as . maxSpeed = maxSpeed ;
return as ;
}
2012-03-03 13:08:01 +03:00
FuzzyHelper : : FuzzyHelper ( )
2012-03-04 13:43:46 +03:00
{
2013-12-20 12:43:12 +03:00
engine . hedgeSet ( ) . add ( new fl : : HedgeSomewhat ( ) ) ;
engine . hedgeSet ( ) . add ( new fl : : HedgeVery ( ) ) ;
2014-02-07 11:23:31 +03:00
engine . fuzzyOperator ( ) . setAggregation ( new fl : : FuzzyOrSum ( ) ) ; //to consider all cases simultaneously
2013-12-20 12:43:12 +03:00
2012-03-04 13:43:46 +03:00
initBank ( ) ;
initTacticalAdvantage ( ) ;
2013-12-20 12:43:12 +03:00
initVisitTile ( ) ;
2014-02-05 21:45:51 +03:00
logAi - > infoStream ( ) < < engine . toString ( ) ;
2012-03-04 13:43:46 +03:00
}
void FuzzyHelper : : initBank ( )
2012-03-03 13:08:01 +03:00
{
2012-03-03 14:39:54 +03:00
try
{
2012-03-04 13:43:46 +03:00
//Trivial bank estimation
2012-03-03 14:39:54 +03:00
bankInput = new fl : : InputLVar ( " BankInput " ) ;
bankDanger = new fl : : OutputLVar ( " BankDanger " ) ;
2014-02-05 21:45:51 +03:00
bankInput - > addTerm ( new fl : : SingletonTerm ( " SET " , 0.5 ) ) ;
2012-03-03 13:08:01 +03:00
2012-03-03 14:39:54 +03:00
engine . addInputLVar ( bankInput ) ;
engine . addOutputLVar ( bankDanger ) ;
2014-02-05 21:45:51 +03:00
engine . addRuleBlock ( & bankBlock ) ;
2012-03-03 14:39:54 +03:00
for ( int i = 0 ; i < 4 ; + + i )
{
bankDanger - > addTerm ( new fl : : TriangularTerm ( " Bank " + boost : : lexical_cast < std : : string > ( i ) , 0 , 1 ) ) ;
2012-03-04 13:43:46 +03:00
bankBlock . addRule ( new fl : : MamdaniRule ( " if BankInput is SET then BankDanger is Bank " + boost : : lexical_cast < std : : string > ( i ) , engine ) ) ;
2012-03-03 14:39:54 +03:00
}
}
2013-11-07 15:48:41 +03:00
catch ( fl : : FuzzyException & fe )
2012-03-03 13:08:01 +03:00
{
2013-04-10 19:28:14 +03:00
logAi - > errorStream ( ) < < " initBank " < < fe . name ( ) < < " : " < < fe . message ( ) ;
2012-03-04 13:43:46 +03:00
}
}
void FuzzyHelper : : initTacticalAdvantage ( )
{
try
{
//Tactical advantage calculation
std : : vector < fl : : InputLVar * > helper ;
2013-12-20 12:43:12 +03:00
ta . ourShooters = new fl : : InputLVar ( " OurShooters " ) ;
ta . ourWalkers = new fl : : InputLVar ( " OurWalkers " ) ;
ta . ourFlyers = new fl : : InputLVar ( " OurFlyers " ) ;
ta . enemyShooters = new fl : : InputLVar ( " EnemyShooters " ) ;
ta . enemyWalkers = new fl : : InputLVar ( " EnemyWalkers " ) ;
ta . enemyFlyers = new fl : : InputLVar ( " EnemyFlyers " ) ;
helper + = ta . ourShooters , ta . ourWalkers , ta . ourFlyers , ta . enemyShooters , ta . enemyWalkers , ta . enemyFlyers ;
2012-03-04 13:43:46 +03:00
2013-06-29 16:05:48 +03:00
for ( auto val : helper )
2012-03-04 13:43:46 +03:00
{
2014-02-05 21:45:51 +03:00
engine . addInputLVar ( val ) ;
2014-02-07 11:23:31 +03:00
val - > addTerm ( new fl : : ShoulderTerm ( " FEW " , 0 , 0.6 , true ) ) ;
val - > addTerm ( new fl : : ShoulderTerm ( " MANY " , 0.4 , 1 , false ) ) ;
2012-03-04 13:43:46 +03:00
}
helper . clear ( ) ;
2013-12-20 12:43:12 +03:00
ta . ourSpeed = new fl : : InputLVar ( " OurSpeed " ) ;
ta . enemySpeed = new fl : : InputLVar ( " EnemySpeed " ) ;
2012-03-04 13:43:46 +03:00
2013-12-20 12:43:12 +03:00
helper + = ta . ourSpeed , ta . enemySpeed ;
2012-03-04 13:43:46 +03:00
2013-06-29 16:05:48 +03:00
for ( auto val : helper )
2012-03-04 13:43:46 +03:00
{
engine . addInputLVar ( val ) ;
2014-02-05 21:45:51 +03:00
val - > addTerm ( new fl : : ShoulderTerm ( " LOW " , 3 , 6.5 , true ) ) ;
val - > addTerm ( new fl : : TriangularTerm ( " MEDIUM " , 5.5 , 10.5 ) ) ;
val - > addTerm ( new fl : : ShoulderTerm ( " HIGH " , 8.5 , 16 , false ) ) ;
2012-03-04 13:43:46 +03:00
}
2014-02-05 21:45:51 +03:00
2013-12-20 12:43:12 +03:00
ta . castleWalls = new fl : : InputLVar ( " CastleWalls " ) ;
2014-02-05 21:45:51 +03:00
engine . addInputLVar ( ta . castleWalls ) ;
2013-12-20 12:43:12 +03:00
ta . castleWalls - > addTerm ( new fl : : SingletonTerm ( " NONE " , CGTownInstance : : NONE ) ) ;
ta . castleWalls - > addTerm ( new fl : : TrapezoidalTerm ( " MEDIUM " , CGTownInstance : : FORT , 2.5 ) ) ;
2013-12-20 19:49:51 +03:00
ta . castleWalls - > addTerm ( new fl : : ShoulderTerm ( " HIGH " , CGTownInstance : : CITADEL - 0.1 , CGTownInstance : : CASTLE , false ) ) ;
2013-12-20 12:43:12 +03:00
ta . bankPresent = new fl : : InputLVar ( " Bank " ) ;
2014-02-05 21:45:51 +03:00
engine . addInputLVar ( ta . bankPresent ) ;
2013-12-20 12:43:12 +03:00
ta . bankPresent - > addTerm ( new fl : : SingletonTerm ( " FALSE " , 0 ) ) ;
ta . bankPresent - > addTerm ( new fl : : SingletonTerm ( " TRUE " , 1 ) ) ;
ta . threat = new fl : : OutputLVar ( " Threat " ) ;
2014-02-05 21:45:51 +03:00
engine . addOutputLVar ( ta . threat ) ;
ta . threat - > addTerm ( new fl : : ShoulderTerm ( " LOW " , MIN_AI_STRENGHT , 1 , true ) ) ;
2013-12-20 12:43:12 +03:00
ta . threat - > addTerm ( new fl : : TriangularTerm ( " MEDIUM " , 0.8 , 1.2 ) ) ;
ta . threat - > addTerm ( new fl : : ShoulderTerm ( " HIGH " , 1 , 1.5 , false ) ) ;
2014-02-05 21:45:51 +03:00
engine . addRuleBlock ( & ta . tacticalAdvantage ) ;
2013-12-20 12:43:12 +03:00
2014-02-07 11:23:31 +03:00
ta . tacticalAdvantage . addRule ( new fl : : MamdaniRule ( " if OurShooters is MANY and EnemySpeed is LOW then Threat is LOW " , engine ) ) ;
ta . tacticalAdvantage . addRule ( new fl : : MamdaniRule ( " if OurShooters is MANY and EnemyShooters is FEW then Threat is LOW " , engine ) ) ;
ta . tacticalAdvantage . addRule ( new fl : : MamdaniRule ( " if OurSpeed is LOW and EnemyShooters is MANY then Threat is HIGH " , engine ) ) ;
ta . tacticalAdvantage . addRule ( new fl : : MamdaniRule ( " if OurSpeed is HIGH and EnemyShooters is MANY then Threat is LOW " , engine ) ) ;
ta . tacticalAdvantage . addRule ( new fl : : MamdaniRule ( " if OurWalkers is FEW and EnemyShooters is MANY then Threat is somewhat LOW " , engine ) ) ;
2013-12-20 12:43:12 +03:00
ta . tacticalAdvantage . addRule ( new fl : : MamdaniRule ( " if OurShooters is MANY and EnemySpeed is HIGH then Threat is somewhat HIGH " , engine ) ) ;
2014-02-05 21:45:51 +03:00
//just to cover all cases
ta . tacticalAdvantage . addRule ( new fl : : MamdaniRule ( " if EnemySpeed is MEDIUM then Threat is MEDIUM " , engine ) ) ;
ta . tacticalAdvantage . addRule ( new fl : : MamdaniRule ( " if EnemySpeed is LOW and OurShooters is FEW then Threat is MEDIUM " , engine ) ) ;
2012-03-04 13:43:46 +03:00
2013-12-20 12:43:12 +03:00
ta . tacticalAdvantage . addRule ( new fl : : MamdaniRule ( " if Bank is TRUE and OurShooters is MANY then Threat is somewhat HIGH " , engine ) ) ;
ta . tacticalAdvantage . addRule ( new fl : : MamdaniRule ( " if Bank is TRUE and EnemyShooters is MANY then Threat is LOW " , engine ) ) ;
2012-03-04 13:43:46 +03:00
2013-12-20 12:43:12 +03:00
ta . tacticalAdvantage . addRule ( new fl : : MamdaniRule ( " if CastleWalls is HIGH and OurWalkers is MANY then Threat is very HIGH " , engine ) ) ;
ta . tacticalAdvantage . addRule ( new fl : : MamdaniRule ( " if CastleWalls is HIGH and OurFlyers is MANY and OurShooters is MANY then Threat is MEDIUM " , engine ) ) ;
ta . tacticalAdvantage . addRule ( new fl : : MamdaniRule ( " if CastleWalls is MEDIUM and OurShooters is MANY and EnemyWalkers is MANY then Threat is LOW " , engine ) ) ;
2012-03-04 13:43:46 +03:00
}
2013-11-07 15:48:41 +03:00
catch ( fl : : ParsingException & pe )
2012-03-04 13:43:46 +03:00
{
2013-04-10 19:28:14 +03:00
logAi - > errorStream ( ) < < " initTacticalAdvantage " < < pe . name ( ) < < " : " < < pe . message ( ) ;
2012-03-04 13:43:46 +03:00
}
2013-11-07 15:48:41 +03:00
catch ( fl : : FuzzyException & fe )
2012-03-04 13:43:46 +03:00
{
2013-04-10 19:28:14 +03:00
logAi - > errorStream ( ) < < " initTacticalAdvantage " < < fe . name ( ) < < " : " < < fe . message ( ) ;
2012-03-03 13:08:01 +03:00
}
}
2014-06-22 13:39:40 +03:00
ui64 FuzzyHelper : : estimateBankDanger ( const CBank * bank )
2012-03-03 13:08:01 +03:00
{
2014-06-22 13:39:40 +03:00
auto info = VLC - > objtypeh - > getHandlerFor ( bank - > ID , bank - > subID ) - > getObjectInfo ( bank - > appearance ) ;
2012-03-12 23:11:46 +03:00
ui64 val = std : : numeric_limits < ui64 > : : max ( ) ;
2012-03-04 13:43:46 +03:00
try
2012-03-03 13:08:01 +03:00
{
2014-06-22 13:39:40 +03:00
bankDanger - > term ( " Bank0 " ) - > setMinimum ( info - > minGuards ( ) . totalStrength * 0.5f ) ;
bankDanger - > term ( " Bank0 " ) - > setMaximum ( info - > minGuards ( ) . totalStrength * 1.5f ) ;
bankDanger - > term ( " Bank1 " ) - > setMinimum ( info - > maxGuards ( ) . totalStrength * 0.5f ) ;
bankDanger - > term ( " Bank1 " ) - > setMaximum ( info - > maxGuards ( ) . totalStrength * 1.5f ) ;
//comparison purposes
//int averageValue = (evaluateBankConfig (VLC->objh->banksInfo[ID][0]) + evaluateBankConfig (VLC->objh->banksInfo[ID][3])) * 0.5;
//dynamic_cast<fl::SingletonTerm*>(bankInput->term("SET"))->setValue(0.5);
bankInput - > setInput ( 0.5 ) ;
engine . process ( BANK_DANGER ) ;
//engine.process();
val = bankDanger - > output ( ) . defuzzify ( ) ; //some expected value of this bank
2012-03-04 13:43:46 +03:00
}
2013-11-07 15:48:41 +03:00
catch ( fl : : FuzzyException & fe )
2012-03-04 13:43:46 +03:00
{
2013-04-10 19:28:14 +03:00
logAi - > errorStream ( ) < < " estimateBankDanger " < < fe . name ( ) < < " : " < < fe . message ( ) ;
2012-03-03 13:08:01 +03:00
}
return val ;
2012-03-04 13:43:46 +03:00
}
float FuzzyHelper : : getTacticalAdvantage ( const CArmedInstance * we , const CArmedInstance * enemy )
{
float output = 1 ;
try
{
armyStructure ourStructure = evaluateArmyStructure ( we ) ;
armyStructure enemyStructure = evaluateArmyStructure ( enemy ) ;
2013-12-20 12:43:12 +03:00
ta . ourWalkers - > setInput ( ourStructure . walkers ) ;
ta . ourShooters - > setInput ( ourStructure . shooters ) ;
ta . ourFlyers - > setInput ( ourStructure . flyers ) ;
ta . ourSpeed - > setInput ( ourStructure . maxSpeed ) ;
2012-03-04 13:43:46 +03:00
2013-12-20 12:43:12 +03:00
ta . enemyWalkers - > setInput ( enemyStructure . walkers ) ;
ta . enemyShooters - > setInput ( enemyStructure . shooters ) ;
ta . enemyFlyers - > setInput ( enemyStructure . flyers ) ;
ta . enemySpeed - > setInput ( enemyStructure . maxSpeed ) ;
2012-03-04 13:43:46 +03:00
bool bank = dynamic_cast < const CBank * > ( enemy ) ;
if ( bank )
2013-12-20 12:43:12 +03:00
ta . bankPresent - > setInput ( 1 ) ;
2012-03-04 13:43:46 +03:00
else
2013-12-20 12:43:12 +03:00
ta . bankPresent - > setInput ( 0 ) ;
2012-03-04 13:43:46 +03:00
const CGTownInstance * fort = dynamic_cast < const CGTownInstance * > ( enemy ) ;
if ( fort )
{
2013-12-20 12:43:12 +03:00
ta . castleWalls - > setInput ( fort - > fortLevel ( ) ) ;
2012-03-04 13:43:46 +03:00
}
else
2013-12-20 12:43:12 +03:00
ta . castleWalls - > setInput ( 0 ) ;
2012-03-04 13:43:46 +03:00
engine . process ( TACTICAL_ADVANTAGE ) ;
2014-02-05 21:45:51 +03:00
//engine.process();
2013-12-20 12:43:12 +03:00
output = ta . threat - > output ( ) . defuzzify ( ) ;
2012-03-04 13:43:46 +03:00
}
2013-11-07 15:48:41 +03:00
catch ( fl : : FuzzyException & fe )
2012-03-04 13:43:46 +03:00
{
2013-04-10 19:28:14 +03:00
logAi - > errorStream ( ) < < " getTacticalAdvantage " < < fe . name ( ) < < " : " < < fe . message ( ) ;
2012-03-04 13:43:46 +03:00
}
return output ;
2012-03-05 16:31:59 +03:00
}
2013-11-24 11:21:51 +03:00
2013-12-20 12:43:12 +03:00
FuzzyHelper : : TacticalAdvantage : : ~ TacticalAdvantage ( )
{
//TODO: smart pointers?
delete ourWalkers ;
delete ourShooters ;
delete ourFlyers ;
delete enemyWalkers ;
delete enemyShooters ;
delete enemyFlyers ;
delete ourSpeed ;
delete enemySpeed ;
delete bankPresent ;
delete castleWalls ;
delete threat ;
}
2013-11-24 11:21:51 +03:00
//shared_ptr<AbstractGoal> chooseSolution (std::vector<shared_ptr<AbstractGoal>> & vec)
2013-12-20 16:07:58 +03:00
Goals : : TSubgoal FuzzyHelper : : chooseSolution ( Goals : : TGoalVec vec )
2013-11-25 14:55:48 +03:00
{
2013-12-20 12:43:12 +03:00
if ( vec . empty ( ) ) //no possibilities found
return sptr ( Goals : : Invalid ( ) ) ;
2014-03-31 17:26:09 +03:00
//a trick to switch between heroes less often - calculatePaths is costly
auto sortByHeroes = [ ] ( const Goals : : TSubgoal & lhs , const Goals : : TSubgoal & rhs ) - > bool
{
return lhs - > hero . h < rhs - > hero . h ;
} ;
boost : : sort ( vec , sortByHeroes ) ;
2013-11-25 14:55:48 +03:00
for ( auto g : vec )
{
2013-12-27 16:20:40 +03:00
setPriority ( g ) ;
2013-11-25 14:55:48 +03:00
}
2013-12-28 15:47:55 +03:00
auto compareGoals = [ ] ( const Goals : : TSubgoal & lhs , const Goals : : TSubgoal & rhs ) - > bool
2013-11-25 14:55:48 +03:00
{
2013-12-25 19:05:11 +03:00
return lhs - > priority < rhs - > priority ;
2013-11-25 14:55:48 +03:00
} ;
2013-12-25 19:05:11 +03:00
boost : : sort ( vec , compareGoals ) ;
2014-03-31 17:26:09 +03:00
2013-12-25 19:05:11 +03:00
return vec . back ( ) ;
2013-12-19 23:21:21 +03:00
}
float FuzzyHelper : : evaluate ( Goals : : Explore & g )
{
2013-12-20 12:43:12 +03:00
return 1 ;
2013-12-19 23:21:21 +03:00
}
float FuzzyHelper : : evaluate ( Goals : : RecruitHero & g )
{
2013-12-20 12:43:12 +03:00
return 1 ; //just try to recruit hero as one of options
}
FuzzyHelper : : EvalVisitTile : : ~ EvalVisitTile ( )
{
delete strengthRatio ;
delete heroStrength ;
2014-02-07 11:23:31 +03:00
delete turnDistance ;
2013-12-20 12:43:12 +03:00
delete missionImportance ;
}
void FuzzyHelper : : initVisitTile ( )
{
2014-02-05 21:45:51 +03:00
try
{
std : : vector < fl : : InputLVar * > helper ;
2013-12-20 12:43:12 +03:00
2014-02-05 21:45:51 +03:00
vt . strengthRatio = new fl : : InputLVar ( " strengthRatio " ) ; //hero must be strong enough to defeat guards
vt . heroStrength = new fl : : InputLVar ( " heroStrength " ) ; //we want to use weakest possible hero
2014-02-07 11:23:31 +03:00
vt . turnDistance = new fl : : InputLVar ( " turnDistance " ) ; //we want to use hero who is near
2014-02-05 21:45:51 +03:00
vt . missionImportance = new fl : : InputLVar ( " lockedMissionImportance " ) ; //we may want to preempt hero with low-priority mission
vt . value = new fl : : OutputLVar ( " Value " ) ;
2013-12-23 23:46:01 +03:00
2014-02-07 11:23:31 +03:00
helper + = vt . strengthRatio , vt . heroStrength , vt . turnDistance , vt . missionImportance ;
2014-02-05 21:45:51 +03:00
for ( auto val : helper )
{
engine . addInputLVar ( val ) ;
}
engine . addOutputLVar ( vt . value ) ;
2014-02-06 09:05:45 +03:00
vt . strengthRatio - > addTerm ( new fl : : ShoulderTerm ( " LOW " , 0 , SAFE_ATTACK_CONSTANT , true ) ) ;
2014-02-07 11:23:31 +03:00
vt . strengthRatio - > addTerm ( new fl : : ShoulderTerm ( " HIGH " , SAFE_ATTACK_CONSTANT , SAFE_ATTACK_CONSTANT * 3 , false ) ) ;
2014-02-05 21:45:51 +03:00
//strength compared to our main hero
2014-02-06 09:05:45 +03:00
vt . heroStrength - > addTerm ( new fl : : ShoulderTerm ( " LOW " , 0 , 0.2 , true ) ) ;
2014-02-05 21:45:51 +03:00
vt . heroStrength - > addTerm ( new fl : : TriangularTerm ( " MEDIUM " , 0.2 , 0.8 ) ) ;
2014-02-07 11:23:31 +03:00
vt . heroStrength - > addTerm ( new fl : : ShoulderTerm ( " HIGH " , 0.5 , 1 , false ) ) ;
2014-02-05 21:45:51 +03:00
2014-02-07 11:23:31 +03:00
vt . turnDistance - > addTerm ( new fl : : ShoulderTerm ( " SMALL " , 0 , 0.5 , true ) ) ;
vt . turnDistance - > addTerm ( new fl : : TriangularTerm ( " MEDIUM " , 0.1 , 0.8 ) ) ;
vt . turnDistance - > addTerm ( new fl : : ShoulderTerm ( " LONG " , 0.5 , 3 , false ) ) ;
2014-02-05 21:45:51 +03:00
2014-02-06 09:05:45 +03:00
vt . missionImportance - > addTerm ( new fl : : ShoulderTerm ( " LOW " , 0 , 2.5 , true ) ) ;
vt . missionImportance - > addTerm ( new fl : : TriangularTerm ( " MEDIUM " , 2 , 3 ) ) ;
vt . missionImportance - > addTerm ( new fl : : ShoulderTerm ( " HIGH " , 2.5 , 5 , false ) ) ;
2014-02-05 21:45:51 +03:00
2014-02-07 11:23:31 +03:00
//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 : : ShoulderTerm ( " LOW " , 0 , 2.5 , true ) ) ;
vt . value - > addTerm ( new fl : : TriangularTerm ( " MEDIUM " , 2 , 3 ) ) ; //can't be center of mass :/
2014-02-05 21:45:51 +03:00
vt . value - > addTerm ( new fl : : ShoulderTerm ( " HIGH " , 2.5 , 5 , false ) ) ;
engine . addRuleBlock ( & vt . rules ) ;
//use unarmed scouts if possible
vt . rules . addRule ( new fl : : MamdaniRule ( " if strengthRatio is HIGH and heroStrength is LOW then Value is very HIGH " , engine ) ) ;
//we may want to use secondary hero(es) rather than main hero
vt . rules . addRule ( new fl : : MamdaniRule ( " if strengthRatio is HIGH and heroStrength is MEDIUM then Value is somewhat HIGH " , engine ) ) ;
vt . rules . addRule ( new fl : : MamdaniRule ( " if strengthRatio is HIGH and heroStrength is HIGH then Value is somewhat LOW " , engine ) ) ;
//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 ( new fl : : MamdaniRule ( " if strengthRatio is LOW and heroStrength is LOW then Value is very LOW " , engine ) ) ;
2014-02-07 11:23:31 +03:00
//attempt to arm secondary heroes is not stupid
vt . rules . addRule ( new fl : : MamdaniRule ( " if strengthRatio is LOW and heroStrength is MEDIUM then Value is somewhat HIGH " , engine ) ) ;
2014-02-05 21:45:51 +03:00
vt . rules . addRule ( new fl : : MamdaniRule ( " if strengthRatio is LOW and heroStrength is HIGH then Value is LOW " , engine ) ) ;
//do not cancel important goals
vt . rules . addRule ( new fl : : MamdaniRule ( " if lockedMissionImportance is HIGH then Value is very LOW " , engine ) ) ;
vt . rules . addRule ( new fl : : MamdaniRule ( " if lockedMissionImportance is MEDIUM then Value is somewhat LOW " , engine ) ) ;
vt . rules . addRule ( new fl : : MamdaniRule ( " if lockedMissionImportance is LOW then Value is HIGH " , engine ) ) ;
//pick nearby objects if it's easy, avoid long walks
2014-02-07 11:23:31 +03:00
vt . rules . addRule ( new fl : : MamdaniRule ( " if turnDistance is SMALL then Value is HIGH " , engine ) ) ;
vt . rules . addRule ( new fl : : MamdaniRule ( " if turnDistance is MEDIUM then Value is MEDIUM " , engine ) ) ;
vt . rules . addRule ( new fl : : MamdaniRule ( " if turnDistance is LONG then Value is LOW " , engine ) ) ;
2014-02-05 21:45:51 +03:00
}
catch ( fl : : FuzzyException & fe )
2013-12-20 12:43:12 +03:00
{
2014-02-05 21:45:51 +03:00
logAi - > errorStream ( ) < < " visitTile " < < fe . name ( ) < < " : " < < fe . message ( ) ;
2013-12-20 12:43:12 +03:00
}
2013-12-19 23:21:21 +03:00
}
float FuzzyHelper : : evaluate ( Goals : : VisitTile & g )
{
2013-12-20 16:01:44 +03:00
//we assume that hero is already set and we want to choose most suitable one for the mission
2013-12-20 12:43:12 +03:00
if ( ! g . hero )
return 0 ;
2014-02-07 11:23:31 +03:00
//assert(cb->isInTheMap(g.tile));
2013-12-20 12:43:12 +03:00
cb - > setSelection ( g . hero . h ) ;
2014-02-07 11:23:31 +03:00
float turns = 0 ;
float distance = cb - > getMovementCost ( g . hero . h , g . tile ) ;
if ( ! distance ) //we stand on that tile
turns = 0 ;
else
{
if ( distance < g . hero - > movement ) //we can move there within one turn
turns = ( fl : : flScalar ) distance / g . hero - > movement ;
else
turns = 1 + ( fl : : flScalar ) ( distance - g . hero - > movement ) / g . hero - > maxMovePoints ( true ) ; //bool on land?
}
2013-12-20 12:43:12 +03:00
float missionImportance = 0 ;
if ( vstd : : contains ( ai - > lockedHeroes , g . hero ) )
2013-12-25 19:05:11 +03:00
missionImportance = ai - > lockedHeroes [ g . hero ] - > priority ;
2013-12-20 12:43:12 +03:00
2014-02-07 11:23:31 +03:00
float strengthRatio = 10.0f ; //we are much stronger than enemy
2013-12-20 12:43:12 +03:00
ui64 danger = evaluateDanger ( g . tile , g . hero . h ) ;
if ( danger )
2014-02-05 21:45:51 +03:00
strengthRatio = ( fl : : flScalar ) g . hero . h - > getTotalStrength ( ) / danger ;
2013-12-20 12:43:12 +03:00
try
{
vt . strengthRatio - > setInput ( strengthRatio ) ;
2014-02-05 21:45:51 +03:00
vt . heroStrength - > setInput ( ( fl : : flScalar ) g . hero - > getTotalStrength ( ) / ai - > primaryHero ( ) - > getTotalStrength ( ) ) ;
2014-02-07 11:23:31 +03:00
vt . turnDistance - > setInput ( turns ) ;
2013-12-20 12:43:12 +03:00
vt . missionImportance - > setInput ( missionImportance ) ;
2014-02-05 21:45:51 +03:00
//engine.process();
2013-12-20 12:43:12 +03:00
engine . process ( VISIT_TILE ) ;
2013-12-25 19:05:11 +03:00
g . priority = vt . value - > output ( ) . defuzzify ( ) ;
2013-12-20 12:43:12 +03:00
}
catch ( fl : : FuzzyException & fe )
{
logAi - > errorStream ( ) < < " evaluate VisitTile " < < fe . name ( ) < < " : " < < fe . message ( ) ;
}
2013-12-25 19:05:11 +03:00
return g . priority ;
2013-12-20 12:43:12 +03:00
2013-12-19 23:21:21 +03:00
}
float FuzzyHelper : : evaluate ( Goals : : VisitHero & g )
{
2014-03-23 15:59:03 +03:00
auto obj = cb - > getObj ( ObjectInstanceID ( g . objid ) ) ; //we assume for now that these goals are similar
2014-02-17 20:28:39 +03:00
if ( ! obj )
return - 100 ; //hero died in the meantime
2013-12-20 16:01:44 +03:00
//TODO: consider direct copy (constructor?)
2013-12-27 16:20:40 +03:00
g . setpriority ( Goals : : VisitTile ( obj - > visitablePos ( ) ) . sethero ( g . hero ) . setisAbstract ( g . isAbstract ) . accept ( this ) ) ;
return g . priority ;
2013-12-19 23:21:21 +03:00
}
2014-02-07 23:09:15 +03:00
float FuzzyHelper : : evaluate ( Goals : : GatherArmy & g )
{
//the more army we need, the more important goal
//the more army we lack, the less important goal
float army = g . hero - > getArmyStrength ( ) ;
2014-02-15 19:38:51 +03:00
return g . value / std : : max ( g . value - army , 1000.0f ) ;
2014-02-07 23:09:15 +03:00
}
2014-02-20 23:18:49 +03:00
float FuzzyHelper : : evaluate ( Goals : : ClearWayTo & g )
{
if ( ! g . hero . h )
throw cannotFulfillGoalException ( " ClearWayTo called without hero! " ) ;
SectorMap sm ( g . hero ) ;
int3 t = sm . firstTileToGet ( g . hero , g . tile ) ;
if ( t . valid ( ) )
{
if ( isSafeToVisit ( g . hero , t ) )
{
g . setpriority ( Goals : : VisitTile ( g . tile ) . sethero ( g . hero ) . setisAbstract ( g . isAbstract ) . accept ( this ) ) ;
}
else
{
g . setpriority ( Goals : : GatherArmy ( evaluateDanger ( t , g . hero . h ) * SAFE_ATTACK_CONSTANT ) .
sethero ( g . hero ) . setisAbstract ( true ) . accept ( this ) ) ;
}
return g . priority ;
}
else
return - 1 ;
}
2013-12-19 23:21:21 +03:00
float FuzzyHelper : : evaluate ( Goals : : BuildThis & g )
{
2013-12-23 23:46:01 +03:00
return 1 ;
2013-12-19 23:21:21 +03:00
}
float FuzzyHelper : : evaluate ( Goals : : DigAtTile & g )
{
return 0 ;
}
float FuzzyHelper : : evaluate ( Goals : : CollectRes & g )
{
return 0 ;
}
float FuzzyHelper : : evaluate ( Goals : : Build & g )
{
return 0 ;
}
float FuzzyHelper : : evaluate ( Goals : : Invalid & g )
{
return - 1e10 ;
}
float FuzzyHelper : : evaluate ( Goals : : AbstractGoal & g )
{
2013-12-26 12:53:37 +03:00
logAi - > warnStream ( ) < < boost : : format ( " Cannot evaluate goal %s " ) % g . name ( ) ;
return g . priority ;
2013-12-20 16:07:58 +03:00
}
2013-12-27 16:20:40 +03:00
void FuzzyHelper : : setPriority ( Goals : : TSubgoal & g )
{
g - > setpriority ( g - > accept ( this ) ) ; //this enforces returned value is set
2013-12-28 15:47:55 +03:00
}