Original Post — Direct link

WOTCStaff

There’s a closely guarded secret about Magic Arena, and in particular the rules engine, that I’m going to reveal to you now. Here it is. Brace yourself:

Magic Arena is a software product developed by human beings. Therefore, it sometimes contains bugs

I know, shocking, right?

But, just in case you didn’t notice that we had to ban Fabrication Foundry for a week when it was first released, or in case you weren’t aware of the minor unpleasantness surrounding Ninja’s Kunai, I will repeat: Arena’s rule engine sometimes contains bugs.

Today I’m going to tell you about some of those bugs. Not the ones you’ve already heard about. But the other kind of bugs. Secret bugs. Bugs in rules interaction that are so obscure, so special-case, that they actually existed on the live Arena servers for weeks, months, or even years, and no one ever encountered them, until we stumbled across them and fixed them. These are their stories.

The Warboss’s overly aggressive minions

The card:

https://preview.redd.it/200oocsahaid1.png?width=672&format=png&auto=webp&s=0ccdc7a24639f06aaf6dbe113cc978485bcb9739

The bug:

Legion Warboss creates a goblin. That goblin “attacks this combat if able”. What does that mean, precisely? In particular, when does that wear off? When does it stop being "this combat"?

When the card was first implemented on Arena, the effect wore off at the beginning of the next end of combat step. Which makes sense. What signals that combat is ending? The end of combat step. 

But… that’s not quite right. Because turns can end at any point, thanks to our good friend [[Time Stop]]. When Time Stop is cast, there’s an immediate cleanup step, during which "this turn" and "until end of turn" effects wear off. But the end of combat step was skipped over entirely.

So, if you controlled Legion Warboss, and created a goblin, and the goblin had to attack, but then during declare blockers you cast Time Stop, and then you waited until your next turn, when you got to combat, your original goblin… would correctly not have to attack. Because that effect would have ended at the beginning of your opponent’s end of combat step. But if you did that experiment, and then cast Time Stop again before your opponent’s combat step, then, and only then, you would be rewarded by the extremely abusable bug of… having a goblin that had to attack when it shouldn’t have had to attack.

How it was found:

While working on “that creature attacks during its controller’s next combat phase if able” on [[Sizzling Soloist]].

The Fix:

Adding an event that fires whenever a phase is ending, regardless of whether the phase is ending normally or due to a time stop effect; and using that event to clear the “must attack” effect.

Likelihood that any player ever encountered it:

Extraordinarily low. Requires a sequence of actions no player would take for any reason other than to test if this bug existed.

Mr. Zada’s Opus

The card:

https://preview.redd.it/xos0opfchaid1.png?width=672&format=png&auto=webp&s=3b1e95aa246ba1f562f09e49493e9e658069f7c2

The bug:

Consider the text “a spell that targets only Zada”. What does that mean? Well, it’s not quite as simple as it sounds, because Magic spells can have multiple targets. In fact, they can have multiple groups of targets. Consider something like “tap one or more target lands. Untap one or more target creatures.” That’s two different targeting actions, each with its own group of targets, each of which can be empty, which might or might not overlap. So, we need a function to look at the targets of a spell and determine if it targets “only” something. 

As originally written, that function said:

“If there’s one group of targets with only one member, and that member is the relevant object AND if every group of targets includes the relevant object”.

But… that’s not quite right. Because it will return a false positive in the case of a spell with multiple targeting groups, with one group of targets containing only the relevant object, and the other group containing the relevant object plus other objects.

So, what’s a spell that could be cast in that fashion? Well, turns out there’s a pretty prominent one:

[[Magma Opus]]

When Zada first went live, if you cast Magma Opus, chose Zada as the only target for “deal 4 damage”, and then chose Zada and another permanent for “tap two target permanents”, then Zada would, incorrectly, trigger, and attempt to make a lot of copies of Magma Opus.

How it was found:

While working on the “instant or sorcery spell you control that targets only a single creature” clause of [[Immodane the Pyrohammer]].

The fix:

An object is the only target of a spell if it is the only member of at least one group of targets, and if no group of targets contains any other objects.

Likelihood that any player ever encountered it:

Possible but unlikely. Typically, the only reason to assign all four damage from Magma Opus to a single creature is if that damage is lethal. And if the damage is lethal, why bother tapping that creature?

A humiliate-ing loss

The card:

https://preview.redd.it/3fk5zdndhaid1.png?width=672&format=png&auto=webp&s=d08d88c0a2b82ddb8606d340834cb637d87410d2

The setup for the bug:

Here’s the situation:

You’ve just drawn the last card in your library, so you need to win this turn. Your opponent controls no ccreatures. You control a [[Suncleanser]], which was targeted by its own “it can’t have counters put on it” ability, and a [[Jewel Thief]]. The Suncleanser is tapped, so it can’t attack. But your opponent is at 3 life. So, the way seems clear to attack with your Jewel Thief for lethal.

