Tasks/Operations Research/Heuristic Scheduling

Schedule the terminal cranes to dispatch every container in order

Choreograph five terminal cranes that cannot pass each other

terminal_crane_scheduling Operations Research Heuristic Scheduling
instruction.mdthis is what the agent is given

A container terminal is a 5 x 5 yard. Containers pour in through five receiving gates on the left edge and must leave through five dispatch gates on the right edge in a strict order. You command five cranes — one big crane that can hoist a container high and glide over other stacks, and four small cranes that must keep their load low and cannot pass over an occupied square. Every turn you move all cranes at once. Mis-order a dispatch, send a container out the wrong gate, or leave one stranded and the yard is fined enormously; do it cleanly and your only cost is the number of turns you took. Your job is to choreograph the cranes so every container is dispatched from its correct gate, in order, in as few turns as possible.

Hard Constraints

The yard. An N x N grid, N = 5. Cell (0,0) is the top-left; (i,j) is i cells down and j cells right. N^2 = 25 containers numbered 0 .. 24 enter the yard.

  • Receiving gates (left edge, column 0): from the gate at (i,0), N containers arrive one at a time. The j-th container to arrive at row i is A[i][j] (given in the input). The next container in a row's queue only materialises at (i,0) once that cell is empty (and no crane is holding a container while parked on it).
  • Dispatch gates (right edge, column N-1): any container placed on (i,N-1) is dispatched instantly (it leaves the board at the end of that turn). Gate (i,N-1) is supposed to dispatch containers N*i, N*i+1, ..., N*i+N-1, in that order.
  • Every square (gates included) holds at most one container. Non-dispatch squares are free scratch space for reordering.

Initially the big crane (crane 0) sits at (0,0) and the small cranes 1..N-1 sit at (1,0) .. (N-1,0). Every crane starts empty. The first container of each row is already sitting on its receiving gate at turn start.

Cranes and moves. Crane 0 is the big crane: while carrying a container it may still move onto a square that already holds a container. Cranes 1..N-1 are small: while carrying a container they may not move onto a square that holds a container. (Empty, any crane may move onto an occupied square.) Each turn you issue one action per crane simultaneously:

  • P — pick up the container on the current square (illegal if the crane already holds one, or the square is empty).
  • Q — put down the held container on the current square (illegal if the crane holds nothing, or the square is already occupied).
  • U/D/L/R — move one square up / down / left / right (illegal to leave the board; the small-crane carrying restriction applies).
  • B — retire this crane (illegal while holding a container). A retired crane is removed from the yard for good and no longer occupies a square.
  • . — do nothing.

Collisions are illegal and abort the run: after a turn no two (non-retired) cranes may occupy the same square, and no two may swap squares. Any illegal action anywhere makes the whole output invalid and forfeits that case.

Beyond the game rules:

  • Submit an algorithm, not precomputed answers — the grader re-runs your code on instances you have never seen. Do not key on seeds or instance names.
  • /app/methods/main/ is what gets graded. Keep the run.sh contract below.
  • Each sealed case runs your run.sh under a wall-clock cap of 20 s. A crash, timeout, or malformed output forfeits that case.
  • There is no network at run time, on the workbench or in the grader. Python 3, g++ and a JDK 17 are available in both; anything else you have to write yourself.

What You Have

  • tools/in/ — 100 visible instances (seeds 0-99).
  • tools/gen — the generator. Make more instances with ./tools/gen seeds.txt --dir=OUTDIR, one unsigned-64-bit seed per line. For local testing use seeds in 0..10000 only — the sealed grading seeds live far outside that range, so staying inside it keeps your practice set from colliding with the hidden set.
  • tools/vis — the visualiser/judge, the same one the grader uses: ./tools/vis in.txt out.txt prints Score = <absolute score> and writes a vis.html you can open.
  • python3 selfcheck.py [N] — free and unlimited: runs your run.sh on the first N visible cases (default 100) and prints each case's raw score.
  • methods/main/solution.py — a crude single-big-crane sequential dispatcher, yours to rewrite or delete.

The instance arrives on stdin:

N
A[0][0] A[0][1] ... A[0][N-1]
...
A[N-1][0] ... A[N-1][N-1]

