Added action logging during attack/block of the AI player. To do that, I wrapped some of the direct access from the player to the action layer into the game observer.

First version where I managed to finish a normal game while undoing several actions until the end. There are still some problems in direct damage spells and interruption management. I added several assert in the code to catch them.
This commit is contained in:
Xawotihs
2011-10-16 22:16:47 +00:00
parent 8554076f3c
commit 84a074aede
5 changed files with 198 additions and 124 deletions
+5 -2
View File
@@ -183,11 +183,14 @@ bool JFileSystem::DirExists(const string& strDirname)
bool JFileSystem::FileExists(const string& strFilename) bool JFileSystem::FileExists(const string& strFilename)
{ {
bool result = false;
if(strFilename != "")
{
izfstream temp; izfstream temp;
bool result = openForRead(temp, strFilename); result = openForRead(temp, strFilename);
if (temp) if (temp)
temp.close(); temp.close();
}
return result; return result;
} }
+8 -4
View File
@@ -26,7 +26,11 @@ class GameObserver{
protected: protected:
MTGCardInstance * cardWaitingForTargets; MTGCardInstance * cardWaitingForTargets;
queue<WEvent *> eventsQueue; queue<WEvent *> eventsQueue;
// used when we're running to log actions
list<string> actionsList; list<string> actionsList;
// used when we're loading to know what to load
list<string> loadingList;
list<string>::iterator loadingite;
int untap(MTGCardInstance * card); int untap(MTGCardInstance * card);
bool WaitForExtraPayment(MTGCardInstance* card); bool WaitForExtraPayment(MTGCardInstance* card);
@@ -34,9 +38,7 @@ class GameObserver{
void cleanup(); void cleanup();
string startupGameSerialized; string startupGameSerialized;
bool parseLine(const string& s); bool parseLine(const string& s);
void logAction(const string& s) { void logAction(const string& s);
actionsList.push_back(s);
};
bool processActions(bool undo); bool processActions(bool undo);
friend ostream& operator<<(ostream&, GameObserver&); friend ostream& operator<<(ostream&, GameObserver&);
bool load(const string& s, bool undo); bool load(const string& s, bool undo);
@@ -65,6 +67,8 @@ class GameObserver{
TargetChooser * getCurrentTargetChooser(); TargetChooser * getCurrentTargetChooser();
void stackObjectClicked(Interruptible * action); void stackObjectClicked(Interruptible * action);
int cardClick(MTGCardInstance * card, MTGAbility *ability);
int cardClick(MTGCardInstance * card, int abilityType);
int cardClick(MTGCardInstance * card,Targetable * _object = NULL ); int cardClick(MTGCardInstance * card,Targetable * _object = NULL );
int getCurrentGamePhase(); int getCurrentGamePhase();
const char * getCurrentGamePhaseName(); const char * getCurrentGamePhaseName();
@@ -106,7 +110,7 @@ class GameObserver{
void logAction(int playerId, const string& s="") { void logAction(int playerId, const string& s="") {
logAction(players[playerId], s); logAction(players[playerId], s);
}; };
void logAction(MTGCardInstance* card, MTGGameZone* zone = NULL); void logAction(MTGCardInstance* card, MTGGameZone* zone, size_t index, int result);
bool undo(); bool undo();
bool isLoading(){ return mLoading; }; bool isLoading(){ return mLoading; };
}; };
+1 -1
View File
@@ -53,7 +53,7 @@ int AIAction::Act()
} }
if (ability) if (ability)
{ {
g->mLayers->actionLayer()->reactToClick(ability, click); g->cardClick(click, ability);
if (target && !mAbilityTargets.size()) if (target && !mAbilityTargets.size())
{ {
g->cardClick(target); g->cardClick(target);
+6 -8
View File
@@ -1985,10 +1985,9 @@ int AIPlayerBaka::chooseAttackers()
cd.init(); cd.init();
cd.setType("creature"); cd.setType("creature");
MTGCardInstance * card = NULL; MTGCardInstance * card = NULL;
MTGAbility * a = observer->mLayers->actionLayer()->getAbility(MTGAbility::MTG_ATTACK_RULE);
while ((card = cd.nextmatch(game->inPlay, card))) while ((card = cd.nextmatch(game->inPlay, card)))
{ {
observer->mLayers->actionLayer()->reactToClick(a, card); observer->cardClick(card, MTGAbility::MTG_ATTACK_RULE);
} }
} }
return 1; return 1;
@@ -2033,12 +2032,11 @@ int AIPlayerBaka::chooseBlockers()
cd.setType("Creature"); cd.setType("Creature");
cd.unsecureSetTapped(-1); cd.unsecureSetTapped(-1);
card = NULL; card = NULL;
MTGAbility * a = observer->mLayers->actionLayer()->getAbility(MTGAbility::MTG_BLOCK_RULE);
// We first try to block the major threats, those that are marked in the Top 3 of our stats // We first try to block the major threats, those that are marked in the Top 3 of our stats
while ((card = cd.nextmatch(game->inPlay, card))) while ((card = cd.nextmatch(game->inPlay, card)))
{ {
observer->mLayers->actionLayer()->reactToClick(a, card); observer->cardClick(card, MTGAbility::MTG_BLOCK_RULE);
int set = 0; int set = 0;
while (!set) while (!set)
{ {
@@ -2062,7 +2060,7 @@ int AIPlayerBaka::chooseBlockers()
} }
else else
{ {
observer->mLayers->actionLayer()->reactToClick(a, card); observer->cardClick(card, MTGAbility::MTG_BLOCK_RULE);
} }
} }
} }
@@ -2077,7 +2075,7 @@ int AIPlayerBaka::chooseBlockers()
{ {
while (card->defenser) while (card->defenser)
{ {
observer->mLayers->actionLayer()->reactToClick(a, card); observer->cardClick(card, MTGAbility::MTG_BLOCK_RULE);
} }
} }
} }
@@ -2088,7 +2086,7 @@ int AIPlayerBaka::chooseBlockers()
{ {
if (!card->defenser) if (!card->defenser)
{ {
observer->mLayers->actionLayer()->reactToClick(a, card); observer->cardClick(card, MTGAbility::MTG_BLOCK_RULE);
int set = 0; int set = 0;
while (!set) while (!set)
{ {
@@ -2102,7 +2100,7 @@ int AIPlayerBaka::chooseBlockers()
if (opponentsToughness[attacker] <= 0 || (card->toughness <= attacker->power && opponentForce * 2 < life if (opponentsToughness[attacker] <= 0 || (card->toughness <= attacker->power && opponentForce * 2 < life
&& !canFirstStrikeKill(card, attacker)) || attacker->nbOpponents() > 1) && !canFirstStrikeKill(card, attacker)) || attacker->nbOpponents() > 1)
{ {
observer->mLayers->actionLayer()->reactToClick(a, card); observer->cardClick(card, MTGAbility::MTG_BLOCK_RULE);
} }
else else
{ {
+101 -32
View File
@@ -245,6 +245,8 @@ void GameObserver::nextCombatStep()
void GameObserver::userRequestNextGamePhase() void GameObserver::userRequestNextGamePhase()
{ {
stringstream stream;
stream << "next " << currentGamePhase;
if(getCurrentTargetChooser() && getCurrentTargetChooser()->maxtargets == 1000) if(getCurrentTargetChooser() && getCurrentTargetChooser()->maxtargets == 1000)
{ {
getCurrentTargetChooser()->done = true; getCurrentTargetChooser()->done = true;
@@ -278,7 +280,9 @@ void GameObserver::userRequestNextGamePhase()
{ {
nextGamePhase(); nextGamePhase();
} }
logAction(currentPlayer, "next");
stream << " " << currentGamePhase ;
logAction(currentPlayer, stream.str());
} }
int GameObserver::forceShuffleLibraries() int GameObserver::forceShuffleLibraries()
@@ -945,15 +949,46 @@ bool GameObserver::WaitForExtraPayment(MTGCardInstance * card)
return result; return result;
} }
int GameObserver::cardClick(MTGCardInstance * card, MTGAbility *ability)
{
MTGGameZone* zone = card->currentZone;
size_t index = card->currentZone->getIndex(card);
int result = ability->reactToClick(card);
logAction(card, zone, index, result);
return result;
}
int GameObserver::cardClick(MTGCardInstance * card, int abilityType)
{
int result = 0;
MTGAbility * a = mLayers->actionLayer()->getAbility(abilityType);
if(a)
{
result = cardClick(card, a);
}
return result;
}
int GameObserver::cardClick(MTGCardInstance * card, Targetable * object) int GameObserver::cardClick(MTGCardInstance * card, Targetable * object)
{ {
Player * clickedPlayer = NULL; Player * clickedPlayer = NULL;
int toReturn;
MTGGameZone* zone;
size_t index;
MTGCardInstance* backup;
if (!card) { if (!card) {
clickedPlayer = ((Player *) object); clickedPlayer = ((Player *) object);
logAction(clickedPlayer); logAction(clickedPlayer);
} else { } else {
logAction(card); backup = card;
zone = card->currentZone;
index = zone->getIndex(card);
} }
do {
if (targetChooser) if (targetChooser)
{ {
int result; int result;
@@ -986,12 +1021,16 @@ int GameObserver::cardClick(MTGCardInstance * card, Targetable * object)
} }
if (result == TARGET_OK_FULL) if (result == TARGET_OK_FULL)
card = cardWaitingForTargets; card = cardWaitingForTargets;
else else {
return 1; toReturn = 1;
break;
}
} }
if (WaitForExtraPayment(card)) if (WaitForExtraPayment(card)) {
return 1; toReturn = 1;
break;
}
int reaction = 0; int reaction = 0;
@@ -999,7 +1038,8 @@ int GameObserver::cardClick(MTGCardInstance * card, Targetable * object)
{ {
//TODO it is possible at this point that card is NULL. if so, what do we return since card->defenser would result in a crash? //TODO it is possible at this point that card is NULL. if so, what do we return since card->defenser would result in a crash?
card->defenser->raiseBlockerRankOrder(card); card->defenser->raiseBlockerRankOrder(card);
return 1; toReturn = 1;
break;
} }
if (card) if (card)
@@ -1017,22 +1057,29 @@ int GameObserver::cardClick(MTGCardInstance * card, Targetable * object)
if (targetChooser) if (targetChooser)
{ {
MTGAbility * a = mLayers->actionLayer()->getAbility(card->paymenttype); MTGAbility * a = mLayers->actionLayer()->getAbility(card->paymenttype);
return a->reactToClick(card); toReturn = a->reactToClick(card);
break;
} }
reaction = mLayers->actionLayer()->isReactingToClick(card); reaction = mLayers->actionLayer()->isReactingToClick(card);
if (reaction == -1) if (reaction == -1) {
return mLayers->actionLayer()->reactToClick(card); toReturn = mLayers->actionLayer()->reactToClick(card);
break;
}
} }
else else
{//this handles abilities on a menu...not just when card is being played {//this handles abilities on a menu...not just when card is being played
reaction = mLayers->actionLayer()->isReactingToTargetClick(object); reaction = mLayers->actionLayer()->isReactingToTargetClick(object);
if (reaction == -1) if (reaction == -1) {
return mLayers->actionLayer()->reactToTargetClick(object); toReturn = mLayers->actionLayer()->reactToTargetClick(object);
break;
}
} }
if (!card) if (!card) {
return 0; toReturn = 0;
break;
}
//Current player's hand //Current player's hand
if (currentPlayer->game->hand->hasCard(card) && currentGamePhase == Constants::MTG_PHASE_CLEANUP if (currentPlayer->game->hand->hasCard(card) && currentGamePhase == Constants::MTG_PHASE_CLEANUP
@@ -1046,20 +1093,30 @@ int GameObserver::cardClick(MTGCardInstance * card, Targetable * object)
{ {
if (reaction == 1) if (reaction == 1)
{ {
return mLayers->actionLayer()->reactToClick(card); toReturn = mLayers->actionLayer()->reactToClick(card);
break;
} }
else else
{ {
mLayers->actionLayer()->setMenuObject(object); mLayers->actionLayer()->setMenuObject(object);
return 1; toReturn = 1;
break;
} }
} }
else if (card->isTapped() && card->controller() == currentPlayer) else if (card->isTapped() && card->controller() == currentPlayer)
{ {
return untap(card); toReturn = untap(card);
break;
}
} while(0);
if (clickedPlayer) {
logAction(clickedPlayer);
} else {
logAction(backup, zone, index, toReturn);
} }
return 0; return toReturn;
} }
int GameObserver::untap(MTGCardInstance * card) int GameObserver::untap(MTGCardInstance * card)
@@ -1337,22 +1394,21 @@ bool GameObserver::processActions(bool undo)
{ {
bool result = false; bool result = false;
list<string> copyList = actionsList; loadingList = actionsList;
actionsList.clear(); actionsList.clear();
mLoading = true; mLoading = true;
list<string>::iterator ite;
float counter = 0.0f; float counter = 0.0f;
// To handle undo, we'll remove the last P1 action and all P2 actions after. // To handle undo, we'll remove the last P1 action and all P2 actions after.
if(undo && copyList.size()) { if(undo && loadingList.size()) {
while(copyList.back().find("p2") != string::npos) while(loadingList.back().find("p2") != string::npos)
copyList.pop_back(); loadingList.pop_back();
copyList.pop_back(); loadingList.pop_back();
} }
for(ite = copyList.begin(); ite != copyList.end(); ite++) for(loadingite = loadingList.begin(); loadingite != loadingList.end(); loadingite++)
{ {
string s = *ite; string s = *loadingite;
Player* p = players[1]; Player* p = players[1];
if (s.find("p1") != string::npos) if (s.find("p1") != string::npos)
p = players[0]; p = players[0];
@@ -1370,7 +1426,8 @@ bool GameObserver::processActions(bool undo)
if(zone) { if(zone) {
size_t begin = s.find("[")+1; size_t begin = s.find("[")+1;
size_t size = s.find("]")-begin; size_t size = s.find("]")-begin;
int index = atoi(s.substr(begin, size).c_str()); size_t index = atoi(s.substr(begin, size).c_str());
assert(index < zone->cards.size());
cardClick(zone->cards[index], zone->cards[index]); cardClick(zone->cards[index], zone->cards[index]);
} else if (s.find("yes") != string::npos) { } else if (s.find("yes") != string::npos) {
mLayers->stackLayer()->setIsInterrupting(p); mLayers->stackLayer()->setIsInterrupting(p);
@@ -1389,12 +1446,12 @@ bool GameObserver::processActions(bool undo)
assert(0); assert(0);
} }
for (int i = 0; i<10; i++)
{
// let's fake an update // let's fake an update
Update(counter); Update(counter);
counter += 1.000f; counter += 1.000f;
// or two }
Update(counter);
counter += 1.000f;
} }
mLoading = false; mLoading = false;
@@ -1414,14 +1471,26 @@ void GameObserver::logAction(Player* player, const string& s) {
logAction("p2"); logAction("p2");
} }
void GameObserver::logAction(MTGCardInstance* card, MTGGameZone* zone) { void GameObserver::logAction(MTGCardInstance* card, MTGGameZone* zone, size_t index, int result) {
stringstream stream; stringstream stream;
if(zone == NULL) zone = card->currentZone; if(zone == NULL) zone = card->currentZone;
stream << "p" << ((card->controller()==players[0])?"1.":"2.") stream << "p" << ((card->controller()==players[0])?"1.":"2.")
<< zone->getName()<< "[" << zone->getIndex(card) << "]"; << zone->getName()<< "[" << index << "] "
<< result << card->getLCName();
logAction(stream.str()); logAction(stream.str());
} }
void GameObserver::logAction(const string& s)
{
if(mLoading)
{
string toCheck = *loadingite;
if(toCheck != s)
assert(0);
}
actionsList.push_back(s);
};
bool GameObserver::undo() bool GameObserver::undo()
{ {
stringstream stream; stringstream stream;