But, your opponent has two cards in hand, and lots of untapped lands. And you just drew [[Humiliate]]. What’s the play? Clearly, you should cast Humiliate first, in case your opponent has some instant that can destroy an attacking creature. Right?

But, when Humiliate resolves, you see that your opponent’s hand is two copies of [[Defend the Campus]]. You make them discard one, but they still have one left. However, Humiliate has more text: “Put a +1/+1 counter on a creature you control.” And if you put a +1/+1 counter on Jewel Thief, it will have 4 power, and your opponent will be able to kill it with Defend the Campus.

So, should you be able to win this game?

The answer, possibly surprisingly, is no. It might seem like you should be able to. It feels like you ought to be able to say “OK, I choose to put a +1/+1 counter on Suncleanser”. Then Suncleanser’s ability stops that from happening. Which is fine, you didn’t need it anyhow. Then your Jewel Thief still only has three power, and you can attack for the win.

But, that’s not accounting for the Magic Comprehensive Rules, 608.2d. You can’t choose to do an impossible thing. If an effect instructs you to “tap a creature you control”, you can’t choose a tapped creature and fail to tap it. If an effect instructs you to “sacrifice a creature you control”, you can’t choose one equipped with [[Assault Suit]] and fail to sacrifice it. And when Humiliate resolves, you can’t choose a creature which can’t have +1/+1 counters put on it, then fail to put a counter on it.

The bug:

As you might have guessed, Arena wasn’t enforcing this correctly. So on Arena, you could have chosen the Suncleanser to get the counter. The Suncleanser effect would have properly prevented the counter from actually being put there, but not from you choosing it in the first place.

How it was found:

While working on [[Bustle]], whose text “you may turn a creature you control face up” similarly lets you choose a creature to do something to, which should be constrained to only creatures you can actually do-the-thing to.

Likelihood that any player ever encountered it:

Very very low. Only a tiny number of cards are affected by this interaction (in particular, you can still target a Suncleanser with a spell or ability that would put a +1/+1 counter on it), and it requires a pretty contrived situation to want to choose an illegal recipient for a +1/+1 counter.

The Gitrog doesn’t care about math!

The card:

https://preview.redd.it/suqzepsfhaid1.png?width=672&format=png&auto=webp&s=83cbfad37896d3ed6580c54adffba1f4178446f7

The bug:

Crew The Gitrog with a 2-power creature. Attack with The Gitrog. Now, shrink the power of the creature that crewed it to be negative (for instance, with [[Code of Constraint]]). Then, The Gitrog deals damage to your opponent, and its trigger goes on the stack. When the trigger resolves, sacrifice the negative-powered creature. At that point, nothing should happen. You should draw zero cards and not even get an option to choose zero land cards in hand to put onto the battlefield.

But what DID happen, for a while, was that the game would show you this message:

https://preview.redd.it/6vxqdgu2haid1.png?width=623&format=png&auto=webp&s=4c885a5ba4d70c4f6f740ae2af3399fd8fc8fe32

And then you would be stuck forever. The server would send a message to the client saying “please have the player select a number of land cards in their hand that is greater than or equal to zero, but also is less than or equal to negative two”. Nothing the client did could possibly correctly fulfill that request, so the game would be stuck in a loop forever.

How it was found:

This bug was found by a program we have called RoboQA, which plays tens of thousands of games of Arena every night. It puts together random decks. It plays them against each other. And every time it needs to make a decision, it chooses a random legal choice. And, if any of those games either crash or hang, it reports that bug for a programmer to fix.

The cool thing about RoboQA is that it plays vastly more games than our QA or development team could possibly play, and it happily makes bizarre choices that no human would ever make, leading it to find crazy interactions like this one.

The drawback of RoboQA is that it won’t notice if things work wrong. (After all, if we had a perfect rules engine that could examine the correctness of every RoboQA game, well, then we would use THAT rules engine as the Arena rules engine. But… what would verify the correctness of THAT rules engine? It would need its own RoboQA, etc.) So RoboQA can’t catch incorrect rules enforcement, it can only catch crashes and hangs. (We have an entirely different set of human-written tests that are constantly re-verifying the correctness of rules interactions, but they only verify cases that we think of.)

The fix:

Any time we’re sending a message from the server to the client requesting that the player choose a quantity of game objects, with a minimum and/or maximum number of objects selectable, we limit the min and max constraints to be non-negative.

Likelihood that any player ever encountered it:

Very low. A creature with negative power can’t (by itself) crew The Gitrog. And creatures don’t often end up with negative power on the battlefield. This is another one that a player would generally only encounter while specifically looking for this bug.

Finally, we have one additional bug story provided by another programmer, who definitely enjoys crustacean-related wordplay.

Stacking up some mana for convoke 

The card: 

The Convoke mechanic  

The setup for the bug: 

