FAILURE MAP
← Case archive

FA-86311 / Game economy crafting balance / Open access

Crafting XP with rested pool: One kill drains the whole rested pool · case 01

A tiny xp gain doubles by the entire rested pool.

Verified by executionVariant 1 · 8 checks per implementationDownload source bundle ↓JSON ↗

ROOT CAUSE

The rested bonus is not limited to the size of the gain.

VERIFIED REPAIR

Restore `bonus = min(g, rested)` at the rested bonus size step.

Unsuccessful approach: Halving the bonus under-pays the rested doubling.

Case contract

Level L needs 80 + 20*L xp to reach L+1; max level 50. Gains are ignored once level 50 is reached (rested untouched). For each gain g: bonus = min(g, rested) is added on top of g and removed from the rested pool; then as many levels as possible are taken, carrying remaining xp; on reaching level 50 the xp bar is set to 0. Returns [level, xp, rested].

Why this case matters

Game economies leak or destroy currency when one crafting or pricing rule is off by one boundary, rounding stage or state update; the defect is observable in exact integer outcomes.

1 / The failure

Exit 1
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(level, xp, gains, rested):
    for g in gains:
        if level >= 50:
            break
        bonus = rested
        rested -= bonus
        xp += g + bonus
        while level < 50 and xp >= 80 + 20 * level:
            xp -= 80 + 20 * level
            level += 1
        if level >= 50:
            xp = 0
    return [level, xp, rested]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
cases = [[('regression rested bonus size #1', [30, 20, [50, 100, 10, 10], 1000], [30, 360, 830]),
  ('regression rested bonus size #2', [48, 26, [100, 99], 1000], [48, 424, 801]),
  ('partial repair boundary #1', [48, 123, [50, 10, 420], 100], [48, 703, 0]),
  ('partial repair boundary #2', [48, 108, [300, 300, 99, 86, 300], 30], [49, 183, 0]),
  ('multi-level gain #1', [1, 0, [300], 0], [3, 80, 0]),
  ('exact threshold #1', [10, 0, [280], 0], [11, 0, 0]),
  ('gain at cap keeps rested #1', [50, 0, [100], 40], [50, 0, 40]),
  ('control #1', [50, 56, [99], 100], [50, 56, 100])],
 [('regression rested bonus size #1', [1, 130, [300, 99], 1000], [7, 28, 601]),
  ('regression rested bonus size #2', [5, 106, [10], 1000], [5, 126, 990]),
  ('regression rested bonus size #3', [30, 20, [50, 100, 10, 10], 1000], [30, 360, 830]),
  ('partial repair boundary #1', [1, 37, [100, 100, 2000], 1000], [15, 17, 0]),
  ('gain at cap keeps rested #1', [50, 0, [100], 40], [50, 0, 40]),
  ('multi-level gain #1', [1, 0, [300], 0], [3, 80, 0]),
  ('control #1', [50, 56, [99], 100], [50, 56, 100]),
  ('control #2', [5, 79, [300], 0], [6, 199, 0])],
 [('regression rested bonus size #1', [48, 105, [100], 395], [48, 305, 295]),
  ('regression rested bonus size #2', [1, 132, [10, 100, 99, 100], 1000], [6, 50, 691]),
  ('partial repair boundary #1', [1, 15, [100], 100], [2, 115, 0]),
  ('regression rested bonus size #3', [48, 26, [100, 99], 1000], [48, 424, 801]),
  ('exact threshold #1', [10, 0, [280], 0], [11, 0, 0]),
  ('gain at cap keeps rested #1', [50, 0, [100], 40], [50, 0, 40]),
  ('control #1', [5, 79, [300], 0], [6, 199, 0]),
  ('control #2', [10, 7, [300, 50, 99, 100], 0], [11, 276, 0])],
 [('regression rested bonus size #1', [27, 6, [121], 265], [27, 248, 144]),
  ('regression rested bonus size #2', [5, 80, [300, 99, 50, 99, 300], 1000], [12, 96, 152]),
  ('partial repair boundary #1', [5, 102, [50, 100], 30], [6, 102, 0]),
  ('partial repair boundary #2', [5, 92, [179, 50, 437, 100], 155], [9, 173, 0]),
  ('multi-level gain #1', [1, 0, [300], 0], [3, 80, 0]),
  ('exact threshold #1', [10, 0, [280], 0], [11, 0, 0]),
  ('control #1', [5, 79, [100, 10, 2000], 0], [13, 189, 0]),
  ('control #2', [29, 125, [2000], 0], [32, 85, 0])],
 [('regression rested bonus size #1', [1, 31, [100], 1000], [3, 11, 900]),
  ('regression rested bonus size #2', [30, 107, [100], 1000], [30, 307, 900]),
  ('partial repair boundary #1', [1, 19, [50, 2000, 50], 1000], [14, 259, 0]),
  ('partial repair boundary #2', [30, 136, [2000, 100], 1000], [34, 396, 0]),
  ('gain at cap keeps rested #1', [50, 0, [100], 40], [50, 0, 40]),
  ('multi-level gain #1', [1, 0, [300], 0], [3, 80, 0]),
  ('control #1', [30, 122, [50, 10, 300, 50, 100], 0], [30, 632, 0]),
  ('control #2', [50, 12, [2000, 50, 300, 50], 30], [50, 12, 30])]]
