Card distribution across splinterlands visualized

Words
673
Reading
3 min
Listen
Play
2y

Card distrubtion.webp

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.

Total Card Distribution Grouped by Rarity and Edition

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.

Zoomed-In View of Lesser Distributed Editions

Specially for davemccoy@davemccoy last minute update. Added the soulbound unlock disctibution

Total soulbound unbounded

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%

Table with unbound percentage

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.

Regular Foil Total Distribution Grouped by Rarity and Edition

Regular Foil Total Distribution of epic and rare

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.

Gold Foil Total Distribution Grouped by Rarity and Edition

Gold Foil Total Distribution of epic and rare

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.

Regular Foil Cards Burned Grouped by Rarity and Edition

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.

Gold Foil Cards Burned Grouped by Rarity and Edition

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.

Percentage of Cards Burned vs. Total Distribution Grouped by Rarity and Edition

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"![{markdown_prefix}]({image_base_url}{card_name}_lv{level}_gold.png)"
    else:
        return f"![{markdown_prefix}]({image_base_url}{card_name}_lv{level}.png)"


# 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/

Total distrubution gif





regular foil gif





Table percentage unbounded gif

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.

Card distribution across splinterlands visualized | Ecency