You control no tapped lands, 4 [[Wishcoin Crab]]s and a [[Prophetic Prism]].  Your opponent (the jerk) is attacking you for a bunch and you have a [[Pause for Reflection]] in your hand that you Wish you Coin cast. But you can’t. You’re dead, but haven’t passed through the first stage of grief yet. You’re in denial. So you pull the Pause for Reflection out of your hand to cast. You look at your Prophetic Prism. It could make Green mana. You click on it. “Pay (1)”. You Wish you could.  

The bug: 

You move your mouse-pincer over to your crabs and they happily tap themselves to pay for your Prism.  You cast your Pause for Reflection by tapping 2 more crabs. You live through the turn, and then attack back for the win.  Maybe your opponent wasn’t the one who was the jerk after all.  

Convoke lets you tap creatures to pay for a spell’s cost.  But we have to spell this out very clearly in our code.  If you’re paying for mana, and if that mana is for a spell**, and if** that spell has convoke**, and if** that spell is the topmost item on the Stack, then you can tap a creature you control to pay for some of that mana. 

Except, mana abilities, like Prophetic Prism, don’t use the stack.  They’re too impatient.  So this check wasn’t completely accurate. 

How it was found: 

March of the Machine: Aftermath has [[Markov Baron]] (with Madness and Convoke).  Convoke also didn’t work right when cast on an opponent’s turn using Madness.  While reading through the code for the convoke, I randomly spotted this issue. Hurray for good variable and function names. 

Likelihood that any player ever encountered it: 

Very Low. This bug existed on Arena since basically forever. But the interaction was unlikely to occur because Guilds of Ravnica (the main place with Convoke) didn’t have anything like Prophetic Prism. The combination of cards was unlikely to occur in constructed given the overall power level of the cards involved, and how unlikely it would be that you didn’t have lands or creatures of the correct color for your spell. However, we managed to fix this bug in time for March of the Machine, which had [[Urn of the Godfire]] as well as many convoke cards. That would have dramatically increased the chance of being hit (from ‘Very Low’ to ‘Low’) among the many matches of limited being played.  It still would have required not having other corresponding mana or creatures, as well as thinking to try casting the convoke spell anyway.

External link →

Originally posted by fractalspire

So, if there's one card on Arena that we can be very very certain won't cause crashes, it's Unknown Shores.

Unknown Shores (and mana filtering in general) is kind of a pain in the butt for Autotap too, so having it in RoboQA games helps stress test Autotap's performance and internal assertions about its correctness too. #wotc_staff

4 months ago - /u/WotC_Jay - Direct link

Originally posted by harambe_did911

Can we get an in game report option? How much resources are put into investigating reports? I take the time to go to the website and report people who just close the all when they lose and make me watch their rope, but I'd like to know if I'm wasting my time?

We do look at player reports when deciding which accounts get suspended/banned for roping, but we also have pretty comprehensive gameplay data to let us identify which players are being problematic here as well. So, you can report people if it makes you feel better, and it does help. But don't feel like you have to.

Originally posted by fubo

What was the fix for the bug where you could evoke Mulldrifter repeatedly from the graveyard with Muldrotha?

The issue there was that the Evoke action was incorrectly taking credit both for being an alternative cost (correct) AND for being the permission source of the action (wrong, Muldrotha is). So when deciding whether to use up part of Muldrotha's permission, the action isn't marked as relevant. The fix was a one line change to have the Evoke action copy over the permission source from the action it was duplicating instead of overwriting it. Definitely the sort of typical human error Alex alludes to. #wotc_staff

Originally posted by Approximation_Doctor

Interesting that the Gitrog bug didn't have any problems with drawing -2 cards.

Which card would you say had caused the most bugs overall? Either on live or in testing? Anything that looked simple but turned out ridiculous?

I'd like to give a shoutout to Underworld Breach. It had two classes of major problems due to being the first card to confer a non-mana cost to arbitrary cards.

Firstly, it made payment and affordability checks much more complicated. If you have something like Tormenting Voice, you can discard a card for the additional cost before exiling it from your graveyard. We don't let you choose payment order on Arena, so we had to change things to get the order to be smart here. For affordability, we had to predict how the costs could interact in terms of payment resources.

The other major category was linked ability bugs. Cards exiled to pay the escape cost were incorrectly linking to text like "the exiled card" due to us not yet having a good signal that the escape cost wasn't "native" to the card. We played whack-a-mole with bugs in this space for months before getting a more general solution that seems to work for all current MTG behavior (fingers crossed!). #wotc_staff

4 months ago - /u/WotC_Jay - Direct link

Originally posted by MrMarijuanuh

Do you ban/Suspend people who just close the app instead of conceding?

Yes, that will get picked up as well

4 months ago - /u/WotC_Jay - Direct link

Originally posted by jimbojones2211

"Would you like to play a nice game of Chess?"

I've played against these random bots, and there's nothing nice about those games. Even when you know that it's random, it's still disorienting, like trying to have a conversation with someone who replies with random words.

4 months ago - /u/WotC_Jay - Direct link

Originally posted by jimbojones2211

"What if chat GPT drooled and played magic?" I can get that.

You might be missing my reference to an 80s movie though.

The only winning move with references is not to play ;)