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:
@@ -182,12 +182,15 @@ 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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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; };
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
@@ -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
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
Reference in New Issue
Block a user