Howp to Upload Your Mod to Slay the Spire Workshop
Inside AI
Bringing Deep Neural Networks to Slay the Spire
Predicting Fight Outcomes and Evaluating Cards with Keras and TensorFlow
Slay the Spire is one of my favorite video games and combines elements of rogue-like dungeon crawlers with deck edifice. Having just completed a deep learning course in my final year of university, I thought Slay the Spire would exist a peachy platform for my first auto learning project.
Deck Edifice is a Hard Problem to Solve
Choosing which cards to add to your deck is i of the nigh of import strategic decisions in Slay the Spire. Players need to add good cards, remove bad cards, and upgrade important cards to improve their deck and defeat the increasing difficult enemies and bosses in a run. Determining the best card to add your deck in different situations is extremely difficult and the best players in the world still struggle to know what the right choice is all the fourth dimension.
To further add to the problem of evaluating which bill of fare is better to add to your deck, different cards perform better against different enemies. Typically, a card that attacks all enemies is better in a fight with multiple enemies than in a fight against a single enemy. Additionally, the strength of cards relative to the other cards in a thespian'south deck changes throughout a run. For example, mediocre attack cards are often a player's main source of damage in hard fights early in the game, and therefore, are fairly valuable. Still, these same cards are a lot worse relative to the other cards in a thespian'south deck later in the run when the role player has acquired meliorate sources of impairment.
Each playable character has a different set of 75 unique cards which can be combined in numerous ways, creating a diversity of decks and play styles. Evaluating a card based on the boilerplate damage reduced in a fight if a thespian adds the card to their deck, is an unbiased arroyo to create decks relative to these deck sets. Providing this metric to player's helps them make the all-time possible option at each determination node.
Introducing Slay-I
Slay-I is a mod available on the Steam Workshop that predicts how much damage you will take in a fight in Slay the Spire and evaluates adding, upgrading and removing cards from your deck according to average impairment reduced in a fight. This encompasses post-battle carte du jour choices where player'due south tin can cull 1 of iii cards to add together to their deck, upgrading cards at campfires and events, and removing cards at shops and events.
High Level Overview
The middle of Slay-I is a model that predicts the damage taken in a fight based on a player's cards, a player's relics, the enemies the player is fighting and a few other features. Further down I'll unpack how this model can exist used to evaluate cards.
There were three chief components used in training and deploying the Slay-I model:
- Python script to turn run history files into training samples (GitHub repo)
- Python script on Google Colab to build and train a neural network using Keras and TensorFlow (Colab Notebook)
- Java mod running the trained model that Slay the Spire players can download and run with their game (Steam Link)
Creating Training Examples
The to the lowest degree sexy part of whatever machine learning project is about always information pre-processing. But it'southward of import!
Ii different data sets of run history files were experimented with. The first information gear up is from Jorbs, a YouTuber and Twitch streamer who is widely regarded as one of the best Slay the Spire players. The 2d data set is from Spire Logs, a website where players can upload their run information and see analytics.
Run history files have an upshot-similar format:
// Example Run History File
{
"damage_taken":[
{"damage":7,"enemies":"2 Louse","floor":1,"turns":2},
...
],
"campfire_choices":[
{"data": "Combust", "floor": 6, "key": "SMITH"},
...
],
"card_choices":[
{"picked": "Clothesline", "floor":ane, ...},
...
],
"event_choices":[
{"cards_removed":["Strike_R"], "floor": 22, ...},
...
],
"items_purged":["Pain", ...]
...
} A single preparation sample is the data for ane fight in one run. This data includes the cards in a player's deck, the player'south relics, the enemy the actor is fighting, amongst other features. Training samples are created past iterating through the effect-similar format of run history files to recreate runs, floor by floor.
// Training sample
{
"cards": [
"Strike_R",
"Strike_R",
"Defend_R",
"Defend_R",
"Clothesline",
"Fe Wave",
"True Grit",
...
],
"relics": [
"Called-for Blood",
"Pen Nib",
...
],
"max_hp": 82,
"entering_hp": 82,
"ascent": twenty,
"enemies": "Lots of Slimes",
"potion_used": fake,
"damage_taken": 14 // Y-value used in training
} After processing, the Jorbs data set yielded 2 000 training samples and the Spire Logs information set yielded 325 000 grooming samples.
Encoding Inputs
Now comes the question of how to encode a training case to laissez passer into a neural network. max_hp, entering_hp, ascension, and potion_used are already numbers (or booleans). enemies was ane hot encoded.
A modified one hot approach was used to encode cards, relics. Every bill of fare was one hot encoded and then the 1 hot vectors were summed together to create a count of how many of each card were in the deck. The aforementioned process was used for relics.
Note: Upgraded cards were treated as entirely different cards.
# Used for cards vector and relics vector
def encode_list(list_to_encode, category):
np_array = np.array(list_to_encode)
encoder = OneHotEncoder(categories=[category], sparse=False)
n_by_1 = np_array.reshape(len(np_array), ane)
onehot_encoded = encoder.fit_transform(n_by_1)
summed = np.sum(onehot_encoded, axis=0)
return summed The encoded cards, relics, enemies, and other features were concatenated into a single vector to feed to the model.
np.concatenate((cards, relics, see, num_and_bool_data)) Inputs were scaled using MaxAbsScaler so sparsity was not lost during scaling.
max_abs_scaler = MaxAbsScaler()
X_train_maxabs = max_abs_scaler.fit_transform(x_train)
X_test_maxabs = max_abs_scaler.transform(x_test) Outputs (damage taken in the fight) were capped at -100 and +100 and divided past 100 to calibration the output to a value between -1 and ane.
Building and Preparation the Model
Keras makes building deep neural networks a breeze. Dropout layers were added to assist reduce overfit. The Google Colab notebook can exist establish here if you want to build and train the model yourself.
model = tf.keras.models.Sequential([
tf.keras.layers.Dumbo(400, input_shape=(970,), activation='relu'),
tf.keras.layers.Dropout(.2),
tf.keras.layers.Dense(forty, activation='relu'),
tf.keras.layers.Dropout(.1),
tf.keras.layers.Dumbo(1)
]) model.compile(
optimizer=keras.optimizers.RMSprop(learning_rate=.001),
loss='mean_absolute_error'
) history = model.fit(X_train, Y_train, batch_size=32, epochs=5, validation_split=0.ii)
Mean Absolute Mistake (MAE) was used as the loss role for two main reasons:
- MAE had a lower validation loss than Hateful Squared Error (MSE) on the data gear up.
- MAE represented the avg error on predictions. Example: "You will lose vii HP +/- 10 HP in this fight" where X is the Mean Absolute Fault times 100.
Evaluating the Model
Loss * 100 = Avg Impairment Taken Mistake
Jorbs Model Stats
- Training loss = 0.0718
- Validation loss = 0.0767
- Examination loss = .0878
This means that the model is on boilerplate within +/- ix HP of the fight outcome.
Spire Logs Stats
- Grooming loss = 0.0584
- Validation loss = 0.0705
- Examination loss = 0.0679
This means that the model is on average within +/- vii HP of the fight result.
The bigger data set has a 23% better test loss! What's interesting is that the model trained on Spire Logs information has a better test loss when using Jorbs data equally examination data (0.0758) than a model trained simply on Jorbs data (0.0878).
Thoughts Virtually the Stats
- Due to the randomness (sometimes called RNG) inherent in the game, fighting the aforementioned fight with the aforementioned cards and relics multiple times could take many unlike outcomes. This makes the data inherently noisy and the model needs a lot of data to make up for the racket.
- For the Jorbs model, there is non enough data (merely 2000 grooming examples for 970 parameters) to make upwardly for the dissonance in the data.
- The model trained on Jorbs data predicts values close to 0 a lot. This is probably due to Jorbs being a very good player who can take very trivial harm (or heal) in a lot of fights. This is non an authentic sample for the average role player.
Next Steps for the Model
The model has a problem with overfit. Some potential solutions could include:
- Using Embedding layers for cards, relics, and enemies. An Embedding layer tin be used to learn relations between cards and map a card to a vector.
- Getting more (and diverse) information.
Deploying the Model
Slay the Spire is adult in Coffee 8, so the Java TensorFlow 2.x bindings were used to deploy the trained model in the game. Players can download and run the mod and see how the model predicts they volition do in a fight!
Evaluating Cards
Now that we have a model that predicts how a set of cards and relics will do in different fights, we can utilise that data to evaluate how good a card is. Cards are evaluated by average damage taken per fight, weighted more heavily towards more important fights in the game (bosses and elites). This is done by comparing the deck as-is against the deck with the card added, for every fight in the act.
The same process applies for evaluating removing cards and upgrading cards.
Acknowledgements
Cheers to Spire Logs and Jorbs for making your information sets bachelor. And besides a huge shout out to the Slay the Spire #modding community on discord (especially Gk who helped out with the UI for the mod). All of the code for each part of the project are on GitHub if you find a bug or are interested in contributing.
Source: https://towardsdatascience.com/bringing-deep-neural-networks-to-slay-the-spire-a2971d5a5115
0 Response to "Howp to Upload Your Mod to Slay the Spire Workshop"
Post a Comment