Descriptive statistics from 231,717 games played by 2,741 players on Starknet mainnet (Sep 2025 – May 2026). Each chart is rendered from data recorded on a public blockchain and links to a query that returns the same figures.
Every Loot Survivor game is a non-fungible token (NFT) minted on Starknet mainnet, and the final score of each game is written to the blockchain by the game's smart contract. The figures below are read directly from that on-chain record.
0x036017e69d21d6d8c13e266eabb73ef1f1d02722d86bdcabe5f168f8e549d3cd — view on Voyager ↗https://api.cartridge.gg/x/pg-mainnet-10/torii/sql. The “re-run” link under each chart returns the exact figures shown.0x018108b32cea514a78ef1b0e4a0753e855cdf620bc0565202c02456f618c4dc4 (Voyager ↗), read from Starknet mint events, distinguishes players new to the franchise from returning ones.Method applied to every chart: official game minter only, standard ruleset (settings_id = 1). Figures are medians (the middle / typical value), which are not skewed by extreme outliers.
Each game grouped by the player's experience at the time it was played — their 1st game, 2nd–5th, 6th–20th, and so on — shown against the median (typical) score in each group.
-- Loot Survivor — Claim 1: Median score by player experience
-- Source: https://api.cartridge.gg/x/pg-mainnet-10/torii/sql (SQLite)
-- Each game is grouped by the player's experience AT THE TIME it was played
-- (i.e. where the game falls in that player's own chronological sequence), then we
-- take the median score per experience tier. This measures experience-at-time-of-play
-- and is monotonic; it avoids the confound of bucketing by a player's lifetime game
-- total (which mixes in selection effects and produces a spurious mid-range dip).
WITH g AS (
SELECT ( (instr('0123456789abcdef',substr(s.score,7,1))-1)*17592186044416
+ (instr('0123456789abcdef',substr(s.score,8,1))-1)*1099511627776
+ (instr('0123456789abcdef',substr(s.score,9,1))-1)*68719476736
+ (instr('0123456789abcdef',substr(s.score,10,1))-1)*4294967296
+ (instr('0123456789abcdef',substr(s.score,11,1))-1)*268435456
+ (instr('0123456789abcdef',substr(s.score,12,1))-1)*16777216
+ (instr('0123456789abcdef',substr(s.score,13,1))-1)*1048576
+ (instr('0123456789abcdef',substr(s.score,14,1))-1)*65536
+ (instr('0123456789abcdef',substr(s.score,15,1))-1)*4096
+ (instr('0123456789abcdef',substr(s.score,16,1))-1)*256
+ (instr('0123456789abcdef',substr(s.score,17,1))-1)*16
+ (instr('0123456789abcdef',substr(s.score,18,1))-1) ) AS sc,
ROW_NUMBER() OVER (PARTITION BY o.owner ORDER BY s.internal_executed_at) AS game_no
FROM "relayer_0_0_1-TokenMetadataUpdate" m
JOIN "relayer_0_0_1-TokenScoreUpdate" s ON s.id = m.id
JOIN "relayer_0_0_1-OwnersUpdate" o ON o.token_id = m.id
WHERE m.settings_id = 1 AND m.minted_by = '0x0000000000000006'
),
b AS (
SELECT CASE WHEN game_no=1 THEN '1st game'
WHEN game_no<=5 THEN '2nd-5th game'
WHEN game_no<=20 THEN '6th-20th game'
WHEN game_no<=50 THEN '21st-50th game'
ELSE '51st+ game' END AS experience,
CASE WHEN game_no=1 THEN 1 WHEN game_no<=5 THEN 2 WHEN game_no<=20 THEN 3
WHEN game_no<=50 THEN 4 ELSE 5 END AS ord,
sc
FROM g WHERE sc BETWEEN 1 AND 3000
),
r AS (SELECT experience, ord, sc,
ROW_NUMBER() OVER (PARTITION BY experience ORDER BY sc) rn,
COUNT(*) OVER (PARTITION BY experience) c FROM b)
SELECT experience,
c AS games,
ROUND(AVG(CASE WHEN rn IN ((c+1)/2,(c+2)/2) THEN sc END),1) AS median_score
FROM r GROUP BY experience ORDER BY MIN(ord);
Players with 20+ games are split into four tiers by their median score over their first 10 games, then each tier's median score is tracked across their later games. The tiers stay ordered and separated across 100+ games — they never cross. A game decided by chance would show the four lines converging.
-- Loot Survivor — Claim: skill persists (skilled players consistently outscore weaker players over many games)
-- Source: https://api.cartridge.gg/x/pg-mainnet-10/torii/sql (SQLite)
-- Players (>=20 games) are split into four tiers by their MEDIAN score over their FIRST 10 games,
-- then each tier's median score is tracked across later game windows. If outcomes were driven by
-- chance the tiers would converge; instead they stay ordered and separated across 100+ games.
WITH g AS (
SELECT o.owner AS player,
( (instr('0123456789abcdef',substr(s.score,7,1))-1)*17592186044416
+ (instr('0123456789abcdef',substr(s.score,8,1))-1)*1099511627776
+ (instr('0123456789abcdef',substr(s.score,9,1))-1)*68719476736
+ (instr('0123456789abcdef',substr(s.score,10,1))-1)*4294967296
+ (instr('0123456789abcdef',substr(s.score,11,1))-1)*268435456
+ (instr('0123456789abcdef',substr(s.score,12,1))-1)*16777216
+ (instr('0123456789abcdef',substr(s.score,13,1))-1)*1048576
+ (instr('0123456789abcdef',substr(s.score,14,1))-1)*65536
+ (instr('0123456789abcdef',substr(s.score,15,1))-1)*4096
+ (instr('0123456789abcdef',substr(s.score,16,1))-1)*256
+ (instr('0123456789abcdef',substr(s.score,17,1))-1)*16
+ (instr('0123456789abcdef',substr(s.score,18,1))-1) ) AS sc,
ROW_NUMBER() OVER (PARTITION BY o.owner ORDER BY s.internal_executed_at) AS gn,
COUNT(*) OVER (PARTITION BY o.owner) AS n
FROM "relayer_0_0_1-TokenMetadataUpdate" m
JOIN "relayer_0_0_1-TokenScoreUpdate" s ON s.id = m.id
JOIN "relayer_0_0_1-OwnersUpdate" o ON o.token_id = m.id
WHERE m.settings_id = 1 AND m.minted_by = '0x0000000000000006'
),
e AS (SELECT player, sc, gn FROM g WHERE n >= 20 AND sc BETWEEN 1 AND 3000),
em AS ( -- each player's median over their first 10 games
SELECT player, AVG(CASE WHEN rn IN ((c+1)/2,(c+2)/2) THEN sc END) AS e10
FROM (SELECT player, sc, ROW_NUMBER() OVER (PARTITION BY player ORDER BY sc) rn,
COUNT(*) OVER (PARTITION BY player) c FROM e WHERE gn <= 10) GROUP BY player),
q AS (SELECT player, NTILE(4) OVER (ORDER BY e10) AS skill_tier FROM em),
later AS (
SELECT q.skill_tier, e.sc,
CASE WHEN e.gn<=20 THEN 1 WHEN e.gn<=40 THEN 2 WHEN e.gn<=70 THEN 3 ELSE 4 END AS later_window
FROM e JOIN q USING(player) WHERE e.gn > 10 AND e.gn <= 120),
r AS (SELECT skill_tier, later_window, sc,
ROW_NUMBER() OVER (PARTITION BY skill_tier,later_window ORDER BY sc) rn,
COUNT(*) OVER (PARTITION BY skill_tier,later_window) c FROM later)
SELECT skill_tier, later_window, c AS games,
ROUND(AVG(CASE WHEN rn IN ((c+1)/2,(c+2)/2) THEN sc END),1) AS median_score
FROM r GROUP BY skill_tier, later_window ORDER BY skill_tier, later_window;
Median score over each player's first three games, comparing players with no prior Loot Survivor history against players who had previously played the original title (similar game mechanics).
-- Claim 3: Prior skill transfers — OG veterans outscore new players from game 1
-- Source: https://api.cartridge.gg/x/pg-mainnet-10/torii/sql (SQLite)
-- COHORT-AWARE: separates genuinely-new players from OG "Loot Survivor" veterans
-- (contract 0x018108b3...618c4dc4). The og_veterans list below is the set of new-game
-- wallets that ALSO minted an OG adventurer, derived from on-chain mint events
-- (see scripts/enumerate_og_players.py). Exact-address match => a conservative LOWER bound.
WITH og_veterans(addr) AS (VALUES
('0x0550212d3f13a373dfe9e3ef6aa41fba4124bde63fd7955393f879de19f3f47f'
... (96 returning-player addresses embedded) ...