Some preliminary work for minmax
This commit is contained in:
@@ -12,6 +12,8 @@ using std::vector;
|
||||
class ManaCost;
|
||||
class MTGAbility;
|
||||
|
||||
namespace AI {
|
||||
|
||||
class AIHint
|
||||
{
|
||||
public:
|
||||
@@ -66,4 +68,6 @@ public:
|
||||
~AIHints();
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -3,6 +3,8 @@
|
||||
|
||||
#include "AIPlayerBaka.h"
|
||||
|
||||
namespace AI {
|
||||
|
||||
class AIMomirPlayer: public AIPlayerBaka
|
||||
{
|
||||
public:
|
||||
@@ -14,4 +16,6 @@ public:
|
||||
MTGAbility * getMomirAbility();
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -18,20 +18,44 @@
|
||||
#include "Player.h"
|
||||
#include "config.h"
|
||||
|
||||
#include <vector>
|
||||
#include <queue>
|
||||
using std::queue;
|
||||
using std::vector;
|
||||
|
||||
|
||||
namespace AI {
|
||||
|
||||
class AIStats;
|
||||
class AIPlayer;
|
||||
|
||||
|
||||
class Action
|
||||
{
|
||||
protected:
|
||||
GameObserver* m_pObserver;
|
||||
bool parseLine(const string& s);
|
||||
|
||||
public:
|
||||
Action(GameObserver* g, const string& s) : m_pObserver(g)
|
||||
{
|
||||
parseLine(s);
|
||||
};
|
||||
|
||||
friend ostream& operator<<(ostream&, const Action&);
|
||||
friend istream& operator>>(istream&, Action&);
|
||||
};
|
||||
|
||||
class AIAction
|
||||
{
|
||||
protected:
|
||||
int clickMultiAct(vector<Targetable*>&actionTargets);
|
||||
|
||||
public:
|
||||
AIPlayer * owner;
|
||||
MTGAbility * ability;
|
||||
NestedAbility * nability;
|
||||
Player * player;
|
||||
int id;
|
||||
// int id;
|
||||
MTGCardInstance * click;
|
||||
MTGCardInstance * target; // TODO Improve
|
||||
vector<Targetable*>mAbilityTargets;
|
||||
@@ -60,7 +84,6 @@ public:
|
||||
{
|
||||
};
|
||||
int Act();
|
||||
int clickMultiAct(vector<Targetable*>&actionTargets);
|
||||
};
|
||||
|
||||
|
||||
@@ -77,8 +100,20 @@ protected:
|
||||
int clickMultiTarget(TargetChooser * tc,vector<Targetable*>&potentialTargets);
|
||||
int clickSingleTarget(TargetChooser * tc,vector<Targetable*>&potentialTargets, MTGCardInstance * Choosencard = NULL);
|
||||
RandomGenerator randomGenerator;
|
||||
virtual bool canFirstStrikeKill(MTGCardInstance * card, MTGCardInstance *ennemy);
|
||||
virtual bool canPlay(MTGCardInstance * card);
|
||||
virtual int getCreaturesInfo(Player * player, int neededInfo = INFO_NBCREATURES , int untapMode = 0, int canAttack = 0);
|
||||
|
||||
virtual int createAbilityPotentialsActions(MTGAbility * a, MTGCardInstance * c, vector<AIAction>& actions);
|
||||
|
||||
public:
|
||||
enum {
|
||||
INFO_NBCREATURES,
|
||||
INFO_CREATURESPOWER,
|
||||
INFO_CREATURESRANK,
|
||||
INFO_CREATURESTOUGHNESS,
|
||||
INFO_CREATURESATTACKINGPOWER
|
||||
};
|
||||
|
||||
//These variables are used by TestSuite and Rules.cpp... TODO change that?
|
||||
int agressivity;
|
||||
@@ -89,7 +124,7 @@ public:
|
||||
virtual int receiveEvent(WEvent * event);
|
||||
virtual void Render();
|
||||
|
||||
AIPlayer(GameObserver *observer, string deckFile, string deckFileSmall, MTGDeck * deck = NULL);
|
||||
AIPlayer(GameObserver *observer, string deckFile, string deckFileSmall, string avatarFile, MTGDeck * deck = NULL);
|
||||
virtual ~AIPlayer();
|
||||
|
||||
virtual int chooseTarget(TargetChooser * tc = NULL, Player * forceTarget = NULL, MTGCardInstance * Chosencard = NULL, bool checkonly = false) = 0;
|
||||
@@ -116,5 +151,6 @@ class AIPlayerFactory{
|
||||
#endif
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
@@ -4,6 +4,8 @@
|
||||
#include "AIPlayer.h"
|
||||
#include "AllAbilities.h"
|
||||
|
||||
namespace AI {
|
||||
|
||||
class AIStats;
|
||||
class AIHints;
|
||||
class AIHint;
|
||||
@@ -57,7 +59,7 @@ public:
|
||||
OrderedAIAction* a2Ptr = const_cast<OrderedAIAction*>(&a2);
|
||||
int e1 = a1Ptr->getEfficiency();
|
||||
int e2 = a2Ptr->getEfficiency();
|
||||
if (e1 == e2) return a1Ptr->id < a2Ptr->id;
|
||||
// if (e1 == e2) return a1Ptr->id < a2Ptr->id;
|
||||
return (e1 > e2);
|
||||
}
|
||||
};
|
||||
@@ -72,7 +74,7 @@ class AIPlayerBaka: public AIPlayer{
|
||||
virtual int interruptIfICan();
|
||||
virtual int chooseAttackers();
|
||||
virtual int chooseBlockers();
|
||||
virtual int canFirstStrikeKill(MTGCardInstance * card, MTGCardInstance *ennemy);
|
||||
virtual bool canFirstStrikeKill(MTGCardInstance * card, MTGCardInstance *ennemy);
|
||||
virtual int effectBadOrGood(MTGCardInstance * card, int mode = MODE_PUTINTOPLAY, TargetChooser * tc = NULL);
|
||||
|
||||
|
||||
@@ -105,19 +107,10 @@ class AIPlayerBaka: public AIPlayer{
|
||||
virtual int getEfficiency(OrderedAIAction * action);
|
||||
virtual int getEfficiency(MTGAbility * ability);
|
||||
virtual bool payTheManaCost(ManaCost * cost, MTGCardInstance * card = NULL,vector<MTGAbility*> gotPayment = vector<MTGAbility*>());
|
||||
virtual int getCreaturesInfo(Player * player, int neededInfo = INFO_NBCREATURES , int untapMode = 0, int canAttack = 0);
|
||||
virtual ManaCost * getPotentialMana(MTGCardInstance * card = NULL);
|
||||
virtual int selectAbility();
|
||||
|
||||
public:
|
||||
enum {
|
||||
INFO_NBCREATURES,
|
||||
INFO_CREATURESPOWER,
|
||||
INFO_CREATURESRANK,
|
||||
INFO_CREATURESTOUGHNESS,
|
||||
INFO_CREATURESATTACKINGPOWER
|
||||
};
|
||||
|
||||
vector<MTGAbility*>gotPayments;
|
||||
|
||||
AIPlayerBaka(GameObserver *observer, string deckFile, string deckfileSmall, string avatarFile, MTGDeck * deck = NULL);
|
||||
@@ -137,4 +130,5 @@ class AIPlayerBaka: public AIPlayer{
|
||||
virtual int createAbilityTargets(MTGAbility * a, MTGCardInstance * c, RankingContainer& ranking);
|
||||
};
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -11,6 +11,8 @@ class AIStats;
|
||||
class AIHints;
|
||||
|
||||
|
||||
namespace AI {
|
||||
|
||||
class AIPlayerBakaB: public AIPlayerBaka{
|
||||
protected:
|
||||
int orderBlockers();
|
||||
@@ -18,7 +20,7 @@ protected:
|
||||
int interruptIfICan();
|
||||
int chooseAttackers();
|
||||
int chooseBlockers();
|
||||
int canFirstStrikeKill(MTGCardInstance * card, MTGCardInstance *ennemy);
|
||||
bool canFirstStrikeKill(MTGCardInstance * card, MTGCardInstance *ennemy);
|
||||
int effectBadOrGood(MTGCardInstance * card, int mode = MODE_PUTINTOPLAY, TargetChooser * tc = NULL);
|
||||
|
||||
|
||||
@@ -61,6 +63,8 @@ protected:
|
||||
int createAbilityTargets(MTGAbility * a, MTGCardInstance * c, RankingContainer& ranking);
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
#endif
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
/*
|
||||
* Wagic, The Homebrew ?! is licensed under the BSD license
|
||||
* See LICENSE in the Folder's root
|
||||
* http://wololo.net/wagic/
|
||||
|
||||
AIPlayerMinMax is the MinMax implementation of the AIPlayer interface
|
||||
*/
|
||||
|
||||
#ifndef _IAPLAYER_MINMAX_H
|
||||
#define _IAPLAYER_MINMAX_H
|
||||
|
||||
#include "AIPlayer.h"
|
||||
#include "config.h"
|
||||
|
||||
namespace AI {
|
||||
|
||||
class AIPlayerMinMax: public AIPlayer{
|
||||
|
||||
protected:
|
||||
void LookAround();
|
||||
|
||||
public:
|
||||
AIPlayerMinMax(GameObserver *observer, string deckFile, string deckFileSmall, string avatarFile, MTGDeck * deck = NULL);
|
||||
virtual ~AIPlayerMinMax();
|
||||
|
||||
virtual int chooseTarget(TargetChooser * tc = NULL, Player * forceTarget = NULL, MTGCardInstance * Chosencard = NULL, bool checkonly = false) = 0;
|
||||
virtual int affectCombatDamages(CombatStep) = 0;
|
||||
virtual int Act(float dt) = 0;
|
||||
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -18,6 +18,8 @@ class MTGCard;
|
||||
class Damage;
|
||||
class WEvent;
|
||||
|
||||
namespace AI {
|
||||
|
||||
class AIStat
|
||||
{
|
||||
public:
|
||||
@@ -49,4 +51,6 @@ public:
|
||||
void Render();
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -111,6 +111,7 @@ class GameObserver{
|
||||
void loadPlayer(int playerId, PlayerType playerType = PLAYER_TYPE_HUMAN, int decknb=0, bool premadeDeck=false);
|
||||
virtual void loadPlayer(int playerId, Player* player);
|
||||
|
||||
int getPlayerId(Player* player) {if(player == players[0]) return 1; else if(player == players[1]) return 2; else return 0;};
|
||||
Player * currentPlayer;
|
||||
Player * currentActionPlayer;
|
||||
Player * isInterrupting;
|
||||
|
||||
@@ -105,7 +105,7 @@ public:
|
||||
std::string GetCurrentDeckStatsFile();
|
||||
virtual bool parseLine(const string& s);
|
||||
friend ostream& operator<<(ostream&, const Player&);
|
||||
friend istream& operator>>(istream&, Player&);
|
||||
friend istream& operator>>(istream&, Player&);
|
||||
bool operator<(Player& aPlayer);
|
||||
bool isDead();
|
||||
};
|
||||
|
||||
@@ -114,7 +114,7 @@ public:
|
||||
int run();
|
||||
};
|
||||
|
||||
class TestSuiteAI:public AIPlayerBaka
|
||||
class TestSuiteAI:public AI::AIPlayerBaka
|
||||
{
|
||||
private:
|
||||
MTGCardInstance * getCard(string action);
|
||||
|
||||
@@ -7,6 +7,8 @@
|
||||
|
||||
#include <sstream>
|
||||
|
||||
namespace AI {
|
||||
|
||||
AIHint::AIHint(string _line)
|
||||
{
|
||||
string line = _line;
|
||||
@@ -584,3 +586,5 @@ AIAction * AIHints::suggestAbility(ManaCost * potentialMana)
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
};
|
||||
@@ -6,6 +6,8 @@
|
||||
#include "AIStats.h"
|
||||
#include "AllAbilities.h"
|
||||
|
||||
namespace AI {
|
||||
|
||||
AIMomirPlayer::AIMomirPlayer(GameObserver *observer, string file, string fileSmall, string avatarFile, MTGDeck * deck) :
|
||||
AIPlayerBaka(observer, file, fileSmall, avatarFile, deck)
|
||||
{
|
||||
@@ -132,3 +134,4 @@ int AIMomirPlayer::computeActions()
|
||||
return AIPlayerBaka::computeActions();
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
@@ -13,11 +13,36 @@
|
||||
#include "AIPlayerBakaB.h"
|
||||
#endif
|
||||
|
||||
namespace AI {
|
||||
|
||||
|
||||
bool Action::parseLine(const string& s)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
ostream& operator<<(ostream& out, const Action&)
|
||||
{
|
||||
return out;
|
||||
}
|
||||
|
||||
istream& operator>>(istream& in, Action& a)
|
||||
{
|
||||
string s;
|
||||
|
||||
while(std::getline(in, s))
|
||||
{
|
||||
if(!a.parseLine(s))
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return in;
|
||||
}
|
||||
|
||||
int AIPlayer::totalAIDecks = -1;
|
||||
|
||||
const char * const MTG_LAND_TEXTS[] = { "artifact", "forest", "island", "mountain", "swamp", "plains", "other lands" };
|
||||
|
||||
AIAction::AIAction(AIPlayer * owner, MTGCardInstance * c, MTGCardInstance * t)
|
||||
: owner(owner), ability(NULL), player(NULL), click(c), target(t)
|
||||
{
|
||||
@@ -116,7 +141,7 @@ int AIAction::clickMultiAct(vector<Targetable*>& actionTargets)
|
||||
return 1;
|
||||
}
|
||||
|
||||
AIPlayer::AIPlayer(GameObserver *observer, string file, string fileSmall, MTGDeck * deck) :
|
||||
AIPlayer::AIPlayer(GameObserver *observer, string file, string fileSmall, string avatarFile, MTGDeck * deck) :
|
||||
Player(observer, file, fileSmall, deck)
|
||||
{
|
||||
agressivity = 50;
|
||||
@@ -124,6 +149,33 @@ AIPlayer::AIPlayer(GameObserver *observer, string file, string fileSmall, MTGDec
|
||||
playMode = Player::MODE_AI;
|
||||
mFastTimerMode = false;
|
||||
|
||||
if(avatarFile != "")
|
||||
{
|
||||
if(!loadAvatar(avatarFile, "bakaAvatar"))
|
||||
{
|
||||
avatarFile = "baka.jpg";
|
||||
loadAvatar(avatarFile, "bakaAvatar");
|
||||
}
|
||||
mAvatarName = avatarFile;
|
||||
}
|
||||
else //load a random avatar.
|
||||
{
|
||||
avatarFile = "avatar";
|
||||
char buffer[3];
|
||||
sprintf(buffer, "%i", int(observer->getRandomGenerator()->random()%100));
|
||||
avatarFile.append(buffer);
|
||||
avatarFile.append(".jpg");
|
||||
if(!loadAvatar(avatarFile, "bakaAvatar"))
|
||||
{
|
||||
avatarFile = "baka.jpg";
|
||||
loadAvatar(avatarFile, "bakaAvatar");
|
||||
}
|
||||
mAvatarName = avatarFile;
|
||||
}
|
||||
|
||||
if (fileSmall == "ai_baka_eviltwin")
|
||||
mAvatar->SetHFlip(true);
|
||||
|
||||
}
|
||||
|
||||
AIPlayer::~AIPlayer()
|
||||
@@ -372,3 +424,151 @@ void AIPlayer::invalidateTotalAIDecks()
|
||||
totalAIDecks = -1;
|
||||
}
|
||||
|
||||
bool AIPlayer::canFirstStrikeKill(MTGCardInstance * card, MTGCardInstance *ennemy)
|
||||
{
|
||||
if (ennemy->has(Constants::FIRSTSTRIKE) || ennemy->has(Constants::DOUBLESTRIKE))
|
||||
return false;
|
||||
if (!(card->has(Constants::FIRSTSTRIKE) || card->has(Constants::DOUBLESTRIKE)))
|
||||
return false;
|
||||
if (!(card->power >= ennemy->toughness))
|
||||
return false;
|
||||
if (!(card->power >= ennemy->toughness + 1) && ennemy->has(Constants::FLANKING))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool AIPlayer::canPlay(MTGCardInstance * card)
|
||||
{
|
||||
if (card->hasType(Subtypes::TYPE_LAND))
|
||||
{
|
||||
if (game->playRestrictions->canPutIntoZone(card, game->inPlay) == PlayRestriction::CANT_PLAY)
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (game->playRestrictions->canPutIntoZone(card, game->stack) == PlayRestriction::CANT_PLAY)
|
||||
return false;
|
||||
}
|
||||
if (!manaPool->canAfford(card->getManaCost()))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int AIPlayer::getCreaturesInfo(Player * player, int neededInfo, int untapMode, int canAttack)
|
||||
{
|
||||
int result = 0;
|
||||
CardDescriptor cd;
|
||||
cd.init();
|
||||
cd.setType("Creature");
|
||||
cd.unsecureSetTapped(untapMode);
|
||||
MTGCardInstance * card = NULL;
|
||||
while ((card = cd.nextmatch(player->game->inPlay, card)))
|
||||
{
|
||||
if (!canAttack || card->canAttack())
|
||||
{
|
||||
if (neededInfo == INFO_NBCREATURES)
|
||||
{
|
||||
result++;
|
||||
}
|
||||
else
|
||||
{
|
||||
result += card->power;
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
int AIPlayer::createAbilityPotentialsActions(MTGAbility * a, MTGCardInstance * c, vector<AIAction>& actions)
|
||||
{
|
||||
if (!a->getActionTc())
|
||||
{
|
||||
AIAction aiAction(this, a, c, NULL);
|
||||
actions.push_back(aiAction);
|
||||
return 1;
|
||||
}
|
||||
|
||||
vector<Targetable*>potentialTargets;
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
Player * p = observer->players[i];
|
||||
MTGGameZone * playerZones[] = { p->game->graveyard, p->game->library, p->game->hand, p->game->inPlay,p->game->stack };
|
||||
// try player first
|
||||
if(a->getActionTc()->canTarget((Targetable*)p))
|
||||
{
|
||||
if(a->getActionTc()->maxtargets == 1)
|
||||
{
|
||||
AIAction aiAction(this, a, p, c);
|
||||
actions.push_back(aiAction);
|
||||
}
|
||||
else
|
||||
potentialTargets.push_back(p);
|
||||
}
|
||||
for (int j = 0; j < 5; j++)
|
||||
{
|
||||
MTGGameZone * zone = playerZones[j];
|
||||
for (int k = 0; k < zone->nb_cards; k++)
|
||||
{
|
||||
MTGCardInstance * t = zone->cards[k];
|
||||
if (a->getActionTc()->canTarget(t))
|
||||
{
|
||||
if(a->getActionTc()->maxtargets == 1)
|
||||
{
|
||||
AIAction aiAction(this, a, c, t);
|
||||
actions.push_back(aiAction);
|
||||
}
|
||||
else
|
||||
{
|
||||
potentialTargets.push_back(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
vector<Targetable*>realTargets;
|
||||
if(a->getActionTc()->maxtargets != 1)
|
||||
{
|
||||
if(a->getActionTc()->getNbTargets() && a->getActionTc()->attemptsToFill > 4)
|
||||
{
|
||||
a->getActionTc()->done = true;
|
||||
return 0;
|
||||
}
|
||||
while(potentialTargets.size())
|
||||
{
|
||||
AIAction * check = NULL;
|
||||
|
||||
Player * pTargeting = 0;
|
||||
MTGCardInstance * cTargeting = dynamic_cast<MTGCardInstance*>(potentialTargets[0]);
|
||||
if(cTargeting)
|
||||
{
|
||||
check = NEW AIAction(this, a,c,cTargeting);
|
||||
}
|
||||
else
|
||||
{
|
||||
pTargeting = dynamic_cast<Player*>(potentialTargets[0]);
|
||||
if(pTargeting)
|
||||
check = NEW AIAction(this, a,pTargeting,c);
|
||||
}
|
||||
|
||||
if(check && pTargeting)
|
||||
{
|
||||
AIAction aiAction(this, a,pTargeting,c);
|
||||
actions.push_back(aiAction);
|
||||
}
|
||||
if(check)
|
||||
realTargets.push_back(potentialTargets[0]);
|
||||
potentialTargets.erase(potentialTargets.begin());
|
||||
SAFE_DELETE(check);
|
||||
}
|
||||
if(!realTargets.size() || (int(realTargets.size()) < a->getActionTc()->maxtargets && a->getActionTc()->targetMin))
|
||||
return 0;
|
||||
AIAction aiAction(this, a, c,realTargets);
|
||||
aiAction.target = dynamic_cast<MTGCardInstance*>(realTargets[0]);
|
||||
aiAction.playerAbilityTarget = dynamic_cast<Player*>(realTargets[0]);
|
||||
actions.push_back(aiAction);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -14,6 +14,7 @@
|
||||
// AIAction
|
||||
//
|
||||
|
||||
namespace AI {
|
||||
|
||||
Player * OrderedAIAction::getPlayerTarget()
|
||||
{
|
||||
@@ -2204,36 +2205,6 @@ int AIPlayerBaka::computeActions()
|
||||
return 1;
|
||||
};
|
||||
|
||||
|
||||
//
|
||||
// Combat //
|
||||
//
|
||||
|
||||
int AIPlayerBaka::getCreaturesInfo(Player * player, int neededInfo, int untapMode, int canAttack)
|
||||
{
|
||||
int result = 0;
|
||||
CardDescriptor cd;
|
||||
cd.init();
|
||||
cd.setType("Creature");
|
||||
cd.unsecureSetTapped(untapMode);
|
||||
MTGCardInstance * card = NULL;
|
||||
while ((card = cd.nextmatch(player->game->inPlay, card)))
|
||||
{
|
||||
if (!canAttack || card->canAttack())
|
||||
{
|
||||
if (neededInfo == INFO_NBCREATURES)
|
||||
{
|
||||
result++;
|
||||
}
|
||||
else
|
||||
{
|
||||
result += card->power;
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
int AIPlayerBaka::chooseAttackers()
|
||||
{
|
||||
//Attack with all creatures
|
||||
@@ -2280,19 +2251,12 @@ int AIPlayerBaka::chooseAttackers()
|
||||
}
|
||||
|
||||
/* Can I first strike my oponent and get away with murder ? */
|
||||
int AIPlayerBaka::canFirstStrikeKill(MTGCardInstance * card, MTGCardInstance *ennemy)
|
||||
bool AIPlayerBaka::canFirstStrikeKill(MTGCardInstance * card, MTGCardInstance *ennemy)
|
||||
{
|
||||
if(hints && hints->HintSaysAlwaysBlock(observer,ennemy))
|
||||
return 1;
|
||||
if (ennemy->has(Constants::FIRSTSTRIKE) || ennemy->has(Constants::DOUBLESTRIKE))
|
||||
return 0;
|
||||
if (!(card->has(Constants::FIRSTSTRIKE) || card->has(Constants::DOUBLESTRIKE)))
|
||||
return 0;
|
||||
if (!(card->power >= ennemy->toughness))
|
||||
return 0;
|
||||
if (!(card->power >= ennemy->toughness + 1) && ennemy->has(Constants::FLANKING))
|
||||
return 0;
|
||||
return 1;
|
||||
return true;
|
||||
|
||||
return AIPlayer::canFirstStrikeKill(card, ennemy);
|
||||
}
|
||||
|
||||
int AIPlayerBaka::chooseBlockers()
|
||||
@@ -2476,7 +2440,7 @@ int AIPlayerBaka::receiveEvent(WEvent * event)
|
||||
|
||||
|
||||
AIPlayerBaka::AIPlayerBaka(GameObserver *observer, string file, string fileSmall, string avatarFile, MTGDeck * deck) :
|
||||
AIPlayer(observer, file, fileSmall, deck)
|
||||
AIPlayer(observer, file, fileSmall, avatarFile, deck)
|
||||
{
|
||||
|
||||
nextCardToPlay = NULL;
|
||||
@@ -2491,34 +2455,6 @@ AIPlayerBaka::AIPlayerBaka(GameObserver *observer, string file, string fileSmall
|
||||
for (size_t i = 0; i < mDeck->meta_AIHints.size(); ++i)
|
||||
hints->add(mDeck->meta_AIHints[i]);
|
||||
}
|
||||
|
||||
|
||||
if(avatarFile != "")
|
||||
{
|
||||
if(!loadAvatar(avatarFile, "bakaAvatar"))
|
||||
{
|
||||
avatarFile = "baka.jpg";
|
||||
loadAvatar(avatarFile, "bakaAvatar");
|
||||
}
|
||||
mAvatarName = avatarFile;
|
||||
}
|
||||
else //load a random avatar.
|
||||
{
|
||||
avatarFile = "avatar";
|
||||
char buffer[3];
|
||||
sprintf(buffer, "%i", int(observer->getRandomGenerator()->random()%100));
|
||||
avatarFile.append(buffer);
|
||||
avatarFile.append(".jpg");
|
||||
if(!loadAvatar(avatarFile, "bakaAvatar"))
|
||||
{
|
||||
avatarFile = "baka.jpg";
|
||||
loadAvatar(avatarFile, "bakaAvatar");
|
||||
}
|
||||
mAvatarName = avatarFile;
|
||||
}
|
||||
|
||||
if (fileSmall == "ai_baka_eviltwin")
|
||||
mAvatar->SetHFlip(true);
|
||||
|
||||
initTimer();
|
||||
}
|
||||
@@ -2610,3 +2546,5 @@ AIPlayerBaka::~AIPlayerBaka() {
|
||||
}
|
||||
SAFE_DELETE(hints);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
// Abilities/Target Selection
|
||||
//
|
||||
|
||||
namespace AI {
|
||||
|
||||
MTGCardInstance * AIPlayerBakaB::chooseCard(TargetChooser * tc, MTGCardInstance * source, int random)
|
||||
{
|
||||
@@ -117,7 +118,7 @@ int AIPlayerBakaB::chooseAttackers()
|
||||
}
|
||||
|
||||
/* Can I first strike my oponent and get away with murder ? */
|
||||
int AIPlayerBakaB::canFirstStrikeKill(MTGCardInstance * card, MTGCardInstance *ennemy)
|
||||
bool AIPlayerBakaB::canFirstStrikeKill(MTGCardInstance * card, MTGCardInstance *ennemy)
|
||||
{
|
||||
return AIPlayerBaka::canFirstStrikeKill(card, ennemy);
|
||||
}
|
||||
@@ -180,7 +181,7 @@ AIPlayerBakaB::~AIPlayerBakaB() {
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
#include "PrecompiledHeader.h"
|
||||
|
||||
#include "AIPlayerMinMax.h"
|
||||
#include "CardDescriptor.h"
|
||||
#include "AIStats.h"
|
||||
#include "AllAbilities.h"
|
||||
#include "ExtraCost.h"
|
||||
#include "GuiCombat.h"
|
||||
#include "AIHints.h"
|
||||
#include "ManaCostHybrid.h"
|
||||
#include "MTGRules.h"
|
||||
|
||||
namespace AI {
|
||||
|
||||
//
|
||||
// Abilities/Target Selection
|
||||
//
|
||||
|
||||
AIPlayerMinMax::AIPlayerMinMax(GameObserver *observer, string deckFile, string deckFileSmall, string avatarFile, MTGDeck * deck) :
|
||||
AIPlayer(observer, deckFile, deckFileSmall, avatarFile, deck)
|
||||
{
|
||||
}
|
||||
|
||||
int AIPlayerMinMax::Act(float dt)
|
||||
{
|
||||
return 0;
|
||||
};
|
||||
|
||||
AIPlayerMinMax::~AIPlayerMinMax()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
void AIPlayerMinMax::LookAround()
|
||||
{
|
||||
vector<MTGCardInstance*>::iterator ite;
|
||||
vector<AIAction> potentialActions;
|
||||
|
||||
// look for something useable (including mana)
|
||||
for (size_t i = 1; i < observer->mLayers->actionLayer()->mObjects.size(); i++)
|
||||
{
|
||||
MTGAbility * a = ((MTGAbility *) observer->mLayers->actionLayer()->mObjects[i]);
|
||||
//Make sure we can use the ability
|
||||
for (int j = 0; j < game->inPlay->nb_cards; j++)
|
||||
{
|
||||
MTGCardInstance * card = game->inPlay->cards[j];
|
||||
if (a->isReactingToClick(card, 0))
|
||||
{
|
||||
createAbilityPotentialsActions(a, card, potentialActions);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// look for something playable
|
||||
for(ite = game->hand->cards.begin(); ite != game->hand->cards.end(); ite++)
|
||||
{
|
||||
if(canPlay(*ite))
|
||||
{
|
||||
AIAction a(this, (*ite));
|
||||
potentialActions.push_back(a);
|
||||
}
|
||||
}
|
||||
|
||||
stringstream stream;
|
||||
stream << *observer;
|
||||
vector<AIAction>::iterator it;
|
||||
for(it = potentialActions.begin(); it != potentialActions.end(); it++)
|
||||
{
|
||||
GameObserver g;
|
||||
g.load(stream.str());
|
||||
// g.processAction((*it));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -6,6 +6,9 @@
|
||||
#include "MTGCardInstance.h"
|
||||
#include "WEvent.h"
|
||||
#include "AllAbilities.h"
|
||||
|
||||
namespace AI {
|
||||
|
||||
//TODO:better comments this is too cryptic to work on by anyone but original coder.
|
||||
bool compare_aistats(AIStat * first, AIStat * second)
|
||||
{
|
||||
@@ -217,3 +220,5 @@ void AIStats::Render()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -994,15 +994,15 @@ void ActionStack::Update(float dt)
|
||||
void ActionStack::cancelInterruptOffer(InterruptDecision cancelMode, bool log)
|
||||
{
|
||||
int playerId = (observer->isInterrupting == observer->players[1]) ? 1 : 0;
|
||||
interruptDecision[playerId] = cancelMode;
|
||||
askIfWishesToInterrupt = NULL;
|
||||
observer->isInterrupting = NULL;
|
||||
timer = -1;
|
||||
if(log) {
|
||||
stringstream stream;
|
||||
stream << "no " << cancelMode;
|
||||
observer->logAction(playerId, stream.str());
|
||||
}
|
||||
interruptDecision[playerId] = cancelMode;
|
||||
askIfWishesToInterrupt = NULL;
|
||||
observer->isInterrupting = NULL;
|
||||
timer = -1;
|
||||
}
|
||||
|
||||
void ActionStack::endOfInterruption(bool log)
|
||||
|
||||
@@ -625,7 +625,7 @@ int Credits::isDifficultyUnlocked(DeckStats * stats)
|
||||
{
|
||||
if (options[Options::DIFFICULTY_MODE_UNLOCKED].number)
|
||||
return 0;
|
||||
int nbAIDecks = AIPlayer::getTotalAIDecks();
|
||||
int nbAIDecks = AI::AIPlayer::getTotalAIDecks();
|
||||
|
||||
int wins = 0;
|
||||
|
||||
@@ -743,7 +743,7 @@ int Credits::IsMoreAIDecksUnlocked(DeckStats * stats) {
|
||||
// the number of currently unlocked decks in order to go through.
|
||||
if (stats->nbGames() < currentlyUnlocked * 1.2) return 0;
|
||||
|
||||
if (AIPlayer::getTotalAIDecks() > currentlyUnlocked)
|
||||
if (AI::AIPlayer::getTotalAIDecks() > currentlyUnlocked)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
|
||||
@@ -1668,7 +1668,7 @@ bool GameObserver::load(const string& ss, bool undo, int controlledPlayerIndex
|
||||
}
|
||||
else
|
||||
{
|
||||
logAction(s);
|
||||
actionsList.push_back(s);
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -1772,11 +1772,11 @@ bool GameObserver::processActions(bool undo
|
||||
|
||||
for(loadingite = loadingList.begin(); loadingite != loadingList.end(); loadingite++, cmdIndex++)
|
||||
{
|
||||
processAction(*loadingite);
|
||||
|
||||
string s = *loadingite;
|
||||
processAction(s);
|
||||
size_t nb = actionsList.size();
|
||||
|
||||
for (int i = 0; i<6; i++)
|
||||
for (int i = 0; i < 6; i++)
|
||||
{
|
||||
// let's fake an update
|
||||
GameObserver::Update(counter);
|
||||
@@ -1815,12 +1815,15 @@ void GameObserver::logAction(MTGCardInstance* card, MTGGameZone* zone, size_t in
|
||||
|
||||
void GameObserver::logAction(const string& s)
|
||||
{
|
||||
stringstream stream;
|
||||
stream << s << " cp " << getPlayerId(currentPlayer) << ", ii " << getPlayerId(isInterrupting) << ", cap " << getPlayerId(currentActionPlayer);
|
||||
|
||||
if(mLoading)
|
||||
{
|
||||
string toCheck = *loadingite;
|
||||
dumpAssert(toCheck == s);
|
||||
dumpAssert(toCheck == stream.str());
|
||||
}
|
||||
actionsList.push_back(s);
|
||||
actionsList.push_back(stream.str());
|
||||
};
|
||||
|
||||
bool GameObserver::undo()
|
||||
@@ -1850,7 +1853,7 @@ Player* GameObserver::createPlayer(const string& playerMode
|
||||
switch(aMode)
|
||||
{
|
||||
case Player::MODE_AI:
|
||||
AIPlayerFactory playerCreator;
|
||||
AI::AIPlayerFactory playerCreator;
|
||||
if(players.size())
|
||||
pPlayer = playerCreator.createAIPlayer(this, MTGCollection(), players[0]);
|
||||
else
|
||||
@@ -1923,7 +1926,7 @@ void GameObserver::loadPlayer(int playerId, PlayerType playerType, int decknb, b
|
||||
}
|
||||
else
|
||||
{ //AI Player, chooses deck
|
||||
AIPlayerFactory playerCreator;
|
||||
AI::AIPlayerFactory playerCreator;
|
||||
Player * opponent = NULL;
|
||||
if (playerId == 1) opponent = players[0];
|
||||
|
||||
@@ -1933,7 +1936,7 @@ void GameObserver::loadPlayer(int playerId, PlayerType playerType, int decknb, b
|
||||
else
|
||||
{
|
||||
//Random deck
|
||||
AIPlayerFactory playerCreator;
|
||||
AI::AIPlayerFactory playerCreator;
|
||||
Player * opponent = NULL;
|
||||
|
||||
// Reset the random logging.
|
||||
@@ -1950,7 +1953,7 @@ void GameObserver::loadPlayer(int playerId, PlayerType playerType, int decknb, b
|
||||
}
|
||||
|
||||
if (playerType == PLAYER_TYPE_CPU_TEST)
|
||||
((AIPlayer *) players[playerId])->setFastTimerMode();
|
||||
((AI::AIPlayer *) players[playerId])->setFastTimerMode();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -332,7 +332,7 @@ void GameStateDeckViewer::saveDeck()
|
||||
void GameStateDeckViewer::saveAsAIDeck(string deckName)
|
||||
{
|
||||
|
||||
int deckId = AIPlayer::getTotalAIDecks() + 1;
|
||||
int deckId = AI::AIPlayer::getTotalAIDecks() + 1;
|
||||
|
||||
std::ostringstream oss;
|
||||
oss << "deck" <<deckId;
|
||||
@@ -347,7 +347,7 @@ void GameStateDeckViewer::saveAsAIDeck(string deckName)
|
||||
filepath.append(aiDeckName).append(".txt");
|
||||
DebugTrace("saving AI deck " << filepath);
|
||||
myDeck->save(filepath, true, deckName, deckDesc);
|
||||
AIPlayer::invalidateTotalAIDecks(); //We added one AI deck, so we need to invalidate the count cache
|
||||
AI::AIPlayer::invalidateTotalAIDecks(); //We added one AI deck, so we need to invalidate the count cache
|
||||
}
|
||||
|
||||
void GameStateDeckViewer::sellCard()
|
||||
|
||||
@@ -1733,8 +1733,8 @@ void GameStateDuel::setAISpeed()
|
||||
{
|
||||
if (mParent->players[i] == PLAYER_TYPE_CPU)
|
||||
{
|
||||
if(dynamic_cast<AIPlayer*>(game->players[i]))
|
||||
((AIPlayer *)game->players[i])->setFastTimerMode(tournament->getFastTimerMode());
|
||||
if(dynamic_cast<AI::AIPlayer*>(game->players[i]))
|
||||
((AI::AIPlayer *)game->players[i])->setFastTimerMode(tournament->getFastTimerMode());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -253,7 +253,7 @@ int GameStateMenu::gamePercentComplete() {
|
||||
|
||||
//unlocked AI decks
|
||||
int currentlyUnlocked = options[Options::AIDECKS_UNLOCKED].number;
|
||||
int totalAIDecks = AIPlayer::getTotalAIDecks();
|
||||
int totalAIDecks = AI::AIPlayer::getTotalAIDecks();
|
||||
int reallyUnlocked = MIN(currentlyUnlocked, totalAIDecks);
|
||||
total+= totalAIDecks / 10;
|
||||
done+= reallyUnlocked / 10;
|
||||
|
||||
@@ -727,7 +727,7 @@ int GuiCombat::receiveEventMinus(WEvent* e)
|
||||
DAMAGE: step = event->step;
|
||||
if (!observer->currentPlayer->displayStack())
|
||||
{
|
||||
((AIPlayer *) observer->currentPlayer)->affectCombatDamages(step);
|
||||
((AI::AIPlayer *) observer->currentPlayer)->affectCombatDamages(step);
|
||||
observer->userRequestNextGamePhase(false, false);
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -385,18 +385,30 @@ bool Player::operator<(Player& aPlayer)
|
||||
if(isDead() && !aPlayer.isDead())
|
||||
return true;
|
||||
|
||||
// if this opponent is not dead and aPlayer opponent is dead then this < aPlayer
|
||||
if(!opponent()->isDead() && aPlayer.opponent()->isDead())
|
||||
return true;
|
||||
|
||||
// heuristics for min-max
|
||||
|
||||
// if this is more poisoined than aPlayer then this < aPlayer
|
||||
if(poisonCount > aPlayer.poisonCount)
|
||||
if((poisonCount - opponent()->poisonCount) > (aPlayer.poisonCount - aPlayer.opponent()->poisonCount))
|
||||
return true;
|
||||
|
||||
// if this has less life than aPlayer then this < aPlayer
|
||||
if(life < aPlayer.life)
|
||||
if((life - opponent()->life) < (aPlayer.life - aPlayer.opponent()->life))
|
||||
return true;
|
||||
|
||||
// if this has less parmanents in game that aPlayer then this < aPlayer
|
||||
if(game->battlefield->cards.size() < aPlayer.game->battlefield->cards.size())
|
||||
// if this has less permanents in game that aPlayer then this < aPlayer
|
||||
if(((int)game->battlefield->cards.size() - (int)opponent()->game->battlefield->cards.size()) < ((int)aPlayer.game->battlefield->cards.size() - (int)aPlayer.opponent()->game->battlefield->cards.size()))
|
||||
return true;
|
||||
|
||||
// if this has less cards in hand that aPlayer then this < aPlayer
|
||||
if(((int)game->hand->cards.size() - (int)opponent()->game->hand->cards.size()) < ((int)aPlayer.game->hand->cards.size() - (int)aPlayer.opponent()->game->hand->cards.size()))
|
||||
return true;
|
||||
|
||||
// if this has less mana than aPlayer then this < aPlayer
|
||||
if(aPlayer.manaPool->canAfford(manaPool))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
|
||||
@@ -183,8 +183,8 @@ void Rules::addExtraRules(GameObserver* g)
|
||||
else if (p->isAI() && (p->playMode == Player::MODE_AI && p->opponent()->playMode== Player::MODE_AI))
|
||||
{
|
||||
handsize = ((AADrawer *)a)->getNumCards();
|
||||
((AIPlayer *) p)->forceBestAbilityUse = true;
|
||||
((AIPlayer *) p)->agressivity += 100;
|
||||
((AI::AIPlayer *) p)->forceBestAbilityUse = true;
|
||||
((AI::AIPlayer *) p)->agressivity += 100;
|
||||
hand->OptimizedHand(p,handsize, 3, 1, 3);
|
||||
}
|
||||
else if (!p->isAI() && !Optimizedhandcheat)
|
||||
@@ -202,8 +202,8 @@ void Rules::addExtraRules(GameObserver* g)
|
||||
handsize = ((AADrawer *)a)->getNumCards();
|
||||
if(difficultyRating == EASY)
|
||||
{
|
||||
((AIPlayer *) p)->forceBestAbilityUse = true;
|
||||
((AIPlayer *) p)->agressivity += 100;
|
||||
((AI::AIPlayer *) p)->forceBestAbilityUse = true;
|
||||
((AI::AIPlayer *) p)->agressivity += 100;
|
||||
hand->OptimizedHand(p,handsize, 3, 1, 3);//easy decks get a major boost, open hand is 2lands,1 creature under 3 mana,3spells under 3 mana.
|
||||
}
|
||||
else if (difficultyRating == NORMAL)
|
||||
@@ -265,7 +265,7 @@ Player * Rules::loadPlayerMomir(GameObserver* observer, int isAI)
|
||||
if (!isAI) // Human Player
|
||||
player = NEW HumanPlayer(observer, options.profileFile("momir.txt", "", true).c_str(), deckFileSmall, false, tempDeck);
|
||||
else
|
||||
player = NEW AIMomirPlayer(observer, options.profileFile("momir.txt", "", true).c_str(), deckFileSmall, empty, tempDeck);
|
||||
player = NEW AI::AIMomirPlayer(observer, options.profileFile("momir.txt", "", true).c_str(), deckFileSmall, empty, tempDeck);
|
||||
|
||||
return player;
|
||||
}
|
||||
@@ -299,7 +299,7 @@ Player * Rules::loadPlayerRandom(GameObserver* observer, int isAI, int mode)
|
||||
if (!isAI) // Human Player
|
||||
player = NEW HumanPlayer(observer, deckFile, deckFileSmall, false, tempDeck);
|
||||
else
|
||||
player = NEW AIPlayerBaka(observer, deckFile, deckFileSmall, "", tempDeck);
|
||||
player = NEW AI::AIPlayerBaka(observer, deckFile, deckFileSmall, "", tempDeck);
|
||||
|
||||
return player;
|
||||
}
|
||||
|
||||
@@ -310,7 +310,7 @@ void StoryDuel::init()
|
||||
|
||||
sprintf(deckFile, "%s/opponent_deck.txt", folder);
|
||||
sprintf(deckFileSmall, "campaign_ennemy_%s_%s", mParent->folder.c_str(), pageId.c_str());
|
||||
game->loadPlayer(1, NEW AIPlayerBaka(game, deckFile, deckFileSmall, "baka.jpg"));
|
||||
game->loadPlayer(1, NEW AI::AIPlayerBaka(game, deckFile, deckFileSmall, "baka.jpg"));
|
||||
|
||||
string rulesFile = folder;
|
||||
rulesFile.append("/rules.txt");
|
||||
|
||||
@@ -616,7 +616,7 @@ string TaskWinAgainst::getShortDesc()
|
||||
|
||||
bool TaskWinAgainst::isDone(GameObserver* observer, GameApp *)
|
||||
{
|
||||
AIPlayerBaka * baka = (AIPlayerBaka*) observer->players[1];
|
||||
AI::AIPlayerBaka * baka = (AI::AIPlayerBaka*) observer->players[1];
|
||||
return ((baka) && (!observer->players[0]->isAI()) && (observer->players[1]->isAI()) && (observer->didWin(observer->players[0])) // Human player wins
|
||||
&& (baka->deckId == opponent));
|
||||
}
|
||||
|
||||
@@ -567,7 +567,7 @@ int TestSuite::loadNext()
|
||||
#elif defined(IOS)
|
||||
thread_count = 6;
|
||||
#else
|
||||
thread_count = 4;
|
||||
thread_count = 2;
|
||||
#endif
|
||||
for(size_t i = 0; i < (thread_count-1); i++)
|
||||
mWorkerThread.push_back(new boost::thread(ThreadProc, this));
|
||||
@@ -600,6 +600,13 @@ void TestSuite::ThreadProc(void* inParam)
|
||||
theGame.observer->startGame(theGame.gameType, /*instance->mRules*/Rules::getRulesByFilename("testsuite.txt"));
|
||||
theGame.initGame();
|
||||
|
||||
while(!theGame.observer->didWin())
|
||||
theGame.observer->Update(counter++);
|
||||
|
||||
stringstream stream;
|
||||
stream << (*theGame.observer);
|
||||
theGame.observer->load(stream.str(), false, 0, &theGame);
|
||||
|
||||
while(!theGame.observer->didWin())
|
||||
theGame.observer->Update(counter++);
|
||||
}
|
||||
@@ -834,7 +841,7 @@ void TestSuiteGame::initGame()
|
||||
|
||||
for (int i = 0; i < 2; i++)
|
||||
{
|
||||
AIPlayerBaka * p = (AIPlayerBaka *) (observer->players[i]);
|
||||
AI::AIPlayerBaka * p = (AI::AIPlayerBaka *) (observer->players[i]);
|
||||
p->forceBestAbilityUse = forceAbility;
|
||||
p->life = initState.players[i]->life;
|
||||
p->poisonCount = initState.players[i]->poisonCount;
|
||||
|
||||
@@ -310,6 +310,7 @@
|
||||
<ClCompile Include="src\AIPlayer.cpp" />
|
||||
<ClCompile Include="src\AIPlayerBaka.cpp" />
|
||||
<ClCompile Include="src\AIPlayerBakaB.cpp" />
|
||||
<ClCompile Include="src\AIPlayerMinMax.cpp" />
|
||||
<ClCompile Include="src\AIStats.cpp" />
|
||||
<ClCompile Include="src\AllAbilities.cpp" />
|
||||
<ClCompile Include="src\CardDescriptor.cpp" />
|
||||
@@ -450,6 +451,7 @@
|
||||
<ClInclude Include="include\AIPlayer.h" />
|
||||
<ClInclude Include="include\AIPlayerBaka.h" />
|
||||
<ClInclude Include="include\AIPlayerBakaB.h" />
|
||||
<ClInclude Include="include\AIPlayerMinMax.h" />
|
||||
<ClInclude Include="include\AIStats.h" />
|
||||
<ClInclude Include="include\AllAbilities.h" />
|
||||
<ClInclude Include="include\CacheEngine.h" />
|
||||
|
||||
@@ -331,6 +331,9 @@
|
||||
<ClCompile Include="src\NetworkPlayer.cpp">
|
||||
<Filter>src</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\AIPlayerMinMax.cpp">
|
||||
<Filter>src</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="include\ActionElement.h">
|
||||
@@ -687,6 +690,9 @@
|
||||
<ClInclude Include="include\NetworkPlayer.h">
|
||||
<Filter>inc</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="include\AIPlayerMinMax.h">
|
||||
<Filter>inc</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="Makefile" />
|
||||
|
||||
Reference in New Issue
Block a user