1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-11-24 08:32:34 +02:00

AI loads .brain file which contains a list of objects to visit and what features are necessary to determine how valuable that objective is.

AI creates random neural networks and assumes that their outputs are trained values.
This commit is contained in:
Trevor Standley 2009-08-18 07:37:45 +00:00
parent c7a307dfed
commit a3b6bb4892
7 changed files with 963 additions and 83 deletions

52
AI/GeniusAI.brain Normal file
View File

@ -0,0 +1,52 @@
34 16 17
R
34 16 17
R
47 16
R
101 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 24
R
98 16 17
R
98 16 17
R
100 16
R
38 16
R
61 16
R
53 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
R
53 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
R
53 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
R
53 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
R
53 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
R
53 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
R
53 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
R
53 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
R
28 16
R
81 16
R
83 25
R
31 16
R
57 24
R
23 16
R
102 16
R
37 24
R
51 16
R

View File

@ -0,0 +1,244 @@
#ifndef AI_PRIORITIES
#define AI_PRIORITIES
#include "AIPriorities.h"
#include <sstream>
using namespace GeniusAI;
Network::Network()
{}
Network::Network(vector<unsigned int> whichFeatures)// random network
:net(whichFeatures.size(), whichFeatures.size()*.601+2, whichFeatures.size()*.251+2, 1),whichFeatures(whichFeatures)
{
}
Network::Network(istream & input)
{
vector<int> whichFeatures;
int feature;
string line;
getline(input,line);
stringstream lineIn(line);
while(lineIn>>feature)
whichFeatures.push_back(feature);
getline(input,line);//get R
net = neuralNetwork(whichFeatures.size(), whichFeatures.size()*.601+2, whichFeatures.size()*.251+2, 1);
}
float Network::feedForward(const vector<float> & stateFeatures)
{
double * input = new double[whichFeatures.size()];
for(int i = 0; i < whichFeatures.size();i++)
input[i]=stateFeatures[whichFeatures[i]];
float ans = net.feedForwardPattern(input)[0];
delete input;
return ans;
}
Priorities::Priorities()//random brain
:numSpecialFeatures(8)
{
/* vector<unsigned int> whichFeatures;//(512);
whichFeatures.push_back(16);
whichFeatures.push_back(17);
networks.push_back(Network(whichFeatures)); //for a friendly hero
networks.push_back(Network(whichFeatures)); //for an enemy hero
whichFeatures.clear();
whichFeatures.push_back(16); //hero's AI value
networks.push_back(Network(whichFeatures)); //for school of magic
whichFeatures.clear();
for(int i = 0; i <=16;i++)
whichFeatures.push_back(i); //hero's AI value is 16
networks.push_back(Network(whichFeatures)); //for treasure chest
whichFeatures.clear();
whichFeatures.push_back(17);
networks.push_back(Network(whichFeatures)); //for a friendly town
networks.push_back(Network(whichFeatures)); //for an enemy town
whichFeatures.clear();
whichFeatures.push_back(16);
networks.push_back(Network(whichFeatures)); //for learning stone
*/
}
Priorities::Priorities(const string & filename) //read brain from file
:numSpecialFeatures(8)
{
ifstream infile(filename.c_str());
// object_num [list of features]
// brain data or "R" for random brain
networks.resize(255);
int object_num;
while(infile>>object_num)
{
networks[object_num].push_back(Network(infile));
}
}
void Priorities::fillFeatures(const CGeniusAI::HypotheticalGameState & hgs)
{
stateFeatures.clear();
stateFeatures.resize(50);
for(int i = 0; i < stateFeatures.size();i++)
stateFeatures[i]=0;
for(int i = 0; i < hgs.resourceAmounts.size();i++) //features 0-7 are resources
stateFeatures[i]=hgs.resourceAmounts[i];
//TODO: //features 8-15 are incomes
specialFeaturesStart = 16; //features 16-23 are special features (filled in by get functions before ANN)
stateFeatures[24] = hgs.AI->m_cb->getDate();
stateFeatures[25] = 1;
}
float Priorities::getCost(vector<int> &resourceCosts,const CGHeroInstance * moved,int distOutOfTheWay)
{
if(resourceCosts.size()==0)return -1;
//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;
if(moved!=NULL) //TODO: multiply by importance of hero
cost+=distOutOfTheWay/10000.0;
return cost;
}
float Priorities::getValue(const CGeniusAI::AIObjective & obj)
{ //resource
vector<int> resourceAmounts(8,0);
int amount;
if(obj.type==CGeniusAI::AIObjective::finishTurn) //TODO: replace with value of visiting that object divided by days till completed
return .0001; //small nonzero
float a;
if(dynamic_cast<const CGeniusAI::HeroObjective* >(&obj))
{
const CGeniusAI::HeroObjective* hobj = dynamic_cast<const CGeniusAI::HeroObjective* >(&obj);
stateFeatures[16] = hobj->whoCanAchieve.front()->h->getTotalStrength();
if(dynamic_cast<const CArmedInstance*>(hobj->object))
stateFeatures[17] = dynamic_cast<const CArmedInstance*>(hobj->object)->getArmyStrength();
switch(hobj->object->ID)
{
case 5: //artifact //TODO: return value of each artifact
return 0;
case 79:
switch(hobj->object->subID)
{
case 6:
amount = 800;
break;
case 0: case 2:
amount = 9.5; //will be rounded, sad
break;
default:
amount = 4;
break;
}
resourceAmounts[hobj->object->subID]=amount;
return getCost(resourceAmounts,NULL,0);
break;
case 55://mystical garden
resourceAmounts[6]=500;
a=getCost(resourceAmounts,NULL,0);
resourceAmounts[6]=0;
resourceAmounts[5]=5;
return (a+getCost(resourceAmounts,NULL,0))*.5;
case 109:
resourceAmounts[6]=1000;
return getCost(resourceAmounts,NULL,0);
case 12://campfire
resourceAmounts[6]=500;
for(int i = 0; i < 6;i++)
resourceAmounts[i]=1;
return getCost(resourceAmounts,NULL,0);
case 112://windmill
for(int i = 1; i < 6;i++)//no wood
resourceAmounts[i]=1;
return getCost(resourceAmounts,NULL,0);
break;
case 49://magic well
//TODO: add features for hero's spell points
break;
case 34: //hero
if(dynamic_cast<const CGHeroInstance*>(hobj->object)->getOwner()==obj.AI->m_cb->getMyColor())//friendly hero
{
stateFeatures[17] = dynamic_cast<const CGHeroInstance*>(hobj->object)->getTotalStrength();
return networks[34][0].feedForward(stateFeatures);
}
else
{
stateFeatures[17] = dynamic_cast<const CGHeroInstance*>(hobj->object)->getTotalStrength();
return networks[34][1].feedForward(stateFeatures);
}
break;
case 98:
if(dynamic_cast<const CGTownInstance*>(hobj->object)->getOwner()==obj.AI->m_cb->getMyColor())//friendly town
{
stateFeatures[17] = dynamic_cast<const CGTownInstance*>(hobj->object)->getArmyStrength();
return networks[98][0].feedForward(stateFeatures);
}
else
{
stateFeatures[17] = dynamic_cast<const CGTownInstance*>(hobj->object)->getArmyStrength();
return networks[98][1].feedForward(stateFeatures);
}
break;
case 88:
//TODO: average value of unknown level 1 spell, or value of known spell
case 89:
//TODO: average value of unknown level 2 spell, or value of known spell
case 90:
//TODO: average value of unknown level 3 spell, or value of known spell
return 0;
break;
case 215://quest guard
return 0;
case 53: //various mines
return networks[53][hobj->object->subID].feedForward(stateFeatures);
case 113://TODO: replace with value of skill for the hero
return 0;
case 103:case 58://TODO: replace with value of seeing x number of new tiles
return 0;
default:
if(networks[hobj->object->ID].size()!=0)
return networks[hobj->object->ID][0].feedForward(stateFeatures);
cout << "don't know the value of ";
switch(obj.type)
{
case CGeniusAI::AIObjective::visit:
cout << "visiting " << hobj->object->ID;
break;
case CGeniusAI::AIObjective::attack:
cout << "attacking " << hobj->object->ID;
break;
case CGeniusAI::AIObjective::finishTurn:
obj.print();
break;
}
cout << endl;
}
}
else //town objective
{
}
return 0;
}
#endif

