mirror of
https://github.com/vcmi/vcmi.git
synced 2025-07-17 01:32:21 +02:00
Some work towards useful AI:
-It is possible to toggle adventure AI on / off typing "ai" in the console (default is true) -General cleaning and formatting of code -Some minor optimizations -Teal console log for AI AI tends to hang in the maze of mutexes and current player interface, further investigation is needed. Fixed #580 minor tweaks
This commit is contained in:
@ -100,11 +100,11 @@ void Priorities::fillFeatures(const CGeniusAI::HypotheticalGameState & hgs)
|
|||||||
|
|
||||||
float Priorities::getCost(vector<int> &resourceCosts,const CGHeroInstance * moved,int distOutOfTheWay)
|
float Priorities::getCost(vector<int> &resourceCosts,const CGHeroInstance * moved,int distOutOfTheWay)
|
||||||
{
|
{
|
||||||
if(resourceCosts.size()==0)return -1;
|
if(!resourceCosts.size())return -1;
|
||||||
//TODO: replace with ann
|
//TODO: replace with ann
|
||||||
float cost = resourceCosts[0]/4.0+resourceCosts[1]/2.0+resourceCosts[2]/4.0+resourceCosts[3]/2.0+resourceCosts[4]/2.0+resourceCosts[5]/2.0+resourceCosts[6]/3000.0;
|
float cost = resourceCosts[0]/4.0+resourceCosts[1]/2.0+resourceCosts[2]/4.0+resourceCosts[3]/2.0+resourceCosts[4]/2.0+resourceCosts[5]/2.0+resourceCosts[6]/3000.0;
|
||||||
|
|
||||||
if(moved!=NULL) //TODO: multiply by importance of hero
|
if(moved) //TODO: multiply by importance of hero
|
||||||
cost+=distOutOfTheWay/10000.0;
|
cost+=distOutOfTheWay/10000.0;
|
||||||
return cost;
|
return cost;
|
||||||
}
|
}
|
||||||
@ -210,25 +210,24 @@ float Priorities::getValue(const CGeniusAI::AIObjective & obj)
|
|||||||
//objectNetworks[53][hobj->object->subID].feedForward(stateFeatures);
|
//objectNetworks[53][hobj->object->subID].feedForward(stateFeatures);
|
||||||
case 113://TODO: replace with value of skill for the hero
|
case 113://TODO: replace with value of skill for the hero
|
||||||
return 0;
|
return 0;
|
||||||
case 103:case 58://TODO: replace with value of seeing x number of new tiles
|
case 103: case 58://TODO: replace with value of seeing x number of new tiles
|
||||||
return 0;
|
return 0;
|
||||||
default:
|
default:
|
||||||
if(objectNetworks[hobj->object->ID].size()!=0)
|
if (objectNetworks[hobj->object->ID].size())
|
||||||
return objectNetworks[hobj->object->ID][0].feedForward(stateFeatures);
|
return objectNetworks[hobj->object->ID][0].feedForward(stateFeatures);
|
||||||
cout << "don't know the value of ";
|
tlog6 << "don't know the value of ";
|
||||||
switch(obj.type)
|
switch(obj.type)
|
||||||
{
|
{
|
||||||
case CGeniusAI::AIObjective::visit:
|
case CGeniusAI::AIObjective::visit:
|
||||||
cout << "visiting " << hobj->object->ID;
|
tlog6 << "visiting " << hobj->object->ID;
|
||||||
break;
|
break;
|
||||||
case CGeniusAI::AIObjective::attack:
|
tlog6 << "attacking " << hobj->object->ID;
|
||||||
cout << "attacking " << hobj->object->ID;
|
|
||||||
break;
|
break;
|
||||||
case CGeniusAI::AIObjective::finishTurn:
|
case CGeniusAI::AIObjective::finishTurn:
|
||||||
obj.print();
|
obj.print();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
cout << endl;
|
tlog6 << endl;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else //town objective
|
else //town objective
|
||||||
@ -240,9 +239,9 @@ float Priorities::getValue(const CGeniusAI::AIObjective & obj)
|
|||||||
return buildingNetworks[tnObj->whichTown->t->subID][tnObj->which].feedForward(stateFeatures);
|
return buildingNetworks[tnObj->whichTown->t->subID][tnObj->which].feedForward(stateFeatures);
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
cout << "don't know the value of ";
|
tlog6 << "don't know the value of ";
|
||||||
obj.print();
|
obj.print();
|
||||||
cout << endl;
|
tlog6 << endl;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -28,11 +28,10 @@ class CGeniusAI : public CGlobalAI
|
|||||||
{
|
{
|
||||||
private:
|
private:
|
||||||
// TODO: cb... come back, croach busters!?
|
// TODO: cb... come back, croach busters!?
|
||||||
ICallback* m_cb;
|
ICallback* m_cb;
|
||||||
geniusai::BattleAI::CBattleLogic* m_battleLogic;
|
geniusai::BattleAI::CBattleLogic* m_battleLogic;
|
||||||
geniusai::GeneralAI::CGeneralAI m_generalAI;
|
geniusai::GeneralAI::CGeneralAI m_generalAI;
|
||||||
geniusai::Priorities* m_priorities;
|
geniusai::Priorities* m_priorities;
|
||||||
|
|
||||||
|
|
||||||
CondSh<BattleState> m_state; //are we engaged into battle?
|
CondSh<BattleState> m_state; //are we engaged into battle?
|
||||||
|
|
||||||
@ -71,7 +70,7 @@ private:
|
|||||||
void update(CGeniusAI & ai);
|
void update(CGeniusAI & ai);
|
||||||
CGeniusAI * AI;
|
CGeniusAI * AI;
|
||||||
std::vector<const CGHeroInstance *> AvailableHeroesToBuy;
|
std::vector<const CGHeroInstance *> AvailableHeroesToBuy;
|
||||||
std::vector<int> resourceAmounts;
|
std::vector<ui32> resourceAmounts;
|
||||||
std::vector<HeroModel> heroModels;
|
std::vector<HeroModel> heroModels;
|
||||||
std::vector<TownModel> townModels;
|
std::vector<TownModel> townModels;
|
||||||
std::set< AIObjectContainer > knownVisitableObjects;
|
std::set< AIObjectContainer > knownVisitableObjects;
|
||||||
@ -110,7 +109,7 @@ private:
|
|||||||
public:
|
public:
|
||||||
HypotheticalGameState hgs;
|
HypotheticalGameState hgs;
|
||||||
int3 pos;
|
int3 pos;
|
||||||
const CGObjectInstance * object;
|
const CGObjectInstance * object; //interactive object
|
||||||
mutable std::vector<HypotheticalGameState::HeroModel*> whoCanAchieve;
|
mutable std::vector<HypotheticalGameState::HeroModel*> whoCanAchieve;
|
||||||
|
|
||||||
//HeroObjective(){}
|
//HeroObjective(){}
|
||||||
|
@ -45,11 +45,7 @@ template <typename ruleType, typename facts> template <typename cond> void Exper
|
|||||||
++goalCounter;
|
++goalCounter;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
matchedConditions = 0;
|
|
||||||
//modify set until something happens (hopefully!)
|
//modify set until something happens (hopefully!)
|
||||||
for (iF = factsToErase.begin(); iF != factsToErase.end(); iF++)
|
|
||||||
factList.erase(knowledge.find(*iF));
|
|
||||||
factsToErase.clear(); //TODO: what if fact is remembered by rule, yet already erased?
|
|
||||||
for (iF = factsToAdd.begin(); iF != factsToAdd.end(); iF++)
|
for (iF = factsToAdd.begin(); iF != factsToAdd.end(); iF++)
|
||||||
factList.insert(*iF);
|
factList.insert(*iF);
|
||||||
if (factsToAdd.size())
|
if (factsToAdd.size())
|
||||||
@ -58,6 +54,9 @@ template <typename ruleType, typename facts> template <typename cond> void Exper
|
|||||||
factWasAdded = true;
|
factWasAdded = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
for (iF = factsToErase.begin(); iF != factsToErase.end(); iF++) //remove facts discarded in this run
|
||||||
|
factList.erase(knowledge.find(*iF));
|
||||||
|
factsToErase.clear(); //erase only after all rules had a chance to trigger
|
||||||
for (ir = rulesToErase.begin(); ir != rulesToErase.end(); ir++)
|
for (ir = rulesToErase.begin(); ir != rulesToErase.end(); ir++)
|
||||||
knowledge.erase(knowledge.find(*ir));
|
knowledge.erase(knowledge.find(*ir));
|
||||||
rulesToErase.clear();
|
rulesToErase.clear();
|
||||||
@ -89,13 +88,13 @@ void BonusRule::fireRule()
|
|||||||
case BonusCondition::duration:
|
case BonusCondition::duration:
|
||||||
if (!it->first.functor(it->second->object->duration, it->first.value)) return;
|
if (!it->first.functor(it->second->object->duration, it->first.value)) return;
|
||||||
break;
|
break;
|
||||||
case BonusCondition::source:
|
case BonusCondition::source: //likely to handle by selector
|
||||||
if (!it->first.functor(it->second->object->source, it->first.value)) return;
|
if (!it->first.functor(it->second->object->source, it->first.value)) return;
|
||||||
break;
|
break;
|
||||||
case BonusCondition::id:
|
case BonusCondition::id:
|
||||||
if (!it->first.functor(it->second->object->id, it->first.value)) return;
|
if (!it->first.functor(it->second->object->id, it->first.value)) return;
|
||||||
break;
|
break;
|
||||||
case BonusCondition::valType:
|
case BonusCondition::valType: //ever needed?
|
||||||
if (!it->first.functor(it->second->object->valType, it->first.value)) return;
|
if (!it->first.functor(it->second->object->valType, it->first.value)) return;
|
||||||
break;
|
break;
|
||||||
case BonusCondition::additionalInfo:
|
case BonusCondition::additionalInfo:
|
||||||
@ -110,12 +109,18 @@ void BonusRule::fireRule()
|
|||||||
}
|
}
|
||||||
//TODO: add new fact or modify existing one
|
//TODO: add new fact or modify existing one
|
||||||
}
|
}
|
||||||
|
//TODO: find out why it does not compile
|
||||||
//template <typename input, typename conType> void Rule<input, conType>::refreshRule(std::set<conType> &conditionSet)
|
//template <typename input, typename conType> void Rule<input, conType>::refreshRule(std::set<conType> &conditionSet)
|
||||||
//{
|
//{
|
||||||
// cons.clear();
|
// cons.clear();
|
||||||
// for (std::set<conType>::iterator it = conditionSet.begin(); it != conditionSet.end(); it++)
|
// for (std::set<conType>::iterator it = conditionSet.begin(); it != conditionSet.end(); it++)
|
||||||
// cons.insert(std::make_pair<conType,input*>(*it, NULL)); //pointer to condition and null fact
|
// cons.insert(std::make_pair<conType,input*>(*it, NULL)); //pointer to condition and null fact
|
||||||
//}
|
//}
|
||||||
|
//template <typename input, typename conType> void Rule<input, conType>::refreshRule()
|
||||||
|
//{
|
||||||
|
// for (std::set<std::pair<conType, input*>>::iterator it = cons.begin(); it != cons.end(); it++)
|
||||||
|
// *it->second = NULL;
|
||||||
|
//}
|
||||||
bool BonusCondition::matchesFact(Bonus &fact)
|
bool BonusCondition::matchesFact(Bonus &fact)
|
||||||
{
|
{
|
||||||
if (object(fact)) //Bonus(fact) matches local Selector(object)
|
if (object(fact)) //Bonus(fact) matches local Selector(object)
|
||||||
|
@ -53,7 +53,7 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
template <typename input> class condition
|
template <typename input> class condition
|
||||||
{//determines selected object parameter with value using functor
|
{//compares selected object parameter with value using functor. universal logic handler
|
||||||
public:
|
public:
|
||||||
input object; //what the fact is, or what it's like (CSelector)
|
input object; //what the fact is, or what it's like (CSelector)
|
||||||
si32 value;
|
si32 value;
|
||||||
@ -74,9 +74,13 @@ public:
|
|||||||
protected:
|
protected:
|
||||||
std::set<std::pair<conType, input*>> cons; //conditions and matching facts
|
std::set<std::pair<conType, input*>> cons; //conditions and matching facts
|
||||||
input decision;
|
input decision;
|
||||||
virtual void canBeFired(); //if this data makes any sense for rule
|
virtual void canBeFired(); //if this data makes any sense for rule - type check
|
||||||
virtual void fireRule(std::set<input*> &feed);
|
virtual bool checkCondition(); //if condition is true or false
|
||||||
|
virtual bool checkCondition(std::set<input*> &feed);
|
||||||
virtual void fireRule(); //use paired conditions and facts by default
|
virtual void fireRule(); //use paired conditions and facts by default
|
||||||
|
virtual void fireRule(ExpertSystemShell<input, conType> &system);
|
||||||
|
virtual void fireRule(std::set<input*> &feed);
|
||||||
|
virtual void refreshRule();
|
||||||
virtual void refreshRule(std::set<conType> &conditionSet); //in case conditions were erased
|
virtual void refreshRule(std::set<conType> &conditionSet); //in case conditions were erased
|
||||||
public:
|
public:
|
||||||
Rule(){fired = false; conditionCounter = 0; decision = NULL;};
|
Rule(){fired = false; conditionCounter = 0; decision = NULL;};
|
||||||
@ -124,21 +128,56 @@ public:
|
|||||||
};
|
};
|
||||||
bool matchesFact(Bonus &fact);
|
bool matchesFact(Bonus &fact);
|
||||||
};
|
};
|
||||||
|
|
||||||
class BonusHolder : public AIholder<Bonus>
|
class BonusHolder : public AIholder<Bonus>
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
BonusHolder(Bonus &bonus){object = &bonus; aiValue = bonus.val;}
|
BonusHolder(Bonus &bonus){object = &bonus; aiValue = bonus.val;}
|
||||||
BonusHolder(Bonus &bonus, si32 val){object = &bonus; aiValue = val;}
|
BonusHolder(Bonus &bonus, si32 val){object = &bonus; aiValue = val;}
|
||||||
};
|
};
|
||||||
|
|
||||||
class BonusRule : public Rule <BonusHolder, BonusCondition>
|
class BonusRule : public Rule <BonusHolder, BonusCondition>
|
||||||
{
|
{
|
||||||
protected:
|
protected:
|
||||||
void fireRule();
|
void fireRule();
|
||||||
};
|
};
|
||||||
|
|
||||||
bool greaterThan (int prop, si32 val)
|
inline bool greaterThan (int prop, si32 val)
|
||||||
{
|
{
|
||||||
if ((si32)prop > val)
|
if ((si32)prop > val)
|
||||||
return true;
|
return true;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
inline bool lessThan (int prop, si32 val)
|
||||||
|
{
|
||||||
|
if ((si32)prop < val)
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
inline bool eqal (int prop, si32 val)
|
||||||
|
{
|
||||||
|
if ((si32)prop == val)
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
inline bool unequal (int prop, si32 val)
|
||||||
|
{
|
||||||
|
if ((si32)prop != val)
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
inline bool present (int prop, si32 val=0)
|
||||||
|
//inline bool present (int prop) //TODO: can we use function with less arguments?
|
||||||
|
{
|
||||||
|
return(prop); //unfixable warning :(
|
||||||
|
}
|
||||||
|
|
||||||
|
class KnowledgeHandler///I'd opt for one omniscent knowledge manager, so no templates here
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
std::list<BonusRule> knowledge; //permanent storage of rules
|
||||||
|
|
||||||
|
void parseKnowledge(std::string &filename){};
|
||||||
|
void addKnowledge(ExpertSystemShell<BRule,Bonus> &expert);
|
||||||
|
void addFacts(ExpertSystemShell<BRule,Bonus> &expert);
|
||||||
|
};
|
@ -42,6 +42,7 @@
|
|||||||
#define CONSOLE_YELLOW FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY
|
#define CONSOLE_YELLOW FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_INTENSITY
|
||||||
#define CONSOLE_WHITE FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY
|
#define CONSOLE_WHITE FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY
|
||||||
#define CONSOLE_GRAY FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE
|
#define CONSOLE_GRAY FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE
|
||||||
|
#define CONSOLE_TEAL FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
TColor defColor;
|
TColor defColor;
|
||||||
@ -168,6 +169,9 @@ void CConsoleHandler::setColor(int level)
|
|||||||
case 5:
|
case 5:
|
||||||
color = CONSOLE_GRAY;
|
color = CONSOLE_GRAY;
|
||||||
break;
|
break;
|
||||||
|
case -2:
|
||||||
|
color = CONSOLE_TEAL;
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
color = defColor;
|
color = defColor;
|
||||||
break;
|
break;
|
||||||
|
@ -75,7 +75,6 @@ Point screenLT = Point(0, 0); //position of top left corner of the screen
|
|||||||
Point screenLTmax = Point(0, 0); //,maximal values for screenLT coordinates
|
Point screenLTmax = Point(0, 0); //,maximal values for screenLT coordinates
|
||||||
static boost::thread *mainGUIThread;
|
static boost::thread *mainGUIThread;
|
||||||
|
|
||||||
|
|
||||||
SystemOptions GDefaultOptions;
|
SystemOptions GDefaultOptions;
|
||||||
VCMIDirs GVCMIDirs;
|
VCMIDirs GVCMIDirs;
|
||||||
std::queue<SDL_Event*> events;
|
std::queue<SDL_Event*> events;
|
||||||
@ -428,6 +427,11 @@ void processCommand(const std::string &message)
|
|||||||
{
|
{
|
||||||
gOnlyAI = true;
|
gOnlyAI = true;
|
||||||
}
|
}
|
||||||
|
else if (cn == "ai")
|
||||||
|
{
|
||||||
|
VLC->IS_AI_ENABLED = !VLC->IS_AI_ENABLED;
|
||||||
|
tlog4 << "Current AI status: " << (VLC->IS_AI_ENABLED ? "enabled" : "disabled") << std::endl;
|
||||||
|
}
|
||||||
else if(cn == "mp" && adventureInt)
|
else if(cn == "mp" && adventureInt)
|
||||||
{
|
{
|
||||||
if(const CGHeroInstance *h = dynamic_cast<const CGHeroInstance *>(adventureInt->selection))
|
if(const CGHeroInstance *h = dynamic_cast<const CGHeroInstance *>(adventureInt->selection))
|
||||||
|
@ -1676,7 +1676,7 @@ void CRecruitmentWindow::Buy()
|
|||||||
int crid = creatures[which].ID,
|
int crid = creatures[which].ID,
|
||||||
dstslot = dst-> getSlotFor(crid);
|
dstslot = dst-> getSlotFor(crid);
|
||||||
|
|
||||||
if(dstslot < 0) //no available slot
|
if(dstslot < 0 && !vstd::contains(CGI->arth->bigArtifacts,CGI->arth->convertMachineID(crid, true))) //no available slot
|
||||||
{
|
{
|
||||||
std::string txt;
|
std::string txt;
|
||||||
if(dst->ID == HEROI_TYPE)
|
if(dst->ID == HEROI_TYPE)
|
||||||
|
1
global.h
1
global.h
@ -358,6 +358,7 @@ extern DLL_EXPORT CLogger tlog2; //magenta - major warnings
|
|||||||
extern DLL_EXPORT CLogger tlog3; //yellow - minor warnings
|
extern DLL_EXPORT CLogger tlog3; //yellow - minor warnings
|
||||||
extern DLL_EXPORT CLogger tlog4; //white - detailed log info
|
extern DLL_EXPORT CLogger tlog4; //white - detailed log info
|
||||||
extern DLL_EXPORT CLogger tlog5; //gray - minor log info
|
extern DLL_EXPORT CLogger tlog5; //gray - minor log info
|
||||||
|
extern DLL_EXPORT CLogger tlog6; //teal - AI info
|
||||||
|
|
||||||
//XXX pls dont - 'debug macros' are usually more trouble than it's worth
|
//XXX pls dont - 'debug macros' are usually more trouble than it's worth
|
||||||
#define HANDLE_EXCEPTION \
|
#define HANDLE_EXCEPTION \
|
||||||
|
@ -34,6 +34,7 @@ DLL_EXPORT CLogger tlog2(2);
|
|||||||
DLL_EXPORT CLogger tlog3(3);
|
DLL_EXPORT CLogger tlog3(3);
|
||||||
DLL_EXPORT CLogger tlog4(4);
|
DLL_EXPORT CLogger tlog4(4);
|
||||||
DLL_EXPORT CLogger tlog5(5);
|
DLL_EXPORT CLogger tlog5(5);
|
||||||
|
DLL_EXPORT CLogger tlog6(-2);
|
||||||
|
|
||||||
DLL_EXPORT CConsoleHandler *console = NULL;
|
DLL_EXPORT CConsoleHandler *console = NULL;
|
||||||
DLL_EXPORT std::ostream *logfile = NULL
|
DLL_EXPORT std::ostream *logfile = NULL
|
||||||
@ -205,6 +206,8 @@ void LibClasses::init()
|
|||||||
spellh = new CSpellHandler;
|
spellh = new CSpellHandler;
|
||||||
spellh->loadSpells();
|
spellh->loadSpells();
|
||||||
tlog0<<"\tSpell handler: "<<pomtime.getDif()<<std::endl;
|
tlog0<<"\tSpell handler: "<<pomtime.getDif()<<std::endl;
|
||||||
|
|
||||||
|
IS_AI_ENABLED = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void LibClasses::clear()
|
void LibClasses::clear()
|
||||||
|
@ -26,6 +26,7 @@ class CGeneralTextHandler;
|
|||||||
class DLL_EXPORT LibClasses
|
class DLL_EXPORT LibClasses
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
bool IS_AI_ENABLED; //VLC is teh only object visible from both CMT and GeniusAI
|
||||||
CArtHandler * arth;
|
CArtHandler * arth;
|
||||||
CHeroHandler * heroh;
|
CHeroHandler * heroh;
|
||||||
CCreatureHandler * creh;
|
CCreatureHandler * creh;
|
||||||
@ -45,7 +46,7 @@ public:
|
|||||||
void callWhenDeserializing(); //should be called only by serialize !!!
|
void callWhenDeserializing(); //should be called only by serialize !!!
|
||||||
template <typename Handler> void serialize(Handler &h, const int version)
|
template <typename Handler> void serialize(Handler &h, const int version)
|
||||||
{
|
{
|
||||||
h & heroh & arth & creh & townh & objh & dobjinfo & buildh & spellh;
|
h & heroh & arth & creh & townh & objh & dobjinfo & buildh & spellh & IS_AI_ENABLED;;
|
||||||
if(!h.saving)
|
if(!h.saving)
|
||||||
{
|
{
|
||||||
callWhenDeserializing();
|
callWhenDeserializing();
|
||||||
|
Reference in New Issue
Block a user