Skip to content

Fix #182: count problem 145 loop solutions with an exact plug DP - #193

Open
whuang369 wants to merge 1 commit into
FrontierCS:mainfrom
whuang369:fix/182-problem-145-checker
Open

whuang369 wants to merge 1 commit into
FrontierCS:mainfrom
whuang369:fix/182-problem-145-checker

Conversation

@whuang369

Copy link
Copy Markdown
Contributor

Fixes #182.

Problem

algorithmic/problems/145/checker.cpp counted loop solutions with a plain backtracking search over the 312 grid edges. It used a static edge order and only local propagation, and it stopped early only after finding LIMIT = 6 solutions. Its cost grows exponentially as the clues get weaker, and weak clues (mostly 1s and 2s) are exactly what the task rewards. On such boards the search neither finds six solutions nor exhausts the space within the checker limit (10 s CPU / 20 s wall, judge_engine.js). The checker is killed and the case scores 0 with an empty message.

The file header said the checker "fully reproduces behavior of the original Python+Z3 checker", but the SAT solver had been replaced by this search.

Changes

This is option 1 from the issue (proper fix). It adds no node budget and needs no statement change.

  • checker.cpp: replace the counter with a plug (profile) DP over the 13 × 13 grid points in row-major order. Each point decides its right and down edge. The profile has three parts:

    • the vertical edges crossing the sweep line, as a balanced bracket sequence of path ends;
    • one bit per column holding the top edge of a clue cell whose bottom edge is not decided yet;
    • a flag recording that the loop is already closed.

    Each clue is checked when its cell's last edge is decided, and a state is pruned as soon as a cell's partial count rules it out. Counts saturate at six, so the result is exactly min(6, number of single loops satisfying every clue), the quantity the statement scores. The run time depends on the board size, not on how constrained the board is.

    States are kept in a small open-addressing map that packs the count into each 8-byte entry. A hard board reaches about 0.9M states per step, too many for unordered_map to be fast.

  • Unchanged: input validation (readSol, allowed digits per mode, template positions), the scoring formula and the verdict message.

  • The checker no longer prints up to six solution grids to stderr before the verdict. The judge only reads the Ratio: line, so this has no effect on scoring.

Verification

Built with g++ -O2 -pipe -std=gnu++17 (also at -O0; -Wall -Wextra reports nothing in the checker code) against algorithmic/judge/include/testlib.h. Timings below are on an Apple M3 Pro.

Board Before After
testdata/1.ans (bundled reference) 58.93, 1 solution (0.03 s) 58.93, 1 solution (0.01 s)
every clue 1 killed after 15 s 10.00, six or more (0.04 s)
every clue 2 killed after 15 s 1.00, six or more (1.1 s)
5 random 1/2 boards killed on 3 of 5 same verdicts on the other 2, all ≤ 0.1 s

Correctness, checked three independent ways:

  • Brute force on small grids: the counting code was copied unchanged except for the grid size, which became a parameter. At size 12 the copy matches the real checker on 200 board/mode pairs. Compared against exhaustive enumeration on 2.29M instances of 1×1 to 5×5 cell grids, including every 3×3 clue grid: 0 mismatches, both for exact counts and for counts capped at 1 to 6. On blank grids it reproduces the number of cycles in the grid graph (OEIS A140517: 1, 13, 213, 9349, 1222363).
  • Old checker: identical verdict lines on all 367 board/mode pairs where the old checker finishes, covering every bucket (0, 1, 2, 3, 4, 5, six or more). Boards were derived from random loops, mutated, or random.
  • Independent SAT counter: written from the statement only. It enumerates single loops with cuts against extra loops and blocking clauses. 2,434 comparisons, 0 mismatches, including boards the old checker cannot finish. Each bucket has 163 to 945 boards.

Worst case: the hardest board a local search found (677 boards evaluated) is every clue 2 with one 3. It peaks at 869k states (29.7M state visits) and uses about 1.2 s CPU and 70 MB, against the 10 s / 256 MB checker limits.

Archived agent runs, rebuilt from their trajectories and re-judged:

Run Recorded Old checker This checker
GPT-5.6-sol (Codex), trial 275DALt, submissions 1–2 0 killed (> 20 s) 10.00
GPT-5.6-sol (Codex), trial hAXcKs4, submissions 1 and 3 0 killed (> 20 s) 5.82
Gemini 3.1 Pro (Gemini CLI), trial FsXTUy2, all 3 submissions 0 killed on at least one case 4.13 / 1.79 / 4.13

Every other recorded score in these trials is reproduced exactly, except seven judge engine error submissions in 275DALt. Those are unrelated to this bug: the old checker finishes their boards in 0.1–1.1 s. FsXTUy2 is the selected trial for problem 145 in results/agent_scores/, so its published 0.0 comes from this bug.

🤖 Generated with Claude Code

…ug DP

The checker counted loop solutions with a plain backtracking search over
the 312 grid edges (static edge order, local propagation only) and
stopped early only after finding six solutions. Its cost grows
exponentially with how weakly the clues constrain the board, and weak
clues (many 1s and 2s) are exactly what the task rewards. On such boards
it neither found six solutions nor exhausted the space within the
judge's 10 s checker limit, so the checker was killed and the case
scored 0.

Replace the search with a plug (profile) DP over the 13 x 13 grid
points. The profile holds the vertical edges crossing the sweep line as
a bracket sequence of path ends, one top-edge bit per column for clue
cells whose bottom edge is still open, and a flag for "loop already
closed". It counts edge sets forming exactly one non-empty loop that
satisfies every clue, saturating at six, so the count is exact and does
not depend on how weakly the board is constrained. States live in a
small open-addressing map that packs the count into each entry.

Input validation, the scoring formula and the verdict message are
unchanged. The checker no longer prints solution grids to stderr; the
judge reads only the Ratio: line.

Verified: no mismatches against brute force on small grids (2.29M
instances, every 3x3 clue grid included), against the old checker on 367
boards it finishes, or against an independent SAT counter on 2,434
comparisons covering every bucket from 0 to six or more. The hardest
board found (all 2s with one 3) takes about 1.2 s and 70 MB.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Problem 145 checker does not terminate on most agent-constructed boards

1 participant