- Added poison counter check into the testsuite

- Decorelated the testsuite AI timer from the game timer to be able to have reproduceable results with AI tests.
- Created a random generator wrapper class
- Used two seperate instances of this random generator for AI and for the game
- Added methods to load randoms into AI from a testcase
- Fixed a probleme with undo and premade decks introduced in r4035
- Added basic test to test AI proliferate code
- Cleaned up goblin_artillery test
- Added AI tests into the testsuite test list
- Fixed looping bug into the multi target AI code
This commit is contained in:
Xawotihs
2011-10-30 14:31:27 +00:00
parent 53b9bc412f
commit 2f4dd4cd2a
19 changed files with 195 additions and 98 deletions

View File

@@ -664,5 +664,5 @@ momir/overcost.txt
########################
#AI Tests
########################
#Removed until we can have these tests work on all machines :(
#ai/goblin_artillery.txt
ai/goblin_artillery.txt
ai/proliferate_simple.txt

View File

@@ -1,17 +1,16 @@
#Bug:ai uses goblin artilery on own creature
# http://code.google.com/p/wagic/issues/detail?id=194
FORCEABILITY
RValues:2
AICALLS 10
AICALLS 1
[INIT]
FIRSTMAIN
[PLAYER1]
inplay:,grizzly bears,goblin artillery
RValues:2
[PLAYER2]
inplay:raging goblin
[DO]
ai
ai
[ASSERT]
FIRSTMAIN
[PLAYER1]

View File

@@ -0,0 +1,24 @@
FORCEABILITY
AICALLS 18
[INIT]
FIRSTMAIN
[PLAYER1]
life:20
# to check that AI does not target itself
poisoncount:1
inplay:Thrummingbird
[PLAYER2]
life:20
poisoncount:1
[DO]
ai
[ASSERT]
COMBATEND
[PLAYER1]
life:20
poisoncount:1
inplay:Thrummingbird
[PLAYER2]
life:19
poisoncount:2
[END]

View File