for label, args, expected in cases[N-1]:
    check(label, solve(*args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
Boundary fixtureActualExpectedOutcome
regression rested bonus size #1[31, 510, 0][30, 360, 830]Failed
regression rested bonus size #2[49, 185, 0][48, 424, 801]Failed
partial repair boundary #1[48, 703, 0][48, 703, 0]Passed
partial repair boundary #2[49, 183, 0][49, 183, 0]Passed
multi-level gain #1[3, 80, 0][3, 80, 0]Passed
exact threshold #1[11, 0, 0][11, 0, 0]Passed
gain at cap keeps rested #1[50, 0, 40][50, 0, 40]Passed
control #1[50, 56, 100][50, 56, 100]Passed

SHA-256 / 39c24b4ff59709b93e1b89ce4745655730964a20bd1bc9e34da65dd9acc02c29

2 / The unsuccessful fix

Exit 1
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(level, xp, gains, rested):
    for g in gains:
        if level >= 50:
            break
        bonus = min(g, rested) // 2
        rested -= bonus
        xp += g + bonus
        while level < 50 and xp >= 80 + 20 * level:
            xp -= 80 + 20 * level
            level += 1
        if level >= 50:
            xp = 0
    return [level, xp, rested]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
cases = [[('regression rested bonus size #1', [30, 20, [50, 100, 10, 10], 1000], [30, 360, 830]),
  ('regression rested bonus size #2', [48, 26, [100, 99], 1000], [48, 424, 801]),
  ('partial repair boundary #1', [48, 123, [50, 10, 420], 100], [48, 703, 0]),
  ('partial repair boundary #2', [48, 108, [300, 300, 99, 86, 300], 30], [49, 183, 0]),
  ('multi-level gain #1', [1, 0, [300], 0], [3, 80, 0]),
  ('exact threshold #1', [10, 0, [280], 0], [11, 0, 0]),
  ('gain at cap keeps rested #1', [50, 0, [100], 40], [50, 0, 40]),
  ('control #1', [50, 56, [99], 100], [50, 56, 100])],
 [('regression rested bonus size #1', [1, 130, [300, 99], 1000], [7, 28, 601]),
  ('regression rested bonus size #2', [5, 106, [10], 1000], [5, 126, 990]),
  ('regression rested bonus size #3', [30, 20, [50, 100, 10, 10], 1000], [30, 360, 830]),
  ('partial repair boundary #1', [1, 37, [100, 100, 2000], 1000], [15, 17, 0]),
  ('gain at cap keeps rested #1', [50, 0, [100], 40], [50, 0, 40]),
  ('multi-level gain #1', [1, 0, [300], 0], [3, 80, 0]),
  ('control #1', [50, 56, [99], 100], [50, 56, 100]),
  ('control #2', [5, 79, [300], 0], [6, 199, 0])],
 [('regression rested bonus size #1', [48, 105, [100], 395], [48, 305, 295]),
  ('regression rested bonus size #2', [1, 132, [10, 100, 99, 100], 1000], [6, 50, 691]),
  ('partial repair boundary #1', [1, 15, [100], 100], [2, 115, 0]),
  ('regression rested bonus size #3', [48, 26, [100, 99], 1000], [48, 424, 801]),
  ('exact threshold #1', [10, 0, [280], 0], [11, 0, 0]),
  ('gain at cap keeps rested #1', [50, 0, [100], 40], [50, 0, 40]),
  ('control #1', [5, 79, [300], 0], [6, 199, 0]),
  ('control #2', [10, 7, [300, 50, 99, 100], 0], [11, 276, 0])],
 [('regression rested bonus size #1', [27, 6, [121], 265], [27, 248, 144]),
  ('regression rested bonus size #2', [5, 80, [300, 99, 50, 99, 300], 1000], [12, 96, 152]),
  ('partial repair boundary #1', [5, 102, [50, 100], 30], [6, 102, 0]),
  ('partial repair boundary #2', [5, 92, [179, 50, 437, 100], 155], [9, 173, 0]),
  ('multi-level gain #1', [1, 0, [300], 0], [3, 80, 0]),
  ('exact threshold #1', [10, 0, [280], 0], [11, 0, 0]),
  ('control #1', [5, 79, [100, 10, 2000], 0], [13, 189, 0]),
  ('control #2', [29, 125, [2000], 0], [32, 85, 0])],
 [('regression rested bonus size #1', [1, 31, [100], 1000], [3, 11, 900]),
  ('regression rested bonus size #2', [30, 107, [100], 1000], [30, 307, 900]),
  ('partial repair boundary #1', [1, 19, [50, 2000, 50], 1000], [14, 259, 0]),
  ('partial repair boundary #2', [30, 136, [2000, 100], 1000], [34, 396, 0]),
  ('gain at cap keeps rested #1', [50, 0, [100], 40], [50, 0, 40]),
  ('multi-level gain #1', [1, 0, [300], 0], [3, 80, 0]),
  ('control #1', [30, 122, [50, 10, 300, 50, 100], 0], [30, 632, 0]),
  ('control #2', [50, 12, [2000, 50, 300, 50], 30], [50, 12, 30])]]
for label, args, expected in cases[N-1]:
    check(label, solve(*args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
Boundary fixtureActualExpectedOutcome
regression rested bonus size #1[30, 275, 915][30, 360, 830]Failed
regression rested bonus size #2[48, 324, 901][48, 424, 801]Failed
partial repair boundary #1[48, 668, 35][48, 703, 0]Failed
partial repair boundary #2[49, 182, 1][49, 183, 0]Failed
multi-level gain #1[3, 80, 0][3, 80, 0]Passed
exact threshold #1[11, 0, 0][11, 0, 0]Passed
gain at cap keeps rested #1[50, 0, 40][50, 0, 40]Passed
control #1[50, 56, 100][50, 56, 100]Passed

SHA-256 / 36e56b2dfe47f051544e71f891ed339cd82650253e8c3d1c5abf0d4096116ac9

3 / The verified repair

Exit 0
"""Failure Map reference implementation. Python standard library only."""
import json

N = 1
observations = []
def solve(level, xp, gains, rested):
    for g in gains:
        if level >= 50:
            break
        bonus = min(g, rested)
        rested -= bonus
        xp += g + bonus
        while level < 50 and xp >= 80 + 20 * level:
            xp -= 80 + 20 * level
            level += 1
        if level >= 50:
            xp = 0
    return [level, xp, rested]
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
cases = [[('regression rested bonus size #1', [30, 20, [50, 100, 10, 10], 1000], [30, 360, 830]),
  ('regression rested bonus size #2', [48, 26, [100, 99], 1000], [48, 424, 801]),
  ('partial repair boundary #1', [48, 123, [50, 10, 420], 100], [48, 703, 0]),
  ('partial repair boundary #2', [48, 108, [300, 300, 99, 86, 300], 30], [49, 183, 0]),
  ('multi-level gain #1', [1, 0, [300], 0], [3, 80, 0]),
  ('exact threshold #1', [10, 0, [280], 0], [11, 0, 0]),
  ('gain at cap keeps rested #1', [50, 0, [100], 40], [50, 0, 40]),
  ('control #1', [50, 56, [99], 100], [50, 56, 100])],
 [('regression rested bonus size #1', [1, 130, [300, 99], 1000], [7, 28, 601]),
  ('regression rested bonus size #2', [5, 106, [10], 1000], [5, 126, 990]),
  ('regression rested bonus size #3', [30, 20, [50, 100, 10, 10], 1000], [30, 360, 830]),
  ('partial repair boundary #1', [1, 37, [100, 100, 2000], 1000], [15, 17, 0]),
  ('gain at cap keeps rested #1', [50, 0, [100], 40], [50, 0, 40]),
  ('multi-level gain #1', [1, 0, [300], 0], [3, 80, 0]),
  ('control #1', [50, 56, [99], 100], [50, 56, 100]),
  ('control #2', [5, 79, [300], 0], [6, 199, 0])],
 [('regression rested bonus size #1', [48, 105, [100], 395], [48, 305, 295]),
  ('regression rested bonus size #2', [1, 132, [10, 100, 99, 100], 1000], [6, 50, 691]),
  ('partial repair boundary #1', [1, 15, [100], 100], [2, 115, 0]),
  ('regression rested bonus size #3', [48, 26, [100, 99], 1000], [48, 424, 801]),
  ('exact threshold #1', [10, 0, [280], 0], [11, 0, 0]),
  ('gain at cap keeps rested #1', [50, 0, [100], 40], [50, 0, 40]),
  ('control #1', [5, 79, [300], 0], [6, 199, 0]),
  ('control #2', [10, 7, [300, 50, 99, 100], 0], [11, 276, 0])],
 [('regression rested bonus size #1', [27, 6, [121], 265], [27, 248, 144]),
  ('regression rested bonus size #2', [5, 80, [300, 99, 50, 99, 300], 1000], [12, 96, 152]),
  ('partial repair boundary #1', [5, 102, [50, 100], 30], [6, 102, 0]),
  ('partial repair boundary #2', [5, 92, [179, 50, 437, 100], 155], [9, 173, 0]),
  ('multi-level gain #1', [1, 0, [300], 0], [3, 80, 0]),
  ('exact threshold #1', [10, 0, [280], 0], [11, 0, 0]),
  ('control #1', [5, 79, [100, 10, 2000], 0], [13, 189, 0]),
  ('control #2', [29, 125, [2000], 0], [32, 85, 0])],
 [('regression rested bonus size #1', [1, 31, [100], 1000], [3, 11, 900]),
  ('regression rested bonus size #2', [30, 107, [100], 1000], [30, 307, 900]),
  ('partial repair boundary #1', [1, 19, [50, 2000, 50], 1000], [14, 259, 0]),
  ('partial repair boundary #2', [30, 136, [2000, 100], 1000], [34, 396, 0]),
  ('gain at cap keeps rested #1', [50, 0, [100], 40], [50, 0, 40]),
  ('multi-level gain #1', [1, 0, [300], 0], [3, 80, 0]),
  ('control #1', [30, 122, [50, 10, 300, 50, 100], 0], [30, 632, 0]),
  ('control #2', [50, 12, [2000, 50, 300, 50], 30], [50, 12, 30])]]
for label, args, expected in cases[N-1]:
    check(label, solve(*args), expected)
print(json.dumps({"observations": observations, "passed": all(x["passed"] for x in observations)}, ensure_ascii=False))
raise SystemExit(0 if all(x["passed"] for x in observations) else 1)
Boundary fixtureActualExpectedOutcome
regression rested bonus size #1[30, 360, 830][30, 360, 830]Passed
regression rested bonus size #2[48, 424, 801][48, 424, 801]Passed
partial repair boundary #1[48, 703, 0][48, 703, 0]Passed
partial repair boundary #2[49, 183, 0][49, 183, 0]Passed
multi-level gain #1[3, 80, 0][3, 80, 0]Passed
exact threshold #1[11, 0, 0][11, 0, 0]Passed
gain at cap keeps rested #1[50, 0, 40][50, 0, 40]Passed
control #1[50, 56, 100][50, 56, 100]Passed

SHA-256 / f05ad58944f74e60c34fa46950b303e7d135d73e4d0e815a10b4220e46a076fd

Verification & scope

Deterministic toy contract stipulated for this model; integer or exact arithmetic only, not a reproduction of any specific game engine. This reproducer isolates one failure mechanism. Results cover the supplied fixtures. Variants within a family share a test contract and should remain grouped when constructing evaluation splits. Related mechanisms with a shared evaluation_group must also remain together; these controlled models are not independent production incidents.

Observations recorded using Python 3.12.14 at 2026-09-29T14:50:48.305260+00:00.

Case digest / 8641515e410b322e1ed279d27b1351bb5cfd9e20cacb2a2014cb12ae96fd6c8f