N = 5. Row i lists the N containers arriving at receiving gate (i,0) in arrival order. The N^2 values are a permutation of 0 .. N^2-1.

What You Submit

Leave your best solver in methods/main/:

  • run.sh (required): run once per test case as bash run.sh < instance.txt > out.txt. It must read one instance on stdin and write the operation grid on stdout. Any language.
  • build.sh (optional): if present, the grader runs it once before grading (e.g. to compile a C++ or Java solver). Do your compilation here and have run.sh exec the built binary.

The operation grid on stdout is exactly N lines. Line i is the string of actions for crane i over successive turns, each character one of P Q U D L R B .. Lines may differ in length; a line shorter than the longest is padded with . (idle). The number of turns is the length of the longest line, and each line must be non-empty and at most 10000 characters.

There is no submit step and no per-attempt feedback. Work and self-check for as long as your run window allows, then leave your best run.sh in place.

How It Is Judged

The grader reruns your run.sh on 200 sealed instances you never see, drawn from the same generator, and scores each with the same visualiser. For one case, let

  • M0 = number of turns (the length of the operation grid),
  • M1 = total inversions among correctly-dispatched containers: for each gate i, over the containers it dispatched that belong to it (N*i <= b < N*(i+1)) in dispatch order, count pairs that are out of increasing order,
  • M2 = number of containers dispatched from the wrong gate,
  • M3 = number of containers never dispatched.

The absolute score is

M0 + 100*M1 + 10000*M2 + 1000000*M3      (LOWER is better)

A perfect run dispatches all 25 containers from the right gates in order, so its score is just the turn count M0. The huge weights on M2/M3 mean an incomplete or mis-routed plan scores in the tens of thousands or millions — far worse than any honest turn count. Get a fully-correct plan first, then shorten it. Scoring is per case and then aggregated, so a case you forfeit cannot be carried by a case you optimise.

How the raw scores map to the final reward is deliberately not disclosed — optimise the raw score itself. The starter as shipped is the zero of that scale: submitted unchanged it scores 0.

Metric

mean relative score over the 200 sealed instances · higher is better

rel(c) = ref_len(c) / absolute(c); absolute = turns + 100*inversions + 1e4*wrong-gate + 1e6*stranded

anchorvisible setheld-outreward
Bshipped greedy starter0.16000.16050.00
Mreproduced intermediate reference0.89730.30
Rreproduced strong reference1.00000.60
Ureference * 0.95 turn target1.05261.00
normalisation
m <= B0
B < m <= M0.30 * log(m/B) / log(M/B)
M < m <= R0.30 + 0.30 * log(m/M) / log(R/M)
R < m <= U0.60 + 0.40 * log(m/R) / log(U/R)
m > U1

m = this run's held-out metric  ·  B = shipped greedy starter  ·  M = reproduced intermediate reference  ·  R = reproduced strong reference  ·  U = reference * 0.95 turn target

B=0.1605, M=0.8973, R=1, U=1.0526. Per-case rel is averaged first, then the mean is mapped; each segment is linear in log(m).

Rollouts

