#include "PrecompiledHeader.h" #include "TestSuiteAI.h" #include "MTGAbility.h" #include "MTGRules.h" #include "ActionLayer.h" #include "GuiCombat.h" #include "Rules.h" #include "GameObserver.h" #include "GameStateShop.h" using std::string; // NULL is sent in place of a MTGDeck since there is no way to create a MTGDeck without a proper deck file. // TestSuiteAI will be responsible for managing its own deck state. TestSuiteAI::TestSuiteAI(TestSuite * _suite, int playerId):AIPlayerBaka(NULL, "testsuite", "testsuite", "baka.jpg") { this->game = _suite->buildDeck(playerId); game->setOwner( this ); suite = _suite; timer = 0; playMode = MODE_TEST_SUITE; this->deckName = "Test Suite AI"; } MTGCardInstance * TestSuiteAI::getCard(string action){ int mtgid = Rules::getMTGId(action); if (mtgid) return Rules::getCardByMTGId(mtgid); //This mostly handles tokens GameObserver * g = GameObserver::GetInstance(); std::transform(action.begin(), action.end(), action.begin(),::tolower ); for (int i = 0; i < 2; i++){ Player * p = g->players[i]; MTGGameZone * zones[] = {p->game->library,p->game->hand, p->game->inPlay, p->game->graveyard}; for (int j = 0; j < 4; j++){ MTGGameZone * zone = zones[j]; for (int k = 0; k < zone->nb_cards; k++){ MTGCardInstance * card = zone->cards[k]; if (!card) return NULL; string name = card->getLCName(); if (name.compare(action) == 0) return card; } } } DebugTrace("TESTUISTEAI: Can't find card:" << action.c_str()); return NULL; } Interruptible * TestSuite::getActionByMTGId(int mtgid){ ActionStack * as= GameObserver::GetInstance()->mLayers->stackLayer(); Interruptible * action = NULL; while ((action = as->getNext(action,0,0,1))){ if (action->source && action->source->getMTGId() == mtgid){ return action; } } return NULL; } int TestSuiteAI::displayStack(){ if (playMode == MODE_AI) return 0; return 1; } int TestSuiteAI::Act(float dt){ GameObserver * g = GameObserver::GetInstance(); g->gameOver = NULL; // Prevent draw rule from losing the game if (playMode == MODE_AI && suite->aiMaxCalls) { suite->aiMaxCalls--; suite->timerLimit = 40; //TODO Remove this limitation when AI is not using a stupid timer anymore... AIPlayerBaka::Act(dt); } if (playMode == MODE_HUMAN){ g->mLayers->CheckUserInput(0); return 1; } timer+= 1; if (timer < suite->timerLimit) return 1; timer = 0; string action = suite->getNextAction(); g->mLayers->stackLayer()->Dump(); // DamageResolverLayer * drl = g->mLayers->combatLayer(); DebugTrace("TESTSUITE command: " << action); if (g->mLayers->stackLayer()->askIfWishesToInterrupt == this){ if(action.compare("no") != 0 && action.compare("yes") != 0){ g->mLayers->stackLayer()->cancelInterruptOffer(); suite->currentAction--; return 1; } } if (action == ""){ //end of game suite->assertGame(); g->gameOver = g->players[0]; DebugTrace("================================ END OF TEST =======================\n"); return 1; } if (action.compare("eot")== 0){ if (g->getCurrentGamePhase() != Constants::MTG_PHASE_CLEANUP) suite->currentAction--; g->userRequestNextGamePhase(); } else if (action.compare("human")==0){ DebugTrace("TESTSUITE You have control"); playMode = MODE_HUMAN; return 1; } else if (action.compare("ai")==0){ DebugTrace("TESTSUITE Switching to AI"); playMode = MODE_AI; return 1; } else if (action.compare("next")==0){ GuiCombat * gc = g->mLayers->combatLayer(); if (ORDER == g->combatStep || DAMAGE == g->combatStep) gc->clickOK(); else g->userRequestNextGamePhase(); } else if (action.compare("yes")==0) g->mLayers->stackLayer()->setIsInterrupting(this); else if (action.compare("endinterruption")==0) g->mLayers->stackLayer()->endOfInterruption(); else if(action.compare("no")==0){ if (g->mLayers->stackLayer()->askIfWishesToInterrupt == this) g->mLayers->stackLayer()->cancelInterruptOffer(); }else if(action.find("choice ")!=string::npos){ DebugTrace("TESTSUITE choice !!!"); int choice = atoi(action.substr(action.find("choice ") + 7).c_str()); g->mLayers->actionLayer()->doReactTo(choice); }else if(action.find(" -momir- ")!=string::npos){ int start = action.find(" -momir- "); int cardId = Rules::getMTGId(action.substr(start + 9).c_str()); int cardIdHand = Rules::getMTGId(action.substr(0,start).c_str()); MTGMomirRule * a = ((MTGMomirRule *)g->mLayers->actionLayer()->getAbility(MTGAbility::MOMIR)); a->reactToClick(Rules::getCardByMTGId(cardIdHand), cardId); g->mLayers->actionLayer()->stuffHappened = 1; }else if(action.find("p1")!=string::npos || action.find("p2")!=string::npos){ Player * p = g->players[1]; size_t start = action.find("p1"); if (start != string::npos) p = g->players[0]; g->cardClick(NULL, p); }else{ int mtgid = Rules::getMTGId(action); Interruptible * toInterrupt = NULL; if (mtgid){ DebugTrace("TESTSUITE CARD ID:" << mtgid); toInterrupt = suite->getActionByMTGId(mtgid); } if (toInterrupt) { g->stackObjectClicked(toInterrupt); return 1; } MTGCardInstance * card = getCard(action); if (card) { DebugTrace("TESTSUITE Clicking ON: " << card->name); card->currentZone->needShuffle = true; //mimic library shuffle g->cardClick(card,card); g->forceShuffleLibraries(); //mimic library shuffle return 1; } } return 0; } TestSuiteActions::TestSuiteActions(){ nbitems = 0; } void TestSuiteActions::add(string s){ actions[nbitems] = s; nbitems++; } TestSuitePlayerData::TestSuitePlayerData(){ life = 20; manapool = NEW ManaCost(); } TestSuitePlayerData::~TestSuitePlayerData(){ SAFE_DELETE(manapool); } TestSuitePlayerZone::TestSuitePlayerZone(){ nbitems = 0; } void TestSuitePlayerZone::add(int cardId){ cards[nbitems] = cardId; nbitems++; } TestSuiteState::TestSuiteState(){ } void TestSuiteState::parsePlayerState(int playerId, string s){ size_t limiter = s.find(":"); string areaS; int area; if (limiter != string::npos){ areaS = s.substr(0,limiter); if (areaS.compare("graveyard") == 0){ area = 0; }else if(areaS.compare("library") == 0){ area = 1; }else if(areaS.compare("hand") == 0){ area = 2; }else if(areaS.compare("inplay") == 0 || areaS.compare("battlefield") == 0 ){ area = 3; }else if(areaS.compare("life") == 0){ playerData[playerId].life = atoi((s.substr(limiter+1)).c_str()); return; }else if(areaS.compare("manapool") == 0){ SAFE_DELETE(playerData[playerId].manapool); playerData[playerId].manapool = ManaCost::parseManaCost(s.substr(limiter+1)); return; }else{ return; // ERROR } s = s.substr(limiter+1); while (s.size()){ unsigned int value; limiter = s.find(","); if (limiter != string::npos){ value = Rules::getMTGId(s.substr(0,limiter)); s = s.substr(limiter+1); }else{ value = Rules::getMTGId(s); s = ""; } if (value) playerData[playerId].zones[area].add(value); } }else{ //ERROR } } string TestSuite::getNextAction(){ currentAction++; if (actions.nbitems && currentAction <= actions.nbitems){ return actions.actions[currentAction-1]; } return ""; } MTGPlayerCards * TestSuite::buildDeck( int playerId){ int list[100]; int nbcards = 0; for (int j = 0; j < 4; j++){ for (int k = 0; k < initState.playerData[playerId].zones[j].nbitems; k++){ int cardid = initState.playerData[playerId].zones[j].cards[k]; list[nbcards] = cardid; nbcards++; } } MTGPlayerCards * deck = NEW MTGPlayerCards(list, nbcards); return deck; } void TestSuite::initGame(){ //The first test runs slowly, the other ones run faster. //This way a human can see what happens when testing a specific file, // or go faster when it comes to the whole test suite. //Warning, putting this value too low (< 3) will give unexpected results if (!timerLimit){ timerLimit = 40; }else{ timerLimit = 3; } //Put the GameObserver in the initial state GameObserver * g = GameObserver::GetInstance(); DebugTrace("TESTSUITE Init Game"); g->phaseRing->goToPhase(initState.phase, g->players[0]); g->currentGamePhase = initState.phase; for (int i = 0; i < 2; i++){ AIPlayer * p = (AIPlayer *) (g->players[i]); p->forceBestAbilityUse = forceAbility; p->life = initState.playerData[i].life; p->getManaPool()->copy(initState.playerData[i].manapool); MTGGameZone * playerZones[] = {p->game->graveyard, p->game->library, p->game->hand, p->game->inPlay}; for (int j = 0; j < 4; j++){ MTGGameZone * zone = playerZones[j]; for (int k = 0; k < initState.playerData[i].zones[j].nbitems; k++){ MTGCardInstance * card = Rules::getCardByMTGId(initState.playerData[i].zones[j].cards[k]); if (card && zone != p->game->library){ if (zone == p->game->inPlay){ MTGCardInstance * copy = p->game->putInZone(card, p->game->library, p->game->stack); Spell * spell = NEW Spell(copy); spell->resolve(); if (!summoningSickness && p->game->inPlay->nb_cards>k) p->game->inPlay->cards[k]->summoningSickness = 0; delete spell; }else{ if (!p->game->library->hasCard(card)){ LOG ("TESTUITE ERROR, CARD NOT FOUND IN LIBRARY\n"); } p->game->putInZone(card,p->game->library,zone); } }else{ if (!card) { LOG ("TESTUITE ERROR, card is NULL\n"); } } } } } DebugTrace("TESTUITE Init Game Done !"); } int TestSuite::Log(const char * text){ ofstream file (RESPATH"/test/results.html",ios_base::app); if (file){ file << text; file << "\n"; file.close(); } DebugTrace(text); return 1; } int TestSuite::assertGame(){ //compare the game state with the results char result[4096]; sprintf(result,"