Tasks/Operations Research/Heuristic Scheduling

Program the cleanup robot to sort every ball into its basket

Drive a robot with a record-once, replay-many macro button

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

The office floor is a mess. Balls of every colour are scattered across an N x N warehouse grid criss-crossed by shelving walls, and each colour has one matching basket somewhere on the floor. You have exactly one robot and one controller. The robot understands four buttons — forward, turn right, turn left, swap — and the controller has one more trick: a macro you can record once and replay as many times as you like. Every button you press costs you, including each macro replay, so a clumsy plan that walks the robot back and forth racks up a huge tab. Your job is to press the fewest buttons that still lands every ball in its own basket before the move budget runs out.

You control a machine through a macro language; the objective is defined below. The entire difficulty is macro compression: a naive plan is a long string of F/R/L/S, but the tours the robot repeats (walk-to-cell, drop, walk-back) are full of structure, and a well-designed macro turns hundreds of moves into a handful of pressed buttons. You are scored relative to a reproduced contest rank-1 solver, so matching a strong human is the bar.

The machine

There is an N x N grid. Cell (0,0) is the top-left; (i,j) is i cells down and j cells right. The outer boundary is walled, and there may be walls between adjacent interior cells. Every cell is reachable from every other cell without crossing a wall.

On the grid are M balls and M baskets. For each type k (0 <= k < M) there is exactly one ball of type k and one basket of type k. Initially each cell holds at most one ball or basket.

The robot starts at (0,0) facing right, holding nothing. You control it with a sequence of buttons:

Basic buttons

  • F (forward): move one cell in the current facing. If a wall blocks the destination, the robot stays put (the button is still spent).
  • R (turn right): rotate 90° clockwise in place.
  • L (turn left): rotate 90° counter-clockwise in place.
  • S (swap): exchange the ball in hand with the ball on the current cell.
  • empty hand + ball here -> pick it up (cell becomes empty);
  • ball in hand + empty cell -> drop it (hand becomes empty);
  • ball in hand + ball here -> swap the two;
  • empty hand + empty cell -> nothing happens.
  • A ball sitting on a basket cell is swapped just like any other ball.

Controller buttons

  • M (macro): if not currently recording, start recording. If currently recording, stop and register the recorded sequence as the macro (replacing any previous one).
  • P (play): replay the most recently registered macro. If none is registered yet, nothing happens.

While recording, any basic F/R/L/S you press is both executed and appended to the macro being recorded. A P pressed while recording replays the previously registered macro, and the basic operations it expands to are executed and appended to the macro being recorded (you cannot replay the macro you are currently recording — only the last completed one).

Example: with RFF already registered, running MFPM records a new macro. M starts recording; F executes+records; P replays the registered RFF (executed and appended); M stops. The basic ops executed are FRFF, and the newly registered macro is FRFF.

Initially no macro is registered and nothing is being recorded.

Budget and scoring

You are given a basic-operation cap T. After macro expansion, at most T basic operations are executed; the T+1-th basic operation is not executed and the run is cut off there. So P is cheap to press but its expansion is charged against T.

Let A be the length of the button sequence you output — M and P each count as one button. Let V be the number of balls sitting on their matching basket at the end of the simulation. The absolute score for a case is:

  • A if V == M (all balls delivered) — lower is better;
  • T * (M - V) if V < M (a big penalty).

You are scored on the absolute score of each case — lower is better. A reproduced contest rank-1 solver's per-case scores ship with the task so you can gauge how strong your solution is, but how the raw scores map to the final reward is deliberately not disclosed. Optimise the raw score itself.

Closing the gap to a top contest solver is almost entirely about better macro synthesis.

Input format (stdin, one instance)

N M T
v_0
...
v_{N-1}        # N lines: v_i is a length-(N-1) 01 string; v_i[j]=1 <=> wall between (i,j) and (i,j+1)
h_0
...
h_{N-2}        # N-1 lines: h_i is a length-N 01 string; h_i[j]=1 <=> wall between (i,j) and (i+1,j)
b_0 c_0 d_0 e_0
...
b_{M-1} c_{M-1} d_{M-1} e_{M-1}   # ball k starts at (b_k,c_k); basket k is at (d_k,e_k)