246 minwall clock
$51.73spend
76.7Mtokens
27versions, 23 kept
80 84 88 92 96 $0 $15 $30 $45 cumulative spend on the run visible seeds 0-99 mean raw score at 15 s, lower is better v1 v2 v3 v4 v5 v6 v7 v8 v9 v10 v11 v12 v13 v14 v15 v16 v17 v18 v19 v20 v21 v22 v23 v24 v25 v26 v27
keptrevertedno scoreturning point
  1. v1C++ rewrite: exact judge replica, greedy multi-crane scheduler, random restartsEmbed an exact judge replica so plans can be scored in-process, then search randomised multi-crane rollouts against it.1 s and 5 s only28 min · $6.82
  2. v2Parameter hill-climbing plus a patient retry mode with a long stall limit96.5331 min · $7.45
  3. v3Iterated rollout: snapshot the incumbent per turn and re-roll a random suffix92.5635 min · $8.26
  4. v4Pop-order DP over 6^5 states as a soft cost on gate pops; gated relocation3 s budget only51 min · $11.62
  5. v5Simulated annealing over whole plans replaces hill-climbing; five cranes fixed3 s budget only60 min · $13.87
  6. v6Approach-phase cranes re-pick their task each turn with a stickiness bonusStop committing a crane to a task until pickup; re-decide every turn so cranes chase whichever job is nearest now.3 s budget only61 min · $14.04
  7. v7cGate path-detour term added to buffer-cell scoring, measured neutral, disabled84.2764 min · $15.00
  8. v8Speed: shared action buffer, BFS and connectivity rewritten as 25-bit masks3 s budget only70 min · $16.66
  9. v9Speed: articulation-point DFS replaces 25 flood-fills per turn; cached choices81.8572 min · $17.65
  10. v10Prune rollouts already past the incumbent; abort stalls after 30 idle turns81.5883 min · $19.76
  11. v11futureW: unload cost also charges the buffer-cell to dispatch-gate legTUNE300 at 2 s only105 min · $23.47
  12. v12State beam search and idle-crane pre-staging added, both measured and left off80.92118 min · $26.12
  13. v13Replay the emitted plan through the replica; fall back to a single-crane plan30-case check only123 min · $26.97
  14. v14Buffer column preference: boxes needed within 2 dispatches park in column 3TUNE300 only140 min · $29.87
  15. v15Bookkeeping snapshot: retirement, idle and DP-probability knobs, defaults kept81.17144 min · $30.77
  16. v16Scale-aware parameter perturbation; idle cranes yield when they block a load80.8154 min · $33.02
  17. v17Two annealing chains per case; dead beam and windowed planner removed81.13170 min · $35.42
  18. v18Big-crane, dispatch-column and adaptive-restart knobs, all left at neutral81.2179 min · $36.64
  19. v19Cache the occupancy bitmask; fix a push that re-decided a validated craneTUNE300 only189 min · $39.46
  20. v20Validation snapshot of the v19 code at the real 15 s budget on all three slices80.51190 min · $39.71
  21. v21Unload urgency read from a greedy walk of the pop-order DP, not pending+depthAsk the DP how soon an optimal pop schedule wants each row emptied and use that as the urgency, replacing a hand-made formula.TUNE300 only205 min · $42.00
  22. v22Buffer column driven by the DP global dispatch rank instead of per-gate pending80.24206 min · $42.27
  23. v23Randomised tie-breaking inside the DP walkTUNE300 only210 min · $43.53
  24. v24Idle cranes spread one per row, with rebalanced parking urgencyTUNE300 only246 min · $51.20
  25. v25Probes on DP buffer capacity, gate priority and staging cells all inside noise80.3223 min · $47.35
  26. v26Post-process: delete idle slots from a crane line while exact replay verifies79.79229 min · $48.22
  27. v27Compaction refactored to re-enter the annealer; kept as end-of-run only79.77238 min · $49.88

From v11 tuning moved to a 300-instance set, after 100-case comparisons were found to carry about +-0.7 of RNG noise. All 27 snapshots were legal.

On the hidden set