View File

@ -0,0 +1,39 @@
#ifndef AIP_H
#define AIP_H
#include <string>
#include "CGeniusAI.h"
#include "neuralNetwork.h"
namespace GeniusAI {
class Network
{
public:
Network();
Network(vector<unsigned int> whichFeatures);// random network
Network(istream & input);
vector<unsigned int> whichFeatures;
float feedForward(const vector<float> & stateFeatures);
neuralNetwork net; //a network with whichFeatures.size() inputs, and 1 output
};
class Priorities
{
public:
Priorities(); //random brain
Priorities(const string & filename); //read brain from file
vector<float> stateFeatures;
int specialFeaturesStart;
int numSpecialFeatures;
void fillFeatures(const CGeniusAI::HypotheticalGameState & AI);
float getValue(const CGeniusAI::AIObjective & obj);
float getCost(vector<int> &resourceCosts,const CGHeroInstance * moved,int distOutOfTheWay);
vector<vector<Network> > networks;
};
}
#endif

View File

@ -1,9 +1,11 @@
#include "CGeniusAI.h"
#include "AIPriorities.h"
#include <iostream>
#include "../../hch/CBuildingHandler.h"
#include "../../hch/CHeroHandler.h"
#include "../../lib/VCMI_Lib.h"
#include "../../lib/NetPacks.h"
using namespace std;
using namespace GeniusAI;
@ -49,31 +51,33 @@ CGeniusAI::HypotheticalGameState::TownModel::TownModel(const CGTownInstance *t):
creaturesInGarrison = t->army;
}
CGeniusAI::HypotheticalGameState::HypotheticalGameState(CGeniusAI & AI)
:knownVisitableObjects(AI.knownVisitableObjects)
CGeniusAI::HypotheticalGameState::HypotheticalGameState(CGeniusAI & ai)
:knownVisitableObjects(ai.knownVisitableObjects)
{
std::vector < const CGHeroInstance *> heroes = AI.m_cb->getHeroesInfo();
AI = &ai;
std::vector < const CGHeroInstance *> heroes = ai.m_cb->getHeroesInfo();
for(std::vector < const CGHeroInstance *>::iterator i = heroes.begin(); i != heroes.end(); i++)
heroModels.push_back(HeroModel(*i));
std::vector < const CGTownInstance *> towns = AI.m_cb->getTownsInfo();
std::vector < const CGTownInstance *> towns = ai.m_cb->getTownsInfo();
for(std::vector < const CGTownInstance *>::iterator i = towns.begin(); i != towns.end(); i++)
if((*i)->tempOwner==AI.m_cb->getMyColor())
if((*i)->tempOwner==ai.m_cb->getMyColor())
townModels.push_back(TownModel(*i));
if(AI.m_cb->howManyTowns()!=0)
AvailableHeroesToBuy = AI.m_cb->getAvailableHeroes(AI.m_cb->getTownInfo(0,0));
if(ai.m_cb->howManyTowns()!=0)
AvailableHeroesToBuy = ai.m_cb->getAvailableHeroes(ai.m_cb->getTownInfo(0,0));
for(int i = 0; i < 8;i++)resourceAmounts.push_back(AI.m_cb->getResourceAmount(i));
for(int i = 0; i < 8;i++)resourceAmounts.push_back(ai.m_cb->getResourceAmount(i));
}
void CGeniusAI::HypotheticalGameState::update(CGeniusAI & AI)
void CGeniusAI::HypotheticalGameState::update(CGeniusAI & ai)
{
knownVisitableObjects = AI.knownVisitableObjects;
AI = &ai;
// knownVisitableObjects = ai.knownVisitableObjects;
std::vector<HeroModel> oldModels = heroModels;
heroModels.clear();
std::vector < const CGHeroInstance *> heroes = AI.m_cb->getHeroesInfo();
std::vector < const CGHeroInstance *> heroes = ai.m_cb->getHeroesInfo();
for(std::vector < const CGHeroInstance *>::iterator i = heroes.begin(); i != heroes.end(); i++)
heroModels.push_back(HeroModel(*i));
for(int i = 0; i < oldModels.size();i++)
@ -82,19 +86,19 @@ void CGeniusAI::HypotheticalGameState::update(CGeniusAI & AI)
heroModels[ii].finished = true;
townModels.clear();
std::vector < const CGTownInstance *> towns = AI.m_cb->getTownsInfo();
std::vector < const CGTownInstance *> towns = ai.m_cb->getTownsInfo();
for(std::vector < const CGTownInstance *>::iterator i = towns.begin(); i != towns.end(); i++)
if((*i)->tempOwner==AI.m_cb->getMyColor())
if((*i)->tempOwner==ai.m_cb->getMyColor())
townModels.push_back(TownModel(*i));
if(AI.m_cb->howManyTowns()!=0)
AvailableHeroesToBuy = AI.m_cb->getAvailableHeroes(AI.m_cb->getTownInfo(0,0));
if(ai.m_cb->howManyTowns()!=0)
AvailableHeroesToBuy = ai.m_cb->getAvailableHeroes(ai.m_cb->getTownInfo(0,0));
resourceAmounts.clear();
for(int i = 0; i < 8;i++)resourceAmounts.push_back(AI.m_cb->getResourceAmount(i));
for(int i = 0; i < 8;i++)resourceAmounts.push_back(ai.m_cb->getResourceAmount(i));
}
CGeniusAI::HeroObjective::HeroObjective(Type t,const CGObjectInstance * object,HypotheticalGameState::HeroModel *h,CGeniusAI * ai):object(object)
CGeniusAI::HeroObjective::HeroObjective(const HypotheticalGameState &hgs,Type t,const CGObjectInstance * object,HypotheticalGameState::HeroModel *h,CGeniusAI * ai):object(object),hgs(hgs)
{
AI = ai;
pos = object->pos;
@ -105,15 +109,18 @@ CGeniusAI::HeroObjective::HeroObjective(Type t,const CGObjectInstance * object,H
float CGeniusAI::HeroObjective::getValue() const
{
if(_value>0)
return _value;
if(_value>=0)
return _value-_cost;
vector<int> resourceCosts;
vector<int> resourceCosts; //TODO: each object should have an associated cost to visit IE (tree of knowledge 1000 gold/10 gems)
for(int i = 0; i < 8;i++)
resourceCosts.push_back(0);
if(object->ID==47) //school of magic
resourceCosts[6]+=1000;
float bestCost = 9e9;
if(type !=AIObjective::finishTurn)
{
for(int i = 0; i < whoCanAchieve.size();i++)
{
int distOutOfTheWay = 0;
@ -133,15 +140,17 @@ float CGeniusAI::HeroObjective::getValue() const
float cost = CostModel(resourceCosts,whoCanAchieve[i]->h,distOutOfTheWay).getCost();
float cost = AI->m_priorities->getCost(resourceCosts,whoCanAchieve[i]->h,distOutOfTheWay);
if(cost < bestCost)
bestCost = cost;
}
if(bestCost < 10000)
cout << "best cost = " << bestCost << endl;
_value = rand()%1000+100-bestCost;
return _value;
}
else bestCost = 0;
//if(bestCost < 10000) cout << "best cost = " << bestCost << endl;
_value = AI->m_priorities->getValue(*this);
_cost=bestCost;
return _value-_cost;
}
bool CGeniusAI::HeroObjective::operator < (const HeroObjective &other)const
{
@ -162,17 +171,19 @@ void CGeniusAI::HeroObjective::print() const
switch(type)
{
case visit:
cout << "visit " << object->hoverName;
cout << "visit " << object->hoverName << " at (" <<object->pos.x << ","<<object->pos.y << ")" ;
break;
case attack:
cout << "attack " << object->hoverName;
case finishTurn:
cout << "finish turn";
}
if(whoCanAchieve.size()==1)
cout << " with " << whoCanAchieve.front()->h->hoverName;
}
CGeniusAI::TownObjective::TownObjective(Type t,HypotheticalGameState::TownModel * tn,int Which,CGeniusAI * ai)
:whichTown(tn),which(Which)
CGeniusAI::TownObjective::TownObjective(const HypotheticalGameState &hgs,Type t,HypotheticalGameState::TownModel * tn,int Which,CGeniusAI * ai)
:whichTown(tn),which(Which),hgs(hgs)
{
AI=ai;
type = t;
@ -181,22 +192,53 @@ CGeniusAI::TownObjective::TownObjective(Type t,HypotheticalGameState::TownModel
float CGeniusAI::TownObjective::getValue() const
{
if(_value>0)
return _value;
_value = rand()%1000+100;
return _value;
}
if(_value>=0)
return _value-_cost;
float cost;
vector<int> resourceCosts(8,0);
CBuilding * b;
CCreature * creature;
int ID,newID, howMany;
switch(type)
{
case recruitHero:
resourceCosts[6]=2500;
break;
case buildBuilding:
b = VLC->buildh->buildings[whichTown->t->subID][which];
for(int i = 0; b && i < b->resources.size();i++)
resourceCosts[i]=b->resources[i];
break;
case recruitCreatures:
ID = whichTown->creaturesToRecruit[which].second.back(); //buy upgraded if possible
creature = &VLC->creh->creatures[ID];
howMany = whichTown->creaturesToRecruit[which].first;
for(int i = 0; i < creature->cost.size();i++)
amin(howMany,creature->cost[i]?hgs.resourceAmounts[i]/creature->cost[i]:INT_MAX);
for(int i = 0; creature && i < creature->cost.size();i++)
resourceCosts[i]=creature->cost[i]*howMany;
break;
case upgradeCreatures:
UpgradeInfo ui = AI->m_cb->getUpgradeInfo(whichTown->t,which);
ID = whichTown->creaturesInGarrison.slots[which].first;
howMany = whichTown->creaturesInGarrison.slots[which].second;
newID = ui.newID.back();
for(std::set<std::pair<int,int> >::iterator i = ui.cost[which].begin();i!=ui.cost[which].end();i++)
resourceCosts[i->first] = i->second*howMany;
break;
}
CGeniusAI::CostModel::CostModel(vector<int> &resourceCosts,const CGHeroInstance * moved,int distOutOfTheWay)
:resourceCosts(resourceCosts),moved(moved),distOutOfTheWay(distOutOfTheWay)
{}
float CGeniusAI::CostModel::getCost()
{
if(resourceCosts.size()==0||moved == NULL)return -1;
//TODO: replace with ann
return 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+distOutOfTheWay/10000.0;
_cost = AI->m_priorities->getCost(resourceCosts,NULL,0);
_value = AI->m_priorities->getValue(*this);
return _value-_cost;
}
@ -257,7 +299,7 @@ void CGeniusAI::TownObjective::print() const
case upgradeCreatures:
UpgradeInfo ui = AI->m_cb->getUpgradeInfo(whichTown->t,which);
ID = whichTown->creaturesInGarrison.slots[which].first;
cout << "upgrade " << VLC->creh->creatures[ID].namePl << endl;
cout << "upgrade " << VLC->creh->creatures[ID].namePl;
//ui.cost
break;
@ -268,10 +310,12 @@ void CGeniusAI::TownObjective::print() const
CGeniusAI::CGeniusAI()
: m_generalAI(), m_state(NO_BATTLE)
{
m_priorities = new Priorities("AI/GeniusAI.brain");
}
CGeniusAI::~CGeniusAI()
{
delete m_priorities;
}
void CGeniusAI::init(ICallback *CB)
@ -336,9 +380,9 @@ void CGeniusAI::addHeroObjectives(CGeniusAI::HypotheticalGameState::HeroModel &h
if(dynamic_cast<const CGHeroInstance *> (i->o))
enemyStrength = (dynamic_cast<const CGHeroInstance *> (i->o))->getHeroStrength();
if(dynamic_cast<const CGTownInstance *> (i->o))
enemyStrength = (dynamic_cast<const CGTownInstance *> (i->o))->getArmyStrength()*1.5;
enemyStrength = (dynamic_cast<const CGTownInstance *> (i->o))->getArmyStrength()*1.2;
if(enemyStrength*1.5 > h.h->getHeroStrength()) //TODO: ballence these numbers using objective cost formula.
if(enemyStrength*1.2 > h.h->getHeroStrength()) //TODO: ballence these numbers using objective cost formula.
continue;
if(enemyStrength!=0)tp = AIObjective::attack;
}
@ -352,6 +396,13 @@ void CGeniusAI::addHeroObjectives(CGeniusAI::HypotheticalGameState::HeroModel &h
if(i->o->id==h.h->id) //don't visit yourself
continue;
if(i->o->ID==88||i->o->ID==89||i->o->ID==90)
{
//TODO: if no spell book continue
//TODO: if the shrine's spell is identified, and the hero already has it, continue
}
destination = i->o->getSightCenter();
if(hpos.z==destination.z) //don't try to take a path from the underworld to the top or vice versa
@ -362,7 +413,7 @@ void CGeniusAI::addHeroObjectives(CGeniusAI::HypotheticalGameState::HeroModel &h
if(path.nodes[0].dist<movement)
{
HeroObjective ho(tp,i->o,&h,this);
HeroObjective ho(hgs,tp,i->o,&h,this);
std::set<HeroObjective>::iterator found = currentHeroObjectives.find(ho);
if(found==currentHeroObjectives.end())
currentHeroObjectives.insert(ho);
@ -387,7 +438,7 @@ void CGeniusAI::addHeroObjectives(CGeniusAI::HypotheticalGameState::HeroModel &h
h.interestingPos = interestingPos;
if(h.remainingMovement>0&&m_cb->getPath(hpos,interestingPos,h.h,path)) // there ought to be a path
currentHeroObjectives.insert(HeroObjective(HeroObjective::finishTurn,h.h,&h,this));
currentHeroObjectives.insert(HeroObjective(hgs,HeroObjective::finishTurn,h.h,&h,this));
}
@ -406,6 +457,7 @@ void CGeniusAI::HeroObjective::fulfill(CGeniusAI & cg,HypotheticalGameState & hg
{
case finishTurn:
h = whoCanAchieve.front();
h->finished=true;
hpos = h->pos;
destination = h->interestingPos;
if(!cg.m_cb->getPath(hpos,destination,h->h,path)) {cout << "AI error: invalid destination" << endl; return;}
@ -445,7 +497,6 @@ void CGeniusAI::HeroObjective::fulfill(CGeniusAI & cg,HypotheticalGameState & hg
}
destination = bestPos;
h->finished=true;
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////
break;
@ -472,7 +523,7 @@ void CGeniusAI::HeroObjective::fulfill(CGeniusAI & cg,HypotheticalGameState & hg
float cost = CostModel(resourceCosts,whoCanAchieve[i]->h,distOutOfTheWay).getCost();
float cost = AI->m_priorities->getCost(resourceCosts,whoCanAchieve[i]->h,distOutOfTheWay);
if(cost < bestCost)
{
bestCost = cost;
@ -481,7 +532,7 @@ void CGeniusAI::HeroObjective::fulfill(CGeniusAI & cg,HypotheticalGameState & hg
}
h = whoCanAchieve[bestHero];//TODO:replace with best hero for the job
h = whoCanAchieve[bestHero]; //lowest cost hero
//if(dynamic_cast<const CGVisitableOPH *> (object))
// std::cout << h->h->name << " is visiting " << object->hoverName << std::endl;
hpos = h->pos;
@ -595,7 +646,7 @@ void CGeniusAI::addTownObjectives(HypotheticalGameState::TownModel &t, Hypotheti
for(int i =0; i < hgs.AvailableHeroesToBuy.size();i++)
if(hgs.AvailableHeroesToBuy[i]!=NULL&&(t.t->subID==(hgs.AvailableHeroesToBuy[i]->type->heroType/2)))
{
TownObjective to(AIObjective::recruitHero,&t,0,this);
TownObjective to(hgs,AIObjective::recruitHero,&t,0,this);
currentTownObjectives.insert(to);
}
}
@ -608,7 +659,7 @@ void CGeniusAI::addTownObjectives(HypotheticalGameState::TownModel &t, Hypotheti
{
if(m_cb->canBuildStructure(t.t,i->first)==7)
{
TownObjective to(AIObjective::buildBuilding,&t,i->first,this);
TownObjective to(hgs,AIObjective::buildBuilding,&t,i->first,this);
currentTownObjectives.insert(to);
//cout <<"can build " << i->first << " "<< i->second->Name() << endl;
}
@ -630,7 +681,7 @@ void CGeniusAI::addTownObjectives(HypotheticalGameState::TownModel &t, Hypotheti
if(!canAfford) continue;
//cout << "town has " << t.t->creatures[i].first << " "<< creature->namePl << " (AI Strength " << creature->AIValue << ")." << endl;
TownObjective to(AIObjective::recruitCreatures,&t,i,this);
TownObjective to(hgs,AIObjective::recruitCreatures,&t,i,this);
currentTownObjectives.insert(to);
}
@ -649,7 +700,7 @@ void CGeniusAI::addTownObjectives(HypotheticalGameState::TownModel &t, Hypotheti
canAfford = false;
if(canAfford)
{
TownObjective to(AIObjective::upgradeCreatures,&t,i->first,this);
TownObjective to(hgs,AIObjective::upgradeCreatures,&t,i->first,this);
currentTownObjectives.insert(to);
}
}
@ -725,11 +776,13 @@ void CGeniusAI::fillObjectiveQueue(HypotheticalGameState & hgs)
for(std::vector <CGeniusAI::HypotheticalGameState::TownModel>::iterator i = hgs.townModels.begin(); i != hgs.townModels.end(); i++)
addTownObjectives(*i,hgs);
for(std::set<CGeniusAI::HeroObjective>::iterator i = currentHeroObjectives.begin(); i != currentHeroObjectives.end(); i++)
objectiveQueue.push_back(AIObjectivePtrCont((CGeniusAI::HeroObjective *)&(*i)));
for(std::set<CGeniusAI::TownObjective>::iterator i = currentTownObjectives.begin(); i != currentTownObjectives.end(); i++)
objectiveQueue.push_back(AIObjectivePtrCont((CGeniusAI::TownObjective *)&(*i)));
}
CGeniusAI::AIObjective * CGeniusAI::getBestObjective()
{
trueGameState.update(*this);
@ -738,16 +791,25 @@ CGeniusAI::AIObjective * CGeniusAI::getBestObjective()
// if(!objectiveQueue.empty())
// return max_element(objectiveQueue.begin(),objectiveQueue.end())->obj;
m_priorities->fillFeatures(trueGameState);
if(objectiveQueue.empty()) return NULL;
sort(objectiveQueue.begin(),objectiveQueue.end());
reverse(objectiveQueue.begin(),objectiveQueue.end());
int num= 1;
for(std::vector<AIObjectivePtrCont> ::iterator i = objectiveQueue.begin(); i < objectiveQueue.end();i++)
{
cout << num++ << ": ";
i->obj->print();
cout << endl;
}
// for(std::vector<AIObjectivePtrCont> ::iterator i = objectiveQueue.begin(); i < objectiveQueue.end();i++)
// {
// if(!dynamic_cast<HeroObjective*>(i->obj))continue;
// cout << num++ << ": ";
// i->obj->print();
// cout << " value: " << i->obj->getValue();
// cout << endl;
// }
// int choice = 0;
// cout << "which would you do? (enter 0 for none): ";
// cin >> choice;
cout << "doing best of " << objectiveQueue.size() << " ";
objectiveQueue.front().obj->print();
cout << endl;
return objectiveQueue.front().obj;
@ -788,7 +850,7 @@ void CGeniusAI::yourTurn()
m_cb->waitTillRealize = false;
}
/*
void CGeniusAI::startFirstTurn()
{
@ -797,7 +859,7 @@ void CGeniusAI::startFirstTurn()
const CGTownInstance * town = m_cb->getTownInfo(0,0);
const CGHeroInstance * heroInst = m_cb->getHeroInfo(0,0);
TownObjective(AIObjective::recruitHero,&hgs.townModels.front(),0,this).fulfill(*this,hgs);
TownObjective(hgs,AIObjective::recruitHero,&hgs.townModels.front(),0,this).fulfill(*this,hgs);
m_cb->swapGarrisonHero(town);
hgs.update(*this);
@ -811,14 +873,14 @@ void CGeniusAI::startFirstTurn()
if(creature->cost[ii]>hgs.resourceAmounts[ii])
canAfford = false; // can we afford at least one creature?
if(!canAfford) continue;
TownObjective(AIObjective::recruitCreatures,&hgs.townModels.front(),i,this).fulfill(*this,hgs);
TownObjective(hgs,AIObjective::recruitCreatures,&hgs.townModels.front(),i,this).fulfill(*this,hgs);
}
hgs.update(*this);
HypotheticalGameState::HeroModel *hero;
for(int i = 0; i < hgs.heroModels.size();i++)
if(hgs.heroModels[i].h->id==heroInst->id)
HeroObjective(AIObjective::visit,town,hero=&hgs.heroModels[i],this).fulfill(*this,hgs);
HeroObjective(hgs,AIObjective::visit,town,hero=&hgs.heroModels[i],this).fulfill(*this,hgs);
hgs.update(*this);
@ -829,6 +891,7 @@ void CGeniusAI::startFirstTurn()
}
*/
void CGeniusAI::heroKilled(const CGHeroInstance * hero)
{

View File

@ -21,12 +21,17 @@ enum BattleState
ENDING_BATTLE
};
class Priorities;
class CGeniusAI : public CGlobalAI
{
private:
ICallback* m_cb;
GeniusAI::BattleAI::CBattleLogic* m_battleLogic;
GeniusAI::GeneralAI::CGeneralAI m_generalAI;
GeniusAI::Priorities * m_priorities;
CondSh<BattleState> m_state; //are we engaged into battle?
@ -59,10 +64,10 @@ private:
bool hasBuilt;
};
HypotheticalGameState(){}
HypotheticalGameState(CGeniusAI & AI);
void update(CGeniusAI & AI);
HypotheticalGameState(CGeniusAI & ai);
void update(CGeniusAI & ai);
CGeniusAI * AI;
std::vector<const CGHeroInstance *> AvailableHeroesToBuy;
std::vector<int> resourceAmounts;
std::vector<HeroModel> heroModels;
@ -70,28 +75,18 @@ private:
std::set< AIObjectContainer > knownVisitableObjects;
};
struct CostModel
{
CostModel(vector<int> &resourceCosts,const CGHeroInstance * moved,int distOutOfTheWay);
CostModel():moved(NULL){}
vector<int> resourceCosts;
const CGHeroInstance * moved;
int distOutOfTheWay;
float getCost();
};
class AIObjective
{
public:
enum Type
{
//hero objectives
visit, //done
visit, //done TODO: upon visit friendly hero, trade
attack, //done
flee,
//flee,
dismissUnits,
dismissYourself,
finishTurn, //done //uses up remaining motion to get somewhere nice.
finishTurn, //done //uses up remaining motion to get somewhere interesting.
//town objectives
recruitHero, //done
@ -99,7 +94,6 @@ private:
recruitCreatures, //done
upgradeCreatures //done
};
CostModel cost;
CGeniusAI * AI;
Type type;
virtual void fulfill(CGeniusAI &,HypotheticalGameState & hgs)=0;
@ -111,13 +105,14 @@ private:
class HeroObjective: public AIObjective
{
public:
HypotheticalGameState hgs;
int3 pos;
const CGObjectInstance * object;
std::vector<HypotheticalGameState::HeroModel *> whoCanAchieve;
HeroObjective(){}
HeroObjective(Type t):object(NULL){type = t;}
HeroObjective(Type t,const CGObjectInstance * object,HypotheticalGameState::HeroModel *h,CGeniusAI * AI);
HeroObjective(const HypotheticalGameState &hgs,Type t,const CGObjectInstance * object,HypotheticalGameState::HeroModel *h,CGeniusAI * AI);
bool operator < (const HeroObjective &other)const;
void fulfill(CGeniusAI &,HypotheticalGameState & hgs);
HypotheticalGameState pretend(const HypotheticalGameState &hgs){return hgs;};
@ -125,6 +120,7 @@ private:
void print() const;
private:
mutable float _value;
mutable float _cost;
};
//town objectives
@ -136,10 +132,11 @@ private:
class TownObjective: public AIObjective
{
public:
HypotheticalGameState hgs;
HypotheticalGameState::TownModel * whichTown;
int which; //which hero, which building, which creature,
TownObjective(Type t,HypotheticalGameState::TownModel * tn,int Which,CGeniusAI * AI);
TownObjective(const HypotheticalGameState &hgs,Type t,HypotheticalGameState::TownModel * tn,int Which,CGeniusAI * AI);
bool operator < (const TownObjective &other)const;
void fulfill(CGeniusAI &,HypotheticalGameState & hgs);
@ -148,6 +145,7 @@ private:
void print() const;
private:
mutable float _value;
mutable float _cost;
};
class AIObjectivePtrCont
@ -211,6 +209,7 @@ public:
virtual void battleStackIsAttacked(int ID, int dmg, int killed, int IDby, bool byShooting);
virtual BattleAction activeStack(int stackID);
void battleResultsApplied();
friend class Priorities;
};
}

View File

@ -0,0 +1,416 @@
#include "neuralNetwork.h"
//using namespace std;
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
float norm(void)//add desired mean, multiply to get desired SD
{
static float kept = 0;
static bool in = 0;
if(!in)
{
float x = (rand()+1)/float(RAND_MAX+1);
float f = sqrtf( - 2.0f * log(x) );
x = (rand()+1)/float(RAND_MAX+1);
kept = f * cosf( 2.0f * M_PI * x );
in = true;
return f * sinf( 2.0f * M_PI * x );
}
else
{
in = false;
return kept;
}
}
/*******************************************************************
* Constructors
********************************************************************/
neuralNetwork::neuralNetwork() : nInput(0), nHidden1(0), nHidden2(0), nOutput(0)
{
inputNeurons = new double[1] ;
hiddenNeurons1 = new double[1] ;
hiddenNeurons2 = new double[1] ;
outputNeurons = new double[1] ;
wInputHidden = new double*[1] ;
wInputHidden[0] = new double[1];
wHidden2Hidden = new double*[1] ;
wHidden2Hidden[0] = new (double[1]);
wHiddenOutput = new double*[1] ;
wHiddenOutput[0] = new double[1];
}
neuralNetwork::neuralNetwork(const neuralNetwork& other): nInput(0), nHidden1(0), nHidden2(0), nOutput(0)
{
inputNeurons = new double[1] ;
hiddenNeurons1 = new double[1] ;
hiddenNeurons2 = new double[1] ;
outputNeurons = new double[1] ;
wInputHidden = new double*[1] ;
wInputHidden[0] = new double[1];
wHidden2Hidden = new double*[1] ;
wHidden2Hidden[0] = new (double[1]);
wHiddenOutput = new double*[1] ;
wHiddenOutput[0] = new double[1];
*this = other;
}
neuralNetwork::neuralNetwork(int nI, int nH1, int nH2, int nO) : nInput(nI), nHidden1(nH1), nHidden2(nH2), nOutput(nO)
{
//create neuron lists
//--------------------------------------------------------------------------------------------------------
inputNeurons = new double[nInput + 1] ;
for ( int i=0; i < nInput; i++ ) inputNeurons[i] = 0;
//create input bias neuron
inputNeurons[nInput] = -1;
hiddenNeurons1 = new double[nHidden1 + 1] ;
for ( int i=0; i < nHidden1; i++ ) hiddenNeurons1[i] = 0;
//create hidden bias neuron
hiddenNeurons1[nHidden1] = -1;
hiddenNeurons2 = new double[nHidden2 + 1] ;
for ( int i=0; i < nHidden2; i++ ) hiddenNeurons2[i] = 0;
//create hidden bias neuron
hiddenNeurons2[nHidden2] = -1;
outputNeurons = new double[nOutput] ;
for ( int i=0; i < nOutput; i++ ) outputNeurons[i] = 0;
//create weight lists (include bias neuron weights)
//--------------------------------------------------------------------------------------------------------
wInputHidden = new double*[nInput + 1] ;
for ( int i=0; i <= nInput; i++ )
{
wInputHidden[i] = new double[nHidden1];
for ( int j=0; j < nHidden1; j++ ) wInputHidden[i][j] = 0;
}
wHidden2Hidden = new double*[nHidden1 + 1] ;
for ( int i=0; i <= nHidden1; i++ )
{
wHidden2Hidden[i] = new (double[nHidden2]);
for ( int j=0; j < nHidden2; j++ ) wHidden2Hidden[i][j] = 0;
}
wHiddenOutput = new double*[nHidden2 + 1] ;
for ( int i=0; i <= nHidden2; i++ )
{
wHiddenOutput[i] = new double[nOutput];
for ( int j=0; j < nOutput; j++ ) wHiddenOutput[i][j] = 0;
}
//initialize weights
//--------------------------------------------------------------------------------------------------------
initializeWeights();
}
void neuralNetwork::operator = (const neuralNetwork&cpy)//assumes same structure
{
if( nInput != cpy.nInput || nHidden1 != cpy.nHidden1 || nHidden2 != cpy.nHidden2 || nOutput != cpy.nOutput)
{
delete[] inputNeurons;
delete[] hiddenNeurons1;
delete[] hiddenNeurons2;
delete[] outputNeurons;
//delete weight storage
for (int i=0; i <= nInput; i++) delete[] wInputHidden[i];
delete[] wInputHidden;
for (int j=0; j <= nHidden2; j++) delete[] wHiddenOutput[j];
delete[] wHiddenOutput;
for (int j=0; j <= nHidden1; j++) delete[] wHidden2Hidden[j];
delete[] wHidden2Hidden;
nInput = cpy.nInput;
nHidden1 = cpy.nHidden1;
nHidden2 = cpy.nHidden2;
nOutput = cpy.nOutput;
inputNeurons = new double[nInput + 1] ;
inputNeurons[nInput] = -1;
hiddenNeurons1 = new double[nHidden1 + 1] ;
hiddenNeurons1[nHidden1] = -1;
hiddenNeurons2 = new double[nHidden2 + 1] ;
hiddenNeurons2[nHidden2] = -1;
outputNeurons = new double[nOutput] ;
//create weight lists (include bias neuron weights)
//--------------------------------------------------------------------------------------------------------
wInputHidden = new double*[nInput + 1] ;
for ( int i=0; i <= nInput; i++ )
wInputHidden[i] = new double[nHidden1];
wHidden2Hidden = new double*[nHidden1 + 1] ;
for ( int i=0; i <= nHidden1; i++ )
wHidden2Hidden[i] = new (double[nHidden2]);
wHiddenOutput = new double*[nHidden2 + 1] ;
for ( int i=0; i <= nHidden2; i++ )
wHiddenOutput[i] = new double[nOutput];
}
for ( int i=0; i < nInput; i++ ) inputNeurons[i] = cpy.inputNeurons[i];
for ( int i=0; i < nHidden1; i++ ) hiddenNeurons1[i] = cpy.hiddenNeurons1[i];
for ( int i=0; i < nHidden2; i++ ) hiddenNeurons2[i] = cpy.hiddenNeurons2[i];
for ( int i=0; i < nOutput; i++ ) outputNeurons[i] = cpy.outputNeurons[i];
for ( int i=0; i <= nInput; i++ )
for ( int j=0; j < nHidden1; j++ )
wInputHidden[i][j] = cpy.wInputHidden[i][j];
for ( int i=0; i <= nHidden1; i++ )
for ( int j=0; j < nHidden2; j++ )
wHidden2Hidden[i][j] = cpy.wHidden2Hidden[i][j];
for ( int i=0; i <= nHidden2; i++ )
for ( int j=0; j < nOutput; j++ )
wHiddenOutput[i][j] = cpy.wHiddenOutput[i][j];
}
/*******************************************************************
* Destructor
********************************************************************/
neuralNetwork::~neuralNetwork()
{
//delete neurons
delete[] inputNeurons;
delete[] hiddenNeurons1;
delete[] hiddenNeurons2;
delete[] outputNeurons;
//delete weight storage
for (int i=0; i <= nInput; i++) delete[] wInputHidden[i];
delete[] wInputHidden;
for (int j=0; j <= nHidden2; j++) delete[] wHiddenOutput[j];
delete[] wHiddenOutput;
for (int j=0; j <= nHidden1; j++) delete[] wHidden2Hidden[j];
delete[] wHidden2Hidden;
}
double* neuralNetwork::feedForwardPattern(double *pattern)
{
feedForward(pattern);
return outputNeurons;
}
void neuralNetwork::mate(const neuralNetwork&n1,const neuralNetwork&n2)
{
for(int i = 0; i <= nInput; i++)
{
for(int j = 0; j < nHidden1; j++)
{
if(rand()%2==0)
wInputHidden[i][j] = n1.wInputHidden[i][j];
else
wInputHidden[i][j] = n2.wInputHidden[i][j];
}
}
for(int i = 0; i <= nHidden1; i++)
{
for(int j = 0; j < nHidden2; j++)
{
if(rand()%2==0)
wHidden2Hidden[i][j] =n1.wHidden2Hidden[i][j];
else
wHidden2Hidden[i][j] =n2.wHidden2Hidden[i][j];
}
}
for(int i = 0; i <= nHidden2; i++)
{
for(int j = 0; j < nOutput; j++)
{
if(rand()%2==0)
wHiddenOutput[i][j] =n1.wHiddenOutput[i][j];
else
wHiddenOutput[i][j] =n2.wHiddenOutput[i][j];
}
}
}
void neuralNetwork::tweakWeights(double howMuch)
{
//set range
double rH = 1/sqrt( (double) nInput);
double rO = 1/sqrt( (double) nHidden1);
for(int i = 0; i <= nInput; i++)
{
for(int j = 0; j < nHidden1; j++)
{
wInputHidden[i][j] += howMuch*norm();
}
}
for(int i = 0; i <= nHidden1; i++)
{
for(int j = 0; j < nHidden2; j++)
{
wHidden2Hidden[i][j] += howMuch*norm();
}
}
for(int i = 0; i <= nHidden2; i++)
{
for(int j = 0; j < nOutput; j++)
{
wHiddenOutput[i][j] += howMuch* norm();
}
}
//initializeWeights();
}
void neuralNetwork::initializeWeights()
{
//set range
double rH = 2.0/sqrt( (double) nInput);
double rO = 2.0/sqrt( (double) nHidden1);
//set weights between input and hidden
//--------------------------------------------------------------------------------------------------------
for(int i = 0; i <= nInput; i++)
{
for(int j = 0; j < nHidden1; j++)
{
//set weights to random values
wInputHidden[i][j] = norm()* rH;
}
}
for(int i = 0; i <= nHidden1; i++)
{
for(int j = 0; j < nHidden2; j++)
{
//set weights to random values
wHidden2Hidden[i][j] = norm()* rO;
}
}
//set weights between hidden and output
//--------------------------------------------------------------------------------------------------------
for(int i = 0; i <= nHidden2; i++)
{
for(int j = 0; j < nOutput; j++)
{
//set weights to random values
wHiddenOutput[i][j] = norm()* rO;
}
}
}
/*******************************************************************
* Activation Function
********************************************************************/
inline double neuralNetwork::activationFunction( double x )
{
//sigmoid function
return 1/(1+exp(-x));
}
/*******************************************************************
* Feed Forward Operation
********************************************************************/
void neuralNetwork::feedForward(double* pattern)
{
//set input neurons to input values
for(int i = 0; i < nInput; i++) inputNeurons[i] = pattern[i];
//Calculate Hidden Layer values - include bias neuron
//--------------------------------------------------------------------------------------------------------
for(int j=0; j < nHidden1; j++)
{
//clear value
hiddenNeurons1[j] = 0;
//get weighted sum of pattern and bias neuron
for( int i=0; i <= nInput; i++ ) hiddenNeurons1[j] += inputNeurons[i] * wInputHidden[i][j];
//set to result of sigmoid
hiddenNeurons1[j] = activationFunction( hiddenNeurons1[j] );
}
for(int j=0; j < nHidden2; j++)
{
//clear value
hiddenNeurons2[j] = 0;
//get weighted sum of pattern and bias neuron
for( int i=0; i <= nHidden1; i++ ) hiddenNeurons2[j] += hiddenNeurons1[i] * wHidden2Hidden[i][j];
//set to result of sigmoid
hiddenNeurons2[j] = activationFunction( hiddenNeurons2[j] );
}
//Calculating Output Layer values - include bias neuron
//--------------------------------------------------------------------------------------------------------
for(int k=0; k < nOutput; k++)
{
//clear value
outputNeurons[k] = 0;
//get weighted sum of pattern and bias neuron
for( int j=0; j <= nHidden2; j++ ) outputNeurons[k] += hiddenNeurons2[j] * wHiddenOutput[j][k];
//set to result of sigmoid
//outputNeurons[k] = activationFunction( outputNeurons[k] );
}
}
void neuralNetwork::backpropigate(double* pattern, double OLR, double H2LR, double H1LR )
{
//inputError = new double[nInput + 1] ;
double * hiddenError1 = new double[nHidden1 + 1] ;
double * hiddenError2 = new double[nHidden2 + 1] ;
double * outputError = new double[nOutput] ;
memset(hiddenError1,0,sizeof(double)*nHidden1);
memset(hiddenError2,0,sizeof(double)*nHidden2);
for(int i = 0; i < nOutput; i++)
{
outputError[i] = (pattern[i]-outputNeurons[i]);//*(outputNeurons[i]*(1-outputNeurons[i]));
for(int ii = 0; ii <= nHidden2;ii++)
hiddenError2[ii]+=outputError[i]*wHiddenOutput[ii][i];
for(int ii = 0; ii <= nHidden2;ii++)
wHiddenOutput[ii][i]+=OLR*hiddenNeurons2[ii]*outputError[i];
}
for(int i = 0; i < nHidden2; i++)
{
hiddenError2[i] *= (hiddenNeurons2[i]*(1-hiddenNeurons2[i]));
for(int ii = 0; ii <= nHidden1;ii++)
hiddenError1[ii]+=hiddenError2[i]*wHidden2Hidden[ii][i];
for(int ii = 0; ii <= nHidden1;ii++)
wHidden2Hidden[ii][i]+=H2LR*hiddenNeurons1[ii]*hiddenError2[i];
}
for(int i = 0; i < nHidden1; i++)
{
hiddenError1[i] *= (hiddenNeurons1[i]*(1-hiddenNeurons1[i]));
for(int ii = 0; ii <= nInput;ii++)
wInputHidden[ii][i]+=H1LR*inputNeurons[ii]*hiddenError1[i];
}
delete [] hiddenError1;
delete [] hiddenError2;
delete [] outputError;
}

View File

@ -0,0 +1,67 @@
/*******************************************************************
* addapted by Trevor Standley for use as a function approximator
* Addapted from:
* Basic Feed Forward Neural Network Class
* ------------------------------------------------------------------
* Bobby Anguelov - takinginitiative.wordpress.com (2008)
* MSN & email: banguelov@cs.up.ac.za
********************************************************************/
#ifndef NEURAL_NETWORK_H
#define NEURAL_NETWORK_H
//standard includes
#include <iostream>
#include <vector>
#include <fstream>
#include <cmath>
#include <limits>
class neuralNetwork
{
private:
//number of neurons
int nInput, nHidden1, nHidden2, nOutput;
//neurons
double* inputNeurons;
double* hiddenNeurons1;
double* hiddenNeurons2;
double* outputNeurons;
//weights
double** wInputHidden;
double** wHidden2Hidden;
double** wHiddenOutput;
public:
//constructor & destructor
neuralNetwork(int numInput, int numHidden1, int numHidden2, int numOutput);
neuralNetwork(const neuralNetwork&);
neuralNetwork();
void operator = (const neuralNetwork&);
~neuralNetwork();
//weight operations
double* feedForwardPattern( double* pattern );
void backpropigate(double* pattern, double OLR, double H2LR, double H1LR );
void tweakWeights(double howMuch);
void mate(const neuralNetwork&n1,const neuralNetwork&n2);
private:
void initializeWeights();
inline double activationFunction( double x );
void feedForward( double* pattern );
};
std::istream & operator >> (std::istream &, neuralNetwork & ann);
std::ostream & operator << (std::ostream &, const neuralNetwork & ann);
#endif