- fixed memory leaks introduced in previous revision

- removed incorrect casts of MTGCardInstance into Spell objects.
- AI Test system now allows you to put decks in ai/bakaA and ai/bakaB instead of ai/baka. This allows to let AIPlayerBaka and AIPlayerBakaB play with specific decks
- Test suite speed improvement. Improved the card name cache. Test suite now runs in 850 seconds instead of 950 on my machine.
- minor code cleanup
This commit is contained in:
wagic.the.homebrew
2011-09-22 04:43:05 +00:00
parent 997b154d46
commit ee4c7e23bd
15 changed files with 163 additions and 85 deletions

View File

@@ -1,7 +1,8 @@
[INIT]
UNTAP
[PLAYER1]
inplay:Thallid
#1924 is thallid, we use the id to be sure to match the token in the end
inplay:1924
[PLAYER2]
[DO]
eot
@@ -13,11 +14,11 @@ eot
next
next
next
thallid
thallid
1924
1924
[ASSERT]
FIRSTMAIN
[PLAYER1]
inplay:Thallid,-1924
inplay:1924,-1924
[PLAYER2]
[END]

View File

@@ -107,7 +107,7 @@ class AIPlayerFactory{
public:
AIPlayer * createAIPlayer(MTGAllCards * collection, Player * opponent, int deckid = 0);
#ifdef AI_CHANGE_TESTING
AIPlayer * createAIPlayerTest(MTGAllCards * collection, Player * opponent, int deckid = 0);
AIPlayer * createAIPlayerTest(MTGAllCards * collection, Player * opponent, string folder);
#endif
};

View File

@@ -210,7 +210,7 @@ public:
Player * lastActionController;
int setIsInterrupting(Player * player);
int count( int type = 0 , int state = 0 , int display = -1);
int getActionElementFromCard(MTGCardInstance * card);
Interruptible * getActionElementFromCard(MTGCardInstance * card);
Interruptible * getPrevious(Interruptible * next, int type = 0, int state = 0 , int display = -1);
int getPreviousIndex(Interruptible * next, int type = 0, int state = 0 , int display = -1);
Interruptible * getNext(Interruptible * previous, int type = 0, int state = 0 , int display = -1);

View File

