FA-94991 / Warehouse slotting and bin packing / Open access
Pallet build: base height not deducted · case 01
Built pallets exceed the rack opening height by up to the pallet base.
ROOT CAUSE
The layer count divides the full height limit by the case height without subtracting the pallet base.
VERIFIED REPAIR
Subtract the 15-unit base before dividing the height limit into layers.
Unsuccessful approach: Reserving an extra case height loses a legitimate top layer.
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 // 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 = [[['generated mix 2', [[30, 30, 30, 8], [120, 100], 100, 1200], [12, 2, 24, 75, 217]], ['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]], ['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]], ['height exact', [[40, 30, 25, 2], [120, 100], 140, 2000], [9, 5, 45, 140, 115]], ['generated mix 7', [[40, 30, 20, 5], [120, 80], 140, 1025], [8, 6, 48, 135, 265]], ['generated mix 15', [[50, 20, 30, 8], [120, 80], 115, 800], [8, 3, 24, 105, 217]]], [['generated mix 23', [[25, 25, 25, 12], [120, 100], 100, 800], [16, 3, 48, 90, 601]], ['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 55', [[50, 30, 30, 5], [120, 100], 190, 1200], [8, 5, 40, 165, 225]], ['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 2', [[30, 30, 30, 8], [120, 100], 100, 1200], [12, 2, 24, 75, 217]], ['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 33', [[40, 30, 10, 20], [120, 80], 165, 800], [8, 5, 38, 65, 785]], ['generated mix 41', [[25, 25, 10, 20], [120, 100], 165, 1200], [16, 4, 58, 55, 1185]]], [['generated mix 23', [[25, 25, 25, 12], [120, 100], 100, 800], [16, 3, 48, 90, 601]], ['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]], ['generated mix 59', [[30, 15, 10, 20], [120, 80], 165, 500], [20, 2, 23, 35, 485]]]]
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 |
|---|---|---|---|
| generated mix 2 | [12, 3, 36, 105, 313] | [12, 2, 24, 75, 217] | 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 |
| weight bound partial layer | [9, 3, 20, 90, 425] | [9, 3, 20, 90, 425] | Passed |
| too heavy for one | [9, 0, 0, 15, 25] | [9, 0, 0, 15, 25] | Passed |
| height exact | [9, 5, 45, 140, 115] | [9, 5, 45, 140, 115] | Passed |
| generated mix 7 | [8, 7, 56, 155, 305] | [8, 6, 48, 135, 265] | Failed |
| generated mix 15 | [8, 3, 24, 105, 217] | [8, 3, 24, 105, 217] | Passed |
SHA-256 / fdcdf383d31c308594bc0c132dd152f109b2a3007360e4221b5cfafc8daf50ee
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) // 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 = [[['generated mix 2', [[30, 30, 30, 8], [120, 100], 100, 1200], [12, 2, 24, 75, 217]], ['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]], ['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]], ['height exact', [[40, 30, 25, 2], [120, 100], 140, 2000], [9, 5, 45, 140, 115]], ['generated mix 7', [[40, 30, 20, 5], [120, 80], 140, 1025], [8, 6, 48, 135, 265]], ['generated mix 15', [[50, 20, 30, 8], [120, 80], 115, 800], [8, 3, 24, 105, 217]]], [['generated mix 23', [[25, 25, 25, 12], [120, 100], 100, 800], [16, 3, 48, 90, 601]], ['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 55', [[50, 30, 30, 5], [120, 100], 190, 1200], [8, 5, 40, 165, 225]], ['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 2', [[30, 30, 30, 8], [120, 100], 100, 1200], [12, 2, 24, 75, 217]], ['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 33', [[40, 30, 10, 20], [120, 80], 165, 800], [8, 5, 38, 65, 785]], ['generated mix 41', [[25, 25, 10, 20], [120, 100], 165, 1200], [16, 4, 58, 55, 1185]]], [['generated mix 23', [[25, 25, 25, 12], [120, 100], 100, 800], [16, 3, 48, 90, 601]], ['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]], ['generated mix 59', [[30, 15, 10, 20], [120, 80], 165, 500], [20, 2, 23, 35, 485]]]]
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 |
|---|---|---|---|
| generated mix 2 | [12, 1, 12, 45, 121] | [12, 2, 24, 75, 217] | Failed |
| rotation wins | [8, 4, 32, 95, 185] | [8, 5, 40, 115, 225] | Failed |
| listed orientation wins | [20, 4, 80, 95, 425] | [20, 5, 100, 115, 525] | Failed |
| weight bound partial layer | [9, 3, 20, 90, 425] | [9, 3, 20, 90, 425] | Passed |
| too heavy for one | [9, 0, 0, 15, 25] | [9, 0, 0, 15, 25] | Passed |
| height exact | [9, 4, 36, 115, 97] | [9, 5, 45, 140, 115] | Failed |
| generated mix 7 | [8, 5, 40, 115, 225] | [8, 6, 48, 135, 265] | Failed |
| generated mix 15 | [8, 2, 16, 75, 153] | [8, 3, 24, 105, 217] | Failed |
SHA-256 / 140037c3fee5e6618ce82f25d9ed206b7213c51e50574d026efa08b03a2b6d4b
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 = [[['generated mix 2', [[30, 30, 30, 8], [120, 100], 100, 1200], [12, 2, 24, 75, 217]], ['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]], ['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]], ['height exact', [[40, 30, 25, 2], [120, 100], 140, 2000], [9, 5, 45, 140, 115]], ['generated mix 7', [[40, 30, 20, 5], [120, 80], 140, 1025], [8, 6, 48, 135, 265]], ['generated mix 15', [[50, 20, 30, 8], [120, 80], 115, 800], [8, 3, 24, 105, 217]]], [['generated mix 23', [[25, 25, 25, 12], [120, 100], 100, 800], [16, 3, 48, 90, 601]], ['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 55', [[50, 30, 30, 5], [120, 100], 190, 1200], [8, 5, 40, 165, 225]], ['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 2', [[30, 30, 30, 8], [120, 100], 100, 1200], [12, 2, 24, 75, 217]], ['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 33', [[40, 30, 10, 20], [120, 80], 165, 800], [8, 5, 38, 65, 785]], ['generated mix 41', [[25, 25, 10, 20], [120, 100], 165, 1200], [16, 4, 58, 55, 1185]]], [['generated mix 23', [[25, 25, 25, 12], [120, 100], 100, 800], [16, 3, 48, 90, 601]], ['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]], ['generated mix 59', [[30, 15, 10, 20], [120, 80], 165, 500], [20, 2, 23, 35, 485]]]]
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 |
|---|---|---|---|
| generated mix 2 | [12, 2, 24, 75, 217] | [12, 2, 24, 75, 217] | 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 |
| weight bound partial layer | [9, 3, 20, 90, 425] | [9, 3, 20, 90, 425] | Passed |
| too heavy for one | [9, 0, 0, 15, 25] | [9, 0, 0, 15, 25] | Passed |
| height exact | [9, 5, 45, 140, 115] | [9, 5, 45, 140, 115] | Passed |
| generated mix 7 | [8, 6, 48, 135, 265] | [8, 6, 48, 135, 265] | Passed |
| generated mix 15 | [8, 3, 24, 105, 217] | [8, 3, 24, 105, 217] | Passed |
SHA-256 / f6f1c30ca481103d39daa169d0a9cd3ca14dad0342f2a9ceb8caadb0bacf7f71
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.615950+00:00.
Case digest / 234bc95a3858b0debf2d9b6d34bb81739f4138b3961868b27f56c505603e4f20