Korath wrote:The way the AI picks which creatures to attack with... tentatively... is to check each to see whether it can attack and mark it as attacking; and then cull the ones it doesn't like. The problem is that everything gets marked as attacking in the first step, and attack legality isn't ever checked again afterwards.
This was almost right, but not quite.
The way the AI chooses attackers (in the first half of the aptly-labeled ai_choose_attackers() function at 0x4b02c0 Magic.exe/0x41f5e3 Shandalar.exe):
- Make a list of the first 16 creatures that can attack and that don't have banding. (Creatures with banding are kept track of separately. Shandalar doesn't stop when it gets to 16 creatures, but still only has room to store 16, which is why it sometimes goes into an infinite loop or crashes during large creature stalemates.) This is the only point where can_attack() is checked; each non-banding creature that can attacked is marked STATE_ATTACKING, as an implementation detail; that's why the AI can attack with a lone Mogg Flunkies only if there's another creature before it. The STATE_ATTACKING bit is overwritten in step D below.
- If there's at least 8, then
- Remove the attacker with the highest power from consideration for each untapped creature the defending player controls (whether it can block or not).
- If the remaining potential attackers' total power is at least as high as the defending player's life, all of them attack.
- Otherwise, remove all of those creatures except the six with the highest power from consideration; then add back in all the ones that must attack (due to text like Juggernaut's), up to a maximum of, again, 16 potential attackers.
- Number the potential attackers from 0 to 15, and make them into a bitfield.
- Loop over each possible combination of those up-to-16 potential attackers. This is implemented as looping from 0 to (2^number_of_potential_attackers - 1) and flagging each attacker whose bit is set as attacking and the others as not attacking.
- If a potential attacker that's an Erg Raiders, a Juggernaut, or a creature marked as must-attack is marked as not attacking, skip this combination.
- Otherwise, go on to compute a relative desirability of that combination of attackers.
So, in order to prevent the AI from attacking with a single
Mogg Flunkies, we'd need to add another step similar to D.a that looks at the combination of attacking creatures as a whole.
The relevant part of ai_choose_attackers() is easy to identify once you start looking for it; it looks like
- pasted from magic.c | Open
- Code: Select all
v55 = -1;
while (1)
{
++v55;
if (1 << v66 <= v55) // v66 is number_of_potential_attackers
break;
v56 = 1; // this combination ok by default
dword_607CCC = 0;
dword_60779C = 0;
card = -1;
while (1)
{
++card;
if (v66 <= card)
break;
v23 = get_card_instance(player, Dst[card]);
LOBYTE(v23->state) &= 0xFBu; // ~STATE_ATTACKING
v59 = *(_DWORD*)&cards_data[v23->internal_card_id].id;
if ((1 << card) & v55)
{
LOBYTE(v23->state) |= STATE_ATTACKING;
WILDGUESS_ai_combat_attack_card[dword_607CCC] = Dst[card];
WILDGUESS_ai_combat_attack_power[dword_607CCC] = v88[card];
WILDGUESS_ai_combat_attack_toughness[dword_607CCC] = v89[card];
WILDGUESS_ai_combat_attack_abils[dword_607CCC] = v52[card];
WILDGUESS_ai_combat_attack_attack_rating[dword_607CCC] = v64[card];
WILDGUESS_ai_combat_WILDGUESS_blocking_choice[dword_607CCC] = v50[card];
WILDGUESS_ai_combat_attack_defensive_modifier[dword_607CCC] = dword_6078A4[card];
WILDGUESS_ai_combat_block_defensive_modifier[dword_607CCC] = dword_607718[card];
if (v59 == CARD_ID_COCKATRICE || v59 == CARD_ID_LURE)
dword_60779C |= 1 << dword_607CCC;
++dword_607CCC;
}
else
{
if (v59 == CARD_ID_ERG_RAIDERS || v59 == CARD_ID_JUGGERNAUT)
v56 = 0;
if ((1 << card) & v87) // v87 is a bitfield of cards marked must-attack
v56 = 0;
}
}
// insert new step here and set v56 = 0 if it fails
if (v56)
{
// step D.b.
This doesn't address blocking at all, nor the human's choice of attackers. While it'll allow the AI to attack with two
Mogg Flunkies and nothing else, the current implementation won't let the human do so.