FA-95001 / Warehouse slotting and bin packing / Open access
Pallet build: partial top layer not counted · case 01
Height is reported one layer short when the top layer is partial.
ROOT CAUSE
Layers used are computed with floor division, dropping a partial top layer.
VERIFIED REPAIR
Count layers used with ceiling division of cases by cases per layer.
Unsuccessful approach: Always adding one layer overstates height when the last layer is complete.
Case contract
case [l, w, h, kg], pallet [L, W]; cases stay upright but may rotate 90 degrees; all layers share one orientation. Pallet base adds 15 height and 25 kg tare. Cases per pallet are bounded by whole layers within max_h and by max_kg (a partial top layer is allowed). Return [per_layer, layers used, cases, height, weight].
Why this case matters
Pallet build limits govern reserve storage heights and floor loading.
1 / The failure
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(case, pallet, max_h, max_kg):
l, w, h, kg = case
L, W = pallet
per_layer = max((L // l) * (W // w), (L // w) * (W // l))
if per_layer == 0:
return [0, 0, 0, 15, 25]
layers = (max_h - 15) // h
by_weight = (max_kg - 25) // kg
cases = min(per_layer * layers, by_weight)
if cases <= 0:
return [per_layer, 0, 0, 15, 25]
full = cases // per_layer
return [per_layer, full, cases, 15 + full * h, 25 + cases * kg]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['weight bound partial layer', [[40, 30, 25, 20], [120, 100], 190, 425], [9, 3, 20, 90, 425]], ['rotation wins', [[50, 30, 20, 5], [120, 100], 115, 1000], [8, 5, 40, 115, 225]], ['listed orientation wins', [[30, 20, 20, 5], [120, 100], 115, 1000], [20, 5, 100, 115, 525]], ['too big', [[130, 30, 25, 2], [120, 100], 190, 425], [0, 0, 0, 15, 25]], ['square', [[25, 25, 30, 5], [120, 80], 165, 900], [12, 5, 60, 165, 325]], ['height exact', [[40, 30, 25, 2], [120, 100], 140, 2000], [9, 5, 45, 140, 115]], ['too heavy for one', [[40, 30, 25, 500], [120, 100], 190, 425], [9, 0, 0, 15, 25]], ['generated mix 8', [[40, 15, 30, 8], [120, 80], 100, 800], [16, 2, 32, 75, 281]]], [['generated mix 24', [[25, 30, 10, 12], [120, 100], 115, 1025], [16, 6, 83, 75, 1021]], ['generated mix 7', [[40, 30, 20, 5], [120, 80], 140, 1025], [8, 6, 48, 135, 265]], ['weight bound partial layer', [[40, 30, 25, 20], [120, 100], 190, 425], [9, 3, 20, 90, 425]], ['too heavy for one', [[40, 30, 25, 500], [120, 100], 190, 425], [9, 0, 0, 15, 25]], ['listed orientation wins', [[30, 20, 20, 5], [120, 100], 115, 1000], [20, 5, 100, 115, 525]], ['height exact', [[40, 30, 25, 2], [120, 100], 140, 2000], [9, 5, 45, 140, 115]], ['generated mix 10', [[30, 15, 20, 5], [120, 80], 190, 1025], [20, 8, 160, 175, 825]], ['generated mix 18', [[30, 25, 10, 2], [120, 100], 115, 1025], [16, 10, 160, 115, 345]]], [['generated mix 36', [[25, 30, 20, 12], [120, 80], 115, 500], [12, 4, 39, 95, 493]], ['generated mix 12', [[40, 15, 30, 12], [120, 80], 140, 1200], [16, 4, 64, 135, 793]], ['square', [[25, 25, 30, 5], [120, 80], 165, 900], [12, 5, 60, 165, 325]], ['rotation wins', [[50, 30, 20, 5], [120, 100], 115, 1000], [8, 5, 40, 115, 225]], ['weight bound partial layer', [[40, 30, 25, 20], [120, 100], 190, 425], [9, 3, 20, 90, 425]], ['too heavy for one', [[40, 30, 25, 500], [120, 100], 190, 425], [9, 0, 0, 15, 25]], ['generated mix 21', [[25, 20, 30, 2], [120, 80], 140, 300], [18, 4, 72, 135, 169]], ['generated mix 29', [[50, 30, 30, 20], [120, 100], 190, 300], [8, 2, 13, 75, 285]]], [['generated mix 44', [[30, 30, 10, 5], [120, 80], 140, 500], [8, 12, 95, 135, 500]], ['generated mix 17', [[30, 30, 30, 8], [120, 80], 165, 500], [8, 5, 40, 165, 345]], ['height exact', [[40, 30, 25, 2], [120, 100], 140, 2000], [9, 5, 45, 140, 115]], ['too big', [[130, 30, 25, 2], [120, 100], 190, 425], [0, 0, 0, 15, 25]], ['square', [[25, 25, 30, 5], [120, 80], 165, 900], [12, 5, 60, 165, 325]], ['rotation wins', [[50, 30, 20, 5], [120, 100], 115, 1000], [8, 5, 40, 115, 225]], ['generated mix 32', [[40, 30, 10, 8], [120, 100], 165, 300], [9, 4, 34, 55, 297]], ['generated mix 40', [[20, 15, 30, 20], [120, 80], 190, 500], [32, 1, 23, 45, 485]]], [['generated mix 59', [[30, 15, 10, 20], [120, 80], 165, 500], [20, 2, 23, 35, 485]], ['generated mix 26', [[25, 30, 25, 12], [120, 80], 140, 1200], [12, 5, 60, 140, 745]], ['too heavy for one', [[40, 30, 25, 500], [120, 100], 190, 425], [9, 0, 0, 15, 25]], ['listed orientation wins', [[30, 20, 20, 5], [120, 100], 115, 1000], [20, 5, 100, 115, 525]], ['height exact', [[40, 30, 25, 2], [120, 100], 140, 2000], [9, 5, 45, 140, 115]], ['too big', [[130, 30, 25, 2], [120, 100], 190, 425], [0, 0, 0, 15, 25]], ['generated mix 43', [[25, 20, 20, 12], [120, 100], 140, 1200], [24, 5, 97, 115, 1189]], ['generated mix 51', [[25, 15, 10, 8], [120, 80], 165, 500], [24, 3, 59, 45, 497]]]]
for label, args, expected in fixtures[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 fixture | Actual | Expected | Outcome |
|---|---|---|---|
| weight bound partial layer | [9, 2, 20, 65, 425] | [9, 3, 20, 90, 425] | Failed |
| rotation wins | [8, 5, 40, 115, 225] | [8, 5, 40, 115, 225] | Passed |
| listed orientation wins | [20, 5, 100, 115, 525] | [20, 5, 100, 115, 525] | Passed |
| too big | [0, 0, 0, 15, 25] | [0, 0, 0, 15, 25] | Passed |
| square | [12, 5, 60, 165, 325] | [12, 5, 60, 165, 325] | Passed |
| height exact | [9, 5, 45, 140, 115] | [9, 5, 45, 140, 115] | Passed |
| too heavy for one | [9, 0, 0, 15, 25] | [9, 0, 0, 15, 25] | Passed |
| generated mix 8 | [16, 2, 32, 75, 281] | [16, 2, 32, 75, 281] | Passed |
SHA-256 / c27a47da02d6e88c58b3c99cea40f8ccbd93abc683664133f216fabb3fef69f6
2 / The unsuccessful fix
Exit 1"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(case, pallet, max_h, max_kg):
l, w, h, kg = case
L, W = pallet
per_layer = max((L // l) * (W // w), (L // w) * (W // l))
if per_layer == 0:
return [0, 0, 0, 15, 25]
layers = (max_h - 15) // h
by_weight = (max_kg - 25) // kg
cases = min(per_layer * layers, by_weight)
if cases <= 0:
return [per_layer, 0, 0, 15, 25]
full = cases // per_layer + 1
return [per_layer, full, cases, 15 + full * h, 25 + cases * kg]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['weight bound partial layer', [[40, 30, 25, 20], [120, 100], 190, 425], [9, 3, 20, 90, 425]], ['rotation wins', [[50, 30, 20, 5], [120, 100], 115, 1000], [8, 5, 40, 115, 225]], ['listed orientation wins', [[30, 20, 20, 5], [120, 100], 115, 1000], [20, 5, 100, 115, 525]], ['too big', [[130, 30, 25, 2], [120, 100], 190, 425], [0, 0, 0, 15, 25]], ['square', [[25, 25, 30, 5], [120, 80], 165, 900], [12, 5, 60, 165, 325]], ['height exact', [[40, 30, 25, 2], [120, 100], 140, 2000], [9, 5, 45, 140, 115]], ['too heavy for one', [[40, 30, 25, 500], [120, 100], 190, 425], [9, 0, 0, 15, 25]], ['generated mix 8', [[40, 15, 30, 8], [120, 80], 100, 800], [16, 2, 32, 75, 281]]], [['generated mix 24', [[25, 30, 10, 12], [120, 100], 115, 1025], [16, 6, 83, 75, 1021]], ['generated mix 7', [[40, 30, 20, 5], [120, 80], 140, 1025], [8, 6, 48, 135, 265]], ['weight bound partial layer', [[40, 30, 25, 20], [120, 100], 190, 425], [9, 3, 20, 90, 425]], ['too heavy for one', [[40, 30, 25, 500], [120, 100], 190, 425], [9, 0, 0, 15, 25]], ['listed orientation wins', [[30, 20, 20, 5], [120, 100], 115, 1000], [20, 5, 100, 115, 525]], ['height exact', [[40, 30, 25, 2], [120, 100], 140, 2000], [9, 5, 45, 140, 115]], ['generated mix 10', [[30, 15, 20, 5], [120, 80], 190, 1025], [20, 8, 160, 175, 825]], ['generated mix 18', [[30, 25, 10, 2], [120, 100], 115, 1025], [16, 10, 160, 115, 345]]], [['generated mix 36', [[25, 30, 20, 12], [120, 80], 115, 500], [12, 4, 39, 95, 493]], ['generated mix 12', [[40, 15, 30, 12], [120, 80], 140, 1200], [16, 4, 64, 135, 793]], ['square', [[25, 25, 30, 5], [120, 80], 165, 900], [12, 5, 60, 165, 325]], ['rotation wins', [[50, 30, 20, 5], [120, 100], 115, 1000], [8, 5, 40, 115, 225]], ['weight bound partial layer', [[40, 30, 25, 20], [120, 100], 190, 425], [9, 3, 20, 90, 425]], ['too heavy for one', [[40, 30, 25, 500], [120, 100], 190, 425], [9, 0, 0, 15, 25]], ['generated mix 21', [[25, 20, 30, 2], [120, 80], 140, 300], [18, 4, 72, 135, 169]], ['generated mix 29', [[50, 30, 30, 20], [120, 100], 190, 300], [8, 2, 13, 75, 285]]], [['generated mix 44', [[30, 30, 10, 5], [120, 80], 140, 500], [8, 12, 95, 135, 500]], ['generated mix 17', [[30, 30, 30, 8], [120, 80], 165, 500], [8, 5, 40, 165, 345]], ['height exact', [[40, 30, 25, 2], [120, 100], 140, 2000], [9, 5, 45, 140, 115]], ['too big', [[130, 30, 25, 2], [120, 100], 190, 425], [0, 0, 0, 15, 25]], ['square', [[25, 25, 30, 5], [120, 80], 165, 900], [12, 5, 60, 165, 325]], ['rotation wins', [[50, 30, 20, 5], [120, 100], 115, 1000], [8, 5, 40, 115, 225]], ['generated mix 32', [[40, 30, 10, 8], [120, 100], 165, 300], [9, 4, 34, 55, 297]], ['generated mix 40', [[20, 15, 30, 20], [120, 80], 190, 500], [32, 1, 23, 45, 485]]], [['generated mix 59', [[30, 15, 10, 20], [120, 80], 165, 500], [20, 2, 23, 35, 485]], ['generated mix 26', [[25, 30, 25, 12], [120, 80], 140, 1200], [12, 5, 60, 140, 745]], ['too heavy for one', [[40, 30, 25, 500], [120, 100], 190, 425], [9, 0, 0, 15, 25]], ['listed orientation wins', [[30, 20, 20, 5], [120, 100], 115, 1000], [20, 5, 100, 115, 525]], ['height exact', [[40, 30, 25, 2], [120, 100], 140, 2000], [9, 5, 45, 140, 115]], ['too big', [[130, 30, 25, 2], [120, 100], 190, 425], [0, 0, 0, 15, 25]], ['generated mix 43', [[25, 20, 20, 12], [120, 100], 140, 1200], [24, 5, 97, 115, 1189]], ['generated mix 51', [[25, 15, 10, 8], [120, 80], 165, 500], [24, 3, 59, 45, 497]]]]
for label, args, expected in fixtures[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 fixture | Actual | Expected | Outcome |
|---|---|---|---|
| weight bound partial layer | [9, 3, 20, 90, 425] | [9, 3, 20, 90, 425] | Passed |
| rotation wins | [8, 6, 40, 135, 225] | [8, 5, 40, 115, 225] | Failed |
| listed orientation wins | [20, 6, 100, 135, 525] | [20, 5, 100, 115, 525] | Failed |
| too big | [0, 0, 0, 15, 25] | [0, 0, 0, 15, 25] | Passed |
| square | [12, 6, 60, 195, 325] | [12, 5, 60, 165, 325] | Failed |
| height exact | [9, 6, 45, 165, 115] | [9, 5, 45, 140, 115] | Failed |
| too heavy for one | [9, 0, 0, 15, 25] | [9, 0, 0, 15, 25] | Passed |
| generated mix 8 | [16, 3, 32, 105, 281] | [16, 2, 32, 75, 281] | Failed |
SHA-256 / b3fccf25d603de9127ad135eeba6902e93f6e721b91869b5a6a80eb5a742705d
3 / The verified repair
Exit 0"""Failure Map reference implementation. Python standard library only."""
import json
N = 1
observations = []
def solve(case, pallet, max_h, max_kg):
l, w, h, kg = case
L, W = pallet
per_layer = max((L // l) * (W // w), (L // w) * (W // l))
if per_layer == 0:
return [0, 0, 0, 15, 25]
layers = (max_h - 15) // h
by_weight = (max_kg - 25) // kg
cases = min(per_layer * layers, by_weight)
if cases <= 0:
return [per_layer, 0, 0, 15, 25]
full = -(-cases // per_layer)
return [per_layer, full, cases, 15 + full * h, 25 + cases * kg]
def check(label, actual, expected):
observations.append({"check": label, "actual": actual, "expected": expected, "passed": actual == expected})
fixtures = [[['weight bound partial layer', [[40, 30, 25, 20], [120, 100], 190, 425], [9, 3, 20, 90, 425]], ['rotation wins', [[50, 30, 20, 5], [120, 100], 115, 1000], [8, 5, 40, 115, 225]], ['listed orientation wins', [[30, 20, 20, 5], [120, 100], 115, 1000], [20, 5, 100, 115, 525]], ['too big', [[130, 30, 25, 2], [120, 100], 190, 425], [0, 0, 0, 15, 25]], ['square', [[25, 25, 30, 5], [120, 80], 165, 900], [12, 5, 60, 165, 325]], ['height exact', [[40, 30, 25, 2], [120, 100], 140, 2000], [9, 5, 45, 140, 115]], ['too heavy for one', [[40, 30, 25, 500], [120, 100], 190, 425], [9, 0, 0, 15, 25]], ['generated mix 8', [[40, 15, 30, 8], [120, 80], 100, 800], [16, 2, 32, 75, 281]]], [['generated mix 24', [[25, 30, 10, 12], [120, 100], 115, 1025], [16, 6, 83, 75, 1021]], ['generated mix 7', [[40, 30, 20, 5], [120, 80], 140, 1025], [8, 6, 48, 135, 265]], ['weight bound partial layer', [[40, 30, 25, 20], [120, 100], 190, 425], [9, 3, 20, 90, 425]], ['too heavy for one', [[40, 30, 25, 500], [120, 100], 190, 425], [9, 0, 0, 15, 25]], ['listed orientation wins', [[30, 20, 20, 5], [120, 100], 115, 1000], [20, 5, 100, 115, 525]], ['height exact', [[40, 30, 25, 2], [120, 100], 140, 2000], [9, 5, 45, 140, 115]], ['generated mix 10', [[30, 15, 20, 5], [120, 80], 190, 1025], [20, 8, 160, 175, 825]], ['generated mix 18', [[30, 25, 10, 2], [120, 100], 115, 1025], [16, 10, 160, 115, 345]]], [['generated mix 36', [[25, 30, 20, 12], [120, 80], 115, 500], [12, 4, 39, 95, 493]], ['generated mix 12', [[40, 15, 30, 12], [120, 80], 140, 1200], [16, 4, 64, 135, 793]], ['square', [[25, 25, 30, 5], [120, 80], 165, 900], [12, 5, 60, 165, 325]], ['rotation wins', [[50, 30, 20, 5], [120, 100], 115, 1000], [8, 5, 40, 115, 225]], ['weight bound partial layer', [[40, 30, 25, 20], [120, 100], 190, 425], [9, 3, 20, 90, 425]], ['too heavy for one', [[40, 30, 25, 500], [120, 100], 190, 425], [9, 0, 0, 15, 25]], ['generated mix 21', [[25, 20, 30, 2], [120, 80], 140, 300], [18, 4, 72, 135, 169]], ['generated mix 29', [[50, 30, 30, 20], [120, 100], 190, 300], [8, 2, 13, 75, 285]]], [['generated mix 44', [[30, 30, 10, 5], [120, 80], 140, 500], [8, 12, 95, 135, 500]], ['generated mix 17', [[30, 30, 30, 8], [120, 80], 165, 500], [8, 5, 40, 165, 345]], ['height exact', [[40, 30, 25, 2], [120, 100], 140, 2000], [9, 5, 45, 140, 115]], ['too big', [[130, 30, 25, 2], [120, 100], 190, 425], [0, 0, 0, 15, 25]], ['square', [[25, 25, 30, 5], [120, 80], 165, 900], [12, 5, 60, 165, 325]], ['rotation wins', [[50, 30, 20, 5], [120, 100], 115, 1000], [8, 5, 40, 115, 225]], ['generated mix 32', [[40, 30, 10, 8], [120, 100], 165, 300], [9, 4, 34, 55, 297]], ['generated mix 40', [[20, 15, 30, 20], [120, 80], 190, 500], [32, 1, 23, 45, 485]]], [['generated mix 59', [[30, 15, 10, 20], [120, 80], 165, 500], [20, 2, 23, 35, 485]], ['generated mix 26', [[25, 30, 25, 12], [120, 80], 140, 1200], [12, 5, 60, 140, 745]], ['too heavy for one', [[40, 30, 25, 500], [120, 100], 190, 425], [9, 0, 0, 15, 25]], ['listed orientation wins', [[30, 20, 20, 5], [120, 100], 115, 1000], [20, 5, 100, 115, 525]], ['height exact', [[40, 30, 25, 2], [120, 100], 140, 2000], [9, 5, 45, 140, 115]], ['too big', [[130, 30, 25, 2], [120, 100], 190, 425], [0, 0, 0, 15, 25]], ['generated mix 43', [[25, 20, 20, 12], [120, 100], 140, 1200], [24, 5, 97, 115, 1189]], ['generated mix 51', [[25, 15, 10, 8], [120, 80], 165, 500], [24, 3, 59, 45, 497]]]]
for label, args, expected in fixtures[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 fixture | Actual | Expected | Outcome |
|---|---|---|---|
| weight bound partial layer | [9, 3, 20, 90, 425] | [9, 3, 20, 90, 425] | Passed |
| rotation wins | [8, 5, 40, 115, 225] | [8, 5, 40, 115, 225] | Passed |
| listed orientation wins | [20, 5, 100, 115, 525] | [20, 5, 100, 115, 525] | Passed |
| too big | [0, 0, 0, 15, 25] | [0, 0, 0, 15, 25] | Passed |
| square | [12, 5, 60, 165, 325] | [12, 5, 60, 165, 325] | Passed |
| height exact | [9, 5, 45, 140, 115] | [9, 5, 45, 140, 115] | Passed |
| too heavy for one | [9, 0, 0, 15, 25] | [9, 0, 0, 15, 25] | Passed |
| generated mix 8 | [16, 2, 32, 75, 281] | [16, 2, 32, 75, 281] | Passed |
SHA-256 / d1f694e3b3cbaa32df57dee818734f3d1821c2b57baff18df1773de4dc5cd8cc
Verification & scope
Uniform-case, single-orientation layer model; no interlocking patterns. 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:52:09.752830+00:00.
Case digest / 301117638e4aa6d79931e82470e5a6a1d2c3d95e2152f94b65be27a3ce44c1dd