@@ -37,12 +37,9 @@ public:
if (card) return card->X;
return 1; //this should only hapen when the ai calls the ability. This is to give it an idea of the "direction" of X (positive/negative)
}
WParsedInt(int value = 0)
{
intValue = value;
}
WParsedInt(string s, Spell * spell, MTGCardInstance * card)
private:
void init(string s, Spell * spell, MTGCardInstance * card)
{
if(!card)
return;
@@ -73,8 +70,7 @@ public:
}
if(s == "prex")
{
ManaCost * cX = NEW ManaCost(card->controller()->getManaPool()->Diff(card->getManaCost()));
int preX =
ManaCost * cX = card->controller()->getManaPool()->Diff(card->getManaCost());
intValue = cX->getCost(Constants::MTG_NB_COLORS);
delete cX;
}
@@ -220,6 +216,22 @@ public:
}
intValue *= multiplier;
}
public:
WParsedInt(int value = 0)
{
intValue = value;
}
WParsedInt(string s, Spell * spell, MTGCardInstance * card)
{
init(s, spell, card);
}
WParsedInt(string s, MTGCardInstance * card)
{
init(s, NULL, card);
}
int getValue()
{

View File

@@ -119,6 +119,11 @@ public:
map<string, CardPrimitive *> primitives;
MTGCard * _(int id);
MTGCard * getCardById(int id);
#ifdef TESTSUITE
void prefetchCardNameCache();
#endif
MTGCard * getCardByName(string name);
int load(const char * config_file, const char * setName = NULL, int autoload = 1);
int countByType(const char * _type);

View File

@@ -68,6 +68,7 @@ private:
bool forceAbility;
int summoningSickness;
int load(const char * filename);
void cleanup();
@@ -84,6 +85,7 @@ public:
int assertGame();
public:
int startTime, endTime;
int gameType;
unsigned int seed;
int nbFailed, nbTests, nbAIFailed, nbAITests;

View File

@@ -286,46 +286,44 @@ void AIPlayer::Render()
}
#ifdef AI_CHANGE_TESTING
AIPlayer * AIPlayerFactory::createAIPlayerTest(MTGAllCards * collection, Player * opponent, int deckid)
AIPlayer * AIPlayerFactory::createAIPlayerTest(MTGAllCards * collection, Player * opponent, string _folder)
{
char deckFile[512];
string avatarFilename; // default imagename
char deckFileSmall[512];
if (deckid == GameStateDuel::MENUITEM_EVIL_TWIN)
{ //Evil twin
sprintf(deckFile, "%s", opponent->deckFile.c_str());
DebugTrace("Evil Twin => " << opponent->deckFile);
avatarFilename = "avatar.jpg";
sprintf(deckFileSmall, "%s", "ai_baka_eviltwin");
}
else
string folder = _folder.size() ? _folder : "ai/baka/";
int deckid = 0;
//random deck
int nbdecks = 0;
int found = 1;
while (found && nbdecks < options[Options::AIDECKS_UNLOCKED].number)
{
if (!deckid)
found = 0;
char buffer[512];
sprintf(buffer, "%sdeck%i.txt", folder.c_str(), nbdecks + 1);
if (FileExists(buffer))
{
//random deck
int nbdecks = 0;
int found = 1;
while (found && nbdecks < options[Options::AIDECKS_UNLOCKED].number)
{
found = 0;
char buffer[512];
sprintf(buffer, "ai/baka/deck%i.txt", nbdecks + 1);
if (FileExists(buffer))
{
found = 1;
nbdecks++;
}
}
if (!nbdecks)
return NULL;
deckid = 1 + WRand() % (nbdecks);
found = 1;
nbdecks++;
}
sprintf(deckFile, "ai/baka/deck%i.txt", deckid);
DeckMetaData *aiMeta = DeckManager::GetInstance()->getDeckMetaDataByFilename( deckFile, true);
avatarFilename = aiMeta->getAvatarFilename();
sprintf(deckFileSmall, "ai_baka_deck%i", deckid);
}
if (!nbdecks)
{
if (_folder.size())
return createAIPlayerTest(collection, opponent, "");
return NULL;
}
deckid = 1 + WRand() % (nbdecks);
sprintf(deckFile, "%sdeck%i.txt", folder.c_str(), deckid);
DeckMetaData *aiMeta = DeckManager::GetInstance()->getDeckMetaDataByFilename( deckFile, true);
avatarFilename = aiMeta->getAvatarFilename();
sprintf(deckFileSmall, "ai_baka_deck%i", deckid);
int deckSetting = EASY;
if ( opponent )
@@ -337,7 +335,9 @@ AIPlayer * AIPlayerFactory::createAIPlayerTest(MTGAllCards * collection, Player
}
// AIPlayerBaka will delete MTGDeck when it's time
AIPlayerBakaB * baka = NEW AIPlayerBakaB(deckFile, deckFileSmall, avatarFilename, NEW MTGDeck(deckFile, collection,0, deckSetting));
AIPlayerBaka * baka = opponent ?
NEW AIPlayerBakaB(deckFile, deckFileSmall, avatarFilename, NEW MTGDeck(deckFile, collection,0, deckSetting)) :
NEW AIPlayerBaka(deckFile, deckFileSmall, avatarFilename, NEW MTGDeck(deckFile, collection,0, deckSetting));
baka->deckId = deckid;
return baka;
}

View File

@@ -1754,10 +1754,8 @@ int AIPlayerBaka::computeActions()
return 0;
}
Interruptible * action = g->mLayers->stackLayer()->getAt(-1);
Spell * spell = (Spell *) action;
Player * lastStackActionController = NULL;
if(spell && spell->type == ACTION_SPELL)
lastStackActionController = spell->source->controller();
Spell * spell = dynamic_cast<Spell *>(action);
Player * lastStackActionController = spell ? spell->source->controller() : NULL;
if (g->isInterrupting == this
&& this == currentP
//and i am the currentlyActivePlayer

View File

@@ -243,7 +243,7 @@ Interruptible(id), tc(tc), cost(_cost), payResult(payResult)
int Spell::computeX(MTGCardInstance * card)
{
ManaCost * c = NEW ManaCost(cost->Diff(card->getManaCost()));
ManaCost * c = cost->Diff(card->getManaCost());
int x = c->getCost(Constants::MTG_NB_COLORS);
delete c;
return x;
@@ -763,7 +763,7 @@ int ActionStack::count(int type, int state, int display)
return result;
}
int ActionStack::getActionElementFromCard(MTGCardInstance * card)
Interruptible * ActionStack::getActionElementFromCard(MTGCardInstance * card)
{
if(!card)
@@ -773,10 +773,10 @@ int ActionStack::getActionElementFromCard(MTGCardInstance * card)
Interruptible * current = (Interruptible *) mObjects[i];
if (current->source == card)
{
return i;
return current;
}
}
return 0;
return NULL;
}
Interruptible * ActionStack::getNext(Interruptible * previous, int type, int state, int display)

View File