Constraints: 10 <= N <= 20, N/2 <= M <= 2N, 1 <= T <= 2 N^2 M. All ball and basket cells are distinct.

Output format (stdout)

Print the button sequence, one character per line or all on lines with no spaces — any whitespace layout is fine; only the characters F R L S M P are read, in order. The output length A must be <= T.

What you submit

Your solver lives in methods/main/ (the graded directory), containing:

  • 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 button sequence on stdout. Any language.
  • build.sh (optional): if present, the grader runs it once before grading (e.g. to compile a C++ solver). Do your compilation here and have run.sh exec the built binary.

The starter methods/main/run.sh runs the shipped greedy baseline (methods/main/solution.py). Replace it with your solver.

Local dev bench

  • tools/in/ — the 100 visible instances (seeds 0-99).
  • tools/gen — the official generator. Make more instances with ./tools/gen seeds.txt --dir=OUTDIR where seeds.txt is 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 official visualiser/judge: ./tools/vis in.txt out.txt prints Score = <absolute score> and writes a vis.html you can open.
  • python3 selfcheck.py [N] — runs your run.sh on the first N visible cases (default 100), prints each case's raw score alongside the reference solver's raw score on the same case. Free and unlimited.

The generator, the distribution, and the visualiser are exactly the contest's. The sealed evaluation reruns your run.sh on 200 fresh sealed instances from the same generator and scores them with the same visualiser; there is no feedback loop — whatever sits in methods/main/ at the end is what is graded.

Notes

  • CPU only, no network. Python 3 and g++ are available in both the workbench and the grader.
  • Each sealed case runs your run.sh under a wall-clock cap (20 s/case); a crash, timeout, malformed output, or a case that fails to deliver all balls scores that case's rel at ~0. Complete every case first, then optimise length.
  • The reference contest was a 2-second-per-case time limit; you have more slack here, but the aggregate is dominated by macro quality, not raw search time.

Metric

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

rel(c) = rank1 button count / your button count; a crash, timeout or undelivered ball makes rel(c) 0.

anchorvisible setheld-outreward
Bshipped greedy starter, no macro0.30150.30220.00
R30reproduced contest rank-300.95060.20
R1reproduced contest rank-11.00000.80
Urank-1 length cut by 5%1.05261.00
normalisation
m <= B0
B < m <= R300.20 * log(m/B) / log(R30/B)
R30 < m <= R10.20 + 0.60*log(m/R30)/log(R1/R30)
R1 < m <= U0.80 + 0.20*log(m/R1)/log(U/R1)
m > U1

m = this run's held-out metric  ·  B = shipped greedy starter, no macro  ·  R30 = reproduced contest rank-30  ·  R1 = reproduced contest rank-1  ·  U = rank-1 length cut by 5%

The 200 per-case rel values are averaged first, then the single mean is mapped. Linear in log(m) inside each band, clipped to that band.

Rollouts

352 minwall clock
$115.51spend
200.1Mtokens
75versions, 47 kept
180 200 220 240 260 280 $0 $25 $50 $75 $100 cumulative spend on the run visible seeds 0-19 mean button count, 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 v31 v32 v33 v34 v35 v36 v37 v38 v39 v40 v41 v42 v43 v44 v45 v46 v47 v48 v49 v50 v51 v52 v53 v54 v55 v56 v57 v58 v59 v60 v61 v62 v63 v64 v65 v66 v67 v68 v69 v70 v71 v72 v73 v74
keptrevertedno scoreturning point
  1. v0Shipped index-order BFS baseline, no macromeasured on all 1002 min · $0.37
  2. v1Stochastic ordering search, orientation-state paths, one repeated-substring macro278.555 min · $0.82
  3. v2Plan with 31 fixed translation macros as one-button graph edgesStop emitting a raw F/R/L/S string and plan on a graph where one macro replay is a single edge, so the planner buys compression.221.4511 min · $1.55
  4. v3Broaden to dogleg and boundary-reset macros with legs through length 7213.2523 min · $3.29
  5. v4Waypoint DP re-registering among eight leading macros213.1526 min · $3.81
  6. v5Port the planner to C++; dogleg legs through 10 plus N-1212.229 min · $4.49
  7. v6Orientation-aware annealing for the 15 leading macro/order candidates209.2532 min · $5.24
  8. v7Mirrored doglegs, quarter-turn strides, stairs, wall-following strides205.7536 min · $6.13
  9. v8Three generations of instance-local macro evolution around 16 catalog leadersStop picking macros from a fixed catalog and evolve the word per instance against the board it actually has to walk.194.640 min · $6.95
  10. v9Deepen evolution to five generations, 24 parents, block and inverse mutations189.742 min · $7.53
  11. v10Portfolio of the v8 shallow beam and the v9 deep beam, shorter output winsmeasured on seeds 0-7947 min · $8.87
  12. v11Second decorrelated deep evolutionary restart, portfolioed with the first188.5552 min · $10.00
  13. v12Suffix-S macro planner: replays carry the pick/drop, macro-aware order DPtargeted cases only57 min · $10.96
  14. v13Raise both deep-beam macro genome caps from 40 to 60 ops188.859 min · $11.82
  15. v14Keep both 40-op beams, extend the second two generations at a 60-op cap187.7562 min · $12.79
  16. v15Monotonic route polish: swap, reversal, relocate, 2-3 block relocate185.564 min · $13.61
  17. v16Exact subset DP for macro-aware job order on each beam leader when M<=13185.566 min · $14.28
  18. v17Raise the route-polish limit from 3 to 10 rounds185.2568 min · $14.91
  19. v18Specialize the macro body by turn canonicalization and block deletion185.2571 min · $15.64
  20. v19Memetic fitness: refine the top eight route orders before selecting parents185.773 min · $16.41
  21. v20Two or three extra macro restarts on zero-wall boards, scaled by N^2*M185.1583 min · $19.82
  22. v21Generalize the bonus beam count by complexity, walled boards included183.3586 min · $20.89
  23. v22Open-board multi-macro waypoint DP with charged mid-plan redefinitionseven targeted cases89 min · $21.64
  24. v23Inject 300 procedural random-walk macros into every bonus beam18393 min · $23.26
  25. v24Inject procedural macros only into the middle bonus restart183.05100 min · $25.76
  26. v25Enumerate two-turn open-board seeds F^a T F^b U and T F^a U F^bten open cases only109 min · $28.99
  27. v26Exhaustive one-edit macro descent on the seeded leader, up to four rounds182.75114 min · $30.78
  28. v27Descend through short block delete/insert/replace and 1-3 relocate182.45119 min · $32.78
  29. v28Runtime guard: no bonus beams above N^2*M=12000, one above 9000five stress cases only123 min · $34.59
  30. v29Extra unseeded bonus restarts on small instances182.15126 min · $36.01
  31. v30Raise block-neighborhood macro descent from two to five roundsnine targeted cases129 min · $36.89
  32. v31Apply one-edit macro descent to every unseeded bonus leader too182.05132 min · $38.42
  33. v32Widen route polish from the top six to the top ten finalists per beam181.8136 min · $39.97
  34. v33Route-polish all 15 finalists rather than the top ten181.8139 min · $41.41
  35. v34Expand route block-relocate lengths from 2-3 to 2-6181.5143 min · $43.12
  36. v35Portfolio short-block, long-block, and long-after-short route polish181.35150 min · $46.20
  37. v36Extra route basin with reversed short-block relocation below complexity 9000181.3154 min · $47.94
  38. v37One-edit macro descent from seeded finalists ranked 2 and 3twelve targeted cases159 min · $49.77
  39. v38Post-final macro neighborhood around the actual winning macro and order181.05162 min · $51.02
  40. v39Light post-final macro search for complexity 5000-9000timed out on case 32169 min · $54.14
  41. v40Runtime model for M^3 route polish: expensive portfolios need M<=26181.05181 min · $59.66
  42. v41Open-board macros with one S inserted, event-state DP over swap replaysten open cases only211 min · $66.81
  43. v42Exact subset-and-orientation order DP on the winning macro when M<=1334 tuning cases only214 min · $67.23
  44. v43Whole-job S+transport+S macro family with used/not-used orientation DPten open cases only217 min · $67.88
  45. v44Hierarchical waypoint DP: re-record W as WW, WRW or WLW using replays of WMacros can be built from macros: record the new body out of replays of the old one, so W becomes WW for two extra buttons.ten open cases only223 min · $69.04
  46. v451500-step route-order anneal under the hierarchical multi-macro DPopen + generated only230 min · $70.65
  47. v46Tighten the open M=27-29 exception from complexity 7500 to 6800targeted cases only232 min · $71.07
  48. v47Gate the hierarchy at complexity<=5000; add a second squaring level W^4targeted cases only233 min · $71.60
  49. v48Remove the null W^4 candidates, keep the narrower hierarchy gatetargeted cases only234 min · $71.95
  50. v49Add one-turn hierarchical descendants WR, WL, RW, LW per baseeligible cases only239 min · $73.18
  51. v50Restore the 13-state candidate set, lengthen the anneal to 5000 stepstargeted cases only242 min · $73.99
  52. v51Shorten the hierarchical anneal to 3000 steps and rescale cooling22 eligible cases only245 min · $74.83
  53. v52RNG-salt probe: eight independent trajectories on four open casesfour open cases only248 min · $75.41
  54. v53Exhaustive beam over canonical open F/R/L words of length 2-10, M<=10eligible cases only252 min · $76.92
  55. v54Evaluate only canonical words of exactly length 10 in that beamtargeted cases only254 min · $77.36
  56. v55Also enumerate all 10,791 canonical length-11 words for open M<=10ten eligible cases only255 min · $78.02
  57. v56Extend the length-10 canonical beam to walled M<=10, complexity<=1600eligible cases only260 min · $79.92
  58. v57Extend the length-10 beam to M=11-13 at complexity<=160015 eligible cases only262 min · $80.62
  59. v58Add 4,690 canonical sparse macros of length 11-20 with at most two turns66 eligible cases only267 min · $82.38
  60. v59Refine the top 50 exact-length-10 candidates instead of the top 1573 eligible cases only272 min · $83.92
  61. v60Add canonical length-N exhaustive candidates for N=11-12 boards24 eligible cases only276 min · $85.52
  62. v61Keep length-12 enumeration only for N=12, M<=8; drop the null branchestargeted cases only278 min · $86.19
  63. v62Extend the hierarchy and its anneal to walled M<=13, complexity<=2000eligible cases only286 min · $88.71
  64. v63Widen the walled hierarchy to M<=16, complexity<=3000newly eligible only292 min · $91.12
  65. v64Widen the walled hierarchy to M<=18, complexity<=3600newly eligible only297 min · $92.97
  66. v65Widen the walled hierarchy to M<=20, complexity<=4200newly eligible only302 min · $95.03
  67. v66Two monotonic swap/reverse/relocate passes after the hierarchical annealaffected cases only308 min · $97.01
  68. v67Add a fourth hierarchical base and its derivatives, 13 to 17 macro statesaffected cases only311 min · $98.33
  69. v68Second preserved 2000-step hierarchical anneal for walled complexity<=2000eligible cases only321 min · $101.86
  70. v69Seed the hierarchy with the post-final one-edit macro winnertargeted cases only328 min · $104.36
  71. v70Add W^3 as a cheaply recorded hierarchical candidate per base49 visible cases only333 min · $106.41
  72. v71Raise the hierarchical route local-descent cap from two to five passesfive generated cases334 min · $107.17
  73. v72Length-2/3 block relocation inside the hierarchical route descentvisible + generated only340 min · $110.06
  74. v73Reversed length-2/3 block relocation when complexity<=2000affected cases only344 min · $111.53
  75. v74Ordinary block relocation extended to lengths 4-5 at complexity<=2000affected cases only347 min · $113.14

75 snapshots, 5.8 h, $115. The log reports a first-20 mean only through v40; from v41 each change is judged on targeted open or walled case sets.

On the hidden set

held-out metricreward
shipped greedy starter, no macro0.30220.00
reproduced contest rank-300.95060.20
reproduced contest rank-11.00000.80
rank-1 length cut by 5%1.05261.00
this run0.94620.1992