Player vs CPU implementation of Bridges(Simon Tatham)
Goal : An solver that can just get pass the post of failure
Start
if visitedCountdfs(node) != # nodes
ConnectionPhase
else
SatisfactionPhase
- Get the maximum end points
- Connect them to the neighbors
- who are not connected currently to anyone
- The connect wont go over the bridge count required by the island and is not the neighbour of the current node
- Connect them to the neighbors
- Get the minimum node from the graph
- Not satisfied already
- Connect it to its neighbor
- Connection wont go over their bridge count
This repo also contains a DAC-style solver (selectable in the UI) that is meant to show how a divide-and-conquer approach can solve Hashiwokakero / Bridges.
- Nodes (islands) sit on a grid. Each island has a required degree (d(v)) (the number on the island).
- Edges (bridges) can only be drawn orthogonally (horizontal/vertical) to the next visible island in that direction.
- Between a pair of islands you can place 0, 1, or 2 bridges.
- Bridges must not cross and must not pass through an island.
- Final solution must satisfy:
- Degree constraints: for every island (v), (\sum_{e \ni v} x_e = d(v)) where (x_e \in {0,1,2}).
- Connectivity: all islands are in one connected component.
Divide-and-conquer works well when you can cut the board into two parts and only a small amount of information needs to “flow” across the cut.
For Bridges, that information is:
- How many bridges cross the cut at each possible crossing line (0/1/2).
So instead of guessing every edge everywhere, DAC guesses an interface assignment on the cut, then solves both halves consistently.
Imagine a vertical cut at grid column (x = mid).
Some horizontal bridges might cross this cut. Each potential crossing happens on a specific row (y).
For each such row (y), define an interface variable:
- (c_y \in {0,1,2}) = how many bridges cross the cut on row (y).
Those (c_y) values become boundary constraints:
- Left subproblem must “send” exactly (c_y) bridges through its right boundary at row (y).
- Right subproblem must “receive” exactly (c_y) bridges through its left boundary at row (y).
Horizontal cuts are the same idea, but with columns instead of rows.
At a region (R):
- Divide:
- Pick a cut line (vertical if region is wider, horizontal if taller).
- Split (R) into (R_1) and (R_2).
- Enumerate the interface:
- Find all rows/cols where a bridge could cross that cut (line-of-sight exists).
- Generate assignments where each crossing is 0/1/2 (the “interface”).
- Conquer:
- Recursively solve (R_1) with boundary constraints updated by the interface.
- Recursively solve (R_2) with the matching boundary constraints.
- Combine:
- Merge edge sets from both halves + add the interface bridges themselves.
- Validate / prune:
- Degree feasibility checks inside subregions (don’t exceed required degree).
- No-crossing checks.
- At the top level (or sometimes during recursion), enforce global connectivity.
- If any step fails, backtrack and try the next interface assignment.
This is the “real” DAC approach: the hard work is in making interface enumeration small and pruning aggressively so you don’t try (3^k) combinations blindly.
Our DACSolver currently has two distinct parts:
- DAC splitting visualization (divide step)
- Each press of
Nshows the next split:- highlights islands in the current region
- draws a rectangle around the region
- draws the cut line
- Internally, the solver:
- compresses world coordinates into grid indices (so cuts align with island columns/rows)
- generates a queue of split steps by recursively partitioning the region
- Bridge construction (making connections)
- After all split steps are shown, the solver computes bridges using a backtracking search over the legal neighbor edges:
- chooses an unassigned candidate edge
- tries counts 2 → 1 → 0
- prunes when remaining degree cannot be satisfied
- rejects any assignment that creates a crossing
- finally checks that the graph is connected
- Once a satisfying assignment is found, it is applied one move per
Npress usingEdgeSystem.createEdge(...)so you see bridges appear step-by-step.
So: the visualization is DAC-like (divide-and-conquer splitting), while the actual connection generation is currently done by a correct backtracking solver.
- Select the solver in the dropdown: Greedy or DAC (visual).
- Press
N:- Greedy: applies the next greedy move
- DAC (visual): shows region splits first, then starts placing bridges
core/src/main/java/io/solvers/bridgeSolver/Solvers/DACSolver.java: split-step visualization + solving logiccore/src/main/java/io/solvers/bridgeSolver/DebugLineComponent.java: debug line primitive for visualizationcore/src/main/java/io/solvers/bridgeSolver/RenderSystem.java: draws debug linescore/src/main/java/io/solvers/bridgeSolver/Main.java: solver dropdown + wiringNto the selected solver