@@ -416,7 +416,7 @@ ACounterShroud::~ACounterShroud()
SAFE_DELETE(counter);
}
//sheild a card from a certain type of counter.
//shield a card from a certain type of counter.
ACounterTracker::ACounterTracker(int id, MTGCardInstance * source, MTGCardInstance * target, string scounter) :
MTGAbility(id, source, target),scounter(scounter)
{
@@ -428,7 +428,7 @@ int ACounterTracker::addToGame()
MTGCardInstance * _target = (MTGCardInstance*)target;
Counter * counter = NULL;
AbilityFactory af;
counter = af.parseCounter(scounter, _target, (Spell*)source);
counter = af.parseCounter(scounter, _target, NULL); //(Spell*)source);
if (!counter)
{
return 0;
@@ -455,7 +455,7 @@ int ACounterTracker::destroy()
MTGCardInstance * _target = (MTGCardInstance*)target;
Counter * counter = NULL;
AbilityFactory af;
counter = af.parseCounter(scounter, _target, (Spell*)source);
counter = af.parseCounter(scounter, _target, NULL); //(Spell*)source);
if (!counter)
{
return 0;
@@ -665,12 +665,12 @@ int AAFizzler::resolve()
if(!target && source->target)
{
//ai is casting a spell from it's hand to fizzle.
target = stack->getAt(stack->getActionElementFromCard(source->target));
target = stack->getActionElementFromCard(source->target);
}
else if(target->typeAsTarget() == TARGET_CARD)
{
//ai targeted using an ability on a card to fizzle.
target = stack->getAt(stack->getActionElementFromCard((MTGCardInstance*)target));
target = stack->getActionElementFromCard((MTGCardInstance*)target);
}
Spell * sTarget = (Spell *) target;
MTGCardInstance* sCard = (MTGCardInstance*)sTarget->source;

View File

@@ -48,8 +48,13 @@ void Credits::compute(Player * _p1, Player * _p2, GameApp * _app)
GameObserver * g = GameObserver::GetInstance();
if (!g->turn)
return;
//no credits when the AI plays :)
if (p1->isAI())
return;
PlayerData * playerdata = NEW PlayerData(MTGCollection());
if (!p1->isAI() && p2->isAI() && p1 != g->gameOver)
if (p2->isAI() && p1 != g->gameOver)
{
gameLength = time(0) - g->startedAt;
value = 400;

View File

@@ -238,8 +238,8 @@ void GameStateDuel::loadPlayer(int playerId, int decknb, bool isAI, bool isNetwo
Player * opponent = NULL;
if (playerId == 1) opponent = mPlayers[0];
#ifdef AI_CHANGE_TESTING
if (mParent->players[1] == PLAYER_TYPE_CPU_TEST && playerId == 1)
mPlayers[playerId] = playerCreator.createAIPlayerTest(MTGCollection(), opponent);
if (mParent->players[0] == PLAYER_TYPE_CPU_TEST)
mPlayers[playerId] = playerCreator.createAIPlayerTest(MTGCollection(), opponent, playerId == 0 ? "ai/bakaA/" : "ai/bakaB/");
else
#endif
{
@@ -646,6 +646,12 @@ void GameStateDuel::Render()
{
r->ClearScreen(ARGB(255,0,0,0));
char buf[4096];
mFont->SetColor(ARGB(255,255,255,255));
int elapsedTime = (testSuite->endTime - testSuite->startTime);
sprintf(buf, "Time to run the tests: %is", elapsedTime/1000);
mFont->DrawString(buf,0,SCREEN_HEIGHT/2 - 20);
int nbFailed = testSuite->nbFailed;
int nbTests = testSuite->nbTests;
if (!nbFailed)
@@ -656,7 +662,7 @@ void GameStateDuel::Render()
{
sprintf(buf, "%i tests out of %i FAILED!", nbFailed, nbTests);
}
mFont->SetColor(ARGB(255,255,255,255));
mFont->DrawString(buf,0,SCREEN_HEIGHT/2);
nbFailed = testSuite->nbAIFailed;
nbTests = testSuite->nbAITests;

View File

@@ -590,39 +590,75 @@ MTGCard * MTGAllCards::_(int index)
return getCardById(ids[index]);
}
MTGCard * MTGAllCards::getCardByName(string name)
#ifdef TESTSUITE
void MTGAllCards::prefetchCardNameCache()
{
if (!name.size()) return NULL;
if (name[0] == '#') return NULL;
map<int, MTGCard *>::iterator it;
for (it = collection.begin(); it != collection.end(); it++)
{
MTGCard * c = it->second;
map<string, MTGCard * >::iterator cached = mtgCardByNameCache.end();
if (mtgCardByNameCache.size() > 0)
cached = mtgCardByNameCache.find(name);
//Name only
string cardName = c->data->name;
std::transform(cardName.begin(), cardName.end(), cardName.begin(), ::tolower);
mtgCardByNameCache[cardName] = c;
//Name + set
int setId = c->setId;
MTGSetInfo* setInfo = setlist.getInfo(setId);
if (setInfo)
{
string setName = setInfo->getName();
std::transform(setName.begin(), setName.end(), setName.begin(), ::tolower);
cardName = cardName + " (" + setName + ")";
mtgCardByNameCache[cardName] = c;
}
// id
std::stringstream out;
out << c->getMTGId();
mtgCardByNameCache[out.str()] = c;
}
}
#endif
MTGCard * MTGAllCards::getCardByName(string nameDescriptor)
{
if (!nameDescriptor.size()) return NULL;
if (nameDescriptor[0] == '#') return NULL;
std::transform(nameDescriptor.begin(), nameDescriptor.end(), nameDescriptor.begin(), ::tolower);
map<string, MTGCard * >::iterator cached = mtgCardByNameCache.find(nameDescriptor);
if (cached!= mtgCardByNameCache.end())
{
return cached->second;
}
int cardnb = atoi(name.c_str());
int cardnb = atoi(nameDescriptor.c_str());
if (cardnb)
{
MTGCard * result = getCardById(cardnb);
mtgCardByNameCache[name] = result;
mtgCardByNameCache[nameDescriptor] = result;
return result;
}
std::transform(name.begin(), name.end(), name.begin(), ::tolower);
int setId = -1;
size_t found = name.find(" (");
size_t found = nameDescriptor.find(" (");
string name = nameDescriptor;
if (found != string::npos)
{
size_t end = name.find(")");
string setName = name.substr(found + 2, end - found - 2);
size_t end = nameDescriptor.find(")");
string setName = nameDescriptor.substr(found + 2, end - found - 2);
trim(setName);
name = name.substr(0, found);
name = nameDescriptor.substr(0, found);
trim(name);
setId = setlist[setName];
//Reconstruct a clean string "name (set)" for cache consistency
nameDescriptor = name + " (" + setName + ")";
}
map<int, MTGCard *>::iterator it;
for (it = collection.begin(); it != collection.end(); it++)
@@ -632,12 +668,12 @@ MTGCard * MTGAllCards::getCardByName(string name)
string cardName = c->data->name;
std::transform(cardName.begin(), cardName.end(), cardName.begin(), ::tolower);
if (cardName.compare(name) == 0) {
mtgCardByNameCache[name] = c;
mtgCardByNameCache[nameDescriptor] = c;
return c;
}
}
mtgCardByNameCache[name] = NULL;
mtgCardByNameCache[nameDescriptor] = NULL;
return NULL;
}

View File

@@ -180,11 +180,19 @@ TargetChooser * TargetChooserFactory::createTargetChooser(string s, MTGCardInsta
{
targetMin = true;//if upto: is not found, then we need to have a minimum of the amount....
}
WParsedInt * howmuch = NEW WParsedInt(howmany, NULL, card);
howmany.find("anyamount") != string::npos?maxtargets = TargetChooser::UNLITMITED_TARGETS:maxtargets = howmuch->getValue();
if(howmany.find("anyamount") != string::npos)
if (howmany.find("anyamount") != string::npos)
{
maxtargets = TargetChooser::UNLITMITED_TARGETS;
targetMin = false;
delete howmuch;
}
else
{
WParsedInt * howmuch = NEW WParsedInt(howmany, NULL, card);
maxtargets = howmuch->getValue();
delete howmuch;
}
s1 = s1.substr(end + 1);
}
}

View File

@@ -566,7 +566,8 @@ TestSuite::TestSuite(const char * filename)
seed = 0;
forceAbility = false;
aiMaxCalls = -1;
startTime = JGEGetTime();
endTime = startTime;
std::string contents;
if (JFileSystem::GetInstance()->readIntoString(filename, contents))
{
@@ -585,6 +586,11 @@ TestSuite::TestSuite(const char * filename)
}
}
//If more than 1 test, prefecth names to make the suite run faster
if (nbfiles > 1)
MTGCollection()->prefetchCardNameCache();
ofstream file2;
if (JFileSystem::GetInstance()->openForWrite(file2, "/test/results.html"))
{
@@ -603,6 +609,7 @@ TestSuite::TestSuite(const char * filename)
int TestSuite::loadNext()
{
endTime = JGEGetTime();
summoningSickness = 0;
seed = 0;
aiMaxCalls = -1;
@@ -613,8 +620,6 @@ int TestSuite::loadNext()
return loadNext();
else
cout << "Starting test : " << files[currentfile - 1] << endl;
//load(files[currentfile].c_str());
//currentfile++;
return currentfile;
}