1- <!--
2- ## Development Checklist
3-
4- Complete these tasks to fully implement Snake solver:
5-
6- **Note:** Complete phases in order to ensure badges and links work correctly!
7-
8- ### Phase 1: Core Implementation
9- - [ ] **Implement puzzle logic** in `snake_mip_solver/puzzle.py`
10- - [ ] Define puzzle parameters and constraints
11- - [ ] Add validation methods (ensure puzzle is well-formed)
12- - [ ] Implement solution verification
13- - [ ] **Implement MIP solver** in `snake_mip_solver/solver.py`
14- - [ ] Add decision variables and constraints
15- - [ ] Implement solution extraction
16- - [ ] **Update tests** in `tests/`
17- - [ ] Add test cases for puzzle validation
18- - [ ] Add solver test cases with known solutions
19- - [ ] Ensure good test coverage
20-
21- ### Phase 2: Documentation & Examples
22- - [ ] **Update main.py** with working example
23- - [ ] **Update README.md** with proper usage examples
24- - [ ] **Test locally** - ensure `python main.py` and `pytest` work
25- - [ ] **Complete mathematical model** in `model.md`
26- - [ ] Define sets, variables, and constraints and write full model
27- - [ ] Add illustrations/examples if required
28-
29- ### Phase 3: Repository Setup
30- - [ ] **Create GitHub repository** (public)
31- - [ ] **Push initial code** and verify CI passes
32- - [ ] **Set up Codecov**
33- - [ ] Add repository to Codecov
34- - [ ] Configure coverage reporting
35- - [ ] Verify coverage badge works
36-
37- ### Phase 4: Publishing
38- - [ ] **Test package building** with `python build_package.py`
39- - [ ] **Publish to PyPI** with `python -m twine upload dist/*`
40- - [ ] **Verify PyPI page** and badges in README
41- - [ ] **Test installation** from PyPI: `pip install snake-mip-solver`
42-
43- ### Phase 5: Final Polish
44- - [ ] **Remove this checklist** from README
45- - [ ] **Add project-specific documentation**
46- - [ ] **Update version** and create release tag
47- -->
48-
491# Snake MIP Solver
502
513[ ![ CI] ( https://github.com/DenHvideDvaerg/snake-mip-solver/actions/workflows/ci.yml/badge.svg )] ( https://github.com/DenHvideDvaerg/snake-mip-solver/actions/workflows/ci.yml )
@@ -58,7 +10,12 @@ A Snake puzzle solver using mathematical programming.
5810
5911## Overview
6012
61- Snake is a logic puzzle where ...
13+ Snake is a logic puzzle where you must create a single connected path on a grid according to these rules:
14+
15+ - ** Single connected path** - the snake forms one continuous line from start to end cell
16+ - ** No self-touching** - the snake body never touches itself, neither orthogonally nor diagonally
17+ - ** Row and column constraints** - each row/column must have a specific number of filled cells
18+ - ** Missing constraints variant** - supports puzzles where some row/column constraints are unknown (specified as ` None ` )
6219
6320This solver models the puzzle as a ** Mixed Integer Programming (MIP)** problem to find solutions.
6421
@@ -70,16 +27,164 @@ pip install snake-mip-solver
7027
7128## Requirements
7229
73- - Python >= 3.9
30+ - Python 3.9+
7431- Google OR-Tools
7532- pytest (for testing)
7633
34+ ## Example Puzzles
35+
36+ ### 6x6 Easy Puzzle
37+
38+ This 6x6 puzzle demonstrates a straightforward Snake puzzle:
39+
40+ | Puzzle | Solution |
41+ | --------| ----------|
42+ | <img src =" https://github.com/DenHvideDvaerg/snake-mip-solver/raw/main/images/6x6_Easy.png " width =" 400 " > | <img src =" https://github.com/DenHvideDvaerg/snake-mip-solver/raw/main/images/6x6_Easy_solution.png " width =" 400 " > |
43+
44+ ``` python
45+ def example_6x6_easy ():
46+ """ 6x6 easy example"""
47+ puzzle = SnakePuzzle(
48+ row_sums = [1 , 1 , 1 , 3 , 2 , 5 ],
49+ col_sums = [4 , 3 , 1 , 1 , 1 , 3 ],
50+ start_cell = (0 , 0 ),
51+ end_cell = (3 , 5 )
52+ )
53+ return puzzle
54+ ```
55+
56+ ### 12x12 Evil Puzzle with Missing Constraints
57+
58+ This 12x12 puzzle demonstrates a challenging large-scale puzzle with missing row/column constraints:
59+
60+ | Puzzle | Solution |
61+ | --------| ----------|
62+ | <img src =" https://github.com/DenHvideDvaerg/snake-mip-solver/raw/main/images/12x12_Evil.png " width =" 400 " > | <img src =" https://github.com/DenHvideDvaerg/snake-mip-solver/raw/main/images/12x12_Evil_solution.png " width =" 400 " > |
63+
64+ ``` python
65+ def example_12x12_evil ():
66+ """ 12x12 'Evil' puzzle from https://gridpuzzle.com/snake/evil-12"""
67+ puzzle = SnakePuzzle(
68+ row_sums = [11 , 2 , 7 , 4 , 4 , None , None , None , 3 , 2 , None , 5 ],
69+ col_sums = [9 , 7 , None , 2 , 5 , 6 , None , None , 5 , None , None , None ],
70+ start_cell = (2 , 6 ),
71+ end_cell = (7 , 5 )
72+ )
73+ return puzzle
74+ ```
75+
7776## Usage
7877
7978``` python
8079from snake_mip_solver import SnakePuzzle, SnakeSolver
80+ import time
81+
82+ def solve_puzzle (puzzle , name ):
83+ """ Solve a snake puzzle and display results"""
84+ print (f " \n " + " =" * 60 )
85+ print (f " SOLVING { name.upper()} " )
86+ print (" =" * 60 )
87+
88+ # Create and use the solver
89+ solver = SnakeSolver(puzzle)
90+
91+ print (" Solver information:" )
92+ info = solver.get_solver_info()
93+ for key, value in info.items():
94+ print (f " { key} : { value} " )
95+
96+ print (" \n Solving..." )
97+ start_time = time.time()
98+ solution = solver.solve(verbose = False )
99+ solve_time = time.time() - start_time
100+
101+ if solution:
102+ print (f " \n Solution found in { solve_time:.3f } seconds! " )
103+ print (f " Solution has { len (solution)} filled cells " )
104+ print (f " Solution: { sorted (list (solution))} " )
105+
106+ # Display the board with solution
107+ print (" \n Puzzle with solution:" )
108+ print (puzzle.get_board_visualization(solution, show_indices = False ))
109+
110+ # Validate solution
111+ if puzzle.is_valid_solution(solution):
112+ print (" ✅ Solution is valid!" )
113+ else :
114+ print (" ❌ Solution validation failed!" )
115+ else :
116+ print (f " \n No solution found (took { solve_time:.3f } seconds) " )
117+
118+ # Load and solve example puzzles
119+ puzzle_6x6 = example_6x6_easy()
120+ solve_puzzle(puzzle_6x6, " 6x6 Easy" )
121+
122+ puzzle_12x12 = example_12x12_evil()
123+ solve_puzzle(puzzle_12x12, " 12x12 Evil" )
124+ ```
125+
126+ ### Output
81127
82- # TODO : Make example either a direct copy of or very similar to main.py
128+ ```
129+ ============================================================
130+ SOLVING 6X6 EASY
131+ ============================================================
132+ Solver information:
133+ solver_type: SCIP 9.2.2 [LP solver: SoPlex 7.1.3]
134+ num_variables: 36
135+ num_constraints: 159
136+ puzzle_size: 6x6
137+ start_cell: (0, 0)
138+ end_cell: (3, 5)
139+
140+ Solving...
141+
142+ Solution found in 0.002 seconds!
143+ Solution has 13 filled cells
144+ Solution: [(0, 0), (1, 0), (2, 0), (3, 0), (3, 1), (3, 5), (4, 1), (4, 5), (5, 1), (5, 2), (5, 3), (5, 4), (5, 5)]
145+
146+ Puzzle with solution:
147+ 4 3 1 1 1 3
148+ 1 S _ _ _ _ _
149+ 1 x _ _ _ _ _
150+ 1 x _ _ _ _ _
151+ 3 x x _ _ _ E
152+ 2 _ x _ _ _ x
153+ 5 _ x x x x x
154+ ✅ Solution is valid!
155+
156+ ============================================================
157+ SOLVING 12X12 EVIL
158+ ============================================================
159+ Solver information:
160+ solver_type: SCIP 9.2.2 [LP solver: SoPlex 7.1.3]
161+ num_variables: 144
162+ num_constraints: 665
163+ puzzle_size: 12x12
164+ start_cell: (2, 6)
165+ end_cell: (7, 5)
166+
167+ Solving...
168+
169+ Solution found in 0.255 seconds!
170+ Solution has 49 filled cells
171+ Solution: [(0, 1), (0, 2), (0, 3), (0, 4), (0, 5), (0, 6), (0, 7), (0, 8), (0, 9), (0, 10), (0, 11), (1, 1), (1, 11), (2, 0), (2, 1), (2, 6), (2, 8), (2, 9), (2, 10), (2, 11), (3, 0), (3, 5), (3, 6), (3, 8), (4, 0), (4, 1), (4, 5), (4, 8), (5, 1), (5, 5), (5, 6), (5, 7), (5, 8), (6, 0), (6, 1), (7, 0), (7, 5), (8, 0), (8, 4), (8, 5), (9, 0), (9, 4), (10, 0), (10, 4), (11, 0), (11, 1), (11, 2), (11, 3), (11, 4)]
172+
173+ Puzzle with solution:
174+ 9 7 ? 2 5 6 ? ? 5 ? ? ?
175+ 11 _ x x x x x x x x x x x
176+ 2 _ x _ _ _ _ _ _ _ _ _ x
177+ 7 x x _ _ _ _ S _ x x x x
178+ 4 x _ _ _ _ x x _ x _ _ _
179+ 4 x x _ _ _ x _ _ x _ _ _
180+ ? _ x _ _ _ x x x x _ _ _
181+ ? x x _ _ _ _ _ _ _ _ _ _
182+ ? x _ _ _ _ E _ _ _ _ _ _
183+ 3 x _ _ _ x x _ _ _ _ _ _
184+ 2 x _ _ _ x _ _ _ _ _ _ _
185+ ? x _ _ _ x _ _ _ _ _ _ _
186+ 5 x x x x x _ _ _ _ _ _ _
187+ ✅ Solution is valid!
83188```
84189
85190### Running the example
@@ -103,8 +208,15 @@ pytest --cov=snake_mip_solver # Run with coverage
103208
104209The solver uses ** Mixed Integer Programming (MIP)** to model the puzzle constraints. Google OR-Tools provides the optimization framework, with SCIP as the default solver.
105210
106- See the complete formulation in ** [ Complete Mathematical Model Documentation] ( https://github.com/DenHvideDvaerg/snake-mip-solver/blob/main/model.md ) **
211+ The mathematical formulation includes six types of constraints:
212+ 1 . ** Start and End Cell Constraints** - fixing the path endpoints
213+ 2 . ** Row Sum Constraints** - ensuring correct number of cells per row
214+ 3 . ** Column Sum Constraints** - ensuring correct number of cells per column
215+ 4 . ** Snake Path Connectivity Constraints** - forming a single connected path
216+ 5 . ** Diagonal Non-Touching Constraints** - preventing diagonal self-contact
217+ 6 . ** No 2×2 Block Constraints** - preventing disconnected filled blocks
107218
219+ See the complete formulation in ** [ Complete Mathematical Model Documentation] ( https://github.com/DenHvideDvaerg/snake-mip-solver/blob/main/model.md ) **
108220
109221## License
110222
0 commit comments