I’ve been working with Scala a lot as of late, on a lot of personal projects. I’ve decided I need to get more out to show the world. At a loss of what to do, this kind of came out of nowhere. I’ve always been a huge fan of Magic, and last night while observing card and creature types, I got to thinking. “Hey… I can probably represent this and their rules using Scala and it’s type system.”
As an aside: functional programming has changed the way I look at programming and has (in my opinion) turned me into a more robust engineer. As functional programming uses a few different concepts heavily, it has also solidified my understanding and confidence with all sorts of concepts such as recursion and immutability-focused (“pure”) functions. I’ll write more about this soon.
Anyways… Scala’s type system is rather verbose and allows for one to express all sorts of concepts rather abstractly. I am going to be using a few key concepts for this project: monads, first-class functions, implicit parameters and classes, typeclasses, and structural subtyping (also known as duck typing). I am also considering using trait mix-in composition to represent concepts such as a card’s colour, but I’m not sure if this is as viable as simple using case objects.
All source code for the project can be found on Gitlab.
For now, let’s go over some of the basic structures. Here is the basic layout for a card:
That’s a lot of things! It’s ok, they’re all rather simple. name, artist, power, and toughness are all rather straight-forward. Our card’s flavourText is represented with an Option[String] as not all cards will contain flavour text. (Quick edit: I realize the card abilities are not present. That has been remedied and will be talked about in the next post.) However, our cardSetInfo, colours, types, and manaCost are more complex, with the latter being considerably more complex than those prior. Let’s take a closer look at the first three:
I won’t go to into the details about the implementation of the classes represented in CardSetInfo, they can be viewed in the source code. expansions is a Seq[Expansion] as cards can belong to multiple expansions due to reprinting.
I am considering implementing the two above traits as mix-ins as opposed to what are essentially enumerations. I have run into problems in the past with going too far with sub-typing in Scala as the language does not support multiple-inheritence. I have yet to experiment. A post coming soon will contain the results, no doubt.
Now, ManaCost is a bit more complicated:
I’m using a sorted map here so that my mana is displayed in the order one would find mana strings in text. Say, one blue mana, two any mana => 2U. Two black mana, one green mana => BBG. I may change this up to be the order that mana is displayed on a physical card, but this works for now.
To determine the converted mana cost, I’m simply folding over each mana type present in the cost and adding their totals to my accumulator. Now that I’m writing this, I realize I can simplify this as iv.values.sum.
The simplify function simply removes all mana costs that are set at 0. I am looking at having Mana.AnyMana -> 0 to be allowed as there are cards with a cost of 0 (eg, Memnite, Ornithopter, etc.).
How does the ManaCost class work? Quite simply, really:
I may add functionality to make mana costs composable. eg) 3wwMana compose wgMana => 3WWWG.
That’s all I’ll write about for now. Next post will detail how I am going to import a collection of MTG cards (probably using the JSON available at MTGJson. I will also detail my plans for a game state and how to handle the mutability. Also the introduction of some simple rules, such as applying Exalted.
Thanks for reading!