Getting Started¶
Installation¶
Requirements¶
- Python >= 3.9
- NumPy >= 1.22
- Numba >= 0.56 (provides JIT compilation for the hot path)
Your First Solve¶
IPython / Interactive Session¶
from larsdoku_zs import Board
# Solve a forum hardest puzzle (SE 11.0+)
b = Board("98.7..6..75..4......3..8.7.8....9.5..3.2..1.....4....6.7...4.3....8..4......1...2")
print(b)
result = b.solve()
print(f"Solved: {result['success']}")
print(f"Steps: {result['n_steps']}")
# See which advanced techniques were used
L1 = {'crossHatch', 'nakedSingle', 'fullHouse', 'lastRemaining'}
for tech, count in sorted(result['technique_counts'].items(), key=lambda x: -x[1]):
if tech not in L1:
print(f" {tech}: {count}")
Output:
Solve Without DeepResonance (FPF Path)¶
from larsdoku_zs.solver import solve_selective
# Same puzzle, different path — FPF takes over
result = solve_selective(
"98.7..6..75..4......3..8.7.8....9.5..3.2..1.....4....6.7...4.3....8..4......1...2".replace('.','0'),
exclude_techniques={'DeepResonance'}
)
print(f"Solved: {result['success']}") # True — FPF handles it
# FPF fires once, then standard techniques cascade to completion
GF(2) Linear Algebra¶
from larsdoku_zs import Board
b = Board("98.7..6..75..4......3..8.7.8....9.5..3.2..1.....4....6.7...4.3....8..4......1...2")
p, e, dof = b.gf2()
print(f"Placements: {len(p)}, Eliminations: {len(e)}, DoF: {dof}")
# With residual info — which digits are fully determined by algebra?
from larsdoku_zs.gf2_xor import detect_gf2_xor
from larsdoku_zs.engine import BitBoard
bb = BitBoard.from_string(b.puzzle)
p, e, dof, free_cells, skip_digits = detect_gf2_xor(bb, return_residual=True)
det = [d+1 for d in range(9) if skip_digits & (1 << d)]
print(f"Digits fully determined by GF(2): {det}")
Batch Solve Forum Hardest¶
from larsdoku_zs.solver import solve_selective
with open('puzzles5_forum_hardest_1905_11+.txt') as f:
puzzles = [l.strip() for l in f if len(l.strip()) == 81]
solved = 0
for p in puzzles:
r = solve_selective(p.replace('.', '0'))
if r['success']:
solved += 1
print(f"{solved}/{len(puzzles)} solved ({solved/len(puzzles)*100:.1f}%)")
# 48765/48765 solved (100.0%)
Command Line¶
# Basic solve with board output
larsdoku "4...3.......6..8..........1....5..9..8....6...7.2........1.27..5.3....4.9........" --board
# Watch the engine think step-by-step
larsdoku "100007090030020008009600500005300900010080002600004000300000010040000007007000300" --steps
# Detailed round-by-round trace with candidates
larsdoku "100000002090400050006000700050903000000070000000850040700000600030009080002000001" --detail --board
Puzzle Format¶
Puzzles are 81 characters long, representing the board row by row, left to right, top to bottom.
- Use
0or.for empty cells - Digits
1-9for given clues
Both formats are equivalent:
003000600900700010080005020600010900200807003004090005020500060010003002005000300
..3...6..9..7...1..8...5.2.6...1.9..2..8.7..3..4.9...5.2.5...6..1...3..2..5...3..
Try the Famous Puzzles¶
from larsdoku_zs import solve
from larsdoku_zs.puzzles import FAMOUS_10
for name, author, year, puzzle in FAMOUS_10:
result = solve(puzzle, no_oracle=True)
status = "SOLVED" if result['success'] else "STALLED"
print(f"{name:30s} ({author}, {year}) {status} {result['n_steps']} steps")