@@ -22,13 +22,14 @@
using std::queue;
class AIStats;
class AIPlayer;
class AIAction
{
protected:
static int currentId;
public:
Player * owner;
AIPlayer * owner;
MTGAbility * ability;
NestedAbility * nability;
Player * player;
@@ -39,26 +40,26 @@ public:
Targetable * playerAbilityTarget;
//player targeting through abilities is handled completely seperate from spell targeting.
AIAction(Player * owner, MTGAbility * a, MTGCardInstance * c, MTGCardInstance * t = NULL)
AIAction(AIPlayer * owner, MTGAbility * a, MTGCardInstance * c, MTGCardInstance * t = NULL)
: owner(owner), ability(a), player(NULL), click(c), target(t),playerAbilityTarget(NULL)
{
id = currentId++;
};
AIAction(Player * owner, MTGCardInstance * c, MTGCardInstance * t = NULL);
AIAction(AIPlayer * owner, MTGCardInstance * c, MTGCardInstance * t = NULL);
AIAction(Player * owner, Player * p)//player targeting through spells
AIAction(AIPlayer * owner, Player * p)//player targeting through spells
: owner(owner), ability(NULL), player(p), click(NULL), target(NULL),playerAbilityTarget(NULL)
{
};
AIAction(Player * owner, MTGAbility * a, MTGCardInstance * c, vector<Targetable*>targetCards)
AIAction(AIPlayer * owner, MTGAbility * a, MTGCardInstance * c, vector<Targetable*>targetCards)
: owner(owner), ability(a), player(NULL), click(c), mAbilityTargets(targetCards),playerAbilityTarget(NULL)
{
id = currentId++;
};
AIAction(Player * owner, MTGAbility * a, Player * p, MTGCardInstance * c)//player targeting through abilities.
AIAction(AIPlayer * owner, MTGAbility * a, Player * p, MTGCardInstance * c)//player targeting through abilities.
: owner(owner), ability(a), click(c),target(NULL), playerAbilityTarget(p)
{
id = currentId++;
@@ -76,6 +77,7 @@ protected:
queue<AIAction *> clickstream;
int clickMultiTarget(TargetChooser * tc,vector<Targetable*>&potentialTargets);
int clickSingleTarget(TargetChooser * tc,vector<Targetable*>&potentialTargets, MTGCardInstance * Choosencard = NULL);
RandomGenerator randomGenerator;
public:
@@ -98,8 +100,9 @@ public:
int isAI(){return 1;};
void setFastTimerMode(bool mode = true) { mFastTimerMode = mode; };
RandomGenerator* getRandomGenerator(){return &randomGenerator;};
bool parseLine(const string& s);
};

View File

@@ -18,24 +18,24 @@ protected:
public:
int efficiency;
OrderedAIAction(Player * owner, MTGAbility * a, MTGCardInstance * c, MTGCardInstance * t = NULL)
OrderedAIAction(AIPlayer * owner, MTGAbility * a, MTGCardInstance * c, MTGCardInstance * t = NULL)
: AIAction(owner, a, c, t), efficiency(-1)
{
};
OrderedAIAction(Player * owner, MTGCardInstance * c, MTGCardInstance * t = NULL);
OrderedAIAction(AIPlayer * owner, MTGCardInstance * c, MTGCardInstance * t = NULL);
OrderedAIAction(Player * owner, Player * p)//player targeting through spells
OrderedAIAction(AIPlayer * owner, Player * p)//player targeting through spells
: AIAction(owner,p), efficiency(-1)
{
};
OrderedAIAction(Player * owner, MTGAbility * a, MTGCardInstance * c, vector<Targetable*>targetCards)
OrderedAIAction(AIPlayer * owner, MTGAbility * a, MTGCardInstance * c, vector<Targetable*>targetCards)
: AIAction(owner,a, c, targetCards), efficiency(-1)
{
};
OrderedAIAction(Player * owner, MTGAbility * a, Player * p, MTGCardInstance * c)//player targeting through abilities.
OrderedAIAction(AIPlayer * owner, MTGAbility * a, Player * p, MTGCardInstance * c)//player targeting through abilities.
: AIAction(owner, a, p, c), efficiency(-1)
{
};

View File

@@ -45,6 +45,7 @@ class GameObserver{
bool mLoading;
void nextGamePhase();
void shuffleLibrary(Player* p);
RandomGenerator randomGenerator;
public:
int currentPlayerId;
@@ -123,6 +124,7 @@ class GameObserver{
void Mulligan(Player* player = NULL);
Player* getPlayer(size_t index) { return players[index];};
bool isStarted() { return (mLayers!=NULL);};
RandomGenerator* getRandomGenerator() { return &randomGenerator; };
};
#endif

View File

@@ -18,7 +18,7 @@ protected:
JTexture * mAvatarTex;
JQuadPtr mAvatar;
bool loadAvatar(string file, string resName = "playerAvatar");
bool premade;
public:
enum ENUM_PLAY_MODE
@@ -95,20 +95,19 @@ public:
** Returns the path to the stats file of currently selected deck.
*/
std::string GetCurrentDeckStatsFile();
bool parseLine(const string& s);
virtual bool parseLine(const string& s);
friend ostream& operator<<(ostream&, const Player&);
};
class HumanPlayer: public Player
{
protected:
bool premade;
public:
HumanPlayer(GameObserver *observer, string deckFile, string deckFileSmall, bool premade = false, MTGDeck * deck = NULL);
void End(){
if(!premade && opponent())
DeckStats::GetInstance()->saveStats(this, opponent(), observer);
};
friend ostream& operator<<(ostream&, const HumanPlayer&);
};
#endif

View File

@@ -63,11 +63,31 @@ std::string wordWrap(const std::string& s, float width, int fontId);
//basic hash function
unsigned long hash_djb2(const char *str);
void loadRandValues(string s);
ostream& saveRandValues(ostream& out);
// This class wraps random generation and the pre-loading/saving of randoms
// The idea is to make it instantiable to be able to handle randoms differently per class of group of classes.
// In particular, to be able to control the AI randoms independently of the other game randoms so that we can actually test AI
class RandomGenerator
{
protected:
queue<int> loadedRandomValues;
queue<int> usedRandomValues;
bool log;
public:
RandomGenerator(bool doLog = false) : log(doLog) {};
void loadRandValues(string s);
ostream& saveUsedRandValues(ostream& out);
ostream& saveLoadedRandValues(ostream& out);
int random();
template<typename Iter> void random_shuffle(Iter first, Iter last)
{
ptrdiff_t i, n;
n = (last-first);
for (i=n-1; i>0; --i) swap (first[i],first[random()%(i+1)]);
};
};
int filesize(const char * filename);
int WRand(bool log = false);
ptrdiff_t MRand (ptrdiff_t i);
#ifdef LINUX
void dumpStack();

View File

@@ -44,7 +44,7 @@ int AIMomirPlayer::momir()
int converted = potentialMana->getConvertedCost();
SAFE_DELETE(potentialMana);
int efficiency = 100;
int chance = 1 + (WRand() % 100);
int chance = 1 + (randomGenerator.random() % 100);
if (converted == 5 && myCreatures > opponentCreatures && game->hand->nb_cards < 4) efficiency = 5; //Strategy: skip 5 drop
if (converted == 7 && myCreatures > opponentCreatures && game->hand->nb_cards < 2) efficiency = 50; //Strategy: 7 drops have bad upkeep costs and the AI doesn't handle those right now...
if (converted > 8) converted = 8;

View File

@@ -19,7 +19,7 @@ const char * const MTG_LAND_TEXTS[] = { "artifact", "forest", "island", "mountai
int AIAction::currentId = 0;
AIAction::AIAction(Player * owner, MTGCardInstance * c, MTGCardInstance * t)
AIAction::AIAction(AIPlayer * owner, MTGCardInstance * c, MTGCardInstance * t)
: owner(owner), ability(NULL), player(NULL), click(c), target(t)
{
id = currentId++;
@@ -88,16 +88,19 @@ int AIAction::clickMultiAct(vector<Targetable*>& actionTargets)
GameObserver * g = owner->getObserver();
TargetChooser * tc = g->getCurrentTargetChooser();
if(!tc) return 0;
for(size_t f = 0;f < actionTargets.size();f++)
vector<Targetable*>::iterator ite = actionTargets.begin();
while(ite != actionTargets.end())
{
MTGCardInstance * card = ((MTGCardInstance *) actionTargets[f]);
MTGCardInstance * card = ((MTGCardInstance *) (*ite));
if(card == (MTGCardInstance*)tc->source)//click source first.
{
g->cardClick(card);
actionTargets.erase(actionTargets.begin() + f);
ite = actionTargets.erase(ite);
continue;
}
ite++;
}
std::random_shuffle(actionTargets.begin(), actionTargets.end());
owner->getRandomGenerator()->random_shuffle(actionTargets.begin(), actionTargets.end());
//shuffle to make it less predictable, otherwise ai will always seem to target from right to left. making it very obvious.
for(int k = 0;k < int(actionTargets.size());k++)
{
@@ -152,27 +155,31 @@ int AIPlayer::Act(float dt)
int AIPlayer::clickMultiTarget(TargetChooser * tc, vector<Targetable*>& potentialTargets)
{
for(int f = 0;f < int(potentialTargets.size());f++)
vector<Targetable*>::iterator ite = potentialTargets.begin();
while(ite != potentialTargets.end())
{
MTGCardInstance * card = ((MTGCardInstance *) potentialTargets[f]);
Player * pTarget = (Player*)potentialTargets[f];
MTGCardInstance * card = ((MTGCardInstance *) (*ite));
Player * pTarget = (Player*)(*ite);
if(card && card == (MTGCardInstance*)tc->source)//if the source is part of the targetting deal with it first. second click is "confirming click".
{
clickstream.push(NEW AIAction(this, card));
DebugTrace("Ai clicked source as a target: " << (card ? card->name : "None" ) << endl );
potentialTargets.erase(potentialTargets.begin() + f);
ite = potentialTargets.erase(ite);
continue;
}
if(pTarget && pTarget->typeAsTarget() == TARGET_PLAYER)
{
clickstream.push(NEW AIAction(this, pTarget));
DebugTrace("Ai clicked Player as a target");
potentialTargets.erase(potentialTargets.begin() + f);
ite = potentialTargets.erase(ite);
continue;
}
ite++;
}
std::random_shuffle(potentialTargets.begin(), potentialTargets.end());
randomGenerator.random_shuffle(potentialTargets.begin(), potentialTargets.end());
if(potentialTargets.size())
clickstream.push(NEW AIAction(this, NULL,tc->source,potentialTargets));
while(clickstream.size())
@@ -187,7 +194,7 @@ int AIPlayer::clickMultiTarget(TargetChooser * tc, vector<Targetable*>& potentia
int AIPlayer::clickSingleTarget(TargetChooser * tc, vector<Targetable*>& potentialTargets, MTGCardInstance * chosenCard)
{
int i = WRand() % potentialTargets.size();
int i = randomGenerator.random() % potentialTargets.size();
int type = potentialTargets[i]->typeAsTarget();
switch (type)
{
@@ -277,6 +284,25 @@ void AIPlayer::Render()
}
bool AIPlayer::parseLine(const string& s)
{
size_t limiter = s.find("=");
if (limiter == string::npos) limiter = s.find(":");
string areaS;
if (limiter != string::npos)
{
areaS = s.substr(0, limiter);
if (areaS.compare("rvalues") == 0)
{
randomGenerator.loadRandValues(s.substr(limiter + 1));
return true;
}
}
return Player::parseLine(s);
}
#ifdef AI_CHANGE_TESTING
AIPlayer * AIPlayerFactory::createAIPlayerTest(GameObserver *observer, MTGAllCards * collection, Player * opponent, string _folder)
{

View File

@@ -509,12 +509,12 @@ int OrderedAIAction::getEfficiency()
else
{
//without a base to start with Wrand % 5 almost always returns 0.
efficiency = 10 + (WRand() % 20); //Small percentage of chance for unknown abilities
efficiency = 10 + (owner->getRandomGenerator()->random() % 20); //Small percentage of chance for unknown abilities
}
}
else
{
efficiency = 10 + (WRand() % 30);
efficiency = 10 + (owner->getRandomGenerator()->random() % 30);
}
break;
}
@@ -539,11 +539,11 @@ int OrderedAIAction::getEfficiency()
else if( target->currentZone == p->game->inPlay && (MTGCardInstance*)target == a->source)
{
if (z == p->game->hand)
efficiency = 10 + (WRand() % 10);//random chance to bounce their own card;
efficiency = 10 + (owner->getRandomGenerator()->random() % 10);//random chance to bounce their own card;
}
else
{
efficiency = 10 + (WRand() % 5);
efficiency = 10 + (owner->getRandomGenerator()->random() % 5);
}
}
}
@@ -584,7 +584,7 @@ int OrderedAIAction::getEfficiency()
{
AIPlayer * chk = (AIPlayer*)p;
if(may->ability->getActionTc() && chk->chooseTarget(may->ability->getActionTc(),NULL,NULL,true))
efficiency = 50 + (WRand() % 50);
efficiency = 50 + (owner->getRandomGenerator()->random() % 50);
}
if (p->game->hand->nb_cards == 0)
efficiency = (int) ((float) efficiency * 1.3); //increase chance of using ability if hand is empty
@@ -1237,7 +1237,7 @@ int AIPlayerBaka::selectHintAbility()
ManaCost * totalPotentialMana = getPotentialMana();
totalPotentialMana->add(this->getManaPool());
AIAction * action = hints->suggestAbility(totalPotentialMana);
if (action && ((WRand() % 100) < 95)) //95% chance
if (action && ((randomGenerator.random() % 100) < 95)) //95% chance
{
if (!clickstream.size())
{
@@ -1327,7 +1327,7 @@ int AIPlayerBaka::selectAbility()
OrderedAIAction action = ranking.begin()->first;
int chance = 1;
if (!forceBestAbilityUse)
chance = 1 + WRand() % 100;
chance = 1 + randomGenerator.random() % 100;
int actionScore = action.getEfficiency();
if(action.ability->getCost() && action.ability->getCost()->hasX() && this->game->hand->cards.size())
actionScore = actionScore/int(this->game->hand->cards.size());//reduce chance for "x" abilities if cards are in hand.
@@ -1705,7 +1705,7 @@ MTGCardInstance * AIPlayerBaka::FindCardToPlay(ManaCost * pMana, const char * ty
if(!canPlay)
continue;
}
if (WRand() % 100 > shouldPlayPercentage)
if (randomGenerator.random() % 100 > shouldPlayPercentage)
continue;
nextCardToPlay = card;
maxCost = currentCost;

View File

@@ -559,6 +559,7 @@ AARemoveAllCounter * AARemoveAllCounter::clone() const
AAProliferate::AAProliferate(GameObserver* observer, int id, MTGCardInstance * source, Targetable * target,ManaCost * cost) :
ActivatedAbility(observer, id, source, cost, 0)
{
this->GetId();
}
int AAProliferate::resolve()
@@ -3669,7 +3670,7 @@ void ABlink::Update(float dt)
MTGGameZone * inplay = spell->source->owner->game->inPlay;
spell->source->target = NULL;
for(int i = WRand(true)%inplay->nb_cards;;i = WRand(true)%inplay->nb_cards)
for(int i = game->getRandomGenerator()->random()%inplay->nb_cards;;i = game->getRandomGenerator()->random()%inplay->nb_cards)
{
if(tc->canTarget(inplay->cards[i]) && spell->source->target == NULL)
{
@@ -3768,7 +3769,7 @@ void ABlink::resolveBlink()
MTGGameZone * inplay = spell->source->owner->game->inPlay;
spell->source->target = NULL;
for(int i = WRand(true)%inplay->nb_cards;;i = WRand(true)%inplay->nb_cards)
for(int i = game->getRandomGenerator()->random()%inplay->nb_cards;;i = game->getRandomGenerator()->random()%inplay->nb_cards)
{
if(tc->canTarget(inplay->cards[i]) && spell->source->target == NULL)
{

View File

@@ -86,11 +86,13 @@ GameObserver::~GameObserver()
}
GameObserver::GameObserver()
: randomGenerator(true)
{
initialize();
}
GameObserver::GameObserver(vector<Player *> _players)
: randomGenerator(true)
{
initialize();
setPlayers(_players);
@@ -1279,7 +1281,7 @@ ostream& operator<<(ostream& out, GameObserver& g)
else
{
out << "rvalues:";
out << saveRandValues(out);
out << g.randomGenerator.saveUsedRandValues(out);
out << endl;
out << g.startupGameSerialized;
}
@@ -1327,7 +1329,7 @@ bool GameObserver::load(const string& ss, bool undo)
string deckFileSmall = "";//players[0]->deckFileSmall;
DebugTrace("Loading " + ss);
loadRandValues("");
randomGenerator.loadRandValues("");
cleanup();
@@ -1345,7 +1347,7 @@ bool GameObserver::load(const string& ss, bool undo)
}
if (s.find("rvalues:") == 0)
{
loadRandValues(s.substr(8).c_str());
randomGenerator.loadRandValues(s.substr(8).c_str());
continue;
}
switch (state)
@@ -1612,7 +1614,7 @@ void GameObserver::loadPlayer(int playerId, PlayerType playerType, int decknb, b
Player * opponent = NULL;
// Reset the random logging.
loadRandValues("");
randomGenerator.loadRandValues("");
if (playerId == 1) opponent = players[0];
#ifdef AI_CHANGE_TESTING

View File

@@ -390,7 +390,7 @@ void MTGPlayerCards::discardRandom(MTGGameZone * from, MTGCardInstance * source)
{
if (!from->nb_cards)
return;
int r = WRand(true) % (from->nb_cards);
int r = owner->getObserver()->getRandomGenerator()->random() % (from->nb_cards);
WEvent * e = NEW WEventCardDiscard(from->cards[r]);
GameObserver * game = owner->getObserver();
game->receiveEvent(e);
@@ -681,7 +681,7 @@ void MTGGameZone::cleanupPhase()
void MTGGameZone::shuffle()
{
std::random_shuffle(cards.begin(), cards.end(), MRand );
owner->getObserver()->getRandomGenerator()->random_shuffle(cards.begin(), cards.end());
}
void MTGGameZone::addCard(MTGCardInstance * card)

View File

@@ -1517,7 +1517,7 @@ int MTGMomirRule::genRandomCreatureId(int convertedCost)
}
if (!total_cards)
return 0;
int start = (WRand(true) % total_cards);
int start = (game->getRandomGenerator()->random() % total_cards);
return pool[convertedCost][start];
}
@@ -1635,7 +1635,7 @@ int MTGStoneHewerRule::genRandomEquipId(int convertedCost)
if (convertedCost >= 20)
convertedCost = 19;
int total_cards = 0;
int i = (WRand(true) % int(convertedCost+1));//+1 becuase we want to generate a random "<=" the coverted.
int i = (game->getRandomGenerator()->random() % int(convertedCost+1));//+1 becuase we want to generate a random "<=" the coverted.
while (!total_cards && i >= 0)
{
total_cards = pool[i].size();
@@ -1644,7 +1644,7 @@ int MTGStoneHewerRule::genRandomEquipId(int convertedCost)
}
if (!total_cards)
return 0;
int start = (WRand(true) % total_cards);
int start = (game->getRandomGenerator()->random() % total_cards);
return pool[convertedCost][start];
}
@@ -1681,7 +1681,7 @@ int MTGHermitRule::receiveEvent(WEvent * event)
lands.push_back(temp);
}
if(lands.size())
lcard = lands[WRand(true) % lands.size()];
lcard = lands[game->getRandomGenerator()->random() % lands.size()];
if(lcard)
{
MTGCardInstance * copy = game->currentPlayer->game->putInZone(lcard,game->currentPlayer->game->library, game->currentPlayer->game->temp);

View File

@@ -15,6 +15,7 @@ Player::Player(GameObserver *observer, string file, string fileSmall, MTGDeck *
if(deck == NULL && file != "testsuite" && file != "remote" && file != "")
deck = NEW MTGDeck(file.c_str(), MTGCollection());
premade = false;
game = NULL;
deckFile = file;
deckFileSmall = fileSmall;
@@ -114,11 +115,12 @@ Player * Player::opponent()
return this == observer->players[0] ? observer->players[1] : observer->players[0];
}
HumanPlayer::HumanPlayer(GameObserver *observer, string file, string fileSmall, bool premade, MTGDeck * deck) :
Player(observer, file, fileSmall, deck), premade(premade)
HumanPlayer::HumanPlayer(GameObserver *observer, string file, string fileSmall, bool isPremade, MTGDeck * deck) :
Player(observer, file, fileSmall, deck)
{
mAvatarName = "avatar.jpg";
playMode = MODE_HUMAN;
premade = isPremade;
}
ManaPool * Player::getManaPool()
@@ -243,6 +245,11 @@ bool Player::parseLine(const string& s)
phaseRing = s.substr(limiter + 1);
return true;
}
else if (areaS.compare("premade") == 0)
{
premade = atoi(s.substr(limiter + 1).c_str());
return true;
}
else if (areaS.compare("deckfile") == 0)
{
deckFile = s.substr(limiter + 1);
@@ -290,6 +297,7 @@ ostream& operator<<(ostream& out, const Player& p)
if(p.phaseRing != "")
out << "customphasering=" << p.phaseRing << endl;
out << "offerinterruptonphase=" << Constants::MTGPhaseCodeNames[p.offerInterruptOnPhase] << endl;
out << "premade=" << p.premade << endl;
if(p.deckFile != "")
out << "deckfile=" << p.deckFile << endl;
if(p.deckFileSmall != "")
@@ -303,3 +311,4 @@ ostream& operator<<(ostream& out, const Player& p)
return out;
}

View File

@@ -254,8 +254,8 @@ Player * Rules::loadPlayerMomir(GameObserver* observer, int isAI)
Player * Rules::loadPlayerRandom(GameObserver* observer, int isAI, int mode)
{
int color1 = 1 + WRand(true) % 5;
int color2 = 1 + WRand(true) % 5;
int color1 = 1 + observer->getRandomGenerator()->random() % 5;
int color2 = 1 + observer->getRandomGenerator()->random() % 5;
int color0 = Constants::MTG_COLOR_ARTIFACT;
if (mode == GAME_TYPE_RANDOM1) color2 = color1;
int colors[] = { color1, color2, color0 };
@@ -367,7 +367,7 @@ void Rules::initGame(GameObserver *g)
if (g->currentPlayer->playMode!= Player::MODE_TEST_SUITE && g->mRules->gamemode!= GAME_TYPE_STORY)
{
if(OptionWhosFirst::WHO_R == options[Options::FIRSTPLAYER].number)
initState.player = WRand(true) % 2;
initState.player = g->getRandomGenerator()->random() % 2;
if(OptionWhosFirst::WHO_O == options[Options::FIRSTPLAYER].number)
initState.player = 1;
}

View File

@@ -77,7 +77,7 @@ int TestSuiteAI::displayStack()
int TestSuiteAI::Act(float dt)
{
observer->gameOver = NULL; // Prevent draw rule from losing the game
observer->gameOver = NULL; // Prevent draw rule from losing the game
//Last bits of initialization require to be done here, after the first "update" call of the game
if (suite->currentAction == 0)
@@ -88,9 +88,10 @@ observer->gameOver = NULL; // Prevent draw rule from losing the game
if (playMode == MODE_AI && suite->aiMaxCalls)
{
float static counter = 1.0f;
suite->aiMaxCalls--;
suite->timerLimit = 40; //TODO Remove this limitation when AI is not using a stupid timer anymore...
AIPlayerBaka::Act(dt);
AIPlayerBaka::Act(counter++);//dt);
}
if (playMode == MODE_HUMAN)
{
@@ -341,6 +342,10 @@ void TestSuite::initGame(GameObserver* g)
AIPlayerBaka * p = (AIPlayerBaka *) (g->players[i]);
p->forceBestAbilityUse = forceAbility;
p->life = initState.players[i]->life;
p->poisonCount = initState.players[i]->poisonCount;
stringstream stream;
stream << initState.players[i]->getRandomGenerator()->saveLoadedRandValues(stream);
p->getRandomGenerator()->loadRandValues(stream.str());
MTGGameZone * playerZones[] = { p->game->graveyard, p->game->library, p->game->hand, p->game->inPlay };
MTGGameZone * loadedPlayerZones[] = { initState.players[i]->game->graveyard,
initState.players[i]->game->library,
@@ -430,7 +435,13 @@ int TestSuite::assertGame(GameObserver* g)
Log(result);
error++;
}
if (!p->getManaPool()->canAfford(endState.players[i]->getManaPool()))
if (p->poisonCount != endState.players[i]->poisonCount)
{
sprintf(result, "<span class=\"error\">==poison counter problem for player %i. Expected %i, got %i==</span><br />", i,
endState.players[i]->poisonCount, p->poisonCount);
Log(result);
error++;
} if (!p->getManaPool()->canAfford(endState.players[i]->getManaPool()))
{
sprintf(result, "<span class=\"error\">==Mana problem. Was expecting %i but got %i for player %i==</span><br />",
endState.players[i]->getManaPool()->getConvertedCost(), p->getManaPool()->getConvertedCost(), i);
@@ -611,7 +622,6 @@ void TestSuite::cleanup()
initState.cleanup(this);
endState.cleanup(this);
actions.cleanup();
loadRandValues("");
}
int TestSuite::load(const char * _filename)
@@ -623,7 +633,6 @@ int TestSuite::load(const char * _filename)
sprintf(filename, "test/%s", _filename);
std::string s;
loadRandValues("");
int state = -1;
@@ -655,11 +664,6 @@ int TestSuite::load(const char * _filename)
seed = atoi(s.substr(5).c_str());
continue;
}
if (s.find("rvalues:") == 0)
{
loadRandValues(s.substr(8).c_str());
continue;
}
if (s.find("aicalls ") == 0)
{
aiMaxCalls = atoi(s.substr(8).c_str());

View File

@@ -21,13 +21,24 @@ namespace wagic
using std::vector;
using std::queue;
int randValuesCursor = -1;
vector<int> randValues;
int RandomGenerator::random()
{
int result;
if (!loadedRandomValues.size() || !log)
{
result = rand();
}
else
{
result = loadedRandomValues.front();
loadedRandomValues.pop();
}
if(log)
usedRandomValues.push(result);
return result;
}
queue<int> loadedRandomValues;
queue<int> usedRandomValues;
ostream& saveRandValues(ostream& out)
ostream& RandomGenerator::saveUsedRandValues(ostream& out)
{
while(usedRandomValues.size())
{
@@ -41,7 +52,21 @@ ostream& saveRandValues(ostream& out)
return out;
}
void loadRandValues(string s)
ostream& RandomGenerator::saveLoadedRandValues(ostream& out)
{
while(loadedRandomValues.size())
{
out << loadedRandomValues.front();
if(loadedRandomValues.size() >= 1)
out << ",";
loadedRandomValues.pop();
}
return out;
}
void RandomGenerator::loadRandValues(string s)
{
while(loadedRandomValues.size())
loadedRandomValues.pop();
@@ -66,26 +91,9 @@ void loadRandValues(string s)
}
}
ptrdiff_t MRand (ptrdiff_t i)
{
return WRand(true)%i;
}
int WRand(bool log)
{
int result;
if (!loadedRandomValues.size() || !log)
{
result = rand();
}
else
{
result = loadedRandomValues.front();
loadedRandomValues.pop();
}
if(log)
usedRandomValues.push(result);
return result;
return rand();
}
int filesize(const char * filename)