Skip to content

Add Balance Loop solver support (balance) across puzzle core and backend - #230

Draft
semiexp with Copilot wants to merge 3 commits into
mainfrom
copilot/add-balance-loop-solver
Draft

Add Balance Loop solver support (balance) across puzzle core and backend#230
semiexp with Copilot wants to merge 3 commits into
mainfrom
copilot/add-balance-loop-solver

Conversation

Copilot AI commented Apr 23, 2026

Copy link
Copy Markdown
Contributor

Balance Loop was not implemented, so the system could not parse or solve balance puzzle URLs. This change adds full solver support for Balance Loop rules (single loop through all circles, clue = sum of two straight-arm lengths, white = equal arms, black = unequal arms).

  • Puzzle engine: new balance_loop module

    • Added cspuz_rs_puzzles/src/puzzles/balance_loop.rs.
    • Implemented solve_balance_loop(clues) over BoolGridEdges with:
      • single-cycle loop constraint,
      • mandatory passage through all clue cells,
      • arm-length extraction in all 4 directions via consecutive edge prefixes,
      • clue-sum constraint over the two incident straight directions,
      • per-clue equality/inequality constraints for white/black circles.
    • Added URL serializer/deserializer for puzz.link kind balance.
    • Added unit tests for solver behavior and round-trip serialization.
  • Puzzle registry wiring

    • Exported module from cspuz_rs_puzzles/src/puzzles/mod.rs.
  • Backend integration

    • Added cspuz_solver_backend/src/puzzle/balance_loop.rs.
    • Implemented backend solve(url) adapter:
      • parses balance URL,
      • renders white/black circles with clue numbers,
      • overlays irrefutable loop facts.
    • Registered puzzle in backend dispatch list (balance alias, “Balance Loop” label).
    • Added backend-level smoke test for URL solve path.

Example of the new puzzle-side API surface:

let problem = balance_loop::deserialize_problem(url).ok_or("invalid url")?;
let ans = balance_loop::solve_balance_loop(&problem);

@semiexp

semiexp commented Apr 23, 2026

Copy link
Copy Markdown
Owner

@copilot

            solver.add_expr(has_left.clone().imp(len_left.eq(left_len)));
            solver.add_expr((!has_left.clone()).imp(len_left.eq(0)));

このあたり、len_left と left_len を別途持つ必要はありません。has_left == false のとき、left_len は自動的に 0 になるからです。right, down, up についても同様です

Copilot AI commented Apr 23, 2026

Copy link
Copy Markdown
Contributor Author

@copilot

...

len_left/len_right/len_up/len_down を削除し、left_len/right_len/up_len/down_len を直接使う形に修正しました。該当コミットは d8b6f5c です。

Copilot AI requested a review from semiexp April 23, 2026 12:54
@semiexp

semiexp commented Apr 23, 2026

Copy link
Copy Markdown
Owner

@copilot 6 個の条件分岐書くくらいなら前みたいにループに戻してください

Copilot AI commented Apr 23, 2026

Copy link
Copy Markdown
Contributor Author

@copilot 6 個の条件分岐書くくらいなら前みたいにループに戻してください

ループ形式に戻しました。len_* は増やさず left_len/right_len/up_len/down_len をそのまま使っています。対応コミットは c2cfd49 です。UI変更はありません。

@ReverM

ReverM commented Apr 28, 2026

Copy link
Copy Markdown
Contributor

I'm unsure how copilot assisted PR works, but the example puzzle used for testing only has one kind of clue. It might be best to switch to another?


type Problem = Vec<Vec<Option<(i32, bool)>>>;

fn clue_combinator() -> impl Combinator<(i32, bool)> {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Huh, this is in common with light and shadow, interesting

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.

3 participants