- Adding new icons from J
- fixed a bug with Zombie Master (for real this time, I swear !)
- The AI should play Wrath of god in a more "clever" way
This commit is contained in:
wagic.the.homebrew
2008-11-16 10:37:13 +00:00
parent 0b5c940ae3
commit c50ea49e74
8 changed files with 334 additions and 275 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

View File

@@ -50,4 +50,5 @@ stasis.txt
terror.txt terror.txt
volcanic_island.txt volcanic_island.txt
white_knight1.txt white_knight1.txt
wrath_of_god.txt wrath_of_god.txt
zombie_master.txt

View File

@@ -0,0 +1,24 @@
#Testing Zombie Master crash ?
[INIT]
COMBATATTACKERS
[PLAYER1]
inplay:1188,129693,135216
[PLAYER2]
inplay:1299
[DO]
129693
1188
next
1299
next
next
129693
[ASSERT]
COMBATEND
[PLAYER1]
inplay:135216,129693
graveyard:1188
[PLAYER2]
life:18
inplay:1299
[END]

View File

@@ -882,7 +882,8 @@ class ALord:public ListMaintainerAbility{
card->addToToughness(toughness); card->addToToughness(toughness);
if (ability != -1) card->basicAbilities[ability] +=1; if (ability != -1) card->basicAbilities[ability] +=1;
if (regenCost){ if (regenCost){
AStandardRegenerate * regen = NEW AStandardRegenerate(0, card, card, regenCost); ManaCost * _regenCost = NEW ManaCost(regenCost);
AStandardRegenerate * regen = NEW AStandardRegenerate(0, card, card, _regenCost);
regenerations[card] = regen; regenerations[card] = regen;
game->addObserver(regen); game->addObserver(regen);
} }

View File

@@ -22,10 +22,11 @@ using std::map;
//Two stupid variables used to give a hint to the AI: //Two stupid variables used to give a hint to the AI:
// Should I cast a spell on an enemy or friendly unit ? // Should I cast a spell on an enemy or friendly unit ?
#define BAKA_EFFECT_GOOD 10 #define BAKA_EFFECT_GOOD 1
#define BAKA_EFFECT_BAD 11 #define BAKA_EFFECT_BAD -1
#define BAKA_EFFECT_DONTKNOW 0
#define COUNT_POWER 1
class MTGAbility: public ActionElement{ class MTGAbility: public ActionElement{
protected: protected:
@@ -183,6 +184,7 @@ class GenericTriggeredAbility:public TriggeredAbility{
/* Ability Factory */ /* Ability Factory */
class AbilityFactory{ class AbilityFactory{
private: private:
int countCards(TargetChooser * tc, Player * player = NULL, int option = 0);
int destroyAllInPlay(TargetChooser * tc, int bury = 0); int destroyAllInPlay(TargetChooser * tc, int bury = 0);
int putInPlayFromZone(MTGCardInstance * card, MTGGameZone * zone, Player * p); int putInPlayFromZone(MTGCardInstance * card, MTGGameZone * zone, Player * p);
Trigger * parseTrigger(string magicText); Trigger * parseTrigger(string magicText);
@@ -195,3 +197,4 @@ class AbilityFactory{
#include "MTGCardInstance.h" #include "MTGCardInstance.h"
#endif #endif

View File

@@ -76,31 +76,7 @@ void AIPlayer::tapLandsForMana(ManaCost * potentialMana, ManaCost * cost){
delete(diff); delete(diff);
/*
for (int i=MTG_NB_COLORS-1; i>= 0; i--){
#if defined (WIN32) || defined (LINUX)
char buf[4096];
sprintf(buf,"Testing %s \n" ,MTG_LAND_TEXTS[i]);
OutputDebugString(buf);
#endif
currentCost = cost->getCost(i);
while(currentCost){
#if defined (WIN32) || defined (LINUX)
sprintf(buf,"Cost for %s is %i \n" ,MTG_LAND_TEXTS[i], currentCost);
OutputDebugString(buf);
#endif
MTGCardInstance * card = NULL;
while(currentCost && (card = cd.nextmatch(game->inPlay, card))){
if (i==MTG_COLOR_ARTIFACT || card->hasSubtype(MTG_LAND_TEXTS[i]) ){
currentCost--;
gameObs->cardClick(card);
}
}
}
}
*/
#if defined (WIN32) || defined (LINUX) #if defined (WIN32) || defined (LINUX)
OutputDebugString("ok land tapped"); OutputDebugString("ok land tapped");
#endif #endif
@@ -167,7 +143,7 @@ int AIPlayer::effectBadOrGood(MTGCardInstance * card){
int autoGuess = af->magicText(id,NULL,card); int autoGuess = af->magicText(id,NULL,card);
delete af; delete af;
if (autoGuess) return autoGuess; if (autoGuess) return autoGuess;
return BAKA_EFFECT_BAD; return BAKA_EFFECT_DONTKNOW;
} }
int AIPlayer::chooseTarget(TargetChooser * tc){ int AIPlayer::chooseTarget(TargetChooser * tc){
@@ -184,7 +160,7 @@ int AIPlayer::chooseTarget(TargetChooser * tc){
if (!(gameObs->currentlyActing() == this)) return 0; if (!(gameObs->currentlyActing() == this)) return 0;
Player * target = this; Player * target = this;
int cardEffect = effectBadOrGood(tc->source); int cardEffect = effectBadOrGood(tc->source);
if (cardEffect == BAKA_EFFECT_BAD){ if (cardEffect != BAKA_EFFECT_GOOD){
target = this->opponent(); target = this->opponent();
} }
@@ -464,9 +440,18 @@ MTGCardInstance * AIPlayerBaka::FindCardToPlay(ManaCost * potentialMana, const c
TargetChooser * tc = tcf->createTargetChooser(card); TargetChooser * tc = tcf->createTargetChooser(card);
delete tcf; delete tcf;
if (tc){ if (tc){
int hasTarget = (chooseTarget(tc)); int hasTarget = (chooseTarget(tc));
delete tc; delete tc;
if (!hasTarget)continue; if (!hasTarget)continue;
}else{
int shouldPlayPercentage = 10;
int shouldPlay = effectBadOrGood(card);
if (shouldPlay == BAKA_EFFECT_GOOD){
shouldPlayPercentage = 90;
}else if(BAKA_EFFECT_DONTKNOW == shouldPlay){
shouldPlayPercentage = 70;
}
if (rand() % 100 > shouldPlayPercentage) continue;
} }
nextCardToPlay = card; nextCardToPlay = card;
maxCost = currentCost; maxCost = currentCost;

View File

@@ -9,243 +9,288 @@
#include "../include/MTGDeck.h" #include "../include/MTGDeck.h"
int AbilityFactory::destroyAllInPlay(TargetChooser * tc, int bury){ int AbilityFactory::countCards(TargetChooser * tc, Player * player, int option){
tc->source = NULL; // This is to prevent protection from... as objects that destroy all do not actually target int result = 0;
GameObserver * game = GameObserver::GetInstance(); GameObserver * game = GameObserver::GetInstance();
for (int i = 0; i < 2 ; i++){ for (int i = 0; i < 2 ; i++){
for (int j = 0; j < game->players[i]->game->inPlay->nb_cards; j++){ if (player && player!= game->players[i]) continue;
MTGCardInstance * current = game->players[i]->game->inPlay->cards[j]; for (int j = game->players[i]->game->inPlay->nb_cards-1; j >=0 ; j--){
if (tc->canTarget(current)){ MTGCardInstance * current = game->players[i]->game->inPlay->cards[j];
if (bury){ if (tc->canTarget(current)){
game->players[i]->game->putInGraveyard(current); switch (option){
}else{ case COUNT_POWER:
game->mLayers->stackLayer()->addPutInGraveyard(current); result+= current->power;
} break;
} default:
} result++;
} break;
return 1; }
} }
}
}
int AbilityFactory::putInPlayFromZone(MTGCardInstance * card, MTGGameZone * zone, Player * p){ return result;
Spell * spell = NEW Spell(card); }
p->game->putInZone(card, zone, p->game->stack);
spell->resolve(); int AbilityFactory::destroyAllInPlay(TargetChooser * tc, int bury){
delete spell; tc->source = NULL; // This is to prevent protection from... as objects that destroy all do not actually target
return 1; GameObserver * game = GameObserver::GetInstance();
} for (int i = 0; i < 2 ; i++){
for (int j = game->players[i]->game->inPlay->nb_cards-1; j >=0 ; j--){
MTGCardInstance * current = game->players[i]->game->inPlay->cards[j];
Trigger * AbilityFactory::parseTrigger(string magicText){ if (tc->canTarget(current)){
size_t found = magicText.find("@"); if (bury){
if (found == string::npos) return NULL; game->players[i]->game->putInGraveyard(current);
}else{
//Next Time... game->mLayers->stackLayer()->addPutInGraveyard(current);
found = magicText.find("next"); }
if (found != string::npos){ }
for (int i = 0; i < NB_MTG_PHASES; i++){ }
found = magicText.find(MTGPhaseCodeNames[i]); }
if (found != string::npos){ return 1;
return NEW TriggerNextPhase(i); }
}
}
} int AbilityFactory::putInPlayFromZone(MTGCardInstance * card, MTGGameZone * zone, Player * p){
Spell * spell = NEW Spell(card);
return NULL; p->game->putInZone(card, zone, p->game->stack);
} spell->resolve();
delete spell;
//Some basic functionalities that can be added automatically in the text file return 1;
/* }
* Several objects are computed from the text string, and have a direct influence on what action we should take
* (direct impact on the game such as draw a card immediately, or create a new GameObserver and add it to the Abilities,etc..)
* These objects are: Trigger * AbilityFactory::parseTrigger(string magicText){
* - trigger (if there is an "@" in the string, this is a triggered ability) int found = magicText.find("@");
* - target (if there ie a "target(" in the string, then this is a TargetAbility) if (found == string::npos) return NULL;
* - doTap (a dirty way to know if tapping is included in the cost...
*/ //Next Time...
int AbilityFactory::magicText(int id, Spell * spell, MTGCardInstance * card){ found = magicText.find("next");
int dryMode = 0; if (found != string::npos){
if (!spell) dryMode = 1; for (int i = 0; i < NB_MTG_PHASES; i++){
GameObserver * game = GameObserver::GetInstance(); found = magicText.find(MTGPhaseCodeNames[i]);
if (!card) card = spell->source; if (found != string::npos){
MTGCardInstance * target = card->target; return NEW TriggerNextPhase(i);
if (!target) target = card; }
string magicText = card->magicText; }
if (card->alias && magicText.size() == 0){ }
//An awful way to get access to the aliasedcard
magicText = GameObserver::GetInstance()->players[0]->game->collection->getCardById(card->alias)->magicText; return NULL;
} }
string s;
int size = magicText.size(); //Some basic functionalities that can be added automatically in the text file
if (size == 0) return 0; /*
unsigned int found; * Several objects are computed from the text string, and have a direct influence on what action we should take
int result = id; * (direct impact on the game such as draw a card immediately, or create a new GameObserver and add it to the Abilities,etc..)
* These objects are:
* - trigger (if there is an "@" in the string, this is a triggered ability)
while (magicText.size()){ * - target (if there ie a "target(" in the string, then this is a TargetAbility)
found = magicText.find("\n"); * - doTap (a dirty way to know if tapping is included in the cost...
if (found != string::npos){ */
s = magicText.substr(0,found); int AbilityFactory::magicText(int id, Spell * spell, MTGCardInstance * card){
magicText = magicText.substr(found+1); int dryMode = 0;
}else{ if (!spell) dryMode = 1;
s = magicText; GameObserver * game = GameObserver::GetInstance();
magicText = ""; if (!card) card = spell->source;
} MTGCardInstance * target = card->target;
#if defined (WIN32) || defined (LINUX) if (!target) target = card;
char buf[4096]; string magicText = card->magicText;
sprintf(buf, "AUTO ACTION: %s\n", s.c_str()); if (card->alias && magicText.size() == 0){
OutputDebugString(buf); //An awful way to get access to the aliasedcard
#endif magicText = GameObserver::GetInstance()->players[0]->game->collection->getCardById(card->alias)->magicText;
}
TargetChooser * tc = NULL; string s;
int doTap = 0; int size = magicText.size();
string lordType = ""; if (size == 0) return 0;
unsigned int found;
Trigger * trigger = parseTrigger(s); int result = id;
//Dirty way to remove the trigger text (could get in the way)
if (trigger){
found = s.find(":"); while (magicText.size()){
s = s.substr(found+1); found = magicText.find("\n");
} if (found != string::npos){
s = magicText.substr(0,found);
//Tap in the cost ? magicText = magicText.substr(found+1);
if (s.find("{t}") != string::npos) doTap = 1; }else{
s = magicText;
//Target Abilities magicText = "";
found = s.find("target("); }
if (found != string::npos){ #if defined (WIN32) || defined (LINUX)
int end = s.find(")"); char buf[4096];
string starget = s.substr(found + 7,end - found - 7); sprintf(buf, "AUTO ACTION: %s\n", s.c_str());
TargetChooserFactory tcf; OutputDebugString(buf);
tc = tcf.createTargetChooser(starget, card); #endif
} TargetChooser * tc = NULL;
int doTap = 0;
//Lord string lordType = "";
found = s.find("lord(");
if (found != string::npos){ Trigger * trigger = parseTrigger(s);
if (dryMode) return BAKA_EFFECT_GOOD; //Dirty way to remove the trigger text (could get in the way)
unsigned int end = s.find(")", found+5); if (trigger){
if (end != string::npos){ found = s.find(":");
lordType = s.substr(found+5,end-found-5).c_str(); s = s.substr(found+1);
} }
}
//Tap in the cost ?
//Champion. Very basic, needs to be improved ! if (s.find("{t}") != string::npos) doTap = 1;
found = s.find("champion(name:");
if (found != string::npos){ //Target Abilities
if (dryMode) return BAKA_EFFECT_GOOD; found = s.find("target(");
unsigned int end = s.find(")", found+14); if (found != string::npos){
if (end != string::npos){ int end = s.find(")");
string type = s.substr(found+14,end-found-14).c_str(); string starget = s.substr(found + 7,end - found - 7);
game->addObserver(NEW APlagueRats(id,card,type.c_str())); TargetChooserFactory tcf;
result++; tc = tcf.createTargetChooser(starget, card);
continue;
} }
}
//Lord
//Untapper (Ley Druid...) found = s.find("lord(");
found = s.find("untap"); if (found != string::npos){
if (found != string::npos){ if (dryMode) return BAKA_EFFECT_GOOD;
if (dryMode) return BAKA_EFFECT_GOOD; unsigned int end = s.find(")", found+5);
ManaCost * cost = ManaCost::parseManaCost(s); if (end != string::npos){
if (tc){ lordType = s.substr(found+5,end-found-5).c_str();
game->addObserver(NEW AUntaper(id, card, cost, tc)); }
}else{ }
target->tapped = 0;
} //Champion. Very basic, needs to be improved !
found = s.find("champion(name:");
result++; if (found != string::npos){
continue; if (dryMode) return BAKA_EFFECT_GOOD;
} unsigned int end = s.find(")", found+14);
if (end != string::npos){
//Tapper (icy manipulator) string type = s.substr(found+14,end-found-14).c_str();
found = s.find("tap"); game->addObserver(NEW APlagueRats(id,card,type.c_str()));
if (found != string::npos){ result++;
if (dryMode) return BAKA_EFFECT_GOOD; continue;
ManaCost * cost = ManaCost::parseManaCost(s); }
if (tc){ }
game->addObserver(NEW ATapper(id, card, cost, tc));
}else{ //Untapper (Ley Druid...)
target->tapped = 1; found = s.find("untap");
} if (found != string::npos){
if (dryMode) return BAKA_EFFECT_GOOD;
result++; ManaCost * cost = ManaCost::parseManaCost(s);
continue; if (tc){
} game->addObserver(NEW AUntaper(id, card, cost, tc));
}else{
//Regeneration target->tapped = 0;
found = s.find("}:regenerate"); }
if (found != string::npos){
if (dryMode) return BAKA_EFFECT_GOOD; result++;
ManaCost * cost = ManaCost::parseManaCost(s); continue;
}
if (lordType.size() > 0){
game->addObserver(NEW ALord(id,card,lordType.c_str(),0,0,-1,cost)); //Tapper (icy manipulator)
}else{ found = s.find("tap");
if (found != string::npos){
if (tc){ if (dryMode) return BAKA_EFFECT_GOOD;
//TODO ManaCost * cost = ManaCost::parseManaCost(s);
}else{ if (tc){
game->addObserver(NEW AStandardRegenerate(id, card, target, cost)); game->addObserver(NEW ATapper(id, card, cost, tc));
//TODO death ward ! }else{
} target->tapped = 1;
} }
result++;
continue; result++;
} continue;
}
//Bury
found = s.find("bury"); //Regeneration
if (found != string::npos){ found = s.find("}:regenerate");
if (dryMode) return BAKA_EFFECT_BAD; if (found != string::npos){
if (trigger){ if (dryMode) return BAKA_EFFECT_GOOD;
BuryEvent * action = NEW BuryEvent(); ManaCost * cost = ManaCost::parseManaCost(s);
game->addObserver(NEW GenericTriggeredAbility(id, card,trigger,action));
}else{ if (lordType.size() > 0){
found = s.find("all("); game->addObserver(NEW ALord(id,card,lordType.c_str(),0,0,-1,cost));
if (found != string::npos){ }else{
int end = s.find(")");
string starget = s.substr(found + 4,end - found - 4); if (tc){
TargetChooserFactory tcf; //TODO
TargetChooser * targetAll = tcf.createTargetChooser(starget, card); }else{
this->destroyAllInPlay(targetAll,1); game->addObserver(NEW AStandardRegenerate(id, card, target, cost));
delete targetAll; //TODO death ward !
}else{ }
if (tc){ }
game->addObserver(NEW ABurier(id, card,tc)); result++;
}else{ continue;
target->controller()->game->putInGraveyard(target); }
}
} //Bury
} found = s.find("bury");
result++; if (found != string::npos){
continue; if (trigger){
} if (dryMode) return BAKA_EFFECT_BAD;
BuryEvent * action = NEW BuryEvent();
//Destroy game->addObserver(NEW GenericTriggeredAbility(id, card,trigger,action));
found = s.find("destroy"); }else{
if (found != string::npos){ found = s.find("all(");
if (dryMode) return BAKA_EFFECT_BAD; //TODO compute according to nb in case of destroy all if (found != string::npos){
found = s.find("all("); int end = s.find(")");
if (found != string::npos){ string starget = s.substr(found + 4,end - found - 4);
int end = s.find(")"); TargetChooserFactory tcf;
string starget = s.substr(found + 4,end - found - 4); TargetChooser * targetAll = tcf.createTargetChooser(starget, card);
TargetChooserFactory tcf; if (dryMode){
TargetChooser * targetAll = tcf.createTargetChooser(starget, card); int myNbCards = countCards(targetAll,card->controller());
this->destroyAllInPlay(targetAll); int opponentNbCards = countCards(targetAll, card->controller()->opponent());
delete targetAll; int myCardsPower = countCards(targetAll,card->controller(),COUNT_POWER);
}else{ int opponentCardsPower = countCards(targetAll, card->controller()->opponent(),COUNT_POWER);
if (tc){ delete targetAll;
game->addObserver(NEW ADestroyer(id, card,tc)); if (myNbCards < opponentNbCards || myCardsPower < opponentCardsPower) return BAKA_EFFECT_GOOD;
}else{ return BAKA_EFFECT_BAD;
game->mLayers->stackLayer()->addPutInGraveyard(target); }else{
} this->destroyAllInPlay(targetAll,1);
} delete targetAll;
result++; }
continue;
} }else{
if (dryMode) return BAKA_EFFECT_BAD;
if (tc){
game->addObserver(NEW ABurier(id, card,tc));
}else{
target->controller()->game->putInGraveyard(target);
}
}
}
result++;
continue;
}
//Destroy
found = s.find("destroy");
if (found != string::npos){
found = s.find("all(");
if (found != string::npos){
int end = s.find(")");
string starget = s.substr(found + 4,end - found - 4);
TargetChooserFactory tcf;
TargetChooser * targetAll = tcf.createTargetChooser(starget, card);
if (dryMode){
int myNbCards = countCards(targetAll,card->controller());
int opponentNbCards = countCards(targetAll, card->controller()->opponent());
int myCardsPower = countCards(targetAll,card->controller(),COUNT_POWER);
int opponentCardsPower = countCards(targetAll, card->controller()->opponent(),COUNT_POWER);
delete targetAll;
if (myNbCards < opponentNbCards || myCardsPower < opponentCardsPower) return BAKA_EFFECT_GOOD;
return BAKA_EFFECT_BAD;
}else{
this->destroyAllInPlay(targetAll);
delete targetAll;
}
}else{
if (dryMode) return BAKA_EFFECT_BAD;
if (tc){
game->addObserver(NEW ADestroyer(id, card,tc));
}else{
game->mLayers->stackLayer()->addPutInGraveyard(target);
}
}
result++;
continue;
}
//Damage //Damage
found = s.find("damage"); found = s.find("damage");
@@ -1804,4 +1849,4 @@ GenericTriggeredAbility::~GenericTriggeredAbility(){
delete t; delete t;
delete te; delete te;
SAFE_DELETE(dc); SAFE_DELETE(dc);
} }

View File

@@ -294,9 +294,9 @@ int TestSuite::assertGame(){
} }
for (int k = 0; k < endState.playerData[i].zones[j].nbitems; k++){ for (int k = 0; k < endState.playerData[i].zones[j].nbitems; k++){
int cardid = endState.playerData[i].zones[j].cards[k]; int cardid = endState.playerData[i].zones[j].cards[k];
int realcardid = zone->cards[k]->getMTGId(); MTGCardInstance * card = getCardByMTGId(cardid);
if ( realcardid!= cardid){ if (!card || !zone->hasCard(card)){
sprintf(result, "<span class=\"error\">==Card ID not the same. Expected %i, got %i==</span><br />", cardid, realcardid); sprintf(result, "<span class=\"error\">==Card ID not the same. Didn't find %i</span><br />", cardid);
Log(result); Log(result);
error++; error++;
} }