Introduction
Understanding the Splinterlands Card Distribution
In this post, we’ll delve into the current distribution of Splinterlands cards, focusing on their classification by rarity and edition. We’ll explore both regular and gold foil cards, offering a comprehensive analysis through a series of visual graphs. These insights will help you understand the market dynamics of Splinterlands cards, including how they’re distributed and how many have been burned. This analysis is crucial for collectors, players, and investors alike, providing a detailed view of the card landscape in Splinterlands.
Total Card Distribution
Overview of Total Card Distribution
Before diving into the specifics of regular and gold foil distributions, it’s important to first get a bird’s-eye view of the overall card distribution in Splinterlands. The chart below showcases the total distribution of cards across all rarities and editions. This high-level view allows you to understand the general spread of cards in the game.
To get a clearer picture of the less commonly distributed editions, we’ve also included a zoomed-in chart. This focuses on the editions with fewer cards in circulation, providing a more detailed look at their distribution.
Specially for @davemccoy last minute update. Added the soulbound unlock disctibution
In the end i created a small overview where all the soulbound unbound % is displayed.
Currently the Kulu Mastermind is the most unbounded soulbound with 1.4%
Regular Foil Distribution
Regular Foil Cards by Rarity and Edition
In this section, we’ll take a closer look at the total distribution of regular foil cards. The chart below categorizes these cards by rarity (Common, Rare, Epic, and Legendary) and edition (Alpha, Beta, Promo, Reward, and Chaos Legion, etc.). This breakdown gives you an insight into which cards are most prevalent and which editions hold the most value in terms of rarity.
Gold Foil Distribution
Gold Foil Cards by Rarity and Edition
Gold foil cards are often seen as the crown jewels of Splinterlands, highly sought after due to their rarity and visual appeal and their bonusses in game. This chapter presents the distribution of gold foil cards, again categorized by rarity and edition. The graph here will show how these cards are spread across different editions and rarity levels, offering a clear picture of their availability in the game.
Regular Foil Burned
Burned Regular Foil Cards by Rarity and Edition
Card burning is a crucial mechanism in Splinterlands, affecting the supply and value of cards. This section focuses on regular foil cards that have been burned, grouped by rarity and edition. The accompanying chart will illustrate how many of these cards have been removed from circulation, providing insights into how burning impacts the overall card distribution.
Gold Foil Burned
Burned Gold Foil Cards by Rarity and Edition
Continuing from the previous section, we now shift our focus to gold foil cards that have been burned. This chart will show the distribution of burned gold foil cards across different rarities and editions, helping you understand which high-value cards have been permanently removed from the game.
Burned Percentage
Percentage of Cards Burned Relative to Total Distribution
This chapter ties everything together by presenting a chart that represents the percentage of burned cards against the total distribution. Grouped by rarity and edition, this analysis highlights the impact of burning on each card type. Pay special attention to the Promo edition, where the burn rate of rare cards is notably high, signaling their increasing scarcity and potential value.
Python Code
Reproducing the Charts with Python
For those who want to delve deeper and create their own visualizations, this section provides the Python code used to generate the charts in this blog post. The code allows you to recreate the charts and even make them interactive. To get started, make sure you have the following Python packages installed:
pip install pandas requests plotly dash urllib3
import dash
import pandas as pd
import plotly.graph_objects as go
import requests
from dash import dcc, html, dash_table
from requests.adapters import HTTPAdapter
from urllib3 import Retry
# Define URLs
base_url = 'https://api2.splinterlands.com/'
# Set up retry strategy
retry_strategy = Retry(
total=10,
status_forcelist=[429, 500, 502, 503, 504],
backoff_factor=2,
allowed_methods=['HEAD', 'GET', 'OPTIONS']
)
adapter = HTTPAdapter(max_retries=retry_strategy)
http = requests.Session()
http.mount('https://', adapter)
def get_card_details():
address = base_url + 'cards/get_details'
return pd.DataFrame(http.get(address).json()).set_index('id')
# Edition and Rarity mapping enums
class Edition:
alpha = 0
beta = 1
promo = 2
reward = 3
untamed = 4
dice = 5
gladius = 6
chaos = 7
rift = 8
soulbound = 10
rebellion = 12
soulboundrb = 13
class Rarity:
common = 1
rare = 2
epic = 3
legendary = 4
# Assume spl is a module with get_card_details() function to fetch card data.
data = get_card_details()
edition_mapping = {
Edition.alpha: 'Alpha',
Edition.beta: 'Beta',
Edition.promo: 'Promo',
Edition.reward: 'Reward',
Edition.untamed: 'Untamed',
Edition.dice: 'Dice',
Edition.gladius: 'Gladius',
Edition.chaos: 'Chaos',
Edition.rift: 'Rift',
Edition.soulbound: 'Soulbound',
Edition.rebellion: 'Rebellion',
Edition.soulboundrb: 'Soulbound Rebellion'
}
rarity_mapping = {
Rarity.common: 'Common',
Rarity.rare: 'Rare',
Rarity.epic: 'Epic',
Rarity.legendary: 'Legendary'
}
# Prepare data by aggregating across all card distributions
all_distributions = []
for card_id, distributions in data['distribution'].items():
for dist in distributions:
all_distributions.append(dist)
df = pd.DataFrame(all_distributions)
df['num_cards'] = df['num_cards'].astype(int)
df['num_burned'] = df['num_burned'].astype(int)
df['unbound_cards'] = df['unbound_cards'].astype(int)
# Create a mapping from card_detail_id to rarity
rarity_mapping_df = {card_detail_id: rarity for card_detail_id, rarity in data['rarity'].items()}
# Add rarity column to the DataFrame
df['rarity'] = df['card_detail_id'].map(rarity_mapping_df)
df['rarity'] = df['rarity'].map(rarity_mapping) # Map to human-readable rarity names
# Map edition to human-readable names
df['edition_num'] = df['edition'] # Preserve edition number for sorting
df['edition'] = df['edition'].map(edition_mapping)
# Ensure the edition is ordered by edition number
edition_order = [edition_mapping[num] for num in sorted(edition_mapping)]
# Sort by edition number and rarity
rarity_order = ['Common', 'Rare', 'Epic', 'Legendary']
df['rarity'] = pd.Categorical(df['rarity'], categories=rarity_order, ordered=True)
df = df.sort_values(by=['edition_num', 'rarity'])
# Filter data for non-gold and gold cards
df_non_gold = df[df['gold'] == False]
df_gold = df[df['gold'] == True]
# Group by edition and rarity and calculate totals
grouped_non_gold_df = df_non_gold.groupby(
['edition_num', 'edition', 'rarity'],
observed=True,
).agg({
'num_cards': 'sum',
'num_burned': 'sum',
}).reset_index()
grouped_gold_df = df_gold.groupby(
['edition_num', 'edition', 'rarity'],
observed=True,
).agg({
'num_cards': 'sum',
'num_burned': 'sum'
}).reset_index()
# Combine non-gold and gold data for total calculations
combined_df = df.groupby(
['edition_num', 'edition', 'rarity'],
observed=True,
).agg({
'num_cards': 'sum',
'num_burned': 'sum'
}).reset_index()
# Group by edition to get total distribution for each set
total_distribution_df = df.groupby(['edition_num', 'edition']).agg({
'num_cards': 'sum'
}).reset_index()
# Calculate percentage burned
combined_df['percent_burned'] = (combined_df['num_burned'] / combined_df['num_cards']) * 100
# Define colors for each rarity
rarity_colors = {
'Common': 'gray',
'Rare': 'blue',
'Epic': 'purple',
'Legendary': 'orange'
}
# Calculate the percentage of distributed cards vs unbound for Soulbound edition
soulbound_df = df[df['edition'] == 'Soulbound'].copy()
soulbound_df['percent_unbound'] = ((soulbound_df['unbound_cards'] / soulbound_df['num_cards']) * 100).round(2)
soulbound_df.loc[:, 'card_name'] = soulbound_df.apply(lambda row: data.loc[row['card_detail_id']]['name'], axis=1)
# Define the base URL
image_base_url = "https://images.hive.blog/125x0/https://d36mxiodymuqjm.cloudfront.net/cards_by_level/soulbound/"
# Mapping rarity to level
rarity_to_level = {
'Common': '10',
'Rare': '8',
'Epic': '6',
'Legendary': '4'
}
# Function to generate image URL
def generate_image_url(row):
card_name = row['card_name'].replace(' ', '%20') # Replace spaces with %20
level = rarity_to_level[row['rarity']]
markdown_prefix = "![" + str(card_name) + "]"
if row['gold']:
return f""
else:
return f""
# Apply the function to generate the image URLs
soulbound_df['image_url'] = soulbound_df.apply(generate_image_url, axis=1)
# Select only necessary columns for the table
soulbound_table_df = soulbound_df[['image_url', 'rarity', 'percent_unbound', 'num_cards', 'unbound_cards']]
# Create a Dash app
app = dash.Dash(__name__)
# Create the layout for the Dash app
app.layout = html.Div(
style={'backgroundColor': '#1a1a1a', 'color': '#FFFFFF'}, # Dark theme styles
children=[
html.H1("Distribution and Burned Totals by Edition and Rarity", style={'textAlign': 'center'}),
dcc.Graph(
id='total-distribution-chart',
figure={
'data': [
go.Bar(
x=[edition], # Single edition per bar
y=[total_distribution_df[total_distribution_df['edition'] == edition][
'num_cards'].sum()],
name=edition,
# marker=dict(color=color) # Assign different colors for each edition
)
for edition in edition_order
] + [
go.Bar(
x=['Soulbound Unbound'], # Only for Soulbound
y=[df[df['edition'] == 'Soulbound']['unbound_cards'].sum()],
# Total unbound cards for Soulbound
name='Soulbound (Unbound Cards)',
marker=dict(color='green') # Different color for distinction
)
],
'layout': go.Layout(
title='Total Distribution by Edition',
title_font=dict(color='#FFFFFF'),
xaxis={'title': 'Edition', 'color': '#FFFFFF', 'categoryorder': 'array',
'categoryarray': edition_order + ['Soulbound']},
yaxis={'title': 'Total Distribution', 'color': '#FFFFFF'},
paper_bgcolor='#1a1a1a',
plot_bgcolor='#1a1a1a',
font=dict(color='#FFFFFF'),
showlegend=True # Ensure the legend is displayed
)
}
),
# Non-Gold Charts
html.H2("Regular Foil Cards", style={'textAlign': 'center'}),
# Chart 1: Non-Gold Total Distribution
dcc.Graph(
id='non-gold-distribution-chart',
figure={
'data': [
go.Bar(
x=grouped_non_gold_df[grouped_non_gold_df['rarity'] == rarity]['edition'],
y=grouped_non_gold_df[grouped_non_gold_df['rarity'] == rarity]['num_cards'],
name=f'{rarity} Distribution',
marker=dict(color=rarity_colors[rarity])
)
for rarity in rarity_order # Use ordered rarity list
],
'layout': go.Layout(
title='Total Distribution by Edition and Rarity (Regular Foil)',
title_font=dict(color='#FFFFFF'),
xaxis={'title': 'Edition', 'color': '#FFFFFF', 'categoryorder': 'array',
'categoryarray': edition_order},
yaxis={'title': 'Total Distribution', 'color': '#FFFFFF'},
barmode='group',
paper_bgcolor='#1a1a1a',
plot_bgcolor='#1a1a1a',
font=dict(color='#FFFFFF')
)
}
),
# Chart 2: Non-Gold Burned Totals
dcc.Graph(
id='non-gold-burned-chart',
figure={
'data': [
go.Bar(
x=grouped_non_gold_df[grouped_non_gold_df['rarity'] == rarity]['edition'],
y=grouped_non_gold_df[grouped_non_gold_df['rarity'] == rarity]['num_burned'],
name=f'{rarity} Burned',
marker=dict(color=rarity_colors[rarity])
)
for rarity in rarity_order # Use ordered rarity list
],
'layout': go.Layout(
title='Total Burned by Edition and Rarity (Regular Foil)',
title_font=dict(color='#FFFFFF'),
xaxis={'title': 'Edition', 'color': '#FFFFFF', 'categoryorder': 'array',
'categoryarray': edition_order},
yaxis={'title': 'Total Burned', 'color': '#FFFFFF'},
barmode='group',
paper_bgcolor='#1a1a1a',
plot_bgcolor='#1a1a1a',
font=dict(color='#FFFFFF')
)
}
),
# Gold Charts
html.H2("Gold Foil Cards", style={'textAlign': 'center'}),
# Chart 3: Gold Total Distribution
dcc.Graph(
id='gold-distribution-chart',
figure={
'data': [
go.Bar(
x=grouped_gold_df[grouped_gold_df['rarity'] == rarity]['edition'],
y=grouped_gold_df[grouped_gold_df['rarity'] == rarity]['num_cards'],
name=f'{rarity} Distribution',
marker=dict(color=rarity_colors[rarity])
)
for rarity in rarity_order # Use ordered rarity list
],
'layout': go.Layout(
title='Total Distribution by Edition and Rarity (Gold Foil)',
title_font=dict(color='#FFFFFF'),
xaxis={'title': 'Edition', 'color': '#FFFFFF', 'categoryorder': 'array',
'categoryarray': edition_order},
yaxis={'title': 'Total Distribution', 'color': '#FFFFFF'},
barmode='group',
paper_bgcolor='#1a1a1a',
plot_bgcolor='#1a1a1a',
font=dict(color='#FFFFFF')
)
}
),
# Chart 4: Gold Burned Totals
dcc.Graph(
id='gold-burned-chart',
figure={
'data': [
go.Bar(
x=grouped_gold_df[grouped_gold_df['rarity'] == rarity]['edition'],
y=grouped_gold_df[grouped_gold_df['rarity'] == rarity]['num_burned'],
name=f'{rarity} Burned',
marker=dict(color=rarity_colors[rarity])
)
for rarity in rarity_order # Use ordered rarity list
],
'layout': go.Layout(
title='Total Burned by Edition and Rarity (Gold Foil)',
title_font=dict(color='#FFFFFF'),
xaxis={'title': 'Edition', 'color': '#FFFFFF', 'categoryorder': 'array',
'categoryarray': edition_order},
yaxis={'title': 'Total Burned', 'color': '#FFFFFF'},
barmode='group',
paper_bgcolor='#1a1a1a',
plot_bgcolor='#1a1a1a',
font=dict(color='#FFFFFF')
)
}
),
# Combined Chart: Percentage Burned Only
html.H2("Percentage Burned by Edition and Rarity", style={'textAlign': 'center'}),
dcc.Graph(
id='percentage-burned-chart',
figure={
'data': [
go.Bar(
x=combined_df[combined_df['rarity'] == rarity]['edition'],
y=combined_df[combined_df['rarity'] == rarity]['percent_burned'],
name=f'{rarity} % Burned',
marker=dict(color=rarity_colors[rarity])
)
for rarity in rarity_order
],
'layout': go.Layout(
title='Percentage Burned by Edition and Rarity',
title_font=dict(color='#FFFFFF'),
xaxis={'title': 'Edition', 'color': '#FFFFFF', 'categoryorder': 'array',
'categoryarray': edition_order},
yaxis={'title': 'Percentage Burned (%)', 'color': '#FFFFFF'},
barmode='group',
paper_bgcolor='#1a1a1a',
plot_bgcolor='#1a1a1a',
font=dict(color='#FFFFFF')
)
}
),
# New Table for Soulbound Edition
html.H2("Soulbound Edition - Distributed vs Unbound Percentage", style={'textAlign': 'center'}),
dash_table.DataTable(
id='soulbound-table',
columns=[
{'name': 'Image', 'id': 'image_url', 'presentation': 'markdown'}, # Markdown column for images
{'name': 'Rarity', 'id': 'rarity'},
{'name': 'Percentage Unbound (%)', 'id': 'percent_unbound'},
{'name': 'Number of Cards', 'id': 'num_cards'},
{'name': 'Unbound Cards', 'id': 'unbound_cards'},
],
data=soulbound_table_df.to_dict('records'),
style_header={'backgroundColor': '#333333', 'fontWeight': 'bold'},
row_selectable=False,
row_deletable=False,
editable=False,
filter_action='native',
sort_action='native',
markdown_options={"link_target": "_blank"},
style_table={'width': '40%', 'margin': 'auto', 'align': 'left'},
style_cell={
'backgroundColor': '#1a1a1a',
'color': '#FFFFFF',
'minWidth': 'auto', 'width': '20%', 'maxWidth': 'auto',
'padding': '5px',
'textAlign': 'center'
},
)
]
)
if __name__ == '__main__':
app.run_server(debug=True)
Example
Example usage when you execute the code and navigate to: http://localhost:8050/
That's all for this week hope you enjoyed reading this. See you all on the battlefield.
Do you also want to be part of this amazing play to earn game consider using my referral link.