- 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:
@@ -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
|
||||
|
||||
@@ -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]
|
||||
|
||||
24
projects/mtg/bin/Res/test/ai/proliferate_simple.txt
Normal file
24
projects/mtg/bin/Res/test/ai/proliferate_simple.txt
Normal 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]
|
||||
@@ -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);
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
};
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user