held-out metricreward
shipped greedy starter0.16050.00
reproduced intermediate reference0.89730.30
reproduced strong reference1.00000.60
reference * 0.95 turn target1.05261.00
this run0.85140.2909
113 minwall clock
$34.78spend
54.7Mtokens
31versions, 20 kept
150 225 300 375 $0 $7.5 $15 $22 $30 cumulative spend on the run mean raw score on visible seeds 0-99, lower is better v0 v1 v2 v3 v4 v5 v6 v7 v8 v9 v10 v11 v12 v13 v14 v15 v16 v17 v18 v19 v20 v21 v22 v23 v24 v25 v26 v27 v28 v29 v30
keptrevertedno scoreturning point
  1. v0Inherited starter: one big crane dispatches in sequence, the other four retire400.083 min · $0.52
  2. v1Five-crane reactive controller: priority retrieval, blocker staging, joint movesLet all five cranes react each turn to whichever container is ready, instead of dispatching one at a time.184.0212 min · $1.55
  3. v2Run a reactive and a persistent-job policy, keep the shortest verified planRun two unlike policies on every case and keep the shortest plan that replays legally, so neither failure mode decides.163.0519 min · $2.40
  4. v3Just-in-time queue exposure: stage a gate's front only when it hides a target149.5321 min · $2.76
  5. v4Destination-ordered storage racks; blockers shift toward their final row earlyGive each blocker a destination-aware home cell, so relocating it also carries it toward the row it must exit from.126.8322 min · $3.12
  6. v5Map offset-4 blockers to column 0 to complete a four-cell rack127.224 min · $3.44
  7. v6Three complementary tie-break policies chosen by an offline path-order sweep122.9429 min · $4.53
  8. v7Big-crane assignment surcharge raised from 2 to 6 to balance active workvisible 0-49 only30 min · $4.92
  9. v8Diversify equal-priority task order too, adding an ascending-destination variant120.333 min · $5.70
  10. v9Replay-verified compression greedily deletes per-crane idle actions118.7737 min · $6.61
  11. v10Bitmask global crane-task matcher added as complementary ensemble members116.1842 min · $7.51
  12. v11v5's four-slot column-0 rack tested as a per-policy ensemble dimensiondev 100-129 oracle only43 min · $7.88
  13. v12Bounded one-successor queue lookahead added as four lower-priority policies107.2951 min · $9.94
  14. v13Lookahead extended to a second successor at still lower priorityvisible 0-19 only55 min · $10.90
  15. v14Third-successor lookahead probed offlinedev 100-107 only56 min · $11.17
  16. v15Rack exact-cost tie break mirrored from top-left to bottom-rightdev 100-107 only57 min · $11.60
  17. v16Four alternate rack cost balances swept on complementary horizontal policiesdev 108-115 only59 min · $12.02
  18. v17Three horizontal policies with big-crane surcharge 6 added for tail casesdev 220-231 only63 min · $13.76
  19. v18Idle compression run on the three shortest raw schedules, not just the bestdev 232-243 only64 min · $14.43
  20. v19Two-step matching policy with a soft bonus for crane c serving row cdev 240-251 only67 min · $15.37
  21. v20Home-bonus threshold sweep; only bonus 1 kept alongside bonus 2dev 252-263 only69 min · $16.07
  22. v21All 24 direction tie orders swept for the stable one-step policydev 274-277 only72 min · $17.43
  23. v22Four-crane policies retiring small crane 2 or 4 added to the portfoliodev 286-297 only74 min · $18.25
  24. v23One-step policy with a soft bonus for unloading a crane's own receiving rowdev 298-309 only78 min · $19.63
  25. v24Portfolio ablated from 20 policies to 14 at equal measured qualitydev 354-365 only87 min · $22.54
  26. v25Lookahead base priority and second-step decay values sweptdev 366-371 only90 min · $23.24
  27. v26Idle deletion iterated to a fixed point after top-three selectionvisible 0-9 only93 min · $24.89
  28. v27Each of the top three raw schedules fixed-point compressed before selectiondev 390-397 only97 min · $27.27
  29. v28Fixed-point compression tested through raw ranks 4 and 5dev 402-409 only98 min · $27.69
  30. v29Reactive two-step policy with delayed forced-drop threshold 10dev 418-429 only102 min · $29.14
  31. v30Final freeze: locked holdout seeds 9001-9200 opened once, no changes madeholdout 9001-9200 only113 min · $34.44

31 versions, 113 min, $34.78. From v13 tuning ran on fresh 8-12 case development slices, so late gains were never rechecked on the visible 100.

On the hidden set

held-out metricreward
shipped greedy starter0.16050.00
reproduced intermediate reference0.89730.30
reproduced strong reference1.00000.60
reference * 0.95 turn target1.05261.00
this run0.66090.2467
43 minwall clock
-spend
-tokens
8versions, 8 kept
150 225 300 375 0 25 50 75 100 agent step (this harness reports no tokens or timestamps) tune seeds 0-19 mean raw score, lower is better v1 v2 v3 v4 v5 v6 v7 v8
keptrevertedno scoreturning point
  1. v1Shipped starter: one big crane dispatches containers 0..24 in sequence393.3
  2. v2Lockstep four-deep receive, then a nearest-frontier big crane finishesseeds 0-14 only
  3. v3Job-based wave planner; five unlike strategies run, shortest legal plan winsRun several unlike planners on one case and keep the shortest plan a rules clone replays as legal.187.6
  4. v4Leftover holds recovered; every small-crane subset run with junk pickup on and offRetire a different subset of the four small cranes per case, so cramped boards are worked with fewer bodies in the way.182.7
  5. v5Assignment-order RNG seeds added, three shuffles per crane-subset variant174.65
  6. v6C++ port: 30 seeds x 3 storage-column orders x junk/staging, plus stitch and beamRewrite in C++ so the planner can be re-run hundreds of times per case over seeds and yard geometry, then stitch suffixes on.146.3
  7. v7OpenMP parallel search, 50 seeds, suffix stitched from the top three elites141.4
  8. v8Top-four elites, more stitch masks and seeds, big-only suffix, solver wall cap141.2

Eight snapshots in 43 minutes, all legal on tune, mid, extra and fresh seeds; v8 was confirmed once on holdout 80-99 at 147.70 mean turns.

On the hidden set

held-out metricreward
shipped greedy starter0.16050.00
reproduced intermediate reference0.89730.30
reproduced strong reference1.00000.60
reference * 0.95 turn target1.05261.00
this run0.46710.1862
66 minwall clock
$19.38spend
29.5Mtokens
10versions, 8 kept
240 280 320 360 400 $0 $4 $8 $12 $16 cumulative spend on the run mean raw score on visible seeds 0-99, lower is better v1 v2 v3 v4 v5 v6 v7 v8 v9 v10
keptrevertedno scoreturning point
  1. v1Inherited starter: one big crane dispatches in sequence, the other four retire400.081 min · $0.24
  2. v2All five cranes prefill the 15 interior cells in parallel, then a big-crane finishseeds 0-3 only4 min · $0.52
  3. v3Prefill two layers only, keep five emergency cells, finish gates independentlyLeave slack in the yard: filling every scratch cell deadlocks, so cap the prefill and reserve five free cells for reordering.293.845 min · $0.66
  4. v4Online five-crane job scheduler with carrying-aware BFS and reservations45/100 completed11 min · $1.73
  5. v5Beam search over macro costs for gate order, queue exposure, buffer placementPrice a whole dispatch order up front with exact macro costs instead of deciding gate by gate as the yard fills.seeds 0-19 only17 min · $2.34
  6. v6Safe direct-dispatch waves and MAPF intake permuting the first two layers254.5424 min · $3.48
  7. v7Beam may expose queued targets early, joined with favourable buffer placement250.3525 min · $4.01
  8. v8Verified portfolio: the aggressive scheduler is accepted only if replay says legalRun the unsafe fast scheduler anyway, replay its plan locally, and keep it only if it is legal and shorter than the safe beam.236.8135 min · $6.51
  9. v9Portfolio widened to 8 routing orders, 4 buffer weights, 6 saturation policiesseeds 0-19 only53 min · $12.87
  10. v10Exact-replay post-pass trims idle waits and inverse move pairs from candidatesseeds 0-19 only66 min · $19.19

Ten versions in 66 minutes and $19. From v9 the run measured 20-case slices only, so its last two gains were never checked on the full visible 100.

On the hidden set

held-out metricreward
shipped greedy starter0.16050.00
reproduced intermediate reference0.89730.30
reproduced strong reference1.00000.60
reference * 0.95 turn target1.05261.00
this run0.37030.1457
14 minwall clock
$0.69spend
3.6Mtokens
2versions, 2 kept

No trajectory curve: this run left one comparable self-check measurement, so there is nothing to plot against spend. The versions and what each one changed are below.

  1. v1C++ beam search on the big crane alone, cranes 1-4 retired, beam 2000, q_mult 2Retire the four small cranes and beam-search the big crane state space under an admissible turn lower bound.321.513 min · $0.54
  2. v2nth_element beam pruning; rebuilt with -march=native -fltoseeds 0-9 only14 min · $0.65

Two snapshots in 14 minutes and $0.69. v2 changed pruning and compile flags only and was checked on 10 cases, never on the visible 100.

On the hidden set

held-out metricreward
shipped greedy starter0.16050.00
reproduced intermediate reference0.89730.30
reproduced strong reference1.00000.60
reference * 0.95 turn target1.05261.00
this run0.19850.0371