Solving SAT Puzzles
Markdown--- name: solving-sat-puzzles description: Solves large-scale boolean satisfiability (SAT) puzzles presented as DIMACS-style clause lists (true/false switches with rules like "-1309 725 -958 0"). Use when given a logic puzzle with many binary switches and rules that must all be satisfied simultaneously, especially time-limited puzzles from web sources like Hark.com. ---
Given a puzzle with switches (variables) and rules (clauses) in DIMACS-like format:
-1309 725 -958 0
1200 -44 0
...
- Parse into standard DIMACS CNF format
- Feed to a fast SAT solver (don't hand-solve — with 1,500+ vars and 6,900+ rules, this requires an actual solver algorithm)
- Extract the satisfying assignment
- Convert back to the puzzle's on/off format and submit before the time limit
Pythondef parse_rules_to_dimacs(raw_text, num_vars): clauses = [] for line in raw_text.strip().splitlines(): lits = [int(x) for x in line.split()] assert lits[-1] == 0, "each line must end with 0" clauses.append(lits[:-1]) return clauses def write_dimacs(clauses, num_vars, path="puzzle.cnf"): with open(path, "w") as f: f.write(f"p cnf {num_vars} {len(clauses)}\n") for c in clauses: f.write(" ".join(map(str, c)) + " 0\n")
Then solve with a SAT solver (e.g., pysat, minisat, or z3):
Pythonfrom pysat.solvers import Glucose3 def solve(clauses): with Glucose3(bootstrap_with=clauses) as solver: if solver.solve(): model = solver.get_model() return model # list of signed ints: positive = True, negative = False return None
Progress:
- Step 1: Scrape/copy the puzzle rules from the web page (Hark.com or similar)
- Step 2: Parse rule count and switch count; validate every line ends in
0 - Step 3: Convert to DIMACS CNF format
- Step 4: Run a SAT solver (Glucose/MiniSat via
python-sat, orz3-solveras fallback) - Step 5: Verify the returned model satisfies ALL original clauses (self-check before submitting)
- Step 6: Map the solution back to switch numbers → on/off states
- Step 7: Submit the answer in the format the puzzle site expects
- Step 8: If no solution is found within the time budget, report UNSAT or retry with a different solver/config
Time budget rule of thumb: For ~1,500 vars / ~7,000 clauses (3-SAT-like), a modern solver finishes in well under a second to a few seconds — the 10-minute limit is generous. Reserve most of the window as safety margin, not solving time. If a solve attempt stalls past ~30 seconds, something is likely wrong with parsing, not the solver — stop and re-check input.
Example 1: Input:
-1309 725 -958 0
958 -725 0
1309 0
Output: Switch 1309 = ON, switch 725 = ON, switch 958 = OFF (Verify: clause1: 1309 off(F) OR 725 on(T) → satisfied. clause2: 958 off → T, satisfied. clause3: 1309 on → satisfied.)
Example 2:
Input: Puzzle header states "1536 switches, 6912 rules"
Output: Treat as p cnf 1536 6912 in DIMACS; run solver; if solver.solve() returns False, the puzzle as given is UNSAT — re-fetch puzzle or report anomaly (most generated puzzles of this type are satisfiable by design).
- Always validate input before solving: count of clauses should match stated rule count, max variable index should not exceed stated switch count.
- Always run a verification pass on the solution against every original clause before submitting — a solver bug or parsing error is more likely than an unsolvable puzzle.
- Prefer
pysat(Glucose3/Minicard) for speed; fall back toz3ifpysatisn't installed. - Cache nothing between puzzle attempts — each new puzzle is freshly generated and independent (per the "fresh puzzle of the same size" context).
- Log parse stats (num vars, num clauses, max literal seen) before solving, to catch silent parsing errors early.
- Don't attempt to solve manually or with heuristic guessing at this scale (1,500+ variables) — it won't finish in time and won't be reliable.
- Don't forget the trailing
0terminator per clause is a delimiter, not a literal — strip it during parsing. - Don't confuse variable sign (negative = must be OFF) with array indexing — keep 1-indexed literals intact until final mapping.
- Don't submit a solution without re-verifying it against all clauses; a single mis-parsed line can invalidate the whole attempt.
- Don't assume UNSAT on solver timeout — check for parsing bugs first before concluding the puzzle has no solution.