From d078872dfc80df13106aac4dc1f4c883bf10dfee Mon Sep 17 00:00:00 2001 From: "omegablast2002@yahoo.com" Date: Sun, 19 Aug 2012 21:53:57 +0000 Subject: [PATCH] this is the first draft of a simple ai combo system, format is as follows HINT#combo hold(blah|myhand)^until(blah|mygraveyard)^until(blah|opponentshand)^restriction{type(creature|mybattlefield)~morethan~2}^cast(blah) targeting(blah|mygraveyard)^totalmananneeded({g}{g}{r}{u}{2}) the ai can be told to hold more then one card, until as many condiations as you want are met, until( is a TC and can basically be used in a fasion of saying "hold arbor elf until you have a lord of atlantas in play and a gaint growth in you hand" once the condiations are met you can later tell it to cast(gaint growth) targeting(arbor elf[fresh]|mybattlefield)... I also included the whole of the games restrictions system... so you can get really really creative with this so far. the next thing I will do is ability targeting and card favoring. --- projects/mtg/include/AIHints.h | 11 ++ projects/mtg/include/AIPlayerBaka.h | 7 ++ projects/mtg/include/AllAbilities.h | 6 +- projects/mtg/include/GameStateDuel.h | 8 +- projects/mtg/include/ManaCost.h | 8 +- projects/mtg/src/AIHints.cpp | 149 ++++++++++++++++++++++++++- projects/mtg/src/AIMomirPlayer.cpp | 2 +- projects/mtg/src/AIPlayer.cpp | 1 + projects/mtg/src/AIPlayerBaka.cpp | 127 +++++++++++++++++++++-- projects/mtg/src/GameStateDuel.cpp | 20 ++-- projects/mtg/src/GuiMana.cpp | 12 +++ projects/mtg/src/MTGAbility.cpp | 10 +- projects/mtg/src/ManaCost.cpp | 12 +-- projects/mtg/src/TestSuiteAI.cpp | 1 + 14 files changed, 335 insertions(+), 39 deletions(-) diff --git a/projects/mtg/include/AIHints.h b/projects/mtg/include/AIHints.h index e0411b64e..3db429a49 100644 --- a/projects/mtg/include/AIHints.h +++ b/projects/mtg/include/AIHints.h @@ -7,6 +7,7 @@ using std::string; using std::vector; #include "AIPlayerBaka.h" +#include "AllAbilities.h" class ManaCost; class MTGAbility; @@ -18,6 +19,14 @@ public: string mAction; string mCombatAttackTip; vectorcastOrder; + vectorcombos; + //for preformance we disect the combo on first run. + vectorpartOfCombo; + vectorhold; + vectoruntil; + vectorrestrict; + mapcardTargets; + string manaNeeded; int mSourceId; AIHint(string line); }; @@ -39,6 +48,8 @@ public: AIHints (AIPlayerBaka * player); AIAction * suggestAbility(ManaCost * potentialMana); bool HintSaysDontAttack(GameObserver* observer,MTGCardInstance * card = NULL); + bool HintSaysItsForCombo(GameObserver* observer,MTGCardInstance * card = NULL); + bool canWeCombo(GameObserver* observer,MTGCardInstance * card = NULL,AIPlayerBaka * Ai = NULL); vector mCastOrder(); void add(string line); ~AIHints(); diff --git a/projects/mtg/include/AIPlayerBaka.h b/projects/mtg/include/AIPlayerBaka.h index 9b441e508..65816c015 100644 --- a/projects/mtg/include/AIPlayerBaka.h +++ b/projects/mtg/include/AIPlayerBaka.h @@ -6,6 +6,7 @@ class AIStats; class AIHints; +class AIHint; //Would love to define those classes as private nested classes inside of AIPlayerBaka, but they are used by AIHints (which itself should be known only by AIPlayerBaka anyways) @@ -91,11 +92,14 @@ class AIPlayerBaka: public AIPlayer{ virtual AIStats * getStats(); MTGCardInstance * nextCardToPlay; + MTGCardInstance * activateCombo(); + TargetChooser * GetComboTc(GameObserver * observer, TargetChooser * tc = NULL); AIHints * hints; AIStats * stats; int oldGamePhase; float timer; virtual MTGCardInstance * FindCardToPlay(ManaCost * potentialMana, const char * type); + vectorcomboCards; //used by MomirPlayer, hence protected instead of private virtual int getEfficiency(OrderedAIAction * action); @@ -115,7 +119,9 @@ class AIPlayerBaka: public AIPlayer{ }; vectorgotPayments; + AIPlayerBaka(GameObserver *observer, string deckFile, string deckfileSmall, string avatarFile, MTGDeck * deck = NULL); + AIHint * comboHint; virtual int Act(float dt); void initTimer(); virtual int computeActions(); @@ -125,6 +131,7 @@ class AIPlayerBaka: public AIPlayer{ virtual int affectCombatDamages(CombatStep step); virtual int canHandleCost(MTGAbility * ability); virtual int chooseTarget(TargetChooser * tc = NULL, Player * forceTarget = NULL,MTGCardInstance * Chosencard = NULL,bool checkonly = false); + virtual vector canPayManaCost(MTGCardInstance * card = NULL, ManaCost * mCost = NULL){ return canPayMana(card, mCost);}; //used by AIHInts, therefore public instead of private :/ virtual int createAbilityTargets(MTGAbility * a, MTGCardInstance * c, RankingContainer& ranking); diff --git a/projects/mtg/include/AllAbilities.h b/projects/mtg/include/AllAbilities.h index 73d5a0029..74ab56793 100644 --- a/projects/mtg/include/AllAbilities.h +++ b/projects/mtg/include/AllAbilities.h @@ -4360,7 +4360,7 @@ public: MTGAbility(observer, id, _source) { counters = 0; - std::vector _cost; + std::vector _cost; _cost.push_back(Constants::MTG_COLOR_ARTIFACT); _cost.push_back(4); cost = ManaCost(_cost, 1); @@ -4424,7 +4424,7 @@ public: MTGAbility(observer, _id, _source) { canprevent = 0; - std::vector _cost; + std::vector _cost; _cost.push_back(Constants::MTG_COLOR_ARTIFACT); _cost.push_back(2); cost = ManaCost(_cost, 1); @@ -4520,7 +4520,7 @@ public: AFarmstead(GameObserver* observer, int _id, MTGCardInstance * source, MTGCardInstance * _target) : ActivatedAbility(observer, _id, source, 0, 1) { - std::vector _cost; + std::vector _cost; _cost.push_back(Constants::MTG_COLOR_WHITE); _cost.push_back(2); setCost(NEW ManaCost(_cost, 1), true); diff --git a/projects/mtg/include/GameStateDuel.h b/projects/mtg/include/GameStateDuel.h index 2fd971903..ded0d5ea9 100644 --- a/projects/mtg/include/GameStateDuel.h +++ b/projects/mtg/include/GameStateDuel.h @@ -66,9 +66,15 @@ public: static void ThreadProc(void* inParam); void handleResults(GameObserver* aGame){ mMutex.lock(); - totalTestGames++; if (aGame->didWin(aGame->players[1])) + { testPlayer2Victories++; + totalTestGames++; + } + else if( aGame->didWin(aGame->players[0])) + { + totalTestGames++; + } mMutex.unlock(); }; #endif diff --git a/projects/mtg/include/ManaCost.h b/projects/mtg/include/ManaCost.h index f7e56f343..08d706034 100644 --- a/projects/mtg/include/ManaCost.h +++ b/projects/mtg/include/ManaCost.h @@ -23,7 +23,7 @@ class ManaCost friend std::ostream& operator<<(std::ostream& out, ManaCost m); protected: - std::vector cost; + std::vector cost; std::vector hybrids; virtual void init(); @@ -60,7 +60,7 @@ public: int hasSpecificX(); int xColor; int hasAnotherCost(); - ManaCost(std::vector& _cost, int nb_elems = 1); + ManaCost(std::vector& _cost, int nb_elems = 1); ManaCost(); ~ManaCost(); ManaCost(ManaCost * _manaCost); @@ -89,8 +89,8 @@ public: ExtraCost * getExtraCost(unsigned int i); int addHybrid(int c1, int v1, int c2, int v2); - int tryToPayHybrids(std::vector& _hybrids, int _nbhybrids,std::vector& diff); - void randomDiffHybrids(ManaCost * _cost, std::vector& diff); + int tryToPayHybrids(std::vector& _hybrids, int _nbhybrids,std::vector& diff); + void randomDiffHybrids(ManaCost * _cost, std::vector& diff); int add(ManaCost * _cost); int remove(ManaCost * _cost); int removeAll(int color); diff --git a/projects/mtg/src/AIHints.cpp b/projects/mtg/src/AIHints.cpp index b4cc119a2..0a4e8766c 100644 --- a/projects/mtg/src/AIHints.cpp +++ b/projects/mtg/src/AIHints.cpp @@ -16,10 +16,8 @@ AIHint::AIHint(string _line) return; } std::transform(line.begin(), line.end(), line.begin(), ::tolower); - vector parameters = split(line,':'); - mCondition = (parameters.size() == 1)? "" : parameters[0]; - string action = parameters[parameters.size() - 1]; - + mCondition = line; + string action = line; vector splitAction = parseBetween(action, "sourceid(", ")"); if (splitAction.size()) { @@ -43,6 +41,14 @@ AIHint::AIHint(string _line) { castOrder = split(splitCastOrder[1],','); } + + if(action.find( "combo ") != string::npos) + { + string Combo = ""; + Combo = action.c_str() + 6; + combos.push_back(Combo); + } + } AIHints::AIHints(AIPlayerBaka * player): mPlayer(player) @@ -94,6 +100,141 @@ bool AIHints::HintSaysDontAttack(GameObserver* observer,MTGCardInstance * card) return false; } +bool AIHints::HintSaysItsForCombo(GameObserver* observer,MTGCardInstance * card) +{ + TargetChooserFactory tfc(observer); + TargetChooser * hintTc = NULL; + bool forCombo = false; + for(unsigned int i = 0; i < hints.size();i++) + { + if (hints[i]->combos.size()) + { + //time to find the parts and condiations of the combo. + string part = ""; + if(!hints[i]->partOfCombo.size() && hints[i]->combos.size()) + { + for(unsigned int cPart = 0; cPart < hints[i]->combos.size(); cPart++) + { + //here we disect the different parts of a given combo + part = hints[i]->combos[cPart]; + hints[i]->partOfCombo = split(part,'^'); + for(unsigned int dPart = 0; dPart < hints[i]->partOfCombo.size(); dPart++) + { + vectorasTc; + asTc = parseBetween(hints[i]->partOfCombo[dPart],"hold(",")"); + if(asTc.size()) + { + hints[i]->hold.push_back(asTc[1]); + asTc.clear(); + } + asTc = parseBetween(hints[i]->partOfCombo[dPart],"until(",")"); + if(asTc.size()) + { + hints[i]->until.push_back(asTc[1]); + asTc.clear(); + } + asTc = parseBetween(hints[i]->partOfCombo[dPart],"restriction{","}"); + if(asTc.size()) + { + hints[i]->restrict.push_back(asTc[1]); + asTc.clear(); + } + asTc = parseBetween(hints[i]->partOfCombo[dPart],"cast(",")"); + if(asTc.size()) + { + hints[i]->cardTargets[asTc[1]] = parseBetween(hints[i]->partOfCombo[dPart],"targeting(",")")[1]; + } + asTc = parseBetween(hints[i]->partOfCombo[dPart],"totalmananeeded(",")"); + if(asTc.size()) + { + hints[i]->manaNeeded = asTc[1]; + asTc.clear(); + } + } + } + }//we collect the peices of a combo on first run. + for(unsigned int hPart = 0; hPart < hints[i]->hold.size(); hPart++) + { + hintTc = tfc.createTargetChooser(hints[i]->hold[hPart],card); + if(hintTc && hintTc->canTarget(card,true) && hintTc->countValidTargets() <= hintTc->maxtargets) + { + forCombo = true; + } + SAFE_DELETE(hintTc); + } + } + } + return forCombo;//return forCombo that way all hints that are combos are predisected. +} +//if it's not part of a combo or there is more to gather, then return false +bool AIHints::canWeCombo(GameObserver* observer,MTGCardInstance * card,AIPlayerBaka * Ai) +{ + TargetChooserFactory tfc(observer); + TargetChooser * hintTc = NULL; + bool gotCombo = false; + int comboPartsHold = 0; + int comboPartsUntil = 0; + int comboPartsRestriction = 0; + for(unsigned int i = 0; i < hints.size();i++) + { + comboPartsHold = 0; + comboPartsUntil = 0; + comboPartsRestriction = 0; + if(gotCombo) + return gotCombo;//because more then one might be possible at any time. + if (hints[i]->hold.size()) + { + for(unsigned int hPart = 0; hPart < hints[i]->hold.size(); hPart++) + { + hintTc = tfc.createTargetChooser(hints[i]->hold[hPart],card); + int TcCheck = hintTc->countValidTargets(); + if(hintTc && TcCheck >= hintTc->maxtargets) + { + comboPartsHold +=1; + } + SAFE_DELETE(hintTc); + } + } + if (hints[i]->until.size()) + { + for(unsigned int hPart = 0; hPart < hints[i]->until.size(); hPart++) + { + hintTc = tfc.createTargetChooser(hints[i]->until[hPart],card); + int TcCheck = hintTc->countValidTargets(); + if(hintTc && TcCheck >= hintTc->maxtargets) + { + comboPartsUntil +=1; + } + SAFE_DELETE(hintTc); + } + } + if (hints[i]->restrict.size()) + { + for(unsigned int hPart = 0; hPart < hints[i]->restrict.size(); hPart++) + { + AbilityFactory af(observer); + int checkCond = af.parseCastRestrictions(card,card->controller(),hints[i]->restrict[hPart]); + if(checkCond >= 1) + { + comboPartsRestriction +=1; + } + } + } + if( comboPartsUntil >= int(hints[i]->until.size()) && comboPartsHold >= int(hints[i]->hold.size()) && comboPartsRestriction >= int(hints[i]->restrict.size()) && hints[i]->combos.size() ) + { + ManaCost * needed = ManaCost::parseManaCost(hints[i]->manaNeeded, NULL, card); + if(Ai->canPayManaCost(card,needed).size()||!needed->getConvertedCost()) + { + gotCombo = true; + Ai->comboHint = hints[i];//set the combo we are doing. + } + SAFE_DELETE(needed); + } + } + return gotCombo; +} + + vectorAIHints::mCastOrder() { for(unsigned int i = 0; i < hints.size();i++) diff --git a/projects/mtg/src/AIMomirPlayer.cpp b/projects/mtg/src/AIMomirPlayer.cpp index 11d4b6251..0d7e741a5 100644 --- a/projects/mtg/src/AIMomirPlayer.cpp +++ b/projects/mtg/src/AIMomirPlayer.cpp @@ -51,7 +51,7 @@ int AIMomirPlayer::momir() if (efficiency >= chance) { - std::vector _cost; + std::vector _cost; _cost.push_back(Constants::MTG_COLOR_ARTIFACT); _cost.push_back(converted); ManaCost * cost = NEW ManaCost(_cost); diff --git a/projects/mtg/src/AIPlayer.cpp b/projects/mtg/src/AIPlayer.cpp index fa6577200..bef4aa55b 100644 --- a/projects/mtg/src/AIPlayer.cpp +++ b/projects/mtg/src/AIPlayer.cpp @@ -242,6 +242,7 @@ AIPlayer * AIPlayerFactory::createAIPlayer(GameObserver *observer, MTGAllCards * // AIPlayerBaka will delete MTGDeck when it's time AIPlayerBaka * baka = NEW AIPlayerBaka(observer, deckFile, deckFileSmall, avatarFilename, NEW MTGDeck(deckFile, collection,0, deckSetting)); baka->deckId = deckid; + baka->comboHint = NULL; return baka; } diff --git a/projects/mtg/src/AIPlayerBaka.cpp b/projects/mtg/src/AIPlayerBaka.cpp index f1f6f863c..301b8ab1f 100644 --- a/projects/mtg/src/AIPlayerBaka.cpp +++ b/projects/mtg/src/AIPlayerBaka.cpp @@ -645,6 +645,10 @@ int AIPlayerBaka::getEfficiency(OrderedAIAction * action) MTGCardInstance * AIPlayerBaka::chooseCard(TargetChooser * tc, MTGCardInstance * source, int random) { MTGPlayerCards * playerZones = source->controller()->game; + if (comboHint && comboHint->cardTargets.size()) + { + tc = GetComboTc(observer,tc); + } for(int players = 0; players < 2;++players) { MTGGameZone * zones[] = { playerZones->hand, playerZones->library, playerZones->inPlay, playerZones->graveyard,playerZones->stack }; @@ -654,11 +658,9 @@ MTGCardInstance * AIPlayerBaka::chooseCard(TargetChooser * tc, MTGCardInstance * for (int k = 0; k < zone->nb_cards; k++) { MTGCardInstance * card = zone->cards[k]; - if (card != source && !tc->alreadyHasTarget(card) && tc->canTarget(card)) + if (card != source && card != tc->source && !tc->alreadyHasTarget(card) && tc->canTarget(card)) { - return card; - } } } @@ -681,6 +683,8 @@ bool AIPlayerBaka::payTheManaCost(ManaCost * cost, MTGCardInstance * target,vect } ExtraCosts * ec = cost->extraCosts; + if(!ec && observer->mExtraPayment) + ec = observer->mExtraPayment; if (ec) { for (size_t i = 0; i < ec->costs.size(); ++i) @@ -1206,6 +1210,10 @@ int AIPlayerBaka::createAbilityTargets(MTGAbility * a, MTGCardInstance * c, Rank ranking[aiAction] = 1; return 1; } + if (comboHint && comboHint->cardTargets.size()) + { + a->setActionTC(GetComboTc(observer,a->getActionTc())); + } vectorpotentialTargets; for (int i = 0; i < 2; i++) { @@ -1284,6 +1292,37 @@ int AIPlayerBaka::createAbilityTargets(MTGAbility * a, MTGCardInstance * c, Rank return 1; } +TargetChooser * AIPlayerBaka::GetComboTc( GameObserver * observer,TargetChooser * tc) +{ + TargetChooser * gathertc = NULL; + TargetChooserFactory tcf(observer); + map::iterator it = comboHint->cardTargets.begin(); + for(map::iterator it = comboHint->cardTargets.begin();it != comboHint->cardTargets.end();it++) + { + gathertc = tcf.createTargetChooser(it->first.c_str(),tc->source); + gathertc->setAllZones(); + if(gathertc->canTarget(tc->source)) + { + MTGCardInstance * cardBackUp = tc->source; + Player * Oowner = tc->Owner; + TargetChooser * testTc = tcf.createTargetChooser(it->second.c_str(),cardBackUp); + if(testTc->countValidTargets()) + { + tc = testTc; + tc->Owner = Oowner; + tc->other = true; + } + //I know I shouldn't redefine a passed variable, + //if anyone knows a way that doesn't add a major function for this that does this correctly + //then feel free to change this redefine. I do it this way becuase the method is the + //fastest I could find that doesn't produce a noticible lag on ai. + //recreate the targetchooser for this card becuase we planned to use it in a combo + } + SAFE_DELETE(gathertc); + } + return tc; +} + int AIPlayerBaka::selectHintAbility() { if (!hints) @@ -1312,6 +1351,7 @@ int AIPlayerBaka::selectHintAbility() int AIPlayerBaka::selectAbility() { + observer->mExtraPayment = NULL; // Try Deck hints first if (selectHintAbility()) return 1; @@ -1428,6 +1468,9 @@ int AIPlayerBaka::effectBadOrGood(MTGCardInstance * card, int mode, TargetChoose int AIPlayerBaka::chooseTarget(TargetChooser * _tc, Player * forceTarget,MTGCardInstance * chosenCard,bool checkOnly) { + observer->mExtraPayment = NULL; + //there should never be a case where a extra cost target selection is happening at the same time as this.. + //extracost uses "chooseCard()" to determine its targets. vector potentialTargets; TargetChooser * tc = _tc; if (!(observer->currentlyActing() == this)) @@ -1439,6 +1482,10 @@ int AIPlayerBaka::chooseTarget(TargetChooser * _tc, Player * forceTarget,MTGCard if (!tc || !tc->source || tc->maxtargets < 1) return 0; assert(tc); + if (comboHint && comboHint->cardTargets.size()) + { + tc = GetComboTc(observer,tc); + } if(!checkOnly && tc->maxtargets > 1) { tc->initTargets();//just incase.... @@ -1641,6 +1688,14 @@ MTGCardInstance * AIPlayerBaka::FindCardToPlay(ManaCost * pMana, const char * ty int maxCost = -1; MTGCardInstance * nextCardToPlay = NULL; MTGCardInstance * card = NULL; + if(comboCards.size()) + { + nextCardToPlay = comboCards.back(); + comboCards.pop_back(); + if(!comboHint->cardTargets.size() && !comboCards.size()) + comboHint = NULL;//becuase it's no longer needed. + return nextCardToPlay; + } CardDescriptor cd; cd.init(); cd.setType(type); @@ -1668,7 +1723,24 @@ MTGCardInstance * AIPlayerBaka::FindCardToPlay(ManaCost * pMana, const char * ty if (card->hasType(Subtypes::TYPE_PLANESWALKER) && card->types.size() > 0 && game->inPlay->hasTypeSpecificInt(Subtypes::TYPE_PLANESWALKER,card->types[1])) continue; - + if(hints && hints->HintSaysItsForCombo(observer,card)) + { + if(hints->canWeCombo(observer,card,this)) + { + AbilityFactory af(observer); + int canPlay = af.parseCastRestrictions(card,card->controller(),card->getRestrictions()); + if(!canPlay) + continue; + nextCardToPlay = card; + return activateCombo(); + } + else + { + int chance = int(getRandomGenerator()->random() % 100); + if(chance > 1) + continue;//1% chance to just continue evaluating the card to cast. + } + } int currentCost = card->getManaCost()->getConvertedCost(); int hasX = card->getManaCost()->hasX(); gotPayments.clear(); @@ -1780,6 +1852,41 @@ MTGCardInstance * AIPlayerBaka::FindCardToPlay(ManaCost * pMana, const char * ty return nextCardToPlay; } +MTGCardInstance * AIPlayerBaka::activateCombo() +{ + if(!comboHint) + return NULL; + TargetChooser * hintTc = NULL; + TargetChooserFactory tfc(observer); + ManaCost * totalCost = ManaCost::parseManaCost(comboHint->manaNeeded); + for(unsigned int k = 0;k < comboHint->hold.size(); k++) + { + hintTc = tfc.createTargetChooser(comboHint->hold[k],nextCardToPlay); + for(unsigned int j = 0; j < game->hand->cards.size();j++) + { + if(!hintTc) + break; + if(hintTc->canTarget(game->hand->cards[j])) + { + comboCards.push_back(game->hand->cards[j]); + SAFE_DELETE(hintTc); + } + } + SAFE_DELETE(hintTc); + } + if(payTheManaCost(totalCost,nextCardToPlay,gotPayments)) + { + if(comboCards.size()) + { + nextCardToPlay = comboCards.back(); + if (game->playRestrictions->canPutIntoZone(nextCardToPlay, game->stack) == PlayRestriction::CANT_PLAY) + return NULL; + comboCards.pop_back(); + } + } + SAFE_DELETE(totalCost); + return nextCardToPlay; +} void AIPlayerBaka::initTimer() { @@ -1910,9 +2017,8 @@ int AIPlayerBaka::computeActions() currentMana->add(this->getManaPool()); nextCardToPlay = FindCardToPlay(currentMana, "land"); - if (game->playRestrictions->canPutIntoZone(nextCardToPlay, game->stack) == PlayRestriction::CANT_PLAY) + if (nextCardToPlay && nextCardToPlay->isLand() && game->playRestrictions->canPutIntoZone(nextCardToPlay, game->battlefield) == PlayRestriction::CANT_PLAY) nextCardToPlay = NULL;//look for a land, did we find one we can play..if not set to null now. - if(hints && hints->mCastOrder().size()) { vectorfindType = hints->mCastOrder(); @@ -1928,6 +2034,8 @@ int AIPlayerBaka::computeActions() nextCardToPlay = FindCardToPlay(currentMana, findType[j].c_str()); if (game->playRestrictions->canPutIntoZone(nextCardToPlay, game->stack) == PlayRestriction::CANT_PLAY) nextCardToPlay = NULL; + if (nextCardToPlay && nextCardToPlay->isLand() && game->playRestrictions->canPutIntoZone(nextCardToPlay, game->battlefield) == PlayRestriction::CANT_PLAY) + nextCardToPlay = NULL; } } else @@ -1945,6 +2053,8 @@ int AIPlayerBaka::computeActions() nextCardToPlay = FindCardToPlay(currentMana, types[count]); if (game->playRestrictions->canPutIntoZone(nextCardToPlay, game->stack) == PlayRestriction::CANT_PLAY) nextCardToPlay = NULL; + if (nextCardToPlay && nextCardToPlay->isLand() && game->playRestrictions->canPutIntoZone(nextCardToPlay, game->battlefield) == PlayRestriction::CANT_PLAY) + nextCardToPlay = NULL; count++; } } @@ -1978,6 +2088,11 @@ int AIPlayerBaka::computeActions() } else { + if(observer->mExtraPayment) + //no extra payment should be waiting before selecting an ability. + observer->mExtraPayment = NULL; + //this is a fix for a rare bug that somehow ai trips over an extra payment without paying + //then locks in a loop of trying to choose something different to do and trying to pay the extra payment. selectAbility(); } break; diff --git a/projects/mtg/src/GameStateDuel.cpp b/projects/mtg/src/GameStateDuel.cpp index ec1699596..0b445ba19 100644 --- a/projects/mtg/src/GameStateDuel.cpp +++ b/projects/mtg/src/GameStateDuel.cpp @@ -293,18 +293,20 @@ void GameStateDuel::ThreadProc(void* inParam) int oldTurn = -1; int oldPhase = -1; int stagnationCounter = -1; - observer.loadPlayer(0, PLAYER_TYPE_CPU_TEST); observer.loadPlayer(1, PLAYER_TYPE_CPU_TEST); observer.startGame(instance->mParent->gameType, instance->mParent->rules); - - while(!observer.didWin()) { - if(observer.turn == oldTurn && observer.currentGamePhase == oldPhase) { + while(!observer.didWin()) + { + if(observer.turn == oldTurn && observer.currentGamePhase == oldPhase) + { stagnationCounter++; - } else { + } + else + { stagnationCounter = 0; oldTurn = observer.turn; - oldPhase = observer.currentGamePhase; + oldPhase = observer.getCurrentGamePhase(); } if(stagnationCounter >= 1000) { @@ -606,7 +608,7 @@ void GameStateDuel::Render() if (totalTestGames < 2.5 * totalAIDecks) { mFont->SetColor(ARGB(255,255,255,0)); - sprintf(buf, "Results are not significant, you should let at least %i more games run", (int)(totalAIDecks * 2.5) - totalTestGames); + sprintf(buf, " Results are not significant, you should let at least %i more games run", (int)(totalAIDecks * 2.5) - totalTestGames); mFont->DrawString(buf,0,SCREEN_HEIGHT/2 - 20); } @@ -616,8 +618,8 @@ void GameStateDuel::Render() mFont->SetColor(ARGB(255,255,0,0)); if (ratio > 0.52) mFont->SetColor(ARGB(255,0,255,0)); - sprintf(buf, "Victories Player 2/total Games: %i/%i - Games/second: %f", - testPlayer2Victories, totalTestGames, (float)(1000*totalTestGames)/(currentTime - startTime)); + sprintf(buf, " Victories Player 2/total Games: %i/%i - Game Turn: %i", + testPlayer2Victories, totalTestGames, /*(float)(1000*totalTestGames)/(currentTime - startTime)*/game->turn); mFont->DrawString(buf,0,SCREEN_HEIGHT/2); } #endif diff --git a/projects/mtg/src/GuiMana.cpp b/projects/mtg/src/GuiMana.cpp index 006448f73..63d72936d 100644 --- a/projects/mtg/src/GuiMana.cpp +++ b/projects/mtg/src/GuiMana.cpp @@ -289,6 +289,18 @@ void GuiMana::RenderStatic() void GuiMana::Render() { + if (manas.size() > 20) + { + int count = 0; + for (vector::iterator it = manas.begin(); it != manas.end(); ++it) + { + if (count > 20) + break; + count++; + (*it)->Render(); + } + } + else for (vector::iterator it = manas.begin(); it != manas.end(); ++it) (*it)->Render(); diff --git a/projects/mtg/src/MTGAbility.cpp b/projects/mtg/src/MTGAbility.cpp index 9c9193dee..5f4e7407f 100644 --- a/projects/mtg/src/MTGAbility.cpp +++ b/projects/mtg/src/MTGAbility.cpp @@ -3490,7 +3490,7 @@ void AbilityFactory::addAbilities(int _id, Spell * spell) case 1103: //Crystal Rod { - std::vector cost; + std::vector cost; cost.push_back(Constants::MTG_COLOR_ARTIFACT); cost.push_back(1); ASpellCastLife* ability = NEW ASpellCastLife(observer, _id, card, Constants::MTG_COLOR_BLUE, NEW ManaCost(cost, 1), 1); @@ -3525,7 +3525,7 @@ void AbilityFactory::addAbilities(int _id, Spell * spell) case 1113: //Iron Star { - std::vector cost; + std::vector cost; cost.push_back(Constants::MTG_COLOR_ARTIFACT); cost.push_back(1); ASpellCastLife* ability = NEW ASpellCastLife(observer, _id, card, Constants::MTG_COLOR_RED, NEW ManaCost(cost, 1), 1); @@ -3539,7 +3539,7 @@ void AbilityFactory::addAbilities(int _id, Spell * spell) } case 1114: //Ivory cup { - std::vector cost; + std::vector cost; cost.push_back(Constants::MTG_COLOR_ARTIFACT); cost.push_back(1); ASpellCastLife* ability = NEW ASpellCastLife(observer, _id, card, Constants::MTG_COLOR_WHITE, NEW ManaCost(cost, 1), 1); @@ -3614,7 +3614,7 @@ void AbilityFactory::addAbilities(int _id, Spell * spell) case 1140: //Throne of Bone { - std::vector cost; + std::vector cost; cost.push_back(Constants::MTG_COLOR_ARTIFACT); cost.push_back(1); ASpellCastLife* ability = NEW ASpellCastLife(observer, _id, card, Constants::MTG_COLOR_BLACK, NEW ManaCost(cost, 1), 1); @@ -3624,7 +3624,7 @@ void AbilityFactory::addAbilities(int _id, Spell * spell) case 1142: //Wooden Sphere { - std::vector cost; + std::vector cost; cost.push_back(Constants::MTG_COLOR_ARTIFACT); cost.push_back(1); ASpellCastLife* ability = NEW ASpellCastLife(observer, _id, card, Constants::MTG_COLOR_GREEN, NEW ManaCost(cost, 1), 1); diff --git a/projects/mtg/src/ManaCost.cpp b/projects/mtg/src/ManaCost.cpp index bb4f474f9..c0596278d 100644 --- a/projects/mtg/src/ManaCost.cpp +++ b/projects/mtg/src/ManaCost.cpp @@ -283,7 +283,7 @@ ManaCost::ManaCost() init(); } -ManaCost::ManaCost(vector& _cost, int nb_elems) +ManaCost::ManaCost(vector& _cost, int nb_elems) { init(); for (int i = 0; i < nb_elems; i++) @@ -628,7 +628,7 @@ int ManaCost::getConvertedCost() int ManaCost::remove(int color, int value) { assert (value >= 0); - int8_t toRemove = min(cost[color], (int8_t)value); + int16_t toRemove = min(cost[color], (int16_t)value); cost[color] -= toRemove; return 1; } @@ -661,7 +661,7 @@ int ManaCost::remove(ManaCost * _cost) return 0; for ( int i = 0; i < Constants::NB_Colors; i++) { - int8_t toRemove = min(cost[i], (int8_t)_cost->getCost(i)); //we don't want to be negative + int16_t toRemove = min(cost[i], (int16_t)_cost->getCost(i)); //we don't want to be negative cost[i] -= toRemove; assert(cost[i] >= 0); } @@ -773,7 +773,7 @@ int ManaCost::isPositive() } -void ManaCost::randomDiffHybrids(ManaCost * _cost, std::vector& diff) +void ManaCost::randomDiffHybrids(ManaCost * _cost, std::vector& diff) { for (size_t i = 0; i < _cost->hybrids.size(); i++) { @@ -785,7 +785,7 @@ void ManaCost::randomDiffHybrids(ManaCost * _cost, std::vector& diff) /** starting from the end of the array (diff) */ -int ManaCost::tryToPayHybrids(std::vector& _hybrids, int _nbhybrids, std::vector& diff) +int ManaCost::tryToPayHybrids(std::vector& _hybrids, int _nbhybrids, std::vector& diff) { if (!_nbhybrids) return 1; @@ -816,7 +816,7 @@ ManaCost * ManaCost::Diff(ManaCost * _cost) if (!_cost) return NEW ManaCost(*this); //diff with null is equivalent to diff with 0 - vector diff; + vector diff; diff.resize((Constants::NB_Colors + 1) * 2); diff[Constants::NB_Colors * 2] = Constants::NB_Colors; for (int i = 0; i < Constants::NB_Colors; i++) diff --git a/projects/mtg/src/TestSuiteAI.cpp b/projects/mtg/src/TestSuiteAI.cpp index a945618fb..eaf5eceb1 100644 --- a/projects/mtg/src/TestSuiteAI.cpp +++ b/projects/mtg/src/TestSuiteAI.cpp @@ -26,6 +26,7 @@ TestSuiteAI::TestSuiteAI(TestSuiteGame *tsGame, int playerId) : timer = 0; playMode = MODE_TEST_SUITE; this->deckName = "Test Suite AI"; + this->comboHint = NULL; } MTGCardInstance * TestSuiteAI::getCard(string action)