Learning Python to Play Magic the Gathering

By Cole Kepford

Cole Kepford
8 min readMar 1, 2021

The purpose of this project is to learn the programming language Python and use it to write an algorithm to play the first three turns of a specific Magic the Gathering (MTG) deck. I wished to learn Python because it’s the most common language associated with Data Science which I have an interest in. The purpose of the algorithm is to play a specific MTG called Tron. It is called Tron because when it assembles a certain combination of cards the deck becomes much more powerful. This is a problem I’ve always wanted to try and solve so I’ll use this as an opportunity. The first half of this blog will be detailing how Python works, and how it differs from Java. While the second half of the blog will be detailing the problems to solve with creating the algorithm.

Differences between Python and Java

Python is an objected oriented, open source, interpreted, and high-level programming language. It was created and released in 1991 by Guido van Rossum. Python’s main draw for usage is clearly its supposed ease of use and comprehension. Java is also an object oriented, high level programming language like Python, but it’s not open source or interpreted. Java was developed in 1995 by James Gosling at Sun Microsystems. Java’s primary appeal has and still is its virtual machine that allows it to run on any device. Java and Python are similar in many ways. They both compile into bytecode, they each have strong multi-platform capability, and have massive libraries built in. One of the primary performance differences is that Python almost always compiles at runtime rather than before. The way most programming languages compile is that the source code is read line by line into bytecode/machinecode and optimized by a compiler. Meaning the source code in Java will have all been converted into bytecode before any code is executed. Python on the other hand compiles its source code line by line during runtime. This difference makes Python significantly slower than Java but provides the ease of comprehension of the language.

Syntax Differences

After learning some introductory Python I can say with certainty that if you had to program in any language with no experience Python is the obvious choice. Say I wanted to simply make an array of strings and print them all out.

Java Message

public class Learning 
{
public static void main(String args[])
{
String array[] = {"I\'m gonna", "learn some", "Java" , "420"}; for (String i : array)
{
System.out.println(i); } }}

Python Message

message = ["I'm gonna", "learn some", "Python", 420]for i in message:    print(i)

You’ll notice some big differences. In Python statements don’t require a semicolon to end them. This is accomplished because whitespace is actually used as syntax in Python. A new line can determine the next statement and an indentation determines what’s within a prior statement. In Java to print out this message a class had to be declared. Then the Main method is invoked to tell the JVM what to run. Even the print statement is simpler in Python. Another huge difference is that Java is a static language while Python is a dynamic one. In the Java array the number 420 had to be declared as a string because the data type of the array is String. In the Python you can put whatever data you want into an array. In static language your variables cannot change. Where as in a dynamic language the data types aren’t declared and can change during runtime. Let’s try something a bit more complicated with a bubble sort in either language.

Java Bubblesort

static void bubbleSort(int arr[]){    int n = arr.length;    for (int i = 0; i < n-1; i++)        for (int j = 0; j < n-i-1; j++)            if (arr[j] > arr[j+1])            {                int temp = arr[j];                arr[j] = arr[j+1];                arr[j+1] = temp;            }}
public static void main (String [] args)
{
int arr[] = {69, 420, 42, 10000, 314, 316, 101};
bubbleSort(arr); System.out.println("Sorted funky numbers is:"); for(int i: arr) { System.out.println(i); }}

Python BubbleSort

def bubbleSort(arr):    n = len(arr)    for i in range(n-1):       for j in range(0, n-i-1):            if arr[j] > arr[j+1] :                arr[j], arr[j+1] = arr[j+1], arr[j]arr = [69, 420, 42, 10000, 414, 316, 101]bubbleSort(arr)print ("Sorted funky numbers is:")for i in range(len(arr)):print ("%d" %arr[i]),

Some big differences to point out. Datatypes on variables don’t need to be declared in Python as the interpreter determines the most optimal data type at runtime. A function in Python is created by using the def keyword. The len() function returns the length of a given array. A main method is not required. Not that for statements in Java were ever that hard to understand, but having code that says

for i in range(n-1):

is a lot easier to read from a non coding background than the standard Java for loop. A really cool thing I learned you could do is assign multiple variables different values in the same statement.

arr[j], arr[j+1] = arr[j+1], arr[j]

What Do I Prefer?

While I still haven’t gotten very deep into Python’s libraries and inner workings, I can definitely see the appeal of the language. It’s slick, easy to read, and super easy to start developing in right away. On the other side, I do like that I have a bit more control over what’s going on in Java. Although the ease of writing and reading Python comes at the cost of significant performance costs compared to Java and other programming languages. I’ll probably still be a Java fanboy after this project is finished but Python is certainly putting up a good fight.

Playing Tron

What is Magic the Gathering?

For those who aren’t aware, MTG is a two player trading card game designed by Richard Garfield released in 1993. The basic principles of MTG are that there are five colors of mana, blue, red, green, white, and black. The cards that you play with are made up of these colors. Each color has different play styles, properties, and philosophies. There are a bunch of different types of cards in MTG but to keep it simple we’ll divide them by lands and non-land cards. Non-land cards cost 0-X mana (most range between 1 & 5) in any combination of the previously mentioned colors. Land cards produce mana when tapped (turned sideways). There are basic lands which produce the one color they’re related to. Mountain-red, swamp-black, island-blue, plains-white, forest-green. MTG decks have a minimum size of 60 cards and a maximum of four copies of any given card, besides basic lands. Each player has 20 life and loses when either they life is reduced to zero, or when attempting to draw a card there are no cards left in their deck. Additionally, you can play up to one land a turn. At the start of the game each player draws seven cards from their deck and decides whether to keep or mulligan. If you mulligan you draw seven cards then put cards on the bottom equal to how many mulligans you’ve taken. You can mulligan to a zero card starting hand for example.

The Deck

Now the reason I have picked the deck called Tron to write an algorithm for is that it’s a deck that has a simple goal in mind. Assemble three different lands called Urza’s Tower, Urza’s Mine, and Urza’s Powerplant. The decks nickname comes from the cartoon Voltron where a number of vehicles assemble to create the mighty robot Voltron.

When at least one of each copy of these lands is in play the lands produce a total of seven mana or respectively three, two, and two colorless/generic mana. Generic mana can be a combination of any color(s) of mana. The entire purpose of the deck is to assemble the above combination of lands as quickly as possible and then play cards with very high mana costs. The algorithm I will be writing will try and determine the rate at which the deck can assemble “Tron” by turn three as well as having a payoff to play with the large mana pool.

The cards that enable Tron to assemble it’s combo so quickly are (lands are colorless cards):

Problems to Solve

The main things that I’ll have to program using Python will be the deck itself. Which will consist of payoffs, Tron lands, some basic lands, and the searching engine listed above. As well as some cards that are don’t have an effect and are blank. I will then have to decide on a criteria system for keeping and mulling hands. Such as, is one Tron land and 3+ enablers a hand you can keep? How many mulligans should be attempted before you mulligan to zero? Is two Tron lands and no enablers a keepable hand? While the deck won’t be playing against an opponent I will still have to program some of the basic elements of MTG. Such as drawing a card each turn, shuffling your deck, and playing cards in the right order to maximize the chances of assembling Tron. While I won’t be attempting to go past turn 3, there are many different decision trees that I need to write to get accurate results.

In Conclusion

I have enjoyed getting to learn a bit of Python and how it stacks up compared to Java in regards to syntax, composition, and performance. I am excited to try and tackle this MTG problem as it’s something I’ve really wanted to try for a longtime. I hope you have been able to learn a bit about Python and the game Magic the Gathering.

Video of Tron Playthrough and Talkthrough of Code

--

--