MTG Card Data Storage Made Easy: A Tutorial on Using Apache AGE
Tags: tutorial, opensource, database, postgres
Published: 2023-02-21 23:34:12

MTG is a popular collectible card game that has been around for over 25 years. With over 20,000 unique cards in its database, storing and accessing this information can be a daunting task. However, with Apache AGE and Cypher, we can easily create a graph of MTG card data, allowing us to easily query and manipulate this information.

First of all, you'll need to have Apache AGE installed on your machine. Here are the links for the [MacOS](https://www.youtube.com/watch?v=0-qMwpDh0CA&t=0s) and [Windows](https://www.youtube.com/watch?v=ddk8VX8Hm-I) installation (you can also download it for linux following the steps on the [Apache AGE Documentation](https://age.apache.org/age-manual/master/intro/setup.html)). Once you have installed it, create a new database and start the server.

Let's create a graph to store our MTG cards:

```sql
SELECT * FROM create_graph('MTG');
```
Now, let's create the vertex labels for our MTG card data. There are seven different card types in Magic:

1. Creature
2. Instant
3. Sorcery
4. Enchantment
5. Artifact
6. Planeswalker
7. Land

```sql
SELECT create_vlabel('MTG', 'Creature');
SELECT create_vlabel('MTG', 'Instant');
SELECT create_vlabel('MTG', 'Sorcery');
SELECT create_vlabel('MTG', 'Enchantment');
SELECT create_vlabel('MTG', 'Artifact');
SELECT create_vlabel('MTG', 'Planeswalker');
SELECT create_vlabel('MTG', 'Land');
```

In Magic: The Gathering there are many "sets". A "set" refers to a group of new cards that are released together and are designed to be played with one another. Each set typically introduces a unique theme, mechanics, and new card designs to the game. Some sets are connected to a particular plane (a world within the MTG universe), while others are standalone sets that explore a specific theme or idea. 

Here we are going to cover some cards from the *Mirrodin* set. This set focus on artifacts and the synergy that they add to the game. For example, *Auriok Steelshaper* is a creature card that, as long as it is equipped with an artifact, other *Knight* and *Soldier* creatures gain +1/+1 and, also, Auriok reduces the cost to cast equipment spells by 1 mana.  A nice synergy in the game would be to have Auriok in play, equip him with an artifact like *Lightning Greaves*, which protects him from other spells, and then cast creatures that are soldiers/knights or instant/sorcery spells that make them, such as *Raise the Alarm* that creates two 1/1 soldier creature tokens. But then, with Auriok equipped with the greaves, these soldiers become 2/2 because of Auriok's ability.


![Card Synergy](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4u5iy24gausm67oathpf.png)

Lets add these cards as vertices to our graph:

```sql
-- Raise the Alarm
SELECT * FROM cypher('MTG',$$
CREATE (c:Instant {
name: 'Raise the Alarm',
set: 'Mirrodin (MRD)',
card_number: 16, rarity: 'Common',
mana_cost: '{1}{W}',
artist: 'John Matson',
oracle_text: 'Create two 1/1 white Soldier creature tokens.',
flavor_text: 'The nim raid our homes without warning. We must defend our homes without hesitation.'})
RETURN c
$$) as (Instant_Card agtype);

-- Auriok Steelshaper
SELECT * FROM cypher('MTG',$$
CREATE (c:Creature {
name: 'Auriok Steelshaper',
set: 'Mirrodin (MRD)',
power: 1, toughness: 1,
card_number: 4, rarity: 'Rare', type: 'Human Soldier'
mana_cost: '{W}{1}',
artist: 'Dany Orizio',
oracle_text: 'Equip costs you pay cost {1} less. As long as Auriok Steelshaper is equipped, each creature you control that’s a Soldier or a Knight gets +1/+1.',
flavor_text: 'They put their safety in his hands. He puts sharpened steel in theirs.'})
RETURN c
$$) as (Creature_Card agtype);

-- Lightning Greaves
SELECT * FROM cypher('MTG', $$
CREATE (c:Artifact {
name: 'Lightning Greaves',
set: 'Mirrodin (MRD)',
card_number: 199,
rarity: 'Uncommon',
type: 'Equipment',
mana_cost: '{2}',
artist: 'Jeremy Jarvis',
oracle_text: 'Equipped creature has haste and shroud. (It can’t be the target of spells or abilities.) Equip {0}'})
RETURN c
$$) as (Artifact agtype)
```
Since *Auriok Steelshaper* ability is intrinsically related to equipments and also *Soldier* and *Knight* creature cards, we  then can find these cards with the following query:

```sql
-- Find the equipments
SELECT * FROM cypher('MTG', $$
MATCH (card)
WHERE card.type = 'Equipment'
RETURN card
$$) as (card agtype);

-- Find Soldiers and Knights
SELECT * FROM cypher('MTG', $$
MATCH (card)
WHERE card.oracle_text CONTAINS 'Soldier'
OR card.oracle_text CONTAINS 'Knight' 
OR card.type CONTAINS 'Soldier'
OR card.type CONTAINS 'Knight'
RETURN card
$$) as (card agtype);
```

Having successfully found the cards that have a good interaction with Auriok, we can create edges between him and other cards.

```sql
-- Make edges between Auriok and Equipments
SELECT * 
FROM cypher('MTG', $$
    MATCH (a:Creature), (b:Artifact)
    WHERE a.name = 'Auriok Steelshaper' AND b.type = 'Equipment'
    CREATE (a)-[e:HAS_SYNERGY]->(b)
    RETURN e
$$) as (e agtype);

-- Make edges between cards that contains Soldier or Knight as type or in the text
SELECT * 
FROM cypher('MTG', $$
    MATCH (a:Creature), (b)
    WHERE a.name = 'Auriok Steelshaper' AND (
        b.oracle_text CONTAINS 'Soldier' OR
        b.oracle_text CONTAINS 'Knight' 
        OR b.type CONTAINS 'Soldier'
        OR b.type CONTAINS 'Knight')
    CREATE (a)-[e:HAS_SYNERGY]->(b)
    RETURN e
$$) as (e agtype);
```

In AGE Viewer, we can run this query to visualize the nodes and edges we created:

```sql
SELECT * from cypher('MTG', $$
        MATCH (V)-[R]-(V2)
        WHERE V.name CONTAINS 'Auriok'
        RETURN V,R,V2
$$) as (V agtype, R agtype, V2 agtype);
```

![Synergy with Auriok](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7mkrxzqsgksbyqr8txzz.png)

As you can see, AGE Viewer shows us the nodes which Auriok has a synergy with. Imagine what AGE could do with a larger card database, with more cards to analyze, other strategies it could look at, show which decks and cards the pro players have been using, many more things could be done with AGE and in this tutorial we have only scratched the surface of the amount of things it could do!

If you are developing a card game for mobile or desktop and want to add a graph database to it, or if you want to create a website that can analyze the cards from a card game that you love and store the data in a graph database, then Apache AGE is your best choice. Level up your Game Database with Apache AGE: The Ultimate Graph-based Solution!

Apache AGE Website: https://age.apache.org
Apache AGE GitHub repository: https://github.com/apache/age