@@ -654,7 +654,7 @@ int OrderedAIAction::getEfficiency()
|
|||||||
}
|
}
|
||||||
else if (GenericRevealAbility * grA = dynamic_cast<GenericRevealAbility *>(a))
|
else if (GenericRevealAbility * grA = dynamic_cast<GenericRevealAbility *>(a))
|
||||||
{
|
{
|
||||||
if(grA->source->getAICustomCode().size())
|
if(grA->source->getAICustomCode().size() && grA->source->alias != 185709)//Sphinx of Jwar Isle so the ai will ignore it
|
||||||
{
|
{
|
||||||
//efficiency = 45 + (owner->getRandomGenerator()->random() % 50);
|
//efficiency = 45 + (owner->getRandomGenerator()->random() % 50);
|
||||||
AbilityFactory af(g);
|
AbilityFactory af(g);
|
||||||
@@ -4012,98 +4012,46 @@ int AIPlayerBaka::chooseAttackers()
|
|||||||
MTGCardInstance * card = NULL;
|
MTGCardInstance * card = NULL;
|
||||||
while ((card = cd.nextmatch(game->inPlay, card)))
|
while ((card = cd.nextmatch(game->inPlay, card)))
|
||||||
{
|
{
|
||||||
if (shouldAIForceAttack(card, attack))
|
if ((hints && hints->HintSaysAlwaysAttack(observer, card)) || card->has(Constants::UNBLOCKABLE))
|
||||||
|
{
|
||||||
|
if (!card->isAttacker())
|
||||||
{
|
{
|
||||||
if (card->attackCost)
|
if (card->attackCost)
|
||||||
{
|
{
|
||||||
MTGAbility* a = observer->mLayers->actionLayer()->getAbility(MTGAbility::ATTACK_COST);
|
MTGAbility * a = observer->mLayers->actionLayer()->getAbility(MTGAbility::ATTACK_COST);
|
||||||
|
doAbility(a,card);
|
||||||
|
observer->cardClick(card, MTGAbility::ATTACK_COST);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
observer->cardClick(card, MTGAbility::MTG_ATTACK_RULE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (attack)
|
||||||
|
{
|
||||||
|
CardDescriptor cd;
|
||||||
|
cd.init();
|
||||||
|
cd.setType("creature");
|
||||||
|
MTGCardInstance * card = NULL;
|
||||||
|
while ((card = cd.nextmatch(game->inPlay, card)))
|
||||||
|
{
|
||||||
|
if(hints && hints->HintSaysDontAttack(observer,card))
|
||||||
|
continue;
|
||||||
|
if (!card->isAttacker())
|
||||||
|
{
|
||||||
|
if (card->attackCost)
|
||||||
|
{
|
||||||
|
MTGAbility * a = observer->mLayers->actionLayer()->getAbility(MTGAbility::ATTACK_COST);
|
||||||
doAbility(a, card);
|
doAbility(a, card);
|
||||||
observer->cardClick(card, MTGAbility::ATTACK_COST);
|
observer->cardClick(card, MTGAbility::ATTACK_COST);
|
||||||
}
|
}
|
||||||
observer->cardClick(card, MTGAbility::MTG_ATTACK_RULE);
|
observer->cardClick(card, MTGAbility::MTG_ATTACK_RULE);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool AIPlayerBaka::shouldAIForceAttack(MTGCardInstance* card, bool globalAttack)
|
|
||||||
{
|
|
||||||
if (globalAttack)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
if (!card || card->isAttacker())
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (hints)
|
|
||||||
{
|
|
||||||
if (hints->HintSaysDontAttack(observer, card))
|
|
||||||
return false;
|
|
||||||
if (hints->HintSaysAlwaysAttack(observer, card))
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (card->has(Constants::UNBLOCKABLE))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
// Flags for opponent defenses
|
|
||||||
bool oppHasShadow = false;
|
|
||||||
bool oppHasAirDefense = false;
|
|
||||||
bool oppHasHorsemanship = false;
|
|
||||||
bool oppHasBlackOrArtifact = false;
|
|
||||||
bool oppHasMatchingColorOrArtifact = false;
|
|
||||||
|
|
||||||
// Flags for landwalk checks
|
|
||||||
bool oppHasSwamp = opponent()->game->inPlay->hasType("Swamp");
|
|
||||||
bool oppHasIsland = opponent()->game->inPlay->hasType("Island");
|
|
||||||
bool oppHasForest = opponent()->game->inPlay->hasType("Forest");
|
|
||||||
bool oppHasMountain = opponent()->game->inPlay->hasType("Mountain");
|
|
||||||
bool oppHasPlains = opponent()->game->inPlay->hasType("Plains");
|
|
||||||
|
|
||||||
MTGCardInstance* oppCard = NULL;
|
|
||||||
CardDescriptor desc;
|
|
||||||
desc.init();
|
|
||||||
desc.setType("creature");
|
|
||||||
|
|
||||||
while ((oppCard = desc.nextmatch(opponent()->game->inPlay, oppCard)))
|
|
||||||
{
|
|
||||||
if (oppCard->isTapped())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (oppCard->has(Constants::SHADOW))
|
|
||||||
oppHasShadow = true;
|
|
||||||
if (oppCard->has(Constants::FLYING) || oppCard->has(Constants::REACH))
|
|
||||||
oppHasAirDefense = true;
|
|
||||||
if (oppCard->has(Constants::HORSEMANSHIP))
|
|
||||||
oppHasHorsemanship = true;
|
|
||||||
|
|
||||||
if (oppCard->hasColor(Constants::MTG_COLOR_BLACK) || oppCard->hasType("Artifact"))
|
|
||||||
oppHasBlackOrArtifact = true;
|
|
||||||
|
|
||||||
// Intimidate check: artifact or shares color
|
|
||||||
if (oppCard->hasType("Artifact") || (oppCard->colors & card->colors))
|
|
||||||
oppHasMatchingColorOrArtifact = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Decision logic based on evasion
|
|
||||||
if ((card->has(Constants::SHADOW) && !oppHasShadow) ||
|
|
||||||
(card->has(Constants::FLYING) && !oppHasAirDefense) ||
|
|
||||||
(card->has(Constants::HORSEMANSHIP) && !oppHasHorsemanship) ||
|
|
||||||
(card->has(Constants::FEAR) && !oppHasBlackOrArtifact) ||
|
|
||||||
(card->has(Constants::INTIMIDATE) && !oppHasMatchingColorOrArtifact))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
// Landwalk abilities
|
|
||||||
if ((card->has(Constants::SWAMPWALK) && oppHasSwamp) ||
|
|
||||||
(card->has(Constants::ISLANDWALK) && oppHasIsland) ||
|
|
||||||
(card->has(Constants::FORESTWALK) && oppHasForest) ||
|
|
||||||
(card->has(Constants::MOUNTAINWALK) && oppHasMountain) ||
|
|
||||||
(card->has(Constants::PLAINSWALK) && oppHasPlains))
|
|
||||||
return true;
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Can I first strike my oponent and get away with murder ? */
|
/* Can I first strike my oponent and get away with murder ? */
|
||||||
int AIPlayerBaka::canFirstStrikeKill(MTGCardInstance * card, MTGCardInstance *ennemy)
|
int AIPlayerBaka::canFirstStrikeKill(MTGCardInstance * card, MTGCardInstance *ennemy)
|
||||||
{
|
{
|
||||||
@@ -4122,36 +4070,35 @@ int AIPlayerBaka::canFirstStrikeKill(MTGCardInstance * card, MTGCardInstance *en
|
|||||||
|
|
||||||
int AIPlayerBaka::chooseBlockers()
|
int AIPlayerBaka::chooseBlockers()
|
||||||
{
|
{
|
||||||
|
//Should not block during my own turn...
|
||||||
if (observer->currentPlayer == this)
|
if (observer->currentPlayer == this)
|
||||||
return 0;
|
return 0;
|
||||||
|
map<MTGCardInstance *, int> opponentsToughness;
|
||||||
map<MTGCardInstance*, int> opponentsToughness;
|
|
||||||
int opponentForce = getCreaturesInfo(opponent(), INFO_CREATURESPOWER);
|
int opponentForce = getCreaturesInfo(opponent(), INFO_CREATURESPOWER);
|
||||||
|
|
||||||
|
//Initialize the list of opponent's attacking cards toughness
|
||||||
CardDescriptor cdAttackers;
|
CardDescriptor cdAttackers;
|
||||||
cdAttackers.init();
|
cdAttackers.init();
|
||||||
cdAttackers.setType("Creature");
|
cdAttackers.setType("Creature");
|
||||||
MTGCardInstance* card = NULL;
|
MTGCardInstance * card = NULL;
|
||||||
|
|
||||||
// Gather all attacking creatures and store their toughness
|
|
||||||
while ((card = cdAttackers.nextmatch(opponent()->game->inPlay, card)))
|
while ((card = cdAttackers.nextmatch(opponent()->game->inPlay, card)))
|
||||||
{
|
{
|
||||||
if (card->isAttacker())
|
if (card->isAttacker())
|
||||||
opponentsToughness[card] = card->toughness;
|
opponentsToughness[card] = card->toughness;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//A Descriptor to find untapped creatures in our game
|
||||||
CardDescriptor cd;
|
CardDescriptor cd;
|
||||||
cd.init();
|
cd.init();
|
||||||
cd.setType("Creature");
|
cd.setType("Creature");
|
||||||
cd.unsecureSetTapped(-1);
|
cd.unsecureSetTapped(-1);
|
||||||
card = NULL;
|
card = NULL;
|
||||||
|
|
||||||
// First pass: auto-block top 3 threats if can be killed
|
// 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)))
|
||||||
{
|
{
|
||||||
if (hints && hints->HintSaysDontBlock(observer, card))
|
if(hints && hints->HintSaysDontBlock(observer,card))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
observer->cardClick(card, MTGAbility::MTG_BLOCK_RULE);
|
observer->cardClick(card, MTGAbility::MTG_BLOCK_RULE);
|
||||||
int set = 0;
|
int set = 0;
|
||||||
while (!set)
|
while (!set)
|
||||||
@@ -4162,8 +4109,8 @@ int AIPlayerBaka::chooseBlockers()
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
MTGCardInstance* attacker = card->defenser;
|
MTGCardInstance * attacker = card->defenser;
|
||||||
map<MTGCardInstance*, int>::iterator it = opponentsToughness.find(attacker);
|
map<MTGCardInstance *, int>::iterator it = opponentsToughness.find(attacker);
|
||||||
if (it == opponentsToughness.end())
|
if (it == opponentsToughness.end())
|
||||||
{
|
{
|
||||||
opponentsToughness[attacker] = attacker->toughness;
|
opponentsToughness[attacker] = attacker->toughness;
|
||||||
@@ -4178,7 +4125,7 @@ int AIPlayerBaka::chooseBlockers()
|
|||||||
{
|
{
|
||||||
if (card->blockCost)
|
if (card->blockCost)
|
||||||
{
|
{
|
||||||
MTGAbility* a = observer->mLayers->actionLayer()->getAbility(MTGAbility::BLOCK_COST);
|
MTGAbility * a = observer->mLayers->actionLayer()->getAbility(MTGAbility::BLOCK_COST);
|
||||||
doAbility(a, card);
|
doAbility(a, card);
|
||||||
observer->cardClick(card, MTGAbility::BLOCK_COST);
|
observer->cardClick(card, MTGAbility::BLOCK_COST);
|
||||||
}
|
}
|
||||||
@@ -4188,13 +4135,13 @@ int AIPlayerBaka::chooseBlockers()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Second pass: unassign if attacker is not expected to die
|
//If blocking one of the major threats is not enough to kill it,
|
||||||
|
// We change strategy, first we unassign its blockers that where assigned above
|
||||||
card = NULL;
|
card = NULL;
|
||||||
while ((card = cd.nextmatch(game->inPlay, card)))
|
while ((card = cd.nextmatch(game->inPlay, card)))
|
||||||
{
|
{
|
||||||
if (hints && hints->HintSaysDontBlock(observer, card))
|
if(hints && hints->HintSaysDontBlock(observer,card))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (card->defenser && opponentsToughness[card->defenser] > 0)
|
if (card->defenser && opponentsToughness[card->defenser] > 0)
|
||||||
{
|
{
|
||||||
while (card->defenser)
|
while (card->defenser)
|
||||||
@@ -4204,139 +4151,48 @@ int AIPlayerBaka::chooseBlockers()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Third pass: intelligent blocking
|
//Assign the "free" potential blockers to attacking creatures that are not blocked enough
|
||||||
card = NULL;
|
card = NULL;
|
||||||
while ((card = cd.nextmatch(game->inPlay, card)))
|
while ((card = cd.nextmatch(game->inPlay, card)))
|
||||||
{
|
{
|
||||||
if (hints && hints->HintSaysDontBlock(observer, card))
|
if(hints && hints->HintSaysDontBlock(observer,card))
|
||||||
continue;
|
continue;
|
||||||
if (card->defenser)
|
if (!card->defenser)
|
||||||
continue;
|
|
||||||
|
|
||||||
MTGCardInstance* bestAttacker = NULL;
|
|
||||||
int bestScore = -1;
|
|
||||||
|
|
||||||
for (map<MTGCardInstance*, int>::iterator it = opponentsToughness.begin(); it != opponentsToughness.end(); ++it)
|
|
||||||
{
|
|
||||||
MTGCardInstance* attacker = it->first;
|
|
||||||
if (!attacker)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
int currentBlockers = (int)attacker->blockers.size();
|
|
||||||
int totalAssignedDamage = 0;
|
|
||||||
|
|
||||||
list<MTGCardInstance*>::iterator itb;
|
|
||||||
for (itb = attacker->blockers.begin(); itb != attacker->blockers.end(); ++itb)
|
|
||||||
{
|
|
||||||
MTGCardInstance* blocker = *itb;
|
|
||||||
if (blocker)
|
|
||||||
totalAssignedDamage += blocker->power;
|
|
||||||
}
|
|
||||||
|
|
||||||
int maxBlockers = 1;
|
|
||||||
if (attacker->basicAbilities[Constants::MENACE]) maxBlockers = 2;
|
|
||||||
if (attacker->basicAbilities[Constants::THREEBLOCKERS]) maxBlockers = 3;
|
|
||||||
|
|
||||||
if (totalAssignedDamage >= attacker->toughness || currentBlockers >= maxBlockers)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
bool canKill = (card->power >= attacker->toughness);
|
|
||||||
bool survives = (card->toughness > attacker->power);
|
|
||||||
|
|
||||||
if (!canKill && !survives)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (!canKill && survives)
|
|
||||||
{
|
|
||||||
int estimatedDamage = opponentForce;
|
|
||||||
if ((estimatedDamage - attacker->power) < life)
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
int score = attacker->power * 2 + attacker->toughness;
|
|
||||||
if (getStats() && getStats()->isInTop(attacker, 3, false))
|
|
||||||
score += 100;
|
|
||||||
|
|
||||||
if (score > bestScore)
|
|
||||||
{
|
|
||||||
bestScore = score;
|
|
||||||
bestAttacker = attacker;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (bestAttacker)
|
|
||||||
{
|
|
||||||
int requiredBlockers = 1;
|
|
||||||
if (bestAttacker->basicAbilities[Constants::MENACE]) requiredBlockers = 2;
|
|
||||||
if (bestAttacker->basicAbilities[Constants::THREEBLOCKERS]) requiredBlockers = 3;
|
|
||||||
|
|
||||||
int currentBlockers = (int)bestAttacker->blockers.size();
|
|
||||||
if (currentBlockers >= requiredBlockers)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
bool canKill = (card->power >= bestAttacker->toughness);
|
|
||||||
bool survives = (card->toughness > bestAttacker->power);
|
|
||||||
bool hasFirstStrike = card->has(Constants::FIRSTSTRIKE) || card->has(Constants::DOUBLESTRIKE);
|
|
||||||
|
|
||||||
// Block alone if this card is strong enough (e.g., first strike + power)
|
|
||||||
if (hasFirstStrike && canKill)
|
|
||||||
{
|
{
|
||||||
if (card->blockCost)
|
if (card->blockCost)
|
||||||
{
|
{
|
||||||
MTGAbility* a = observer->mLayers->actionLayer()->getAbility(MTGAbility::BLOCK_COST);
|
MTGAbility * a = observer->mLayers->actionLayer()->getAbility(MTGAbility::BLOCK_COST);
|
||||||
doAbility(a, card);
|
doAbility(a, card);
|
||||||
}
|
}
|
||||||
observer->cardClick(card, MTGAbility::MTG_BLOCK_RULE);
|
observer->cardClick(card, MTGAbility::MTG_BLOCK_RULE);
|
||||||
opponentsToughness[bestAttacker] -= card->power;
|
int set = 0;
|
||||||
continue;
|
while (!set)
|
||||||
}
|
|
||||||
|
|
||||||
vector<MTGCardInstance*> extraBlockers;
|
|
||||||
if (requiredBlockers > 1)
|
|
||||||
{
|
{
|
||||||
CardDescriptor cd2;
|
if (!card->defenser)
|
||||||
cd2.init();
|
|
||||||
cd2.setType("Creature");
|
|
||||||
cd2.unsecureSetTapped(-1);
|
|
||||||
MTGCardInstance* c2 = NULL;
|
|
||||||
while ((c2 = cd2.nextmatch(game->inPlay, c2)))
|
|
||||||
{
|
{
|
||||||
if (c2 == card || c2->defenser || (hints && hints->HintSaysDontBlock(observer, c2)))
|
set = 1;
|
||||||
continue;
|
}
|
||||||
|
else
|
||||||
int combinedPower = c2->power + card->power;
|
|
||||||
bool combinedCanKill = (combinedPower >= bestAttacker->toughness);
|
|
||||||
bool eitherSurvives = (c2->toughness > bestAttacker->power || card->toughness > bestAttacker->power);
|
|
||||||
|
|
||||||
if (combinedCanKill || eitherSurvives)
|
|
||||||
{
|
{
|
||||||
extraBlockers.push_back(c2);
|
MTGCardInstance * attacker = card->defenser;
|
||||||
if ((int)extraBlockers.size() + currentBlockers + 1 >= requiredBlockers)
|
if (opponentsToughness[attacker] <= 0 || (card->toughness <= attacker->power && opponentForce * 2 < life && !canFirstStrikeKill(card, attacker)) || attacker->nbOpponents() > 1)
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currentBlockers + (int)extraBlockers.size() + 1 >= requiredBlockers)
|
|
||||||
{
|
{
|
||||||
if (card->blockCost)
|
if (card->blockCost)
|
||||||
{
|
{
|
||||||
MTGAbility* a = observer->mLayers->actionLayer()->getAbility(MTGAbility::BLOCK_COST);
|
MTGAbility * a = observer->mLayers->actionLayer()->getAbility(MTGAbility::BLOCK_COST);
|
||||||
doAbility(a, card);
|
doAbility(a, card);
|
||||||
}
|
}
|
||||||
|
if((!attacker->basicAbilities[Constants::MENACE] && !attacker->basicAbilities[Constants::THREEBLOCKERS]) ||
|
||||||
|
(attacker->basicAbilities[Constants::MENACE] && attacker->blockers.size() > 2) ||
|
||||||
|
(attacker->basicAbilities[Constants::THREEBLOCKERS] && attacker->blockers.size() > 3))
|
||||||
observer->cardClick(card, MTGAbility::MTG_BLOCK_RULE);
|
observer->cardClick(card, MTGAbility::MTG_BLOCK_RULE);
|
||||||
opponentsToughness[bestAttacker] -= card->power;
|
else
|
||||||
|
set = 1;
|
||||||
for (size_t i = 0; i < extraBlockers.size(); ++i)
|
}
|
||||||
{
|
else
|
||||||
MTGCardInstance* extra = extraBlockers[i];
|
{
|
||||||
if (extra->blockCost)
|
set = 1;
|
||||||
{
|
|
||||||
MTGAbility* a = observer->mLayers->actionLayer()->getAbility(MTGAbility::BLOCK_COST);
|
|
||||||
doAbility(a, extra);
|
|
||||||
}
|
}
|
||||||
observer->cardClick(extra, MTGAbility::MTG_BLOCK_RULE);
|
|
||||||
opponentsToughness[bestAttacker] -= extra->power;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -4413,7 +4269,7 @@ int AIPlayerBaka::receiveEvent(WEvent * event)
|
|||||||
|
|
||||||
|
|
||||||
AIPlayerBaka::AIPlayerBaka(GameObserver *observer, string file, string fileSmall, string avatarFile, MTGDeck * deck) :
|
AIPlayerBaka::AIPlayerBaka(GameObserver *observer, string file, string fileSmall, string avatarFile, MTGDeck * deck) :
|
||||||
AIPlayer(observer, file, fileSmall, deck)
|
AIPlayer(observer, file, fileSmall, deck)
|
||||||
{
|
{
|
||||||
|
|
||||||
nextCardToPlay = NULL;
|
nextCardToPlay = NULL;
|
||||||
|
|||||||
Reference in New Issue
Block a user