FAILURE MAP
← Case archive

FA-86261 / Game economy crafting balance / Open access

Daily market rebalance: Price drops round against the seller · case 01

Prices fall one coin more than the floor rule.

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

ROOT CAUSE

The reduced price is floored, rounding the decrease up.

VERIFIED REPAIR

Restore `new = price - price * pct // 100` at the decrease rounding step.

Unsuccessful approach: Rounding the decrease still over-drops when the fraction is at least one half.

Case contract

If target <= 0 the price is only clamped to [floor, ceiling]. If sold > target: pct = min(20, floor((sold-target)*100/target)), step = floor(price*pct/100) but at least 1 when pct > 0, new = price + step. If sold < target: pct = min(15, floor((target-sold)*100/target)), new = price - floor(price*pct/100). Otherwise unchanged. Result clamped to [floor, ceiling].

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(price, sold, target, floor, ceiling):
    if target <= 0:
        return min(max(price, floor), ceiling)
    if sold > target:
        pct = min(20, (sold - target) * 100 // target)
        step = price * pct // 100
        if pct > 0:
            step = max(step, 1)
        new = price + step
    elif sold < target:
        pct = min(15, (target - sold) * 100 // target)
        new = price * (100 - pct) // 100
    else:
        new = price
    return min(max(new, floor), ceiling)
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
cases = [[('regression decrease rounding #1', [50, 42, 70, 38, 107], 43),
  ('regression decrease rounding #2', [99, 39, 40, 15, 1103], 98),
  ('regression decrease rounding #3', [99, 0, 5, 18, 422], 85),
  ('regression decrease rounding #4', [50, 4, 5, 36, 1502], 43),
  ('no target listed #1', [5, 3, 0, 10, 50], 10),
  ('cheap item rises one #1', [3, 13, 10, 1, 100], 4),
  ('crash capped at 15 #1', [1000, 0, 40, 1, 5000], 850),
  ('control #1', [99, 1188, 396, 6, 782], 118)],
 [('regression decrease rounding #1', [99, 0, 5, 18, 422], 85),
  ('regression decrease rounding #2', [50, 4, 5, 36, 1502], 43),
  ('regression decrease rounding #3', [873, 0, 1, 38, 806], 743),
  ('regression decrease rounding #4', [99, 39, 40, 28, 329], 98),
  ('crash capped at 15 #1', [1000, 0, 40, 1, 5000], 850),
  ('no target listed #1', [5, 3, 0, 10, 50], 10),
  ('control #1', [99, 1188, 396, 6, 782], 118),
  ('control #2', [9, 0, 1, 47, 499], 47)],
 [('regression decrease rounding #1', [873, 0, 1, 38, 806], 743),
  ('fault site decrease rounding #1', [50, 50, 51, 3, 1314], 50),
  ('regression decrease rounding #2', [99, 39, 40, 28, 329], 98),
  ('regression decrease rounding #3', [99, 0, 1, 41, 274], 85),
  ('cheap item rises one #1', [3, 13, 10, 1, 100], 4),
  ('crash capped at 15 #1', [1000, 0, 40, 1, 5000], 850),
  ('control #1', [9, 0, 1, 47, 499], 47),
  ('control #2', [1000, 0, 0, 7, 47], 47)],
 [('regression decrease rounding #1', [99, 39, 40, 28, 329], 98),
  ('fault site decrease rounding #1', [675, 0, 41, 20, 744], 574),
  ('regression decrease rounding #2', [99, 0, 1, 41, 274], 85),
  ('regression decrease rounding #3', [99, 0, 897, 18, 606], 85),
  ('no target listed #1', [5, 3, 0, 10, 50], 10),
  ('cheap item rises one #1', [3, 13, 10, 1, 100], 4),
  ('control #1', [736, 68, 66, 28, 100], 100),
  ('control #2', [3, 75, 76, 12, 1170], 12)],
 [('regression decrease rounding #1', [99, 0, 1, 41, 274], 85),
  ('regression decrease rounding #2', [99, 0, 897, 18, 606], 85),
  ('regression decrease rounding #3', [99, 4, 5, 14, 1130], 85),
  ('regression decrease rounding #4', [99, 0, 1, 13, 99], 85),
  ('crash capped at 15 #1', [1000, 0, 40, 1, 5000], 850),
  ('no target listed #1', [5, 3, 0, 10, 50], 10),
  ('control #1', [1000, 5, 5, 37, 1007], 1000),
  ('control #2', [50, 11, 10, 38, 410], 55)]]
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 decrease rounding #14243Failed
regression decrease rounding #29798Failed
regression decrease rounding #38485Failed
regression decrease rounding #44243Failed
no target listed #11010Passed
cheap item rises one #144Passed
crash capped at 15 #1850850Passed
control #1118118Passed

SHA-256 / 9b19d2e6b11ed50e07c210db042223a56264f61135adcb9ced1e76f967b28334

2 / The unsuccessful fix

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

N = 1
observations = []
def solve(price, sold, target, floor, ceiling):
    if target <= 0:
        return min(max(price, floor), ceiling)
    if sold > target:
        pct = min(20, (sold - target) * 100 // target)
        step = price * pct // 100
        if pct > 0:
            step = max(step, 1)
        new = price + step
    elif sold < target:
        pct = min(15, (target - sold) * 100 // target)
        new = price - round(price * pct / 100)
    else:
        new = price
    return min(max(new, floor), ceiling)
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
cases = [[('regression decrease rounding #1', [50, 42, 70, 38, 107], 43),
  ('regression decrease rounding #2', [99, 39, 40, 15, 1103], 98),
  ('regression decrease rounding #3', [99, 0, 5, 18, 422], 85),
  ('regression decrease rounding #4', [50, 4, 5, 36, 1502], 43),
  ('no target listed #1', [5, 3, 0, 10, 50], 10),
  ('cheap item rises one #1', [3, 13, 10, 1, 100], 4),
  ('crash capped at 15 #1', [1000, 0, 40, 1, 5000], 850),
  ('control #1', [99, 1188, 396, 6, 782], 118)],
 [('regression decrease rounding #1', [99, 0, 5, 18, 422], 85),
  ('regression decrease rounding #2', [50, 4, 5, 36, 1502], 43),
  ('regression decrease rounding #3', [873, 0, 1, 38, 806], 743),
  ('regression decrease rounding #4', [99, 39, 40, 28, 329], 98),
  ('crash capped at 15 #1', [1000, 0, 40, 1, 5000], 850),
  ('no target listed #1', [5, 3, 0, 10, 50], 10),
  ('control #1', [99, 1188, 396, 6, 782], 118),
  ('control #2', [9, 0, 1, 47, 499], 47)],
 [('regression decrease rounding #1', [873, 0, 1, 38, 806], 743),
  ('fault site decrease rounding #1', [50, 50, 51, 3, 1314], 50),
  ('regression decrease rounding #2', [99, 39, 40, 28, 329], 98),
  ('regression decrease rounding #3', [99, 0, 1, 41, 274], 85),
  ('cheap item rises one #1', [3, 13, 10, 1, 100], 4),
  ('crash capped at 15 #1', [1000, 0, 40, 1, 5000], 850),
  ('control #1', [9, 0, 1, 47, 499], 47),
  ('control #2', [1000, 0, 0, 7, 47], 47)],
 [('regression decrease rounding #1', [99, 39, 40, 28, 329], 98),
  ('fault site decrease rounding #1', [675, 0, 41, 20, 744], 574),
  ('regression decrease rounding #2', [99, 0, 1, 41, 274], 85),
  ('regression decrease rounding #3', [99, 0, 897, 18, 606], 85),
  ('no target listed #1', [5, 3, 0, 10, 50], 10),
  ('cheap item rises one #1', [3, 13, 10, 1, 100], 4),
  ('control #1', [736, 68, 66, 28, 100], 100),
  ('control #2', [3, 75, 76, 12, 1170], 12)],
 [('regression decrease rounding #1', [99, 0, 1, 41, 274], 85),
  ('regression decrease rounding #2', [99, 0, 897, 18, 606], 85),
  ('regression decrease rounding #3', [99, 4, 5, 14, 1130], 85),
  ('regression decrease rounding #4', [99, 0, 1, 13, 99], 85),
  ('crash capped at 15 #1', [1000, 0, 40, 1, 5000], 850),
  ('no target listed #1', [5, 3, 0, 10, 50], 10),
  ('control #1', [1000, 5, 5, 37, 1007], 1000),
  ('control #2', [50, 11, 10, 38, 410], 55)]]
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 decrease rounding #14243Failed
regression decrease rounding #29798Failed
regression decrease rounding #38485Failed
regression decrease rounding #44243Failed
no target listed #11010Passed
cheap item rises one #144Passed
crash capped at 15 #1850850Passed
control #1118118Passed

SHA-256 / 5baf08d329cf8e0be263aa3c337e5c5c28f0dadc65d9a7276bb792251224d101

3 / The verified repair

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

N = 1
observations = []
def solve(price, sold, target, floor, ceiling):
    if target <= 0:
        return min(max(price, floor), ceiling)
    if sold > target:
        pct = min(20, (sold - target) * 100 // target)
        step = price * pct // 100
        if pct > 0:
            step = max(step, 1)
        new = price + step
    elif sold < target:
        pct = min(15, (target - sold) * 100 // target)
        new = price - price * pct // 100
    else:
        new = price
    return min(max(new, floor), ceiling)
def check(label, actual, expected):
    observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
cases = [[('regression decrease rounding #1', [50, 42, 70, 38, 107], 43),
  ('regression decrease rounding #2', [99, 39, 40, 15, 1103], 98),
  ('regression decrease rounding #3', [99, 0, 5, 18, 422], 85),
  ('regression decrease rounding #4', [50, 4, 5, 36, 1502], 43),
  ('no target listed #1', [5, 3, 0, 10, 50], 10),
  ('cheap item rises one #1', [3, 13, 10, 1, 100], 4),
  ('crash capped at 15 #1', [1000, 0, 40, 1, 5000], 850),
  ('control #1', [99, 1188, 396, 6, 782], 118)],
 [('regression decrease rounding #1', [99, 0, 5, 18, 422], 85),
  ('regression decrease rounding #2', [50, 4, 5, 36, 1502], 43),
  ('regression decrease rounding #3', [873, 0, 1, 38, 806], 743),
  ('regression decrease rounding #4', [99, 39, 40, 28, 329], 98),
  ('crash capped at 15 #1', [1000, 0, 40, 1, 5000], 850),
  ('no target listed #1', [5, 3, 0, 10, 50], 10),
  ('control #1', [99, 1188, 396, 6, 782], 118),
  ('control #2', [9, 0, 1, 47, 499], 47)],
 [('regression decrease rounding #1', [873, 0, 1, 38, 806], 743),
  ('fault site decrease rounding #1', [50, 50, 51, 3, 1314], 50),
  ('regression decrease rounding #2', [99, 39, 40, 28, 329], 98),
  ('regression decrease rounding #3', [99, 0, 1, 41, 274], 85),
  ('cheap item rises one #1', [3, 13, 10, 1, 100], 4),
  ('crash capped at 15 #1', [1000, 0, 40, 1, 5000], 850),
  ('control #1', [9, 0, 1, 47, 499], 47),
  ('control #2', [1000, 0, 0, 7, 47], 47)],
 [('regression decrease rounding #1', [99, 39, 40, 28, 329], 98),
  ('fault site decrease rounding #1', [675, 0, 41, 20, 744], 574),
  ('regression decrease rounding #2', [99, 0, 1, 41, 274], 85),
  ('regression decrease rounding #3', [99, 0, 897, 18, 606], 85),
  ('no target listed #1', [5, 3, 0, 10, 50], 10),
  ('cheap item rises one #1', [3, 13, 10, 1, 100], 4),
  ('control #1', [736, 68, 66, 28, 100], 100),
  ('control #2', [3, 75, 76, 12, 1170], 12)],
 [('regression decrease rounding #1', [99, 0, 1, 41, 274], 85),
  ('regression decrease rounding #2', [99, 0, 897, 18, 606], 85),
  ('regression decrease rounding #3', [99, 4, 5, 14, 1130], 85),
  ('regression decrease rounding #4', [99, 0, 1, 13, 99], 85),
  ('crash capped at 15 #1', [1000, 0, 40, 1, 5000], 850),
  ('no target listed #1', [5, 3, 0, 10, 50], 10),
  ('control #1', [1000, 5, 5, 37, 1007], 1000),
  ('control #2', [50, 11, 10, 38, 410], 55)]]
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 decrease rounding #14343Passed
regression decrease rounding #29898Passed
regression decrease rounding #38585Passed
regression decrease rounding #44343Passed
no target listed #11010Passed
cheap item rises one #144Passed
crash capped at 15 #1850850Passed
control #1118118Passed

SHA-256 / 6e04f13c322f9f0f8f387ecd43103d84cddae4d343f527c3d50db83d9c6081dd

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.009978+00:00.

Case digest / fd1a6cad4d1d8025776f1075ddc9b5861c0327bbe791b5f0411683401679702d