@@ -58,7 +58,12 @@ A Snake puzzle solver using mathematical programming.
5858
5959## Overview
6060
61- Snake is a logic puzzle where ...
61+ Snake is a logic puzzle where you must create a single connected path on a grid according to these rules:
62+
63+ - ** Single connected path** - the snake forms one continuous line from start to end cell
64+ - ** No self-touching** - the snake body never touches itself, neither orthogonally nor diagonally
65+ - ** Row and column constraints** - each row/column must have a specific number of filled cells
66+ - ** Missing constraints variant** - supports puzzles where some row/column constraints are unknown (specified as ` None ` )
6267
6368This solver models the puzzle as a ** Mixed Integer Programming (MIP)** problem to find solutions.
6469
@@ -70,16 +75,164 @@ pip install snake-mip-solver
7075
7176## Requirements
7277
73- - Python >= 3.9
78+ - Python 3.9+
7479- Google OR-Tools
7580- pytest (for testing)
7681
82+ ## Example Puzzles
83+
84+ ### 6x6 Easy Puzzle
85+
86+ This 6x6 puzzle demonstrates a straightforward Snake puzzle:
87+
88+ | Puzzle | Solution |
89+ | --------| ----------|
90+ | <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 " > |
91+
92+ ``` python
93+ def example_6x6_easy ():
94+ """ 6x6 easy example"""
95+ puzzle = SnakePuzzle(
96+ row_sums = [1 , 1 , 1 , 3 , 2 , 5 ],
97+ col_sums = [4 , 3 , 1 , 1 , 1 , 3 ],
98+ start_cell = (0 , 0 ),
99+ end_cell = (3 , 5 )
100+ )
101+ return puzzle
102+ ```
103+
104+ ### 12x12 Evil Puzzle with Missing Constraints
105+
106+ This 12x12 puzzle demonstrates a challenging large-scale puzzle with missing row/column constraints:
107+
108+ | Puzzle | Solution |
109+ | --------| ----------|
110+ | <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 " > |
111+
112+ ``` python
113+ def example_12x12_evil ():
114+ """ 12x12 'Evil' puzzle from https://gridpuzzle.com/snake/evil-12"""
115+ puzzle = SnakePuzzle(
116+ row_sums = [11 , 2 , 7 , 4 , 4 , None , None , None , 3 , 2 , None , 5 ],
117+ col_sums = [9 , 7 , None , 2 , 5 , 6 , None , None , 5 , None , None , None ],
118+ start_cell = (2 , 6 ),
119+ end_cell = (7 , 5 )
120+ )
121+ return puzzle
122+ ```
123+
77124## Usage
78125
79126``` python
80127from snake_mip_solver import SnakePuzzle, SnakeSolver
128+ import time
129+
130+ def solve_puzzle (puzzle , name ):
131+ """ Solve a snake puzzle and display results"""
132+ print (f " \n " + " =" * 60 )
133+ print (f " SOLVING { name.upper()} " )
134+ print (" =" * 60 )
135+
136+ # Create and use the solver
137+ solver = SnakeSolver(puzzle)
138+
139+ print (" Solver information:" )
140+ info = solver.get_solver_info()
141+ for key, value in info.items():
142+ print (f " { key} : { value} " )
143+
144+ print (" \n Solving..." )
145+ start_time = time.time()
146+ solution = solver.solve(verbose = False )
147+ solve_time = time.time() - start_time
148+
149+ if solution:
150+ print (f " \n Solution found in { solve_time:.3f } seconds! " )
151+ print (f " Solution has { len (solution)} filled cells " )
152+ print (f " Solution: { sorted (list (solution))} " )
153+
154+ # Display the board with solution
155+ print (" \n Puzzle with solution:" )
156+ print (puzzle.get_board_visualization(solution, show_indices = False ))
157+
158+ # Validate solution
159+ if puzzle.is_valid_solution(solution):
160+ print (" ✅ Solution is valid!" )
161+ else :
162+ print (" ❌ Solution validation failed!" )
163+ else :
164+ print (f " \n No solution found (took { solve_time:.3f } seconds) " )
165+
166+ # Load and solve example puzzles
167+ puzzle_6x6 = example_6x6_easy()
168+ solve_puzzle(puzzle_6x6, " 6x6 Easy" )
169+
170+ puzzle_12x12 = example_12x12_evil()
171+ solve_puzzle(puzzle_12x12, " 12x12 Evil" )
172+ ```
81173
82- # TODO : Make example either a direct copy of or very similar to main.py
174+ ### Output
175+
176+ ```
177+ ============================================================
178+ SOLVING 6X6 EASY
179+ ============================================================
180+ Solver information:
181+ solver_type: SCIP 9.2.2 [LP solver: SoPlex 7.1.3]
182+ num_variables: 36
183+ num_constraints: 159
184+ puzzle_size: 6x6
185+ start_cell: (0, 0)
186+ end_cell: (3, 5)
187+
188+ Solving...
189+
190+ Solution found in 0.002 seconds!
191+ Solution has 13 filled cells
192+ 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)]
193+
194+ Puzzle with solution:
195+ 4 3 1 1 1 3
196+ 1 S _ _ _ _ _
197+ 1 x _ _ _ _ _
198+ 1 x _ _ _ _ _
199+ 3 x x _ _ _ E
200+ 2 _ x _ _ _ x
201+ 5 _ x x x x x
202+ ✅ Solution is valid!
203+
204+ ============================================================
205+ SOLVING 12X12 EVIL
206+ ============================================================
207+ Solver information:
208+ solver_type: SCIP 9.2.2 [LP solver: SoPlex 7.1.3]
209+ num_variables: 144
210+ num_constraints: 665
211+ puzzle_size: 12x12
212+ start_cell: (2, 6)
213+ end_cell: (7, 5)
214+
215+ Solving...
216+
217+ Solution found in 0.255 seconds!
218+ Solution has 49 filled cells
219+ 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)]
220+
221+ Puzzle with solution:
222+ 9 7 ? 2 5 6 ? ? 5 ? ? ?
223+ 11 _ x x x x x x x x x x x
224+ 2 _ x _ _ _ _ _ _ _ _ _ x
225+ 7 x x _ _ _ _ S _ x x x x
226+ 4 x _ _ _ _ x x _ x _ _ _
227+ 4 x x _ _ _ x _ _ x _ _ _
228+ ? _ x _ _ _ x x x x _ _ _
229+ ? x x _ _ _ _ _ _ _ _ _ _
230+ ? x _ _ _ _ E _ _ _ _ _ _
231+ 3 x _ _ _ x x _ _ _ _ _ _
232+ 2 x _ _ _ x _ _ _ _ _ _ _
233+ ? x _ _ _ x _ _ _ _ _ _ _
234+ 5 x x x x x _ _ _ _ _ _ _
235+ ✅ Solution is valid!
83236```
84237
85238### Running the example
@@ -103,8 +256,15 @@ pytest --cov=snake_mip_solver # Run with coverage
103256
104257The solver uses ** Mixed Integer Programming (MIP)** to model the puzzle constraints. Google OR-Tools provides the optimization framework, with SCIP as the default solver.
105258
106- See the complete formulation in ** [ Complete Mathematical Model Documentation] ( https://github.com/DenHvideDvaerg/snake-mip-solver/blob/main/model.md ) **
259+ The mathematical formulation includes six types of constraints:
260+ 1 . ** Start and End Cell Constraints** - fixing the path endpoints
261+ 2 . ** Row Sum Constraints** - ensuring correct number of cells per row
262+ 3 . ** Column Sum Constraints** - ensuring correct number of cells per column
263+ 4 . ** Snake Path Connectivity Constraints** - forming a single connected path
264+ 5 . ** Diagonal Non-Touching Constraints** - preventing diagonal self-contact
265+ 6 . ** No 2×2 Block Constraints** - preventing disconnected filled blocks
107266
267+ See the complete formulation in ** [ Complete Mathematical Model Documentation] ( https://github.com/DenHvideDvaerg/snake-mip-solver/blob/main/model.md ) **
108268
109269## License
110270
0 commit comments