diff --git a/projects/mtg/bin/Res/test/_tests.txt b/projects/mtg/bin/Res/test/_tests.txt index c5273bf67..32fc79f13 100644 --- a/projects/mtg/bin/Res/test/_tests.txt +++ b/projects/mtg/bin/Res/test/_tests.txt @@ -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 diff --git a/projects/mtg/bin/Res/test/ai/goblin_artillery.txt b/projects/mtg/bin/Res/test/ai/goblin_artillery.txt index d840cde62..678f7e82e 100644 --- a/projects/mtg/bin/Res/test/ai/goblin_artillery.txt +++ b/projects/mtg/bin/Res/test/ai/goblin_artillery.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] diff --git a/projects/mtg/bin/Res/test/ai/proliferate_simple.txt b/projects/mtg/bin/Res/test/ai/proliferate_simple.txt new file mode 100644 index 000000000..d261a1901 --- /dev/null +++ b/projects/mtg/bin/Res/test/ai/proliferate_simple.txt @@ -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] \ No newline at end of file diff --git a/projects/mtg/include/AIPlayer.h b/projects/mtg/include/AIPlayer.h index 5fea490f8..d09802f3f 100644 --- a/projects/mtg/include/AIPlayer.h +++ b/projects/mtg/include/AIPlayer.h @@ -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, vectortargetCards) + AIAction(AIPlayer * owner, MTGAbility * a, MTGCardInstance * c, vectortargetCards) : 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 clickstream; int clickMultiTarget(TargetChooser * tc,vector&potentialTargets); int clickSingleTarget(TargetChooser * tc,vector&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); }; diff --git a/projects/mtg/include/AIPlayerBaka.h b/projects/mtg/include/AIPlayerBaka.h index 305e0e72b..561f451f0 100644 --- a/projects/mtg/include/AIPlayerBaka.h +++ b/projects/mtg/include/AIPlayerBaka.h @@ -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, vectortargetCards) + OrderedAIAction(AIPlayer * owner, MTGAbility * a, MTGCardInstance * c, vectortargetCards) : 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) { }; diff --git a/projects/mtg/include/GameObserver.h b/projects/mtg/include/GameObserver.h index ed014a7e4..25b969eb6 100644 --- a/projects/mtg/include/GameObserver.h +++ b/projects/mtg/include/GameObserver.h @@ -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 diff --git a/projects/mtg/include/Player.h b/projects/mtg/include/Player.h index c5cb4bd42..8835cd7fe 100644 --- a/projects/mtg/include/Player.h +++ b/projects/mtg/include/Player.h @@ -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 diff --git a/projects/mtg/include/utils.h b/projects/mtg/include/utils.h index f2ef9ba54..2fd71cc47 100644 --- a/projects/mtg/include/utils.h +++ b/projects/mtg/include/utils.h @@ -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 loadedRandomValues; + queue 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 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(); diff --git a/projects/mtg/src/AIMomirPlayer.cpp b/projects/mtg/src/AIMomirPlayer.cpp index d4afd5e2a..8b3d89be9 100644 --- a/projects/mtg/src/AIMomirPlayer.cpp +++ b/projects/mtg/src/AIMomirPlayer.cpp @@ -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; diff --git a/projects/mtg/src/AIPlayer.cpp b/projects/mtg/src/AIPlayer.cpp index 2f03053fa..fd27e54b6 100644 --- a/projects/mtg/src/AIPlayer.cpp +++ b/projects/mtg/src/AIPlayer.cpp @@ -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& actionTargets) GameObserver * g = owner->getObserver(); TargetChooser * tc = g->getCurrentTargetChooser(); if(!tc) return 0; - for(size_t f = 0;f < actionTargets.size();f++) + vector::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& potentialTargets) { - for(int f = 0;f < int(potentialTargets.size());f++) + vector::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& potentia int AIPlayer::clickSingleTarget(TargetChooser * tc, vector& 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) { diff --git a/projects/mtg/src/AIPlayerBaka.cpp b/projects/mtg/src/AIPlayerBaka.cpp index 1b3ed9aec..9ec98d2bb 100644 --- a/projects/mtg/src/AIPlayerBaka.cpp +++ b/projects/mtg/src/AIPlayerBaka.cpp @@ -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; diff --git a/projects/mtg/src/AllAbilities.cpp b/projects/mtg/src/AllAbilities.cpp index b6fb91e20..4ac91c3b3 100644 --- a/projects/mtg/src/AllAbilities.cpp +++ b/projects/mtg/src/AllAbilities.cpp @@ -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) { diff --git a/projects/mtg/src/GameObserver.cpp b/projects/mtg/src/GameObserver.cpp index 25516ec7e..ac2bbae0b 100644 --- a/projects/mtg/src/GameObserver.cpp +++ b/projects/mtg/src/GameObserver.cpp @@ -86,11 +86,13 @@ GameObserver::~GameObserver() } GameObserver::GameObserver() + : randomGenerator(true) { initialize(); } GameObserver::GameObserver(vector _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 diff --git a/projects/mtg/src/MTGGameZones.cpp b/projects/mtg/src/MTGGameZones.cpp index 5da13d150..b7d99c156 100644 --- a/projects/mtg/src/MTGGameZones.cpp +++ b/projects/mtg/src/MTGGameZones.cpp @@ -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) diff --git a/projects/mtg/src/MTGRules.cpp b/projects/mtg/src/MTGRules.cpp index 443a5fdb5..7ef3ae98b 100644 --- a/projects/mtg/src/MTGRules.cpp +++ b/projects/mtg/src/MTGRules.cpp @@ -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); diff --git a/projects/mtg/src/Player.cpp b/projects/mtg/src/Player.cpp index 6e95885a3..e1c40ab82 100644 --- a/projects/mtg/src/Player.cpp +++ b/projects/mtg/src/Player.cpp @@ -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; } + diff --git a/projects/mtg/src/Rules.cpp b/projects/mtg/src/Rules.cpp index f47ebd3bc..5d50ef047 100644 --- a/projects/mtg/src/Rules.cpp +++ b/projects/mtg/src/Rules.cpp @@ -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; } diff --git a/projects/mtg/src/TestSuiteAI.cpp b/projects/mtg/src/TestSuiteAI.cpp index 07ecc6dad..c60b7abb4 100644 --- a/projects/mtg/src/TestSuiteAI.cpp +++ b/projects/mtg/src/TestSuiteAI.cpp @@ -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, "==poison counter problem for player %i. Expected %i, got %i==
", i, + endState.players[i]->poisonCount, p->poisonCount); + Log(result); + error++; + } if (!p->getManaPool()->canAfford(endState.players[i]->getManaPool())) { sprintf(result, "==Mana problem. Was expecting %i but got %i for player %i==
", 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()); diff --git a/projects/mtg/src/utils.cpp b/projects/mtg/src/utils.cpp index e6ac648e2..cb347a2cb 100644 --- a/projects/mtg/src/utils.cpp +++ b/projects/mtg/src/utils.cpp @@ -21,13 +21,24 @@ namespace wagic using std::vector; using std::queue; -int randValuesCursor = -1; -vector 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 loadedRandomValues; -queue 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)