- Modified DeckManager class to not use a global instance anymore when used within the game engine

- Modified DuelLayers to not use a global MTGPhaseGame instance anymore
- Moved the reset of currentActionCard out of the ActionLayer render function : that fixes the remaing problematic tests in the multithreaded testsuite
- Added a method in ActionLayer converting a card ability into a menu index
- Used this new method in the game observer to log correctly AI ability actions
- Added a DumpAssert method in the game observer, it can be used to dump the game and assert in order to easy crash reproduction
- Cleaned up TargetList properties access
- Added an optimisation in GuiMana to not compute update code if the rendering is not used (multi-threaded mode)
- Added a deadlock detection in the test AI vs AI multithreaded mode
- Fixed minor bugs in test AI vs AI multithreaded mode
- Added a games/second counter in the test AI vs AI rendering
This commit is contained in:
Xawotihs
2011-11-23 19:11:48 +00:00
parent dca6d3ad38
commit 29132073de
20 changed files with 153 additions and 73 deletions
+4 -4
View File
@@ -236,7 +236,7 @@ AIPlayer * AIPlayerFactory::createAIPlayer(GameObserver *observer, MTGAllCards *
deckid = 1 + WRand() % (nbdecks);
}
sprintf(deckFile, "ai/baka/deck%i.txt", deckid);
DeckMetaData *aiMeta = DeckManager::GetInstance()->getDeckMetaDataByFilename( deckFile, true);
DeckMetaData *aiMeta = observer->getDeckManager()->getDeckMetaDataByFilename( deckFile, true);
avatarFilename = aiMeta->getAvatarFilename();
sprintf(deckFileSmall, "ai_baka_deck%i", deckid);
}
@@ -245,7 +245,7 @@ AIPlayer * AIPlayerFactory::createAIPlayer(GameObserver *observer, MTGAllCards *
if ( opponent )
{
bool isOpponentAI = opponent->isAI() == 1;
DeckMetaData *meta = DeckManager::GetInstance()->getDeckMetaDataByFilename( opponent->deckFile, isOpponentAI);
DeckMetaData *meta = observer->getDeckManager()->getDeckMetaDataByFilename( opponent->deckFile, isOpponentAI);
if ( meta && meta->getVictoryPercentage() >= 65)
deckSetting = HARD;
}
@@ -320,7 +320,7 @@ AIPlayer * AIPlayerFactory::createAIPlayerTest(GameObserver *observer, MTGAllCar
deckid = 1 + WRand() % (nbdecks);
sprintf(deckFile, "%sdeck%i.txt", folder.c_str(), deckid);
DeckMetaData *aiMeta = DeckManager::GetInstance()->getDeckMetaDataByFilename( deckFile, true);
DeckMetaData *aiMeta = observer->getDeckManager()->getDeckMetaDataByFilename( deckFile, true);
avatarFilename = aiMeta->getAvatarFilename();
sprintf(deckFileSmall, "ai_baka_deck%i", deckid);
@@ -329,7 +329,7 @@ AIPlayer * AIPlayerFactory::createAIPlayerTest(GameObserver *observer, MTGAllCar
if ( opponent )
{
bool isOpponentAI = opponent->isAI() == 1;
DeckMetaData *meta = DeckManager::GetInstance()->getDeckMetaDataByFilename( opponent->deckFile, isOpponentAI);
DeckMetaData *meta = observer->getDeckManager()->getDeckMetaDataByFilename( opponent->deckFile, isOpponentAI);
if ( meta->getVictoryPercentage() >= 65)
deckSetting = HARD;
}
+1 -1
View File
@@ -1188,7 +1188,7 @@ int AIPlayerBaka::createAbilityTargets(MTGAbility * a, MTGCardInstance * c, Rank
vector<Targetable*>realTargets;
if(a->getActionTc()->maxtargets != 1)
{
if(a->getActionTc()->targets.size() && a->getActionTc()->attemptsToFill > 4)
if(a->getActionTc()->getNbTargets() && a->getActionTc()->attemptsToFill > 4)
{
a->getActionTc()->done = true;
return 0;
+28 -2
View File
@@ -178,7 +178,7 @@ void ActionLayer::Update(float dt)
without this, the game locks into a freeze state while you try to select the targets and dont have enough to
fill the maxtargets list.
*/
if(int(ae->getActionTc()->targets.size()) == countTargets-1)
if(int(ae->getActionTc()->getNbTargets()) == countTargets-1)
ae->getActionTc()->done = true;
}
}
@@ -192,7 +192,7 @@ void ActionLayer::Render()
abilitiesMenu->Render();
return;
}
currentActionCard = NULL;
for (size_t i = 0; i < mObjects.size(); i++)
{
if (mObjects[i] != NULL)
@@ -290,6 +290,29 @@ int ActionLayer::reactToTargetClick(Targetable * card)
return result;
}
bool ActionLayer::getMenuIdFromCardAbility(MTGCardInstance *card, MTGAbility *ability, int& menuId)
{
int ctr = 0;
for (size_t i = 0; i < mObjects.size(); i++)
{
ActionElement * currentAction = (ActionElement *) mObjects[i];
if (currentAction->isReactingToClick(card))
{
if(currentAction == ability) {
menuId = ctr;
}
ctr++;
}
}
// ability not working with card or only one ability possible
if(ctr == 0 || ctr == 1)
return false;
else
// several abilities working with card, menuId set
return true;
}
//TODO Simplify with only object !!!
int ActionLayer::isReactingToClick(MTGCardInstance * card)
{
@@ -422,11 +445,13 @@ void ActionLayer::ButtonPressed(int controllerid, int controlid)
ActionElement * currentAction = (ActionElement *) mObjects[controlid];
currentAction->reactToTargetClick(menuObject);
menuObject = 0;
currentActionCard = NULL;
}
else if (controlid == kCancelMenuID)
{
observer->mLayers->stackLayer()->endOfInterruption(false);
menuObject = 0;
currentActionCard = NULL;
}
else
{
@@ -462,6 +487,7 @@ void ActionLayer::ButtonPressedOnMultipleChoice(int choice)
observer->mLayers->stackLayer()->endOfInterruption(false);
}
menuObject = 0;
currentActionCard = NULL;
}
ActionLayer::ActionLayer(GameObserver *observer)
+7 -3
View File
@@ -216,8 +216,12 @@ Interruptible(observer, id), tc(tc), cost(_cost), payResult(payResult)
_source->backupTargets.clear();
if (tc)
{
for(size_t i = 0;i < tc->targets.size();i++)
_source->backupTargets.push_back(tc->targets[i]);
Targetable* t = NULL;
for(size_t i = 0;i < tc->getNbTargets();i++)
{
t = tc->getNextTarget(t);
_source->backupTargets.push_back(t);
}
}
// fill information on how the card came into this zone. Right now the quickest way is to do it here, based on how the mana was paid...
@@ -356,7 +360,7 @@ int Spell::getNbTargets()
{
if (!tc)
return 0;
return (int) (tc->targets.size());
return (int) (tc->getNbTargets());
}
void Spell::Render()
+6 -9
View File
@@ -206,7 +206,6 @@ StatsWrapper * DeckManager::getExtendedDeckStats( DeckMetaData *selectedDeck, MT
DeckManager * DeckManager::mInstance = NULL;
bool DeckManager::instanceFlag = false;
void DeckManager::EndInstance()
{
@@ -215,10 +214,9 @@ void DeckManager::EndInstance()
DeckManager* DeckManager::GetInstance()
{
if (!instanceFlag)
if (!mInstance)
{
mInstance = NEW DeckManager();
instanceFlag = true;
}
return mInstance;
@@ -230,7 +228,7 @@ int DeckManager::getDifficultyRating(Player *statsPlayer, Player *player)
{
if(player->deckFile != "")
{
DeckMetaData *meta = DeckManager::GetInstance()->getDeckMetaDataByFilename(player->deckFile, (player->isAI() == 1) );
DeckMetaData *meta = getDeckMetaDataByFilename(player->deckFile, (player->isAI() == 1) );
return meta->getDifficulty();
}
else
@@ -239,23 +237,22 @@ int DeckManager::getDifficultyRating(Player *statsPlayer, Player *player)
DeckManager::~DeckManager()
{
instanceFlag = false;
map<string, StatsWrapper *>::iterator it;
vector<DeckMetaData *>::iterator metaDataIter;
for (it = mInstance->aiDeckStatsMap.begin(); it != mInstance->aiDeckStatsMap.end(); it++){
for (it = aiDeckStatsMap.begin(); it != aiDeckStatsMap.end(); it++){
SAFE_DELETE(it->second);
}
for (it = mInstance->playerDeckStatsMap.begin(); it != mInstance->playerDeckStatsMap.end(); it++){
for (it = playerDeckStatsMap.begin(); it != playerDeckStatsMap.end(); it++){
SAFE_DELETE(it->second);
}
for( metaDataIter = mInstance->aiDeckOrderList.begin(); metaDataIter != mInstance->aiDeckOrderList.end(); ++metaDataIter)
for( metaDataIter = aiDeckOrderList.begin(); metaDataIter != aiDeckOrderList.end(); ++metaDataIter)
{
SAFE_DELETE( *metaDataIter );
}
for( metaDataIter = mInstance->playerDeckOrderList.begin(); metaDataIter != mInstance->playerDeckOrderList.end(); ++metaDataIter)
for( metaDataIter = playerDeckOrderList.begin(); metaDataIter != playerDeckOrderList.end(); ++metaDataIter)
{
SAFE_DELETE( *metaDataIter );
}
+1 -1
View File
@@ -19,7 +19,7 @@ void DuelLayers::init(GameObserver* go)
mCardSelector = NEW CardSelector(go, this);
//1 Action Layer
action = NEW ActionLayer(go);
action->Add(NEW MTGGamePhase(go, action->getMaxId())); //Phases handler
action->Add(phaseHandler = NEW MTGGamePhase(go, action->getMaxId())); //Phases handler
action->Add(NEW OtherAbilitiesEventReceiver(go, -1)); //autohand, etc... handler
//Other display elements
action->Add(NEW HUDDisplay(go, -1));
+41 -14
View File
@@ -13,6 +13,7 @@
#include "AIPlayerBaka.h"
#include "MTGRules.h"
#include "Trash.h"
#include "DeckManager.h"
#ifdef TESTSUITE
#include "TestSuiteAI.h"
#endif
@@ -69,6 +70,8 @@ GameObserver::~GameObserver()
ExtraRules = 0;
LOG("==GameObserver Destroyed==");
SAFE_DELETE(mTrash);
SAFE_DELETE(mDeckManager);
}
GameObserver::GameObserver(WResourceManager *output, JGE* input)
@@ -99,6 +102,7 @@ GameObserver::GameObserver(WResourceManager *output, JGE* input)
mLoading = false;
mLayers = NULL;
mTrash = new Trash();
mDeckManager = new DeckManager();
}
int GameObserver::getCurrentGamePhase()
@@ -475,6 +479,16 @@ bool GameObserver::operator==(const GameObserver& aGame)
return (error == 0);
}
void GameObserver::dumpAssert(bool val)
{
if(!val)
{
cerr << *this << endl;
assert(0);
}
}
void GameObserver::Update(float dt)
{
/*******************/
@@ -489,7 +503,7 @@ void GameObserver::Update(float dt)
oldGame = new GameObserver();
oldGame->mRules = mRules;
oldGame->load(stream.str());
assert(*this == *oldGame);
DumpAssert(*this == *oldGame);
}
#endif // ACTION_LOGGING_TESTING
@@ -526,7 +540,7 @@ void GameObserver::Update(float dt)
//Handles game state based effects
void GameObserver::gameStateBasedEffects()
{
if(getCurrentTargetChooser() && int(getCurrentTargetChooser()->targets.size()) == getCurrentTargetChooser()->maxtargets)
if(getCurrentTargetChooser() && int(getCurrentTargetChooser()->getNbTargets()) == getCurrentTargetChooser()->maxtargets)
getCurrentTargetChooser()->done = true;
if (mLayers->stackLayer()->count(0, NOT_RESOLVED) != 0)
return;
@@ -993,7 +1007,7 @@ void GameObserver::ButtonPressed(PlayGuiObject * target)
}
else if (dynamic_cast<GuiPhaseBar*>(target))
{
MTGGamePhase::GetInstance()->NextGamePhase();
mLayers->getPhaseHandler()->NextGamePhase();
}
}
@@ -1042,9 +1056,20 @@ bool GameObserver::WaitForExtraPayment(MTGCardInstance * card)
int GameObserver::cardClick(MTGCardInstance * card, MTGAbility *ability)
{
MTGGameZone* zone = card->currentZone;
size_t index = card->currentZone->getIndex(card);
size_t index = 0;
if(zone)
index = zone->getIndex(card);
int choice;
bool logChoice = mLayers->actionLayer()->getMenuIdFromCardAbility(card, ability, choice);
int result = ability->reactToClick(card);
logAction(card, zone, index, result);
if(logChoice) {
stringstream stream;
stream << "choice " << choice;
logAction(currentActionPlayer, stream.str());
}
return result;
}
@@ -1089,7 +1114,7 @@ int GameObserver::cardClick(MTGCardInstance * card, Targetable * object)
if (card == cardWaitingForTargets)
{
int _result = targetChooser->ForceTargetListReady();
if(targetChooser->targetMin && int(targetChooser->targets.size()) < targetChooser->maxtargets)
if(targetChooser->targetMin && int(targetChooser->getNbTargets()) < targetChooser->maxtargets)
_result = 0;
if (_result)
{
@@ -1548,7 +1573,7 @@ bool GameObserver::processActions(bool undo)
size_t begin = s.find("[")+1;
size_t size = s.find("]")-begin;
size_t index = atoi(s.substr(begin, size).c_str());
assert(index < zone->cards.size());
dumpAssert(index < zone->cards.size());
cardClick(zone->cards[index], zone->cards[index]);
} else if (s.find("yes") != string::npos) {
mLayers->stackLayer()->setIsInterrupting(p);
@@ -1571,20 +1596,20 @@ bool GameObserver::processActions(bool undo)
// that would allow the AI to use it as well.
shuffleLibrary(p);
} else {
assert(0);
dumpAssert(0);
}
size_t nb = actionsList.size();
for (int i = 0; i<3; i++)
for (int i = 0; i<5; i++)
{
// let's fake an update
Update(counter);
counter += 1.000f;
}
assert(actionsList.back() == *loadingite);
assert(nb == actionsList.size());
assert(cmdIndex == (actionsList.size()-1));
dumpAssert(actionsList.back() == *loadingite);
dumpAssert(nb == actionsList.size());
dumpAssert(cmdIndex == (actionsList.size()-1));
}
mLoading = false;
@@ -1618,7 +1643,7 @@ void GameObserver::logAction(const string& s)
if(mLoading)
{
string toCheck = *loadingite;
assert(toCheck == s);
dumpAssert(toCheck == s);
}
actionsList.push_back(s);
};
@@ -1645,8 +1670,10 @@ void GameObserver::createPlayer(const string& playerMode)
{
case Player::MODE_AI:
AIPlayerFactory playerCreator;
// FIXME: gonna break in AI vs AI mode
players.push_back(playerCreator.createAIPlayer(this, MTGCollection(), players[0]));
if(players.size())
players.push_back(playerCreator.createAIPlayer(this, MTGCollection(), players[0]));
else
players.push_back(playerCreator.createAIPlayer(this, MTGCollection(), 0));
break;
case Player::MODE_HUMAN:
players.push_back(new HumanPlayer(this, "", ""));
+37 -16
View File
@@ -284,12 +284,28 @@ void GameStateDuel::ThreadProc(void* inParam)
while(instance->mGamePhase != DUEL_STATE_BACK_TO_MAIN_MENU)
{
GameObserver observer;
int oldTurn = -1;
int oldPhase = -1;
int stagnationCounter = -1;
observer.loadPlayer(0, PLAYER_TYPE_TESTSUITE);
observer.loadPlayer(1, PLAYER_TYPE_TESTSUITE);
observer.startGame(instance->mParent->gameType, instance->mParent->rules);
while(!observer.gameOver)
while(!observer.gameOver) {
if(observer.turn == oldTurn && observer.currentGamePhase == oldPhase) {
stagnationCounter++;
} else {
stagnationCounter = 0;
oldTurn = observer.turn;
oldPhase = observer.currentGamePhase;
}
if(stagnationCounter >= 1000)
{
observer.dumpAssert(false);
}
observer.Update(counter++);
}
instance->handleResults(&observer);
}
@@ -441,22 +457,25 @@ void GameStateDuel::Update(float dt)
else
#endif
#ifdef AI_CHANGE_TESTING
if (mParent->players[0] == PLAYER_TYPE_CPU_TEST && mParent->players[1] == PLAYER_TYPE_CPU_TEST)
{
handleResults(game);
End();
Start();
}
if(mWorkerThread.empty())
{ // "I don't like to wait" mode
size_t thread_count = 1;
#ifdef QT_CONFIG
thread_count = QThread::idealThreadCount();
#endif
for(size_t i = 0; i < (thread_count-1); i++)
mWorkerThread.push_back(boost::thread(ThreadProc, this));
}
if (mParent->players[0] == PLAYER_TYPE_CPU_TEST && mParent->players[1] == PLAYER_TYPE_CPU_TEST)
{
handleResults(game);
End();
Start();
}
if(mWorkerThread.empty())
{ // "I don't like to wait" mode
size_t thread_count = 1;
startTime = JGEGetTime();
#ifdef QT_CONFIG
thread_count = QThread::idealThreadCount();
#endif
for(size_t i = 0; i < (thread_count-1); i++)
mWorkerThread.push_back(boost::thread(ThreadProc, this));
}
}
#endif
if (mParent->players[0] == PLAYER_TYPE_CPU && mParent->players[1] == PLAYER_TYPE_CPU)
{
@@ -566,6 +585,7 @@ void GameStateDuel::Render()
if (game && totalTestGames)
{
char buf[4096];
int currentTime = JGEGetTime();
if (totalTestGames < 2.5 * totalAIDecks)
{
@@ -580,7 +600,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", testPlayer2Victories, totalTestGames);
sprintf(buf, "Victories Player 2/total Games: %i/%i - Games/second: %f",
testPlayer2Victories, totalTestGames, (float)(1000*totalTestGames)/(currentTime - startTime));
mFont->DrawString(buf,0,SCREEN_HEIGHT/2);
}
#endif
+1
View File
@@ -303,6 +303,7 @@ bool remove_dead(ManaIcon* m)
void GuiMana::Update(float dt)
{
if(observer->getResourceManager())
{
float shift = 0;
for (vector<ManaIcon*>::iterator it = manas.begin(); it != manas.end(); ++it)
+4 -4
View File
@@ -2834,7 +2834,7 @@ int AbilityFactory::magicText(int id, Spell * spell, MTGCardInstance * card, int
a->canBeInterrupted = false;
}
bool moreThanOneTarget = spell && spell->tc && spell->tc->targets.size() > 1;
bool moreThanOneTarget = spell && spell->tc && spell->tc->getNbTargets() > 1;
if(moreThanOneTarget)
a->target = spell->getNextTarget();
@@ -2850,7 +2850,7 @@ int AbilityFactory::magicText(int id, Spell * spell, MTGCardInstance * card, int
}
else
{
if(!aMay || (aMay && a->target == spell->tc->targets[0]))
if(!aMay || (aMay && a->target == spell->tc->getNextTarget(0)))
{
MTGAbility * mClone = a->clone();
mClone->addToGame();
@@ -3990,7 +3990,7 @@ int TargetAbility::resolve()
t = tc->getNextTarget(t);
ability->target = t;
}
tc->targets.clear();
tc->initTargets();
return 1;
}
else
@@ -4002,7 +4002,7 @@ int TargetAbility::resolve()
t = tc->getNextTarget(t);
ability->target = t;
}
tc->targets.clear();
tc->initTargets();
return 1;
}
}
-3
View File
@@ -3,8 +3,6 @@
#include "MTGGamePhase.h"
#include "GuiPhaseBar.h"
MTGGamePhase* MTGGamePhase::instance = 0;
MTGGamePhase::MTGGamePhase(GameObserver* g, int id) :
ActionElement(id), observer(g)
{
@@ -12,7 +10,6 @@ MTGGamePhase::MTGGamePhase(GameObserver* g, int id) :
currentState = -1;
mFont = WResourceManager::Instance()->GetWFont(Fonts::MAIN_FONT);
mFont->SetBase(0); // using 2nd font
instance = this;
}
void MTGGamePhase::Update(float dt)
+1 -1
View File
@@ -268,7 +268,7 @@ bool Player::parseLine(const string& s)
if ( opponent() )
{
bool isOpponentAI = opponent()->isAI() == 1;
DeckMetaData *meta = DeckManager::GetInstance()->getDeckMetaDataByFilename( opponent()->deckFile, isOpponentAI);
DeckMetaData *meta = observer->getDeckManager()->getDeckMetaDataByFilename( opponent()->deckFile, isOpponentAI);
if ( meta && meta->getVictoryPercentage() >= 65)
deckSetting = HARD;
}
+1 -1
View File
@@ -163,7 +163,7 @@ void Rules::addExtraRules(GameObserver* g)
#endif //NETWORK_SUPPORT
)//keep this out of momir and other game modes.
{
difficultyRating = DeckManager::getDifficultyRating(g->players[0], g->players[1]);
difficultyRating = g->getDeckManager()->getDifficultyRating(g->players[0], g->players[1]);
}
if (a)