- 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:
@@ -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]
|
||||
|
||||
@@ -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
|
||||
};
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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()
|
||||
{
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user