multi-puzzle-solver


Namemulti-puzzle-solver JSON
Version 0.9.13 PyPI version JSON
download
home_pageNone
SummaryEfficient solvers for numerous popular and esoteric logic puzzles using CP-SAT
upload_time2025-10-21 03:36:38
maintainerNone
docs_urlNone
authorAr-Kareem
requires_python>=3.9
licenseNone
keywords puzzle solver sat cp-sat google-or-tools efficient logic puzzle-solver puzzle-solver-python sudoku nonograms minesweeper dominosa lightup map inertia tents towers tracks undead unruly bridges pearl range signpost singles magnets
VCS
bugtrack_url
requirements numpy ortools
Travis-CI No Travis.
coveralls test coverage No coveralls.
            # Python Puzzle Solver

Solve numerous classical logic puzzles automatically in Python. 

## Quick Start

Install

```bash
pip install multi-puzzle-solver
```

Use:

```python
from puzzle_solver import nonograms_solver
solver = nonograms_solver.Board(top=[[2], [3], [1], [1, 1]], side=[[3], [1], [2, 1], [1]])
solutions = solver.solve_and_print()
```

Output:

```python
Solution found
[[' ' 'B' 'B' 'B']
 [' ' 'B' ' ' ' ']
 ['B' 'B' ' ' 'B']
 ['B' ' ' ' ' ' ']]
Solutions found: 1
status: OPTIMAL
Time taken: 0.00 seconds
```
(Note: Printing can be turned off by setting `verbose=False`)

## Introduction

The aim of this repo is to provide very efficient solvers (i.e. not brute force solvers) for many popular pencil logic puzzles like Nonograms, Sudoku, Minesweeper, and many more lesser known ones.

If you happen to have a puzzle similar to the ones listed below and want to solve it (or see how many potential solutions a partially covered board has), then this repo is perfect for you.

The simple use-case of this repo is if you want to solve a puzzle given the state of the board. But the other interesting use-cases is if you want to check if removing a clue would still result in a unique solution or would make the puzzle ambiguous and have multiple solutions.

**Why?** There are countless python packages that can solve the popular puzzles below, so a valid question to ask is **why would I want to use this package and why did you create it?**. The answer is that there are multiple problems with most of those packages which this package solves which are:

1. **Sophisticated solvers:** A lot of available online solvers are incredibly inefficient as they implement naive algorithms that brute force and backtrack through all possible solutions. This package solves that issue as all the solvers included here never use naive algorithms and instead use a very efficient CP-SAT solver which is a more sophisticated solver than any one person could possibly write.
2. **Numerous puzzles:** Most of the available python solvers are only designed for a single type of puzzle and each one requires a different way to encode the input and extract the solution. This package solves both those issues as this package provides solvers for many puzzles all with a similar interface that encodes the input and extracts the solution in a similar way.
3. **Esoteric puzzles:** Most packages you can find online are only designed for popular puzzles. This package partially solves this issue by providing solvers for many puzzles. I'm open to suggestions for implementing solvers for more puzzles.
4. **All possible solutions:** The available solvers often lack uniqueness checks and simply stop at the first possible solution without verifying uniqueness or completeness. This package supports checking whether puzzles are uniquely solvable, ambiguous, or unsolvable for all the puzzles.

Play the original puzzles online: https://www.chiark.greenend.org.uk/~sgtatham/puzzles

Almost all the solvers in this repo use the CP-SAT solver from Google OR-Tools.


<div align="center">


## 🕹️ Puzzle Gallery

These are all the puzzles that are implemented in this repo. <br> Click on any of them to go to that section of the README.

<table>
<tr>
  <td align="center">
    <a href="#nonograms-puzzle-type-1"><b>Nonograms</b><br><br>
      <img src="https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/nonogram_solved.png" alt="Nonograms" width="140">
    </a>
  </td>
  <td align="center">
    <a href="#sudoku-puzzle-type-2"><b>Sudoku</b><br><br>
      <img src="https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/sudoku_solved.png" alt="Sudoku" width="140">
    </a>
  </td>
  <td align="center">
    <a href="#minesweeper-puzzle-type-3"><b>Minesweeper</b><br><br>
      <img src="https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/minesweeper_pre.png" alt="Minesweeper" width="140">
    </a>
  </td>
  <td align="center">
    <a href="#dominosa-puzzle-type-4"><b>Dominosa</b><br><br>
      <img src="https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/dominosa_solved.png" alt="Dominosa" width="140">
    </a>
  </td>
  <td align="center">
    <a href="#light-up-puzzle-type-5"><b>Light Up</b><br><br>
      <img src="https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/lightup_solved.png" alt="Light Up" width="140">
    </a>
  </td>
</tr>
<tr>
  <td align="center">
    <a href="#tents-puzzle-type-6"><b>Tents</b><br><br>
      <img src="https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/tents_solved.png" alt="Tents" width="140">
    </a>
  </td>
  <td align="center">
    <a href="#filling-puzzle-type-7"><b>Filling</b><br><br>
      <img src="https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/filling_solved.png" alt="Filling" width="140">
    </a>
  </td>
  <td align="center">
    <a href="#keen-puzzle-type-8"><b>Keen</b><br><br>
      <img src="https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/keen_solved.png" alt="Keen" width="140">
    </a>
  </td>
  <td align="center">
    <a href="#towers-puzzle-type-9"><b>Towers</b><br><br>
      <img src="https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/towers_solved.png" alt="Towers" width="140">
    </a>
  </td>
  <td align="center">
    <a href="#singles-puzzle-type-10"><b>Singles</b><br><br>
      <img src="https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/singles_solved.png" alt="Singles" width="140">
    </a>
  </td>
</tr>
<tr>
  <td align="center">
    <a href="#magnets-puzzle-type-11"><b>Magnets</b><br><br>
      <img src="https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/magnets_solved.png" alt="Magnets" width="140">
    </a>
  </td>
  <td align="center">
    <a href="#signpost-puzzle-type-12"><b>Signpost</b><br><br>
      <img src="https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/signpost_solved.png" alt="Signpost" width="140">
    </a>
  </td>
  <td align="center">
    <a href="#range-puzzle-type-13"><b>Range</b><br><br>
      <img src="https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/range_solved.png" alt="Range" width="140">
    </a>
  </td>
  <td align="center">
    <a href="#undead-puzzle-type-14"><b>Undead</b><br><br>
      <img src="https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/undead_solved.png" alt="Undead" width="140">
    </a>
  </td>
  <td align="center">
    <a href="#unruly-puzzle-type-15"><b>Unruly</b><br><br>
      <img src="https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/unruly_solved.png" alt="Unruly" width="140">
    </a>
  </td>
</tr>
<tr>
  <td align="center">
    <a href="#tracks-puzzle-type-16"><b>Tracks</b><br><br>
      <img src="https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/tracks_solved.png" alt="Tracks" width="140">
    </a>
  </td>
  <td align="center">
    <a href="#mosaic-puzzle-type-17"><b>Mosaic</b><br><br>
      <img src="https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/mosaic_solved.png" alt="Mosaic" width="140">
    </a>
  </td>
  <td align="center">
    <a href="#map-puzzle-type-18"><b>Map</b><br><br>
      <img src="https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/map_solved.png" alt="Map" width="140">
    </a>
  </td>
  <td align="center">
    <a href="#pearl-puzzle-type-19"><b>Pearl</b><br><br>
      <img src="https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/pearl_solved.png" alt="Pearl" width="140">
    </a>
  </td>
  <td align="center">
    <a href="#bridges-puzzle-type-20"><b>Bridges</b><br><br>
      <img src="https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/bridges_solved.png" alt="Bridges" width="140">
    </a>
  </td>
</tr>
<tr>
  <td align="center">
    <a href="#inertia-puzzle-type-21"><b>Inertia</b><br><br>
      <img src="https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/inertia_unsolved.png" alt="Inertia" width="140">
    </a>
  </td>
  <td align="center">
    <a href="#guess-puzzle-type-22"><b>Guess</b><br><br>
      <img src="https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/guess_3.png" alt="Guess" width="140">
    </a>
  </td>
  <td align="center">
    <a href="#chess-range-puzzle-type-23"><b>Chess Range</b><br><br>
      <img src="https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/chess_range_unsolved.png" alt="Chess range" width="140">
    </a>
  </td>
  <td align="center">
    <a href="#chess-solo-puzzle-type-24"><b>Chess Solo</b><br><br>
      <img src="https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/chess_solo_unsolved.png" alt="Chess solo" width="140">
    </a>
  </td>
  <td align="center">
    <a href="#chess-melee-puzzle-type-25"><b>Chess Melee</b><br><br>
      <img src="https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/chess_melee_unsolved.png" alt="Chess melee" width="140">
    </a>
  </td>
</tr>
<tr>
  <td align="center">
    <a href="#thermometers-puzzle-type-26"><b>Thermometers</b><br><br>
      <img src="https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/thermometers_solved.png" alt="Thermometers" width="140">
    </a>
  </td>
  <td align="center">
    <a href="#aquarium-puzzle-type-27"><b>Aquarium</b><br><br>
      <img src="https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/aquarium_solved.png" alt="Aquarium" width="140">
    </a>
  </td>
  <td align="center">
    <a href="#stitches-puzzle-type-28"><b>Stitches</b><br><br>
      <img src="https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/stitches_solved.png" alt="Stitches" width="140">
    </a>
  </td>
  <td align="center">
    <a href="#battleships-puzzle-type-29"><b>Battleships</b><br><br>
      <img src="https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/battleships_solved.png" alt="Battleships" width="140">
    </a>
  </td>
  <td align="center">
    <a href="#kakurasu-puzzle-type-30"><b>Kakurasu</b><br><br>
      <img src="https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/kakurasu_solved.png" alt="Kakurasu" width="140">
    </a>
  </td>
</tr>
<tr>
  <td align="center">
    <a href="#star-battle-puzzle-type-31"><b>Star Battle</b><br><br>
      <img src="https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/star_battle_solved.png" alt="Star Battle" width="140">
    </a>
  </td>
  <td align="center">
    <a href="#star-battle-shapeless-puzzle-type-32"><b>Star Battle Shapeless</b><br><br>
      <img src="https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/star_battle_shapeless_solved.png" alt="Star Battle Shapeless" width="140">
    </a>
  </td>
  <td align="center">
    <a href="#lits-puzzle-type-33"><b>Lits</b><br><br>
      <img src="https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/lits_solved.png" alt="Lits" width="140">
    </a>
  </td>
</tr>
</table>

</div>

---

## Table of Contents

- [Python Puzzle Solver](#python-puzzle-solver)
  - [Quick Start](#quick-start)
  - [Introduction](#introduction)
  - [🕹️ Puzzle Gallery](#️-puzzle-gallery)
  - [Table of Contents](#table-of-contents)
- [Puzzles](#puzzles)
  - [Nonograms (Puzzle Type #1)](#nonograms-puzzle-type-1)
  - [Sudoku (Puzzle Type #2)](#sudoku-puzzle-type-2)
  - [Minesweeper (Puzzle Type #3)](#minesweeper-puzzle-type-3)
  - [Dominosa (Puzzle Type #4)](#dominosa-puzzle-type-4)
  - [Light Up (Puzzle Type #5)](#light-up-puzzle-type-5)
  - [Tents (Puzzle Type #6)](#tents-puzzle-type-6)
  - [Filling (Puzzle Type #7)](#filling-puzzle-type-7)
  - [Keen (Puzzle Type #8)](#keen-puzzle-type-8)
  - [Towers (Puzzle Type #9)](#towers-puzzle-type-9)
  - [Singles (Puzzle Type #10)](#singles-puzzle-type-10)
  - [Magnets (Puzzle Type #11)](#magnets-puzzle-type-11)
  - [Signpost (Puzzle Type #12)](#signpost-puzzle-type-12)
  - [Range (Puzzle Type #13)](#range-puzzle-type-13)
  - [UnDead (Puzzle Type #14)](#undead-puzzle-type-14)
  - [Unruly (Puzzle Type #15)](#unruly-puzzle-type-15)
  - [Tracks (Puzzle Type #16)](#tracks-puzzle-type-16)
  - [Mosaic (Puzzle Type #17)](#mosaic-puzzle-type-17)
  - [Map (Puzzle Type #18)](#map-puzzle-type-18)
  - [Pearl (Puzzle Type #19)](#pearl-puzzle-type-19)
  - [Bridges (Puzzle Type #20)](#bridges-puzzle-type-20)
  - [Inertia (Puzzle Type #21)](#inertia-puzzle-type-21)
  - [Guess (Puzzle Type #22)](#guess-puzzle-type-22)
  - [Chess Range (Puzzle Type #23)](#chess-range-puzzle-type-23)
  - [Chess Solo (Puzzle Type #24)](#chess-solo-puzzle-type-24)
  - [Chess Melee (Puzzle Type #25)](#chess-melee-puzzle-type-25)
  - [Thermometers (Puzzle Type #26)](#thermometers-puzzle-type-26)
  - [Aquarium (Puzzle Type #27)](#aquarium-puzzle-type-27)
  - [Stitches (Puzzle Type #28)](#stitches-puzzle-type-28)
  - [Battleships (Puzzle Type #29)](#battleships-puzzle-type-29)
  - [Kakurasu (Puzzle Type #30)](#kakurasu-puzzle-type-30)
  - [Star Battle (Puzzle Type #31)](#star-battle-puzzle-type-31)
  - [Star Battle Shapeless (Puzzle Type #32)](#star-battle-shapeless-puzzle-type-32)
  - [Lits (Puzzle Type #33)](#lits-puzzle-type-33)
  - [Why SAT / CP-SAT?](#why-sat--cp-sat)
  - [Testing](#testing)
  - [Contributing](#contributing)
    - [Build and push to PyPI](#build-and-push-to-pypi)

---

# Puzzles

The puzzles that have solvers implemented are listed below. Each puzzle has a simple example input board followed by the code to utilize this package and solve the puzzle, followed by the scripts output, and finally the solved puzzle.

## Nonograms (Puzzle Type #1)

Called "Pattern" in the website.

* [**Play online**](https://www.chiark.greenend.org.uk/~sgtatham/puzzles/js/pattern.html)

* [**Instructions**](https://www.chiark.greenend.org.uk/~sgtatham/puzzles/doc/pattern.html#pattern)

* [**Solver Code**][1]

<details>
  <summary><strong>Rules</strong></summary>
You have a grid of squares, which must all be filled in either black or white. Beside each row of the grid are listed, in order, the lengths of the runs of black squares on that row; above each column are listed, in order, the lengths of the runs of black squares in that column. Your aim is to fill in the entire grid black or white. 
</details>

**Unsolved puzzle**

<img src="https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/nonogram_unsolved.png" alt="Nonogram unsolved" width="500">

Code to utilize this package and solve the puzzle:
```python
from puzzle_solver import nonograms_solver as solver
top_numbers = [
  [8, 2],
  [5, 4],
  [2, 1, 4],
  [2, 4],
  [2, 1, 4],
  [2, 5],
  [2, 8],
  [3, 2],
  [1, 6],
  [1, 9],
  [1, 6, 1],
  [1, 5, 3],
  [3, 2, 1],
  [4, 2],
  [1, 5],
]
side_numbers = [
  [7, 3],
  [7, 1, 1],
  [2, 3],
  [2, 3],
  [3, 2],
  [1, 1, 1, 1, 2],
  [1, 6, 1],
  [1, 9],
  [9],
  [2, 4],
  [8],
  [11],
  [7, 1, 1],
  [4, 3],
  [3, 2],
]
binst = solver.Board(top=top_numbers, side=side_numbers)
solutions = binst.solve_and_print()
```
**Script Output**
```python
Solution found
B B B B B B B . B B B . . . .
B B B B B B B . . . . . B . B
B B . . . . . . . . . B B B .
B B . . . . . . . . . . B B B
B B B . . . . . . . . . . B B
B . . . B . B . . B . . . B B
B . . . . . B B B B B B . . B
B . . . . . B B B B B B B B B
. . . . . B B B B B B B B B .
. . . . . B B . B B B B . . .
. . . . B B B B B B B B . . .
B B B B B B B B B B B . . . .
B B B B B B B . . B . B . . .
. B B B B . . . . B B B . . .
. B B B . . . . . . . B B . .
Solutions found: 1
status: OPTIMAL
Time taken: 0.04 seconds
```

**Solved puzzle**

<img src="https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/nonogram_solved.png" alt="Nonogram solved" width="500">

---

## Sudoku (Puzzle Type #2)

Called "Solo" in the website.

* [**Play online**](https://www.chiark.greenend.org.uk/~sgtatham/puzzles/js/solo.html)

* [**Instructions**](https://www.chiark.greenend.org.uk/~sgtatham/puzzles/doc/solo.html#solo)

* [**Solver Code**][2]

<details>
  <summary><strong>Rules</strong></summary>
You have a square grid, which is divided into as many equally sized sub-blocks as the grid has rows. Each square must be filled in with a digit from 1 to the size of the grid, in such a way that

  - every row contains only one occurrence of each digit
  - every column contains only one occurrence of each digit
  - every block contains only one occurrence of each digit.

You are given some of the numbers as clues; your aim is to place the rest of the numbers correctly.
</details>

**Unsolved puzzle**

<img src="https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/sudoku_unsolved.png" alt="Sudoku unsolved" width="500">

Code to utilize this package and solve the puzzle:
```python
import numpy as np
from puzzle_solver import sudoku_solver as solver
board = np.array([
  [' ', '7', '5', '4',  '9', '1', 'c', 'e',  'd', 'f', ' ', ' ',  '2', ' ', '3', ' '],
  [' ', ' ', ' ', ' ',  'f', 'a', ' ', ' ',  ' ', '6', ' ', 'c',  ' ', ' ', '8', 'b'],
  [' ', ' ', '1', ' ',  ' ', '6', ' ', ' ',  ' ', '9', ' ', ' ',  ' ', 'g', ' ', 'd'],
  [' ', '6', ' ', ' ',  ' ', ' ', ' ', ' ',  ' ', ' ', '5', 'g',  'c', '7', ' ', ' '],

  ['4', 'a', ' ', ' ',  ' ', ' ', ' ', ' ',  ' ', ' ', ' ', '9',  ' ', ' ', ' ', ' '],
  [' ', 'g', 'f', ' ',  'e', ' ', ' ', '5',  '4', ' ', ' ', '1',  ' ', '9', ' ', '8'],
  [' ', ' ', ' ', ' ',  'a', '3', 'b', '7',  'c', 'g', ' ', '6',  ' ', ' ', ' ', '4'],
  [' ', 'b', ' ', '7',  ' ', ' ', ' ', ' ',  'f', ' ', '3', ' ',  ' ', 'a', ' ', '6'],

  ['2', ' ', 'a', ' ',  ' ', 'c', ' ', '1',  ' ', ' ', ' ', ' ',  '7', ' ', '6', ' '],
  ['8', ' ', ' ', ' ',  '3', ' ', 'e', 'f',  '7', '5', 'c', 'd',  ' ', ' ', ' ', ' '],
  ['9', ' ', '3', ' ',  '7', ' ', ' ', 'a',  '6', ' ', ' ', '2',  ' ', 'b', '1', ' '],
  [' ', ' ', ' ', ' ',  '4', ' ', ' ', ' ',  ' ', ' ', ' ', ' ',  ' ', ' ', 'e', 'f'],

  [' ', ' ', 'g', 'd',  '2', '9', ' ', ' ',  ' ', ' ', ' ', ' ',  ' ', ' ', '4', ' '],
  ['a', ' ', 'b', ' ',  ' ', ' ', '5', ' ',  ' ', ' ', 'd', ' ',  ' ', '8', ' ', ' '],
  ['e', '8', ' ', ' ',  '1', ' ', '4', ' ',  ' ', ' ', '6', '7',  ' ', ' ', ' ', ' '],
  [' ', '3', ' ', '9',  ' ', ' ', 'f', '8',  'a', 'e', 'g', '5',  'b', 'c', 'd', ' '],
])
binst = solver.Board(board=board)
solutions = binst.solve_and_print()
assert len(solutions) == 1, f'unique solutions != 1, == {len(solutions)}'
```
**Script Output**
```python
Solution found
[['g' '7' '5' '4' '9' '1' 'c' 'e' 'd' 'f' 'b' '8' '2' '6' '3' 'a']
 ['3' '9' 'd' 'e' 'f' 'a' '7' 'g' '2' '6' '4' 'c' '5' '1' '8' 'b']
 ['b' 'c' '1' '8' '5' '6' '3' '2' 'e' '9' '7' 'a' '4' 'g' 'f' 'd']
 ['f' '6' '2' 'a' 'b' '8' 'd' '4' '1' '3' '5' 'g' 'c' '7' '9' 'e']
 ['4' 'a' 'e' '3' '8' 'f' '1' '6' '5' 'b' '2' '9' 'g' 'd' 'c' '7']
 ['6' 'g' 'f' 'c' 'e' 'd' '2' '5' '4' '7' 'a' '1' '3' '9' 'b' '8']
 ['d' '1' '9' '2' 'a' '3' 'b' '7' 'c' 'g' '8' '6' 'e' 'f' '5' '4']
 ['5' 'b' '8' '7' 'g' '4' '9' 'c' 'f' 'd' '3' 'e' '1' 'a' '2' '6']
 ['2' 'e' 'a' 'b' 'd' 'c' 'g' '1' '3' '8' '9' 'f' '7' '4' '6' '5']
 ['8' '4' '6' '1' '3' 'b' 'e' 'f' '7' '5' 'c' 'd' 'a' '2' 'g' '9']
 ['9' 'f' '3' 'g' '7' '5' '8' 'a' '6' '4' 'e' '2' 'd' 'b' '1' 'c']
 ['c' 'd' '7' '5' '4' '2' '6' '9' 'g' 'a' '1' 'b' '8' '3' 'e' 'f']
 ['7' '5' 'g' 'd' '2' '9' 'a' 'b' '8' 'c' 'f' '3' '6' 'e' '4' '1']
 ['a' '2' 'b' '6' 'c' 'e' '5' '3' '9' '1' 'd' '4' 'f' '8' '7' 'g']
 ['e' '8' 'c' 'f' '1' 'g' '4' 'd' 'b' '2' '6' '7' '9' '5' 'a' '3']
 ['1' '3' '4' '9' '6' '7' 'f' '8' 'a' 'e' 'g' '5' 'b' 'c' 'd' '2']]
Solutions found: 1
status: OPTIMAL
Time taken: 0.04 seconds
```

**Solved puzzle**

<img src="https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/sudoku_solved.png" alt="Sudoku solved" width="500">

---

## Minesweeper (Puzzle Type #3)

This Minesweeper solver is a bit different from the other solvers in this repo because Minesweeper is a uniquely different type of puzzle. 

In Minesweeper, you don't solve the puzzle in one go. You need to partially solve the puzzle and get new information to continue. Thus the solver is designed to take the state of the board at any timestep and always gives the most amount of garunteed next steps to take (i.e. garunteed safe positions, garunteed mine positions, and even warns you if you placed a flag in a potentially wrong position).

Then obviously, once the you act upon the guesses and get the new information, you simply put that new info back into the solver and repeat the process until the puzzle is fully solved. 

Below is an example of how to utilize the solver while in the middle of a puzzle. (notice how there's an intentionally placed incorrect flag in the example and the solver will warn you about it)

* [**Play online**](https://www.chiark.greenend.org.uk/~sgtatham/puzzles/js/mines.html)

* [**Instructions**](https://www.chiark.greenend.org.uk/~sgtatham/puzzles/doc/mines.html#mines)

* [**Solver Code**][3]

<details>
  <summary><strong>Rules</strong></summary>
You have a grid of covered squares, some of which contain mines, but you don't know which. Your job is to uncover every square which does not contain a mine. If you uncover a square containing a mine, you lose. If you uncover a square which does not contain a mine, you are told how many mines are contained within the eight surrounding squares.

This game needs no introduction; popularised by Windows, it is perhaps the single best known desktop puzzle game in existence.

This version of it has an unusual property. By default, it will generate its mine positions in such a way as to ensure that you never need to guess where a mine is: you will always be able to deduce it somehow. So you will never, as can happen in other versions, get to the last four squares and discover that there are two mines left but you have no way of knowing for sure where they are. 
</details>

**Partially solved puzzle**

<img src="https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/minesweeper_pre.png" alt="Minesweeper partially solved" width="500">

Code to utilize this package and solve the puzzle:
```python
import numpy as np
from puzzle_solver import minesweeper_solver as solver
board = np.array([
  [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '1', '1', '1', '3', 'F', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '],
  [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '2', '2', '1', 'F', '4', 'F', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '],
  [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'F', '2', '1', '3', 'F', '5', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '],
  [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '2', '4', 'F', '3', '0', '3', 'F', 'F', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '],
  [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '3', '4', 'F', '3', '0', '2', 'F', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '],
  [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'F', '4', 'F', '2', '0', '2', '3', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '],
  [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'F', '4', '1', '1', '0', '1', 'F', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '],
  [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'F', '4', '2', '1', '1', '2', '2', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '],
  [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '],
  [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '],
  [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '],
  [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '],
  [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '],
  [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '],
  [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '],
  [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '],
])
mine_count = 30
safe_positions, new_garuneed_mine_positions, wrong_flag_positions = solver.give_next_guess(board=board, mine_count=mine_count)
```
**Script Output**

Notice how not only did it output all garunteed new safe and mine positions, it also outputs a warning about the incorrectly placed flag position.

```python
Found 8 new guaranteed safe positions
{Pos(x=9, y=0), Pos(x=15, y=8), Pos(x=15, y=7), Pos(x=9, y=2), Pos(x=15, y=6), Pos(x=7, y=2), Pos(x=9, y=1), Pos(x=12, y=8)}
----------
Found 4 new guaranteed mine positions
{Pos(x=8, y=2), Pos(x=7, y=5), Pos(x=10, y=0), Pos(x=9, y=8)}
----------
WARNING | WARNING | WARNING | WARNING | WARNING
Found 1 wrong flag positions
{Pos(x=15, y=3)}
----------
Time taken: 0.92 seconds
```

**Progressed puzzle**

<img src="https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/minesweeper_post.png" alt="Minesweeper progressed" width="500">

---

## Dominosa (Puzzle Type #4)

* [**Play online**](https://www.chiark.greenend.org.uk/~sgtatham/puzzles/js/dominosa.html)

* [**Instructions**](https://www.chiark.greenend.org.uk/~sgtatham/puzzles/doc/dominosa.html#dominosa)

* [**Solver Code**][4]

<details>
  <summary><strong>Rules</strong></summary>
A normal set of dominoes – that is, one instance of every (unordered) pair of numbers from 0 to N – has been arranged irregularly into a rectangle; then the number in each square has been written down and the dominoes themselves removed. 

Your task is to reconstruct the pattern by arranging the set of dominoes to match the provided array of numbers. 
</details>

**Unsolved puzzle**

<img src="https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/dominosa_unsolved.png" alt="Dominosa unsolved" width="500">

Code to utilize this package and solve the puzzle:
```python
import numpy as np
from puzzle_solver import dominosa_solver as solver
board = np.array([
  [6, 8, 2, 7, 1, 3, 3, 4, 6, 6, 0],
  [4, 9, 5, 6, 1, 0, 6, 1, 2, 2, 4],
  [8, 2, 8, 9, 1, 9, 3, 3, 8, 8, 5],
  [1, 1, 7, 3, 4, 7, 0, 8, 7, 7, 7],
  [4, 5, 3, 9, 9, 3, 0, 1, 6, 1, 5],
  [6, 9, 5, 8, 9, 2, 1, 2, 6, 7, 9],
  [2, 7, 4, 3, 5, 5, 9, 6, 4, 0, 9],
  [0, 7, 8, 0, 5, 4, 2, 7, 6, 7, 3],
  [0, 4, 5, 2, 8, 6, 1, 0, 9, 0, 4],
  [0, 8, 8, 3, 2, 1, 3, 2, 5, 5, 4],
])
binst = solver.Board(board=board)
solutions = binst.solve_and_print()
assert len(solutions) == 1, f'unique solutions != 1, == {len(solutions)}'
```
**Script Output**
```python
Solution found
[['R' 'L' 'R' 'L' 'D' 'R' 'L' 'R' 'L' 'R' 'L']
 ['D' 'D' 'R' 'L' 'U' 'D' 'D' 'D' 'R' 'L' 'D']
 ['U' 'U' 'D' 'R' 'L' 'U' 'U' 'U' 'R' 'L' 'U']
 ['D' 'D' 'U' 'D' 'D' 'R' 'L' 'D' 'R' 'L' 'D']
 ['U' 'U' 'D' 'U' 'U' 'R' 'L' 'U' 'D' 'D' 'U']
 ['D' 'D' 'U' 'R' 'L' 'D' 'R' 'L' 'U' 'U' 'D']
 ['U' 'U' 'R' 'L' 'D' 'U' 'R' 'L' 'R' 'L' 'U']
 ['D' 'D' 'D' 'D' 'U' 'R' 'L' 'R' 'L' 'R' 'L']
 ['U' 'U' 'U' 'U' 'D' 'D' 'R' 'L' 'D' 'D' 'D']
 ['R' 'L' 'R' 'L' 'U' 'U' 'R' 'L' 'U' 'U' 'U']]
Solutions found: 1
status: OPTIMAL
Time taken: 0.02 seconds
```

**Solved puzzle**

<img src="https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/dominosa_solved.png" alt="Dominosa solved" width="500">

---

## Light Up (Puzzle Type #5)

* [**Play online**](https://www.chiark.greenend.org.uk/~sgtatham/puzzles/js/lightup.html)

* [**Instructions**](https://www.chiark.greenend.org.uk/~sgtatham/puzzles/doc/lightup.html#lightup)

* [**Solver Code**][5]

<details>
  <summary><strong>Rules</strong></summary>
You have a grid of squares. Some are filled in black; some of the black squares are numbered. Your aim is to ‘light up’ all the empty squares by placing light bulbs in some of them.

Each light bulb illuminates the square it is on, plus all squares in line with it horizontally or vertically unless a black square is blocking the way.

To win the game, you must satisfy the following conditions:

  - All non-black squares are lit.
  - No light is lit by another light.
  - All numbered black squares have exactly that number of lights adjacent to them (in the four squares above, below, and to the side).

Non-numbered black squares may have any number of lights adjacent to them. 
</details>

**Unsolved puzzle**

<img src="https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/lightup_unsolved.png" alt="Light Up unsolved" width="500">

Code to utilize this package and solve the puzzle:
```python
import numpy as np
from puzzle_solver import light_up_solver as solver
board = np.array([
  [' ', '0', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' '],
  [' ', ' ', ' ', '0', ' ', ' ', ' ', ' ', ' ', '1'],
  ['W', ' ', 'W', ' ', ' ', 'W', ' ', ' ', '0', ' '],
  ['0', ' ', ' ', ' ', '3', ' ', 'W', ' ', '0', ' '],
  [' ', ' ', ' ', ' ', 'W', ' ', '2', ' ', 'W', ' '],
  [' ', '1', ' ', 'W', ' ', '2', ' ', ' ', ' ', ' '],
  [' ', '0', ' ', 'W', ' ', 'W', ' ', ' ', ' ', 'W'],
  [' ', '0', ' ', ' ', '1', ' ', ' ', '2', ' ', 'W'],
  ['0', ' ', ' ', ' ', ' ', ' ', '1', ' ', ' ', ' '],
  [' ', ' ', ' ', '2', ' ', ' ', ' ', ' ', 'W', ' '],
])  # W is wall, ' ' is space, '0-9' is number

binst = solver.Board(board=board)
solutions = binst.solve_and_print()
```
**Script Output**
```python
Solution found
[[' ' '0' ' ' ' ' ' ' 'L' 'W' ' ' ' ' 'L']
 ['L' ' ' ' ' '0' ' ' ' ' 'L' ' ' ' ' '1']
 ['W' 'L' 'W' ' ' 'L' 'W' ' ' ' ' '0' ' ']
 ['0' ' ' ' ' 'L' '3' 'L' 'W' ' ' '0' ' ']
 [' ' ' ' 'L' ' ' 'W' ' ' '2' 'L' 'W' 'L']
 ['L' '1' ' ' 'W' 'L' '2' 'L' ' ' ' ' ' ']
 [' ' '0' ' ' 'W' ' ' 'W' ' ' ' ' ' ' 'W']
 [' ' '0' ' ' ' ' '1' 'L' ' ' '2' 'L' 'W']
 ['0' ' ' ' ' 'L' ' ' ' ' '1' 'L' ' ' ' ']
 [' ' 'L' ' ' '2' 'L' ' ' ' ' ' ' 'W' 'L']]
Solutions found: 1
status: OPTIMAL
Time taken: 0.01 seconds
```

**Solved puzzle**

Which exactly matches the true solutions (Remember, the goal of the puzzle is to find where to place the lights, marked as 'L' in the solution above):

<img src="https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/lightup_solved.png" alt="Light Up solved" width="500">

---

## Tents (Puzzle Type #6)

* [**Play online**](https://www.chiark.greenend.org.uk/~sgtatham/puzzles/js/tents.html)

* [**Instructions**](https://www.chiark.greenend.org.uk/~sgtatham/puzzles/doc/tents.html#tents)

* [**Solver Code**][6]

<details>
  <summary><strong>Rules</strong></summary>
You have a grid of squares, some of which contain trees. Your aim is to place tents in some of the remaining squares, in such a way that the following conditions are met:

  - There are exactly as many tents as trees.
  - The tents and trees can be matched up in such a way that each tent is directly adjacent (horizontally or vertically, but not diagonally) to its own tree. However, a tent may be adjacent to other trees as well as its own.
  - No two tents are adjacent horizontally, vertically or diagonally.
  - The number of tents in each row, and in each column, matches the numbers given round the sides of the grid.
</details>

**Unsolved puzzle**

<img src="https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/tents_unsolved.png" alt="Tents unsolved" width="500">

Code to utilize this package and solve the puzzle:
```python
import numpy as np
from puzzle_solver import tents_solver as solver
board = np.array([
  [' ', 'T', ' ', ' ', ' ', ' ', ' ', ' ', 'T', ' ', 'T', ' ', 'T', ' ', ' '],
  [' ', ' ', ' ', ' ', 'T', ' ', ' ', 'T', ' ', 'T', ' ', ' ', 'T', ' ', ' '],
  [' ', 'T', ' ', 'T', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '],
  [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'T', ' ', ' ', ' ', 'T', ' ', 'T'],
  [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '],
  [' ', 'T', ' ', ' ', 'T', ' ', 'T', ' ', ' ', 'T', ' ', ' ', 'T', 'T', ' '],
  [' ', 'T', ' ', ' ', 'T', ' ', ' ', ' ', 'T', ' ', ' ', ' ', ' ', ' ', ' '],
  [' ', ' ', ' ', ' ', ' ', ' ', 'T', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '],
  [' ', ' ', ' ', ' ', ' ', 'T', ' ', ' ', ' ', ' ', 'T', ' ', ' ', 'T', ' '],
  [' ', ' ', ' ', 'T', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'T', ' ', 'T'],
  ['T', ' ', ' ', ' ', ' ', ' ', ' ', 'T', ' ', ' ', ' ', 'T', ' ', ' ', ' '],
  ['T', ' ', ' ', ' ', 'T', ' ', 'T', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '],
  [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'T', ' ', 'T', ' ', ' ', ' ', 'T'],
  [' ', 'T', ' ', ' ', ' ', 'T', ' ', ' ', ' ', ' ', ' ', ' ', 'T', ' ', ' '],
  [' ', 'T', ' ', ' ', 'T', ' ', ' ', ' ', ' ', 'T', ' ', 'T', ' ', ' ', ' '],
])
side = np.array([4, 1, 6, 0, 5, 2, 3, 1, 5, 2, 3, 2, 4, 3, 4])
top = np.array([4, 2, 4, 1, 3, 3, 3, 3, 3, 3, 2, 2, 6, 2, 4])

binst = solver.Board(board=board, sides={'top': top, 'side': side})
solutions = binst.solve_and_print()
```
**Script Output**
```python
Solution found
[[' ' 'T' 'E' ' ' ' ' ' ' ' ' 'E' 'T' ' ' 'T' 'E' 'T' 'E' ' ']
 [' ' ' ' ' ' ' ' 'T' 'E' ' ' 'T' ' ' 'T' ' ' ' ' 'T' ' ' ' ']
 ['E' 'T' 'E' 'T' ' ' ' ' ' ' 'E' ' ' 'E' ' ' ' ' 'E' ' ' 'E']
 [' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' 'T' ' ' ' ' ' ' 'T' ' ' 'T']
 [' ' 'E' ' ' ' ' 'E' ' ' 'E' ' ' 'E' ' ' ' ' ' ' 'E' ' ' ' ']
 [' ' 'T' ' ' ' ' 'T' ' ' 'T' ' ' ' ' 'T' 'E' ' ' 'T' 'T' 'E']
 [' ' 'T' ' ' ' ' 'T' 'E' ' ' 'E' 'T' ' ' ' ' ' ' 'E' ' ' ' ']
 [' ' 'E' ' ' ' ' ' ' ' ' 'T' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ']
 [' ' ' ' ' ' ' ' 'E' 'T' 'E' ' ' ' ' 'E' 'T' ' ' 'E' 'T' 'E']
 ['E' ' ' 'E' 'T' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' 'T' ' ' 'T']
 ['T' ' ' ' ' ' ' ' ' ' ' ' ' 'T' 'E' ' ' ' ' 'T' 'E' ' ' 'E']
 ['T' ' ' ' ' 'E' 'T' 'E' 'T' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ']
 ['E' ' ' ' ' ' ' ' ' ' ' ' ' ' ' 'T' 'E' 'T' 'E' ' ' 'E' 'T']
 [' ' 'T' 'E' ' ' 'E' 'T' 'E' ' ' ' ' ' ' ' ' ' ' 'T' ' ' ' ']
 ['E' 'T' ' ' ' ' 'T' ' ' ' ' ' ' 'E' 'T' 'E' 'T' 'E' ' ' ' ']]
Solutions found: 1
status: OPTIMAL
Time taken: 0.02 seconds
```

**Solved puzzle**

<img src="https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/tents_solved.png" alt="Tents solved" width="500">

---

## Filling (Puzzle Type #7)

* [**Play online**](https://www.chiark.greenend.org.uk/~sgtatham/puzzles/js/filling.html)

* [**Instructions**](https://www.chiark.greenend.org.uk/~sgtatham/puzzles/doc/filling.html#filling)

* [**Solver Code**][7]

<details>
  <summary><strong>Rules</strong></summary>
You have a grid of squares, some of which contain digits, and the rest of which are empty. Your job is to fill in digits in the empty squares, in such a way that each connected region of squares all containing the same digit has an area equal to that digit.

(‘Connected region’, for the purposes of this game, does not count diagonally separated squares as adjacent.)

For example, it follows that no square can contain a zero, and that two adjacent squares can not both contain a one. No region has an area greater than 9 (because then its area would not be a single digit).
</details>

(Note: The solver for this puzzle is the only extremely slow solver in this repo and will take a minute to solve a simple 6x7 puzzle)

**Unsolved puzzle**

<img src="https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/filling_unsolved.png" alt="Filling unsolved" width="500">

Code to utilize this package and solve the puzzle:
```python
import numpy as np
from puzzle_solver import filling_solver as solver
board = np.array([
  [' ', '4', '2', ' ', ' ', '2', ' '],
  [' ', ' ', '7', ' ', ' ', '3', ' '],
  [' ', ' ', ' ', ' ', '4', ' ', '3'],
  [' ', '6', '6', ' ', '3', ' ', ' '],
  [' ', '7', ' ', '6', '4', '5', ' '],
  [' ', '6', ' ', ' ', ' ', ' ', '4'],
])
binst = solver.Board(board=board)
solutions = binst.solve_and_print()
assert len(solutions) == 1, f'unique solutions != 1, == {len(solutions)}'
```
**Script Output**
```python
Solution found
[[4 4 2 2 4 2 2]
 [4 4 7 4 4 3 3]
 [7 7 7 3 4 5 3]
 [7 6 6 3 3 5 5]
 [7 7 6 6 4 5 5]
 [1 6 6 1 4 4 4]]
Solutions found: 1
status: OPTIMAL
Time taken: 46.27 seconds
```

**Solved puzzle**

<img src="https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/filling_solved.png" alt="Filling solved" width="500">

---

## Keen (Puzzle Type #8)

* [**Play online**](https://www.chiark.greenend.org.uk/~sgtatham/puzzles/js/keen.html)

* [**Instructions**](https://www.chiark.greenend.org.uk/~sgtatham/puzzles/doc/keen.html#keen)

* [**Solver Code**][8]

<details>
  <summary><strong>Rules</strong></summary>
You have a square grid; each square may contain a digit from 1 to the size of the grid. The grid is divided into blocks of varying shape and size, with arithmetic clues written in them. Your aim is to fully populate the grid with digits such that:

  - Each row contains only one occurrence of each digit
  - Each column contains only one occurrence of each digit
  - The digits in each block can be combined to form the number stated in the clue, using the arithmetic operation given in the clue. That is:
      - An addition clue means that the sum of the digits in the block must be the given number. For example, ‘15+’ means the contents of the block adds up to fifteen.
      - A multiplication clue (e.g. ‘60×’), similarly, means that the product of the digits in the block must be the given number.
      - A subtraction clue will always be written in a block of size two, and it means that one of the digits in the block is greater than the other by the given amount. For example, ‘2−’ means that one of the digits in the block is 2 more than the other, or equivalently that one digit minus the other one is 2. The two digits could be either way round, though.
      - A division clue (e.g. ‘3÷’), similarly, is always in a block of size two and means that one digit divided by the other is equal to the given amount.

  Note that a block may contain the same digit more than once (provided the identical ones are not in the same row and column).
</details>

**Unsolved puzzle**

<img src="https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/keen_unsolved.png" alt="Keen unsolved" width="500">

Code to utilize this package and solve the puzzle:
```python
import numpy as np
from puzzle_solver import keen_solver as solver
# tells the api the shape of the blocks in the board
board = np.array([
  ['d01', 'd01', 'd03', 'd03', 'd05', 'd05', 'd08', 'd08', 'd10'],
  ['d02', 'd02', 'd03', 'd04', 'd06', 'd06', 'd09', 'd09', 'd10'],
  ['d12', 'd13', 'd14', 'd04', 'd07', 'd07', 'd07', 'd11', 'd11'],
  ['d12', 'd13', 'd14', 'd14', 'd15', 'd16', 'd11', 'd11', 'd18'],
  ['d19', 'd20', 'd24', 'd26', 'd15', 'd16', 'd16', 'd17', 'd18'],
  ['d19', 'd20', 'd24', 'd26', 'd28', 'd28', 'd29', 'd17', 'd33'],
  ['d21', 'd21', 'd24', 'd27', 'd30', 'd30', 'd29', 'd33', 'd33'],
  ['d22', 'd23', 'd25', 'd27', 'd31', 'd32', 'd34', 'd34', 'd36'],
  ['d22', 'd23', 'd25', 'd25', 'd31', 'd32', 'd35', 'd35', 'd36'],
])
# tells the api the operation and the result for each block
block_results = {
  'd01': ('-', 1), 'd02': ('-', 1), 'd03': ('*', 378), 'd04': ('/', 4), 'd05': ('/', 2),
  'd06': ('-', 2), 'd07': ('*', 6), 'd08': ('+', 9), 'd09': ('/', 2), 'd10': ('+', 9),
  'd11': ('+', 22), 'd12': ('-', 1), 'd13': ('*', 30), 'd14': ('+', 12), 'd15': ('-', 1),
  'd16': ('*', 196), 'd17': ('*', 63), 'd18': ('-', 1), 'd19': ('/', 3), 'd20': ('/', 3),
  'd21': ('*', 21), 'd22': ('/', 4), 'd23': ('-', 7), 'd24': ('*', 64), 'd25': ('+', 15),
  'd26': ('-', 1), 'd27': ('+', 11), 'd28': ('-', 4), 'd29': ('/', 4), 'd30': ('*', 54),
  'd31': ('+', 11), 'd32': ('/', 4), 'd33': ('+', 16), 'd34': ('+', 15), 'd35': ('*', 30),
  'd36': ('-', 7),
}
binst = solver.Board(board=board, block_results=block_results)
solutions = binst.solve_and_print()
```
**Script Output**
```python
Solution found
[[5 4 7 9 3 6 8 1 2]
 [9 8 6 1 5 3 2 4 7]
 [7 5 9 4 2 1 3 8 6]
 [8 6 1 2 9 7 5 3 4]
 [6 1 2 5 8 4 7 9 3]
 [2 3 8 6 1 5 4 7 9]
 [3 7 4 8 6 9 1 2 5]
 [4 2 5 3 7 8 9 6 1]
 [1 9 3 7 4 2 6 5 8]]
Solutions found: 1
status: OPTIMAL
Time taken: 0.02 seconds
```

**Solved puzzle**

<img src="https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/keen_solved.png" alt="Keen solved" width="500">

---

## Towers (Puzzle Type #9)

* [**Play online**](https://www.chiark.greenend.org.uk/~sgtatham/puzzles/js/towers.html)

* [**Instructions**](https://www.chiark.greenend.org.uk/~sgtatham/puzzles/doc/towers.html#towers)

* [**Solver Code**][9]

<details>
  <summary><strong>Rules</strong></summary>
You have a square grid. On each square of the grid you can build a tower, with its height ranging from 1 to the size of the grid. Around the edge of the grid are some numeric clues.

Your task is to build a tower on every square, in such a way that:

  - Each row contains every possible height of tower once
  - Each column contains every possible height of tower once
  - Each numeric clue describes the number of towers that can be seen if you look into the square from that direction, assuming that shorter towers are hidden behind taller ones. For example, in a 5×5 grid, a clue marked ‘5’ indicates that the five tower heights must appear in increasing order (otherwise you would not be able to see all five towers), whereas a clue marked ‘1’ indicates that the tallest tower (the one marked 5) must come first.

In harder or larger puzzles, some towers will be specified for you as well as the clues round the edge, and some edge clues may be missing. 
</details>

**Unsolved puzzle**

<img src="https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/towers_unsolved.png" alt="Towers unsolved" width="500">

Code to utilize this package and solve the puzzle:
```python
import numpy as np
from puzzle_solver import towers_solver as solver
board = np.array([
  [' ', ' ', ' ', ' ', ' ', ' '],
  [' ', ' ', ' ', ' ', ' ', ' '],
  [' ', ' ', '3', ' ', ' ', ' '],
  [' ', ' ', ' ', ' ', ' ', ' '],
  [' ', ' ', ' ', ' ', ' ', ' '],
  [' ', ' ', ' ', ' ', ' ', ' '],
])
t = np.array([2, -1, 2, 2, 2, 3])
b = np.array([2, 4, -1, 4, -1, -1])
r = np.array([3, -1, 2, -1, -1, -1])
l = np.array([-1, -1, -1, 2, -1, 4])
binst = solver.Board(board=board, sides={'top': t, 'bottom': b, 'right': r, 'left': l})
solutions = binst.solve_and_print()
```
**Script Output**
```python
Solution found
[[5 6 4 1 2 3]
 [3 4 2 6 1 5]
 [4 5 3 2 6 1]
 [2 1 6 5 3 4]
 [6 3 1 4 5 2]
 [1 2 5 3 4 6]]
Solutions found: 1
status: OPTIMAL
Time taken: 0.03 seconds
```

**Solved puzzle**

<img src="https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/towers_solved.png" alt="Towers solved" width="500">

---

## Singles (Puzzle Type #10)

* [**Play online**](https://www.chiark.greenend.org.uk/~sgtatham/puzzles/js/singles.html)

* [**Instructions**](https://www.chiark.greenend.org.uk/~sgtatham/puzzles/doc/singles.html#singles)

* [**Solver Code**][10]

<details>
  <summary><strong>Rules</strong></summary>
You have a grid of white squares, all of which contain numbers. Your task is to colour some of the squares black (removing the number) so as to satisfy all of the following conditions:

  - No number occurs more than once in any row or column.
  - No black square is horizontally or vertically adjacent to any other black square.
  - The remaining white squares must all form one contiguous region (connected by edges, not just touching at corners).
</details>

**Unsolved puzzle**

<img src="https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/singles_unsolved.png" alt="Singles unsolved" width="500">

Code to utilize this package and solve the puzzle:
```python
import numpy as np
from puzzle_solver import singles_solver as solver
board = np.array([
  [1, 6, 5, 4, 9, 8, 9, 3, 5, 1, 3, 7],
  [2, 8, 5, 7, 1, 1, 4, 3, 6, 3, 10, 7],
  [6, 7, 7, 11, 2, 6, 3, 10, 10, 2, 3, 3],
  [11, 9, 4, 3, 6, 1, 2, 5, 3, 10, 7, 8], 
  [5, 5, 4, 9, 7, 9, 6, 6, 11, 5, 4, 11],
  [1, 3, 7, 9, 12, 5, 4, 2, 9, 6, 12, 4],
  [6, 11, 1, 3, 6, 4, 11, 2, 2, 10, 8, 10],
  [3, 11, 12, 6, 2, 9, 9, 1, 4, 8, 12, 5],
  [4, 8, 8, 5, 11, 3, 3, 6, 5, 9, 1, 4],
  [2, 4, 6, 2, 1, 10, 1, 10, 8, 5, 4, 6],
  [5, 1, 6, 10, 9, 4, 8, 4, 8, 3, 2, 12],
  [11, 2, 12, 10, 8, 3, 5, 4, 10, 4, 8, 11],
])
binst = solver.Board(board=board)
solutions = binst.solve_and_print()
```
**Script Output**
```python
Solution found
[['B' ' ' 'B' ' ' 'B' ' ' ' ' 'B' ' ' ' ' ' ' ' ']
 [' ' ' ' ' ' ' ' ' ' 'B' ' ' ' ' ' ' 'B' ' ' 'B']
 ['B' ' ' 'B' ' ' 'B' ' ' 'B' ' ' 'B' ' ' 'B' ' ']
 [' ' ' ' ' ' 'B' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ']
 ['B' ' ' 'B' ' ' ' ' 'B' ' ' 'B' ' ' 'B' ' ' 'B']
 [' ' ' ' ' ' 'B' ' ' ' ' 'B' ' ' ' ' ' ' 'B' ' ']
 [' ' 'B' ' ' ' ' 'B' ' ' ' ' 'B' ' ' 'B' ' ' ' ']
 [' ' ' ' 'B' ' ' ' ' ' ' 'B' ' ' ' ' ' ' ' ' ' ']
 [' ' 'B' ' ' ' ' ' ' 'B' ' ' ' ' 'B' ' ' ' ' 'B']
 ['B' ' ' 'B' ' ' 'B' ' ' ' ' 'B' ' ' ' ' 'B' ' ']
 [' ' ' ' ' ' ' ' ' ' 'B' ' ' ' ' 'B' ' ' ' ' ' ']
 ['B' ' ' ' ' 'B' ' ' ' ' ' ' 'B' ' ' ' ' 'B' ' ']]
Solutions found: 1
status: OPTIMAL
Time taken: 2.14 seconds
```

**Solved puzzle**

<img src="https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/singles_solved.png" alt="Singles solved" width="500">

---

## Magnets (Puzzle Type #11)

* [**Play online**](https://www.chiark.greenend.org.uk/~sgtatham/puzzles/js/magnets.html)

* [**Instructions**](https://www.chiark.greenend.org.uk/~sgtatham/puzzles/doc/magnets.html#magnets)

* [**Solver Code**][11]

<details>
  <summary><strong>Rules</strong></summary>
A rectangular grid has been filled with a mixture of magnets (that is, dominoes with one positive end and one negative end) and blank dominoes (that is, dominoes with two neutral poles). These dominoes are initially only seen in silhouette. Around the grid are placed a number of clues indicating the number of positive and negative poles contained in certain columns and rows.

Your aim is to correctly place the magnets and blank dominoes such that all the clues are satisfied, with the additional constraint that no two similar magnetic poles may be orthogonally adjacent (since they repel). Neutral poles do not repel, and can be adjacent to any other pole. 
</details>

**Unsolved puzzle**

<img src="https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/magnets_unsolved.png" alt="Magnets unsolved" width="500">

Code to utilize this package and solve the puzzle:
```python
import numpy as np
from puzzle_solver import magnets_solver as solver
board = np.array([
  ['H', 'H', 'H', 'H', 'V', 'V', 'V', 'V', 'H', 'H'],
  ['H', 'H', 'H', 'H', 'V', 'V', 'V', 'V', 'V', 'V'],
  ['H', 'H', 'H', 'H', 'H', 'H', 'H', 'H', 'V', 'V'],
  ['V', 'V', 'V', 'H', 'H', 'H', 'H', 'H', 'H', 'V'],
  ['V', 'V', 'V', 'V', 'V', 'V', 'V', 'H', 'H', 'V'],
  ['V', 'H', 'H', 'V', 'V', 'V', 'V', 'V', 'V', 'V'],
  ['V', 'V', 'V', 'V', 'V', 'H', 'H', 'V', 'V', 'V'],
  ['V', 'V', 'V', 'V', 'V', 'V', 'H', 'H', 'H', 'H'],
  ['V', 'H', 'H', 'H', 'H', 'V', 'H', 'H', 'H', 'H'],
])
pos_v = np.array([-1, -1, 3, 5, 3, 3, -1, 3, -1, 4])
neg_v = np.array([-1, 2, 3, 4, -1, 3, 4, 3, 4, 4])
pos_h = np.array([5, -1, -1, -1, 5, -1, 3, 1, -1])
neg_h = np.array([4, -1, 4, -1, 5, 4, -1, 2, -1])

binst = solver.Board(board=board, sides={'pos_v': pos_v, 'neg_v': neg_v, 'pos_h': pos_h, 'neg_h': neg_h})
solutions = binst.solve_and_print()
```
**Script Output**
```python
Solution found
[['-' '+' '-' '+' ' ' '+' '-' '+' '-' '+']
 [' ' ' ' '+' '-' ' ' '-' '+' '-' '+' '-']
 ['-' '+' '-' '+' ' ' ' ' '-' '+' '-' '+']
 ['+' '-' '+' '-' '+' '-' '+' '-' '+' '-']
 ['-' '+' '-' '+' '-' '+' '-' '+' '-' '+']
 [' ' '-' '+' '-' '+' '-' '+' ' ' '+' '-']
 [' ' ' ' ' ' '+' '-' '+' '-' ' ' '-' '+']
 ['-' ' ' ' ' '-' '+' ' ' ' ' ' ' ' ' ' ']
 ['+' ' ' ' ' '+' '-' ' ' '+' '-' '+' '-']]
Solutions found: 1
status: OPTIMAL
Time taken: 0.02 seconds
```

**Solved puzzle**

<img src="https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/magnets_solved.png" alt="Magnets solved" width="500">


---

## Signpost (Puzzle Type #12)

* [**Play online**](https://www.chiark.greenend.org.uk/~sgtatham/puzzles/js/signpost.html)

* [**Instructions**](https://www.chiark.greenend.org.uk/~sgtatham/puzzles/doc/signpost.html#signpost)

* [**Solver Code**][12]

<details>
  <summary><strong>Rules</strong></summary>
You have a grid of squares; each square (except the last one) contains an arrow, and some squares also contain numbers. Your job is to connect the squares to form a continuous list of numbers starting at 1 and linked in the direction of the arrows – so the arrow inside the square with the number 1 will point to the square containing the number 2, which will point to the square containing the number 3, etc. Each square can be any distance away from the previous one, as long as it is somewhere in the direction of the arrow.

By convention the first and last numbers are shown; one or more interim numbers may also appear at the beginning. 
</details>

**Unsolved puzzle**

<img src="https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/signpost_unsolved.png" alt="Signpost unsolved" width="500">

Code to utilize this package and solve the puzzle:
```python
import numpy as np
from puzzle_solver import signpost_solver as solver
# Q = up-left, W = up, E = up-right, A = left, D = right, Z = down-left, X = down, C = down-right
board1 = np.array([
  ['C', 'D', 'D', 'X', 'D', 'Z', 'X'],
  ['D', 'C', 'D', 'X', 'X', 'A', 'A'],
  ['X', 'X', 'D', 'Q', 'Z', 'W', 'A'],
  ['W', 'D', 'W', 'W', 'X', 'Z', 'X'],
  ['X', 'A', 'Q', 'Q', 'A', 'Q', 'X'],
  ['D', 'W', 'W', 'A', 'E', 'A', 'Z'],
  ['D', 'E', 'D', 'E', 'D', 'A', ' '],
])
board2 = np.array([
  [ 1,  0, 23,  0,  0,  0,  0],
  [30, 32,  0,  0,  0,  0,  0],
  [ 0,  0,  2,  0,  0,  0,  0],
  [ 0,  0,  0,  0,  0,  0,  0],
  [ 0, 45,  0,  0, 33,  0,  0],
  [ 0,  0, 22,  8, 39, 10,  0],
  [ 0,  0,  0,  0,  0, 20, 49],
])

binst = solver.Board(board=board1, values=board2)
solutions = binst.solve_and_print()
```
**Script Output**
```python
Solution found
[[1 42 23 7 43 44 24]
 [30 32 36 5 37 4 31]
 [28 12 2 41 26 3 25]
 [29 13 35 6 38 14 17]
 [46 45 27 34 33 40 18]
 [9 11 22 8 39 10 19]
 [47 21 15 16 48 20 49]]
Solutions found: 1
status: OPTIMAL
Time taken: 0.03 seconds
```

**Solved puzzle**

<img src="https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/signpost_solved.png" alt="Signpost solved" width="500">


---

## Range (Puzzle Type #13)

* [**Play online**](https://www.chiark.greenend.org.uk/~sgtatham/puzzles/js/range.html)

* [**Instructions**](https://www.chiark.greenend.org.uk/~sgtatham/puzzles/doc/range.html#range)

* [**Solver Code**][13]

<details>
  <summary><strong>Rules</strong></summary>
You have a grid of squares; some squares contain numbers. Your job is to colour some of the squares black, such that several criteria are satisfied:

  - no square with a number is coloured black.
  - no two black squares are adjacent (horizontally or vertically).
  - for any two white squares, there is a path between them using only white squares.
  - for each square with a number, that number denotes the total number of white squares reachable from that square going in a straight line in any horizontal or vertical direction until hitting a wall or a black square; the square with the number is included in the total (once).

For instance, a square containing the number one must have four black squares as its neighbours by the last criterion; but then it's impossible for it to be connected to any outside white square, which violates the second to last criterion. So no square will contain the number one. 
</details>

(Note: The solver for this puzzle is slightly slower and could take several seconds to solve a 16x11 puzzle)

**Unsolved puzzle**

<img src="https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/range_unsolved.png" alt="Range unsolved" width="500">

Code to utilize this package and solve the puzzle:
```python
import numpy as np
from puzzle_solver import range_solver as solver
clues = np.array([
    [-1, 4, 2, -1, -1, 3, -1, -1, -1, 8, -1, -1, -1, -1, 6, -1],
    [-1, -1, -1, -1, -1, 13, -1, 18, -1, -1, 14, -1, -1, 22, -1, -1],
    [-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 12, -1, -1, -1, -1],
    [-1, -1, -1, -1, 12, -1, 11, -1, -1, -1, 9, -1, -1, -1, -1, -1],
    [7, -1, -1, -1, -1, -1, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1],
    [-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1],
    [-1, -1, -1, -1, -1, -1, -1, -1, -1, 12, -1, -1, -1, -1, -1, 5],
    [-1, -1, -1, -1, -1, 9, -1, -1, -1, 9, -1, 4, -1, -1, -1, -1],
    [-1, -1, -1, -1, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1],
    [-1, -1, 10, -1, -1, 7, -1, -1, 13, -1, 10, -1, -1, -1, -1, -1],
    [-1, 7, -1, -1, -1, -1, 6, -1, -1, -1, 6, -1, -1, 13, 5, -1],
])
binst = solver.Board(clues)
solutions = binst.solve_and_print()
```
**Script Output**
```python
Solution:
B . . B . . B . B . B . B . . .
. . B . . . . . . . . . . . . B
B . . . . B . . . . . . . . . .
. B . B . . . . . . . B . . . .
. . . . . B . . B . B . . . B .
. . B . . . . . . . . B . . . B
B . . . B . B . . . . . B . . .
. . . . . . . B . . B . . . B .
. B . . . B . . . B . B . . . .
. . . . . . B . . . . . . . . B
B . . . . . . B . . . . B . . .
Solutions found: 1
status: OPTIMAL
Time taken: 3.32 seconds
```

**Solved puzzle**

<img src="https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/range_solved.png" alt="Range solved" width="500">

---

## UnDead (Puzzle Type #14)

* [**Play online**](https://www.chiark.greenend.org.uk/~sgtatham/puzzles/js/undead.html)

* [**Instructions**](https://www.chiark.greenend.org.uk/~sgtatham/puzzles/doc/undead.html#undead)

* [**Solver Code**][14]

<details>
  <summary><strong>Rules</strong></summary>
You are given a grid of squares, some of which contain diagonal mirrors. Every square which is not a mirror must be filled with one of three types of undead monster: a ghost, a vampire, or a zombie.

Vampires can be seen directly, but are invisible when reflected in mirrors. Ghosts are the opposite way round: they can be seen in mirrors, but are invisible when looked at directly. Zombies are visible by any means.

You are also told the total number of each type of monster in the grid. Also around the edge of the grid are written numbers, which indicate how many monsters can be seen if you look into the grid along a row or column starting from that position. (The diagonal mirrors are reflective on both sides. If your reflected line of sight crosses the same monster more than once, the number will count it each time it is visible, not just once.) 
</details>

**Unsolved puzzle**

<img src="https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/undead_unsolved.png" alt="UnDead unsolved" width="500">

Code to utilize this package and solve the puzzle:
```python
import numpy as np
from puzzle_solver import undead_solver as solver
board = np.array([
  ['  ', '//', '  ', '  ', '  ', '  ', '\\'],
  ['  ', '  ', '  ', '//', '  ', '  ', '  '],
  ['  ', '//', '//', '  ', '  ', '\\', '//'],
  ['//', '\\', '//', '  ', '//', '\\', '  '],
  ['//', '  ', '//', '\\', '  ', '//', '//'],
  ['  ', '\\', '\\', '\\', '  ', '  ', '  '],
  ['  ', '//', '  ', '  ', '  ', '  ', '  '],
])
t = np.array([3, 0, 3, 0, 5, 6, 0])
b = np.array([5, 2, 1, 3, 8, 2, 0])
r = np.array([0, 8, 0, 4, 2, 2, 4])
l = np.array([1, 4, 8, 0, 0, 2, 2])
counts = {Monster.GHOST: 5, Monster.VAMPIRE: 12, Monster.ZOMBIE: 11}

# create board and solve
binst = solver.Board(board=board, sides={'top': t, 'bottom': b, 'right': r, 'left': l}, monster_count=counts)
solutions = binst.solve_and_print()
```
**Script Output**
```python
Solution found
[['VA' '//' 'GH' 'GH' 'ZO' 'GH' '\\']
 ['VA' 'VA' 'VA' '//' 'ZO' 'ZO' 'ZO']
 ['VA' '//' '//' 'ZO' 'ZO' '\\' '//']
 ['//' '\\' '//' 'VA' '//' '\\' 'VA']
 ['//' 'VA' '//' '\\' 'ZO' '//' '//']
 ['ZO' '\\' '\\' '\\' 'ZO' 'VA' 'GH']
 ['ZO' '//' 'VA' 'VA' 'ZO' 'VA' 'GH']]
Solutions found: 1
status: OPTIMAL
Time taken: 0.01 seconds
```

**Solved puzzle**

<img src="https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/undead_solved.png" alt="UnDead solved" width="500">

---

## Unruly (Puzzle Type #15)

* [**Play online**](https://www.chiark.greenend.org.uk/~sgtatham/puzzles/js/unruly.html)

* [**Instructions**](https://www.chiark.greenend.org.uk/~sgtatham/puzzles/doc/unruly.html#unruly)

* [**Solver Code**][15]

<details>
  <summary><strong>Rules</strong></summary>
You are given a grid of squares, which you must colour either black or white. Some squares are provided as clues; the rest are left for you to fill in. Each row and column must contain the same number of black and white squares, and no row or column may contain three consecutive squares of the same colour. 
</details>

**Unsolved puzzle**

<img src="https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/unruly_unsolved.png" alt="Unruly unsolved" width="500">

Code to utilize this package and solve the puzzle:
```python
import numpy as np
from puzzle_solver import unruly_solver as solver
board = np.array([
  ['W', 'W', ' ', 'B', ' ', ' ', ' ', ' ', 'B', ' ', ' ', ' ', ' ', ' '],
  [' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', 'W'],
  [' ', ' ', ' ', ' ', ' ', 'B', ' ', 'W', ' ', ' ', 'B', ' ', ' ', ' '],
  [' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', 'W', ' ', ' '],
  ['B', ' ', ' ', 'W', ' ', ' ', ' ', ' ', 'B', ' ', ' ', ' ', ' ', ' '],
  [' ', ' ', ' ', ' ', ' ', 'B', ' ', ' ', ' ', 'B', ' ', 'B', ' ', ' '],
  [' ', 'B', 'B', ' ', ' ', 'B', ' ', ' ', ' ', ' ', ' ', 'B', ' ', ' '],
  [' ', ' ', 'B', ' ', ' ', ' ', ' ', 'W', ' ', 'B', 'B', ' ', ' ', 'W'],
  [' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', 'W'],
  [' ', ' ', ' ', 'B', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '],
  [' ', ' ', 'W', ' ', ' ', ' ', 'W', ' ', ' ', 'W', ' ', 'W', ' ', ' '],
  [' ', 'W', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'B'],
  [' ', ' ', ' ', ' ', 'B', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '],
  ['W', ' ', ' ', ' ', 'W', ' ', ' ', ' ', 'B', ' ', 'W', ' ', 'B', ' '],
])
binst = solver.Board(board=board)
solutions = binst.solve_and_print()
```
**Script Output**
```python
Solution found
[['W' 'W' 'B' 'B' 'W' 'B' 'W' 'B' 'B' 'W' 'B' 'W' 'W' 'B']
 ['B' 'B' 'W' 'W' 'B' 'W' 'B' 'W' 'W' 'B' 'W' 'B' 'B' 'W']
 ['W' 'W' 'B' 'W' 'W' 'B' 'B' 'W' 'B' 'W' 'B' 'B' 'W' 'B']
 ['W' 'B' 'W' 'B' 'B' 'W' 'W' 'B' 'W' 'W' 'B' 'W' 'B' 'B']
 ['B' 'W' 'B' 'W' 'B' 'W' 'B' 'W' 'B' 'B' 'W' 'W' 'B' 'W']
 ['B' 'W' 'W' 'B' 'W' 'B' 'B' 'W' 'B' 'B' 'W' 'B' 'W' 'W']
 ['W' 'B' 'B' 'W' 'W' 'B' 'W' 'B' 'W' 'W' 'B' 'B' 'W' 'B']
 ['B' 'W' 'B' 'W' 'B' 'W' 'B' 'W' 'W' 'B' 'B' 'W' 'B' 'W']
 ['B' 'B' 'W' 'B' 'B' 'W' 'B' 'W' 'B' 'W' 'W' 'B' 'W' 'W']
 ['W' 'W' 'B' 'B' 'W' 'B' 'W' 'B' 'W' 'B' 'W' 'W' 'B' 'B']
 ['B' 'B' 'W' 'W' 'B' 'W' 'W' 'B' 'B' 'W' 'B' 'W' 'B' 'W']
 ['B' 'W' 'B' 'W' 'W' 'B' 'B' 'W' 'W' 'B' 'W' 'B' 'W' 'B']
 ['W' 'B' 'W' 'B' 'B' 'W' 'W' 'B' 'W' 'B' 'B' 'W' 'W' 'B']
 ['W' 'B' 'W' 'B' 'W' 'B' 'W' 'B' 'B' 'W' 'W' 'B' 'B' 'W']]
Solutions found: 1
status: OPTIMAL
Time taken: 0.01 seconds
```

**Solved puzzle**

<img src="https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/unruly_solved.png" alt="Unruly solved" width="500">

---

## Tracks (Puzzle Type #16)

* [**Play online**](https://www.chiark.greenend.org.uk/~sgtatham/puzzles/js/tracks.html)

* [**Instructions**](https://www.chiark.greenend.org.uk/~sgtatham/puzzles/doc/tracks.html#tracks)

* [**Solver Code**][16]

<details>
  <summary><strong>Rules</strong></summary>
Complete the track from A to B so that the rows and columns contain the same number of track segments as are indicated in the clues to the top and right of the grid. There are only straight and 90-degree curved rail sections, and the track may not cross itself. 

</details>

(Note: The solver for this puzzle is slightly slower and could take several seconds to solve a large 15x15 puzzle)

**Unsolved puzzle**

<img src="https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/tracks_unsolved.png" alt="Tracks unsolved" width="500">

Code to utilize this package and solve the puzzle:
```python
import numpy as np
from puzzle_solver import tracks_solver as solver
board = np.array([
  ['  ', '  ', '  ', '  ', '  ', '  ', '  ', '  ', '  ', '  ', '  ', '  ', 'LD', '  ', '  ', ], 
  ['  ', '  ', '  ', '  ', '  ', '  ', '  ', '  ', '  ', '  ', 'LD', '  ', '  ', '  ', '  ', ], 
  ['  ', '  ', '  ', '  ', '  ', '  ', '  ', '  ', '  ', '  ', '  ', '  ', '  ', '  ', '  ', ], 
  ['  ', '  ', '  ', '  ', '  ', 'LD', 'UD', 'DR', '  ', '  ', '  ', '  ', '  ', '  ', '  ', ], 
  ['DR', '  ', '  ', '  ', '  ', '  ', '  ', '  ', '  ', '  ', '  ', '  ', '  ', '  ', '  ', ], 
  ['  ', '  ', '  ', '  ', '  ', '  ', '  ', '  ', '  ', '  ', '  ', '  ', 'DR', '  ', '  ', ], 
  ['  ', '  ', '  ', '  ', '  ', '  ', '  ', '  ', 'DR', '  ', '  ', '  ', '  ', '  ', '  ', ], 
  ['  ', '  ', 'UL', '  ', '  ', '  ', '  ', '  ', '  ', '  ', '  ', '  ', '  ', '  ', '  ', ], 
  ['  ', '  ', '  ', '  ', '  ', '  ', '  ', '  ', '  ', '  ', 'LR', '  ', '  ', '  ', '  ', ], 
  ['  ', '  ', '  ', '  ', '  ', '  ', '  ', '  ', '  ', '  ', 'LD', '  ', '  ', '  ', 'UD', ], 
  ['  ', '  ', '  ', '  ', '  ', '  ', '  ', '  ', 'UR', '  ', '  ', '  ', '  ', 'UD', 'UD', ], 
  ['  ', '  ', '  ', '  ', '  ', '  ', '  ', '  ', '  ', 'LR', '  ', '  ', '  ', '  ', '  ', ], 
  ['UL', '  ', '  ', '  ', '  ', '  ', '  ', '  ', '  ', '  ', 'LR', 'LR', '  ', '  ', '  ', ], 
  ['  ', '  ', '  ', '  ', '  ', '  ', '  ', '  ', '  ', '  ', '  ', '  ', '  ', '  ', '  ', ], 
  ['  ', '  ', '  ', '  ', '  ', '  ', '  ', '  ', '  ', '  ', '  ', '  ', '  ', 'DR', '  ', ], 
])
side = np.array([9, 7, 7, 7, 11, 10, 9, 8, 9, 10, 7, 9, 9, 2, 2])
top = np.array([6, 5, 7, 3, 3, 2, 7, 8, 13, 8, 9, 8, 10, 13, 14])
binst = solver.Board(board=board, top=top, side=side)
solutions = binst.solve_and_print()
```
**Script Output**
```python
[['  ' '  ' '  ' '  ' '  ' '  ' '┏━' '━━' '━━' '━━' '━━' '━━' '━┒' '┏━' '━┒']
 ['  ' '  ' '  ' '  ' '  ' '  ' '┃ ' '  ' '┏━' '━━' '━┒' '  ' '┗━' '━┛' '┃ ']
 ['  ' '  ' '  ' '  ' '  ' '  ' '┃ ' '  ' '┃ ' '  ' '┗━' '━━' '━━' '━━' '━┛']
 ['  ' '  ' '┏━' '━┒' '┏━' '━┒' '┃ ' '┏━' '━┛' '  ' '  ' '  ' '  ' '  ' '  ']
 ['┏━' '━━' '━┛' '┃ ' '┃ ' '┗━' '━┛' '┗━' '━┒' '  ' '  ' '  ' '  ' '┏━' '━┒']
 ['┗━' '━━' '━┒' '┗━' '━┛' '  ' '  ' '┏━' '━┛' '  ' '  ' '  ' '┏━' '━┛' '┃ ']
 ['  ' '  ' '┃ ' '  ' '  ' '  ' '┏━' '━┛' '┏━' '━━' '━━' '━━' '━┛' '  ' '┃ ']
 ['  ' '┏━' '━┛' '  ' '  ' '  ' '┗━' '━━' '━┛' '  ' '  ' '  ' '┏━' '━━' '━┛']
 ['  ' '┗━' '━┒' '  ' '  ' '  ' '  ' '  ' '┏━' '━━' '━━' '━┒' '┃ ' '┏━' '━┒']
 ['┏━' '━━' '━┛' '  ' '  ' '  ' '  ' '  ' '┃ ' '┏━' '━┒' '┗━' '━┛' '┃ ' '┃ ']
 ['┃ ' '  ' '  ' '  ' '  ' '  ' '  ' '  ' '┗━' '━┛' '┗━' '━┒' '  ' '┃ ' '┃ ']
 ['┃ ' '  ' '  ' '  ' '  ' '  ' '  ' '┏━' '━━' '━━' '━━' '━┛' '┏━' '━┛' '┃ ']
 ['━┛' '  ' '  ' '  ' '  ' '  ' '  ' '┗━' '━━' '━━' '━━' '━━' '━┛' '┏━' '━┛']
 ['  ' '  ' '  ' '  ' '  ' '  ' '  ' '  ' '  ' '  ' '  ' '  ' '  ' '┗━' '━┒']
 ['  ' '  ' '  ' '  ' '  ' '  ' '  ' '  ' '  ' '  ' '  ' '  ' '  ' '┏━' '━┛']]
Solutions found: 1
status: OPTIMAL
Time taken: 9.42 seconds
```

**Solved puzzle**

<img src="https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/tracks_solved.png" alt="Tracks solved" width="500">

---

## Mosaic (Puzzle Type #17)

* [**Play online**](https://www.chiark.greenend.org.uk/~sgtatham/puzzles/js/mosaic.html)

* [**Instructions**](https://www.chiark.greenend.org.uk/~sgtatham/puzzles/doc/mosaic.html#mosaic)

* [**Solver Code**][17]

<details>
  <summary><strong>Rules</strong></summary>
You are given a grid of squares, which you must colour either black or white.

Some squares contain clue numbers. Each clue tells you the number of black squares in the 3×3 region surrounding the clue – including the clue square itself. 
</details>

**Unsolved puzzle**

<img src="https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/mosaic_unsolved.png" alt="Mosaic unsolved" width="500">

Code to utilize this package and solve the puzzle:
```python
import numpy as np
from puzzle_solver import mosaic_solver as solver
board = np.array([
  [' ', ' ', '2', '1', ' ', ' ', ' ', '3', ' ', '4', '2', '2', ' ', ' ', '4'],
  ['3', ' ', ' ', ' ', '4', ' ', ' ', ' ', ' ', ' ', '4', ' ', '2', ' ', ' '],
  ['4', ' ', ' ', '5', ' ', '5', ' ', ' ', '5', ' ', '3', '3', '2', '5', ' '],
  [' ', ' ', '7', ' ', '4', ' ', ' ', '5', ' ', ' ', ' ', ' ', ' ', '5', ' '],
  [' ', '6', '7', ' ', ' ', '4', ' ', '7', ' ', ' ', ' ', ' ', '7', '7', ' '],
  ['3', ' ', ' ', '3', ' ', '5', '7', '7', '6', '4', ' ', '4', ' ', '5', ' '],
  [' ', ' ', '4', ' ', '5', '7', '8', ' ', '5', ' ', '1', '3', '4', '5', ' '],
  [' ', '5', ' ', '4', '3', ' ', ' ', ' ', '7', ' ', '3', ' ', '3', ' ', ' '],
  ['3', ' ', ' ', ' ', ' ', ' ', ' ', '5', ' ', '6', ' ', ' ', ' ', ' ', ' '],
  ['4', ' ', '7', ' ', '5', ' ', ' ', '4', '6', '7', ' ', '3', ' ', '3', ' '],
  ['5', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '6', ' ', ' ', '3', '5', ' ', ' '],
  [' ', ' ', ' ', '5', '4', '5', '3', ' ', '7', ' ', ' ', '5', '6', '6', ' '],
  ['2', ' ', ' ', ' ', '3', '4', ' ', ' ', ' ', '7', ' ', ' ', '7', ' ', '3'],
  ['1', ' ', ' ', '5', ' ', ' ', ' ', '5', ' ', ' ', ' ', '6', ' ', '6', ' '],
  [' ', ' ', '3', ' ', '2', ' ', '3', ' ', '2', ' ', ' ', ' ', ' ', ' ', ' ']
])
binst = solver.Board(board=board)
solutions = binst.solve_and_print()
```
**Script Output**
```python
Solution found
[[' ' 'B' ' ' ' ' ' ' ' ' ' ' ' ' 'B' ' ' 'B' ' ' ' ' 'B' 'B']
 [' ' 'B' ' ' ' ' 'B' 'B' ' ' 'B' 'B' ' ' 'B' ' ' ' ' 'B' 'B']
 [' ' 'B' 'B' ' ' 'B' 'B' ' ' ' ' ' ' 'B' 'B' ' ' ' ' ' ' 'B']
 ['B' 'B' 'B' 'B' ' ' ' ' 'B' 'B' 'B' ' ' ' ' ' ' 'B' ' ' 'B']
 [' ' 'B' 'B' ' ' ' ' 'B' ' ' 'B' 'B' 'B' ' ' 'B' 'B' 'B' ' ']
 [' ' 'B' ' ' 'B' ' ' 'B' 'B' ' ' 'B' ' ' ' ' 'B' 'B' 'B' 'B']
 ['B' ' ' 'B' ' ' ' ' 'B' 'B' 'B' 'B' ' ' ' ' ' ' ' ' ' ' ' ']
 [' ' ' ' 'B' ' ' 'B' 'B' 'B' 'B' 'B' ' ' ' ' ' ' 'B' ' ' 'B']
 [' ' 'B' 'B' ' ' ' ' ' ' ' ' 'B' 'B' 'B' 'B' 'B' ' ' 'B' ' ']
 ['B' 'B' 'B' 'B' 'B' 'B' ' ' ' ' ' ' 'B' 'B' ' ' ' ' 'B' ' ']
 [' ' 'B' ' ' 'B' 'B' ' ' 'B' ' ' 'B' 'B' ' ' ' ' ' ' 'B' ' ']
 ['B' 'B' ' ' ' ' 'B' ' ' ' ' 'B' 'B' 'B' ' ' 'B' 'B' 'B' 'B']
 [' ' ' ' 'B' ' ' 'B' ' ' 'B' ' ' 'B' 'B' 'B' 'B' 'B' ' ' 'B']
 [' ' ' ' 'B' 'B' ' ' ' ' 'B' ' ' 'B' 'B' ' ' 'B' 'B' ' ' ' ']
 ['B' ' ' 'B' ' ' ' ' 'B' 'B' ' ' ' ' ' ' ' ' ' ' 'B' 'B' 'B']]
Solutions found: 1
status: OPTIMAL
Time taken: 0.01 seconds
```

**Solved puzzle**

<img src="https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/mosaic_solved.png" alt="Mosaic solved" width="500">

---

## Map (Puzzle Type #18)

* [**Play online**](https://www.chiark.greenend.org.uk/~sgtatham/puzzles/js/map.html)

* [**Instructions**](https://www.chiark.greenend.org.uk/~sgtatham/puzzles/doc/map.html#map)

* [**Solver Code**][18]

<details>
  <summary><strong>Rules</strong></summary>
You are given a map consisting of a number of regions. Your task is to colour each region with one of four colours, in such a way that no two regions sharing a boundary have the same colour. You are provided with some regions already coloured, sufficient to make the remainder of the solution unique, and these cannot be changed.

Only regions which share a length of border are required to be different colours. Two regions which meet at only one point (i.e. are diagonally separated) may be the same colour. 
</details>

**Unsolved puzzle**

<img src="https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/map_unsolved.png" alt="Map unsolved" width="500">

Code to utilize this package and solve the puzzle:
```python
from puzzle_solver import map_solver as solver
regions = {
  0: {1, 11, 12, 27},
  1: {11, 12, 13, 6, 2},
  2: {3, 4, 6, 7, 9, 10},
  # ...
  # ...
  37: {38, 46, 49, 51, 54, 59, 60, 61},
  38: {44, 45, 49, 51, 53, 58, 59},
  39: {40, 46},
  40: {55, 56},
  41: {42, 47},
  42: {48},
  # ...
  # ...
  # ommited for brevity ; this was a pain to type out by hand
}
fixed_colors = {
  0: 'Y', 3: 'R', 7: 'Y', 14: 'Y', 15: 'R', 16: 'Y', 20: 'G', 32: 'B', 33: 'Y', 34: 'R', 35: 'G',
  36: 'B', 39: 'G', 43: 'G', 47: 'R', 55: 'B', 60: 'R', 64: 'G', 66: 'Y', 67: 'G', 73: 'G', 74: 'G',
}
binst = solver.Board(regions=regions, fixed_colors=fixed_colors)
solutions = binst.solve_and_print()
```
**Script Output**
```python
Solution found
{0: 'Y', 1: 'R', 2: 'G', 3: 'R', 4: 'B', 5: 'G', 6: 'B', 7: 'Y', 8: 'R', 9: 'Y', 10: 'B', 11: 'G', 12: 'B', 13: 'G', 14: 'Y', 15: 'R', 16: 'Y', 17: 'R', 18: 'G', 19: 'B', 20: 'G', 21: 'Y', 22: 'R', 23: 'Y', 24: 'Y', 25: 'B', 26: 'R', 27: 'G', 28: 'G', 29: 'B', 30: 'B', 31: 'R', 32: 'B', 33: 'Y', 34: 'R', 35: 'G', 36: 'B', 37: 'G', 38: 'B', 39: 'G', 40: 'Y', 41: 'Y', 42: 'R', 43: 'G', 44: 'R', 45: 'Y', 46: 'Y', 47: 'R', 48: 'Y', 49: 'Y', 50: 'G', 51: 'R', 52: 'R', 53: 'Y', 54: 'B', 55: 'B', 56: 'G', 57: 'B', 58: 'R', 59: 'Y', 60: 'R', 61: 'B', 62: 'B', 63: 'Y', 64: 'G', 65: 'R', 66: 'Y', 67: 'G', 68: 'B', 69: 'R', 70: 'Y', 71: 'R', 72: 'B', 73: 'G', 74: 'G'}
Solutions found: 1
status: OPTIMAL
Time taken: 0.01 seconds
```

**Solved puzzle**

<img src="https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/map_solved.png" alt="Map solved" width="500">

---

## Pearl (Puzzle Type #19)

* [**Play online**](https://www.chiark.greenend.org.uk/~sgtatham/puzzles/js/pearl.html)

* [**Instructions**](https://www.chiark.greenend.org.uk/~sgtatham/puzzles/doc/pearl.html#pearl)

* [**Solver Code**][19]

<details>
  <summary><strong>Rules</strong></summary>
You have a grid of squares. Your job is to draw lines between the centres of horizontally or vertically adjacent squares, so that the lines form a single closed loop. In the resulting grid, some of the squares that the loop passes through will contain corners, and some will be straight horizontal or vertical lines. (And some squares can be completely empty – the loop doesn't have to pass through every square.)

Some of the squares contain black and white circles, which are clues that the loop must satisfy.

A black circle in a square indicates that that square is a corner, but neither of the squares adjacent to it in the loop is also a corner.

A white circle indicates that the square is a straight edge, but at least one of the squares adjacent to it in the loop is a corner.

(In both cases, the clue only constrains the two squares adjacent in the loop, that is, the squares that the loop passes into after leaving the clue square. The squares that are only adjacent in the grid are not constrained.)
</details>

**Unsolved puzzle**

<img src="https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/pearl_unsolved.png" alt="Pearl unsolved" width="500">

Code to utilize this package and solve the puzzle:
```python
import numpy as np
from puzzle_solver import pearl_solver as solver
board = np.array([
  ['B', ' ', ' ', 'W', ' ', ' ', 'W', ' ', 'B', ' ', ' ', 'B'],
  [' ', ' ', ' ', 'B', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '],
  [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'B', ' ', ' ', ' '],
  [' ', 'B', ' ', 'B', ' ', 'W', ' ', 'B', ' ', 'B', 'W', ' '],
  [' ', ' ', 'B', ' ', 'B', ' ', ' ', ' ', ' ', ' ', ' ', ' '],
  [' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', 'W', ' ', ' ', 'B'],
  [' ', ' ', 'B', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '],
  ['B', ' ', ' ', ' ', ' ', 'B', 'B', ' ', ' ', ' ', ' ', 'B'],
])
binst = solver.Board(board)
solutions = binst.solve_and_print()
```
**Script Output**
```python
Solution found
[['┏━' '━━' '━━' '━━' '━┒' '┏━' '━━' '━┒' '┏━' '━━' '━━' '━┒']
 ['┃ ' '┏━' '━━' '━┒' '┗━' '━┛' '┏━' '━┛' '┃ ' '┏━' '━┒' '┃ ']
 ['┗━' '━┛' '  ' '┃ ' '┏━' '━┒' '┗━' '━━' '━┛' '┃ ' '┃ ' '┃ ']
 ['  ' '┏━' '━━' '━┛' '┃ ' '┃ ' '  ' '┏━' '━━' '━┛' '┃ ' '┃ ']
 ['  ' '┃ ' '┏━' '━━' '━┛' '┗━' '━┒' '┃ ' '┏━' '━┒' '┗━' '━┛']
 ['┏━' '━┛' '┃ ' '  ' '┏━' '━┒' '┃ ' '┃ ' '┃ ' '┗━' '━━' '━┒']
 ['┃ ' '  ' '┗━' '━━' '━┛' '┃ ' '┃ ' '┗━' '━┛' '  ' '  ' '┃ ']
 ['┗━' '━━' '━━' '━━' '━━' '━┛' '┗━' '━━' '━━' '━━' '━━' '━┛']]
Solutions found: 1
status: OPTIMAL
Time taken: 0.98 seconds
```

**Solved puzzle**

<img src="https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/pearl_solved.png" alt="Pearl solved" width="500">

---

## Bridges (Puzzle Type #20)

* [**Play online**](https://www.chiark.greenend.org.uk/~sgtatham/puzzles/js/bridges.html)

* [**Instructions**](https://www.chiark.greenend.org.uk/~sgtatham/puzzles/doc/bridges.html#bridges)

* [**Solver Code**][20]

<details>
  <summary><strong>Rules</strong></summary>
You have a set of islands distributed across the playing area. Each island contains a number. Your aim is to connect the islands together with bridges, in such a way that:

  - Bridges run horizontally or vertically.
  - The number of bridges terminating at any island is equal to the number written in that island.
  - Two bridges may run in parallel between the same two islands, but no more than two may do so.
  - No bridge crosses another bridge.
  - All the islands are connected together.

There are some configurable alternative modes, which involve changing the parallel-bridge limit to something other than 2
</details>

**Unsolved puzzle**

<img src="https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/bridges_unsolved.png" alt="Bridges unsolved" width="500">

Code to utilize this package and solve the puzzle:
```python
import numpy as np
from puzzle_solver import bridges_solver as solver
board = np.array([
  [' ', ' ', ' ', ' ', ' ', '1', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '3'],
  ['2', ' ', ' ', ' ', ' ', ' ', ' ', '4', ' ', ' ', '4', ' ', ' ', '2', ' '],
  [' ', ' ', ' ', '2', ' ', '4', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '],
  [' ', '1', ' ', ' ', ' ', ' ', ' ', ' ', '1', ' ', '2', ' ', ' ', ' ', '4'],
  [' ', '2', ' ', '3', ' ', '6', ' ', '4', ' ', ' ', '3', ' ', '1', ' ', ' '],
  ['2', ' ', ' ', ' ', '2', ' ', ' ', ' ', '1', ' ', ' ', '2', ' ', ' ', ' '],
  [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '],
  ['2', ' ', ' ', ' ', ' ', ' ', '5', ' ', ' ', '3', ' ', '4', ' ', ' ', ' '],
  [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '],
  [' ', '3', ' ', ' ', ' ', '3', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '],
  [' ', ' ', '2', ' ', '2', ' ', ' ', ' ', ' ', ' ', ' ', '5', ' ', ' ', '4'],
  [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '],
  [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '],
  [' ', ' ', '1', ' ', ' ', '2', ' ', ' ', ' ', '1', ' ', '2', ' ', ' ', ' '],
  [' ', '4', ' ', ' ', '4', ' ', '3', ' ', ' ', ' ', '4', ' ', ' ', ' ', '4'],
])
binst = solver.Board(board)
solutions = binst.solve_and_print()
```
**Script Output**

Note that the four numbers indicate how many bridges in the 4 directions (right, left, down, up) respectively.
```python
Solution found
|    |    |    |    |    |1000|    |    |    |    |    |    |    |    |0120|

|1010|    |    |    |    |    |    |2110|    |    |2200|    |    |0200|    |

|    |    |    |2000|    |0220|    |    |    |    |    |    |    |    |    |

|    |0010|    |    |    |    |    |    |1000|    |1100|    |    |    |0112|

|    |1001|    |2100|    |2202|    |1201|    |    |1110|    |0100|    |    |

|1001|    |    |    |1100|    |    |    |0100|    |    |0020|    |    |    |

|    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |

|2000|    |    |    |    |    |2210|    |    |0210|    |0022|    |    |    |

|    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |

|    |1020|    |    |    |0120|    |    |    |    |    |    |    |    |    |

|    |    |1010|    |0110|    |    |    |    |    |    |1022|    |    |0121|

|    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |

|    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |

|    |    |0001|    |    |0002|    |    |    |0001|    |0002|    |    |    |

|    |2002|    |    |1201|    |1101|    |    |    |2101|    |    |    |0202|

Solutions found: 1
status: OPTIMAL
Time taken: 0.01 seconds
```

**Solved puzzle**

<img src="https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/bridges_solved.png" alt="Bridges solved" width="500">

---

## Inertia (Puzzle Type #21)

This solver is a bit different from the other solvers in this repo because this game does not have a unique solution (you simply move the ball to collect all the gems).

Thus the solver was developed with the additional much harder goal of collecting all the gems with the least number of moves.

It does so using the following high level steps:

1. Model the board as a directed graph where the cells are nodes and legal moves as directed edges with unit cost. Each gem has to a group of edges where traversing any one of them collects that gem.
2. Model step (1) as a [Generalized Traveling Salesman Problem (GTSP)](https://en.wikipedia.org/wiki/Set_TSP_problem), where each gem’s edge group forms a cluster.
3. Apply the [Noon–Bean transformation](https://deepblue.lib.umich.edu/bitstream/handle/2027.42/6834/ban3102.0001.001.pdf?sequence=5) **(Noon & Bean, 1991)** to convert the GTSP from step (2) into an equivalent Asymmetric TSP (ATSP) that can be solved with OR-Tools’ routing solver. (Noon-Bean transformation is mentioned but not described in the [TSP wikipedia page](https://en.wikipedia.org/wiki/Travelling_salesman_problem).)
4. Use a [Vehicle Routing Problem (VRP)](https://en.wikipedia.org/wiki/Vehicle_routing_problem) solver using the [OR-Tools VRP solver](https://developers.google.com/optimization/routing/routing_tasks) to solve the ATSP.

This achieves a final sequence of moves that is empirically always faster than the website's solution.

* [**Play online**](https://www.chiark.greenend.org.uk/~sgtatham/puzzles/js/inertia.html)

* [**Instructions**](https://www.chiark.greenend.org.uk/~sgtatham/puzzles/doc/inertia.html#inertia)

* [**Solver Code**][21]

<details>
  <summary><strong>Rules</strong></summary>
You are a small green ball sitting in a grid full of obstacles. Your aim is to collect all the gems without running into any mines.

You can move the ball in any orthogonal or diagonal direction. Once the ball starts moving, it will continue until something stops it. A wall directly in its path will stop it (but if it is moving diagonally, it will move through a diagonal gap between two other walls without stopping). Also, some of the squares are ‘stops’; when the ball moves on to a stop, it will stop moving no matter what direction it was going in. Gems do not stop the ball; it picks them up and keeps on going.

Running into a mine is fatal. Even if you picked up the last gem in the same move which then hit a mine, the game will count you as dead rather than victorious. 
</details>

**Unsolved puzzle**

<img src="https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/inertia_unsolved.png" alt="Inertia unsolved" width="500">

Code to utilize this package and solve the puzzle:

(Note: there is a script that parses a screenshot of the board and outputs the below array that the solver uses. The script uses classical computer vision techniques and is called `parse_map.py`)
```python
import numpy as np
from puzzle_solver import inertia_solver as solver
board = np.array([
  ['O', 'O', 'M', ' ', 'G', 'O', 'G', 'O', ' ', ' ', 'M', ' ', ' ', 'O', 'G', 'G', 'W', 'O', 'O', 'O'],
  ['O', ' ', 'W', ' ', 'W', 'O', 'G', 'M', ' ', ' ', ' ', 'G', 'M', 'O', 'W', 'G', ' ', 'M', 'M', 'O'],
  ['O', 'M', 'O', 'O', ' ', 'M', ' ', 'W', 'W', 'M', 'G', 'W', ' ', ' ', 'G', ' ', 'W', 'G', 'O', 'G'],
  ['O', ' ', 'O', 'M', 'G', 'O', 'W', 'G', 'M', 'O', ' ', ' ', 'G', 'G', 'G', ' ', 'M', 'W', 'M', 'O'],
  ['M', 'M', 'O', 'G', ' ', 'W', ' ', ' ', 'O', 'G', ' ', 'M', 'M', ' ', 'W', 'W', ' ', 'W', 'W', 'O'],
  ['G', ' ', 'G', 'W', 'M', 'W', 'W', ' ', 'G', 'G', 'W', 'M', 'G', 'G', ' ', 'G', 'O', 'O', 'M', 'M'],
  ['M', ' ', 'M', ' ', 'W', 'W', 'M', 'M', 'M', 'O', 'M', 'G', 'O', 'M', 'M', 'W', 'B', 'O', 'W', 'M'],
  ['G', 'G', ' ', 'W', 'M', 'M', 'W', 'O', 'W', 'G', 'W', 'O', 'O', 'M', ' ', 'W', 'W', 'G', 'G', 'M'],
  [' ', 'M', 'M', ' ', ' ', ' ', 'G', 'G', 'M', 'O', 'M', 'O', 'M', 'G', 'W', 'M', 'W', ' ', 'O', ' '],
  ['G', ' ', 'M', ' ', ' ', ' ', 'W', 'O', 'W', 'W', 'M', 'M', 'G', 'W', ' ', ' ', 'W', 'M', 'G', 'W'],
  ['G', 'O', 'M', 'M', 'G', 'M', 'W', 'O', 'O', 'G', 'W', 'M', 'M', 'G', 'G', ' ', 'O', ' ', 'W', 'W'],
  ['G', 'G', 'W', 'G', 'M', ' ', 'G', 'W', 'W', ' ', 'G', ' ', 'O', 'W', 'G', 'G', 'O', ' ', 'M', 'M'],
  ['W', 'M', 'O', ' ', 'W', 'O', 'O', 'M', 'M', 'O', 'G', 'W', ' ', 'G', 'O', 'G', 'G', 'O', 'O', 'W'],
  ['W', 'W', 'W', ' ', 'W', 'O', 'W', 'M', 'O', 'M', 'G', 'O', 'O', ' ', ' ', 'W', 'W', 'G', 'W', 'W'],
  ['O', 'W', 'O', 'M', 'O', 'G', ' ', 'O', 'O', 'M', 'O', ' ', 'M', 'M', 'O', 'G', 'W', 'G', 'M', ' '],
  ['M', 'G', 'O', 'G', 'O', 'G', 'O', 'G', ' ', 'W', 'W', 'G', 'O', ' ', 'W', 'M', 'G', ' ', 'W', ' ']
])
start_pos, edges, edges_to_direction, gems_to_edges = solver.parse_nodes_and_edges(board)
optimal_walk = solver.solve_optimal_walk(start_pos, edges, gems_to_edges)
moves = solver.get_moves_from_walk(optimal_walk, edges_to_direction, verbose=True)
```
**Script Output**

Note that the output is the sequence of moves to collect all the gems. This particular solution is 106 moves, which is 15 moves better than the website's solution.
```python
number of moves 106
↗ ↖ ↖ ↙ ↙ ↖ ↖ ↙ → ↘ 
↙ → ↖ → ↙ ↓ → ↘ ↗ ↓
↘ → ↘ ↓ ↗ ↓ ↑ → ↗ ↖
↑ ↗ ↑ ↗ → ↓ ← ↙ ↖ ↗
↓ ↙ ↙ ↑ ← ↘ ↙ ↓ → ↘
↘ ↙ ↖ ↙ ↗ ↘ ↗ ↘ ↑ ↘
↖ ↑ ↗ → → ↘ → ↘ ↗ ↑
← ↑ ↖ ↖ ↗ → ↘ ↓ ↖ ←
↖ ↓ ← ↓ ↓ ↑ ↖ → ↗ ↗
↘ ↘ ↙ ↘ ↓ ↗ ↖ ↘ ↙ ←
↘ ↖ ↗ ↑ ↗ →
Time taken: 13.92 seconds
```

**Solved puzzle**

This picture won't mean much as the game is about the sequence of moves not the final frame as shown here.

<img src="https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/inertia_solved.png" alt="Inertia solved" width="500">

---

## Guess (Puzzle Type #22)

* [**Play online**](https://www.chiark.greenend.org.uk/~sgtatham/puzzles/js/guess.html)

* [**Instructions**](https://www.chiark.greenend.org.uk/~sgtatham/puzzles/doc/guess.html#guess)

* [**Solver Code**][22]

<details>
  <summary><strong>Rules</strong></summary>
You have a set of coloured pegs, and have to reproduce a predetermined sequence of them (chosen by the computer) within a certain number of guesses.

Each guess gets marked with the number of correctly-coloured pegs in the correct places (in black), and also the number of correctly-coloured pegs in the wrong places (in white). 
</details>


Unlike most other puzzles in this repo, 'Guess' is very different. Similar to minesweeper, Guess is a limited information dynamic puzzle where the next best move depends on information revealed by previous moves (The similarities to minesweeper stop here).

The solver is designed to take the state of the board at any timestep and always gives the next optimal guess. This might seem like an impossible task at first but it's actually not too bad. The optimal guess is defined to be the one that maximizes the Shannon entropy (i.e. maximizes the expected information gain).

The steps below formaly describe the algorithm that is also used in [this amazing 3Blue1Brown video](https://www.youtube.com/watch?v=v68zYyaEmEA) on solving Wordle using information theory. Where 3Blue1Browne describes the same steps below but for a slightly harder problem to solve wordle (a very similar game). The video intuitively justifies this algorithm and builds it from scratch using basic intuition.

To formalize the algorithm, let's first define our three inputs as
- $N :=$ the number of pegs (the length of every guess) 
  - must have $N \geq 1$ and by default $N = 4$ in the game
- $C :=$ the set of possible colors 
  - what actually matters is $|C|$, the number of possible choices for each peg, i.e. the number of colors
  - by default in the game, $C = \{R,Y,G,B,O,P\}$ (six distinct symbols; only $|C|$ matters) for Red, Yellow, Green, Blue, Orange, and Purple.
- $\mathrm{MR} := ((m_1, r_1), (m_2, r_2), ..., (m_k, r_k))$ be the sequence of previous guesses and results where $(m_i, r_i)$ is the previous guess and result at round $i$ and $k\geq 0$ is the number of previous guesses the player has made 
  - Note that $m_i$ has length $N$ and each element is $\in C$ by definition
  - $r_i$ is a triplet of non-negative integers that sum to $N$ by definition. This corresponds to counts of exact-match positions, color-only matches, and non-matches (visualized as black, white, and grey dots)

The algorithm is as follows

1. Define $G$ as the set of every possible guess that can be made

   $$G := \{(c_1, \dots, c_N) \mid \forall i \in \{1, \dots, N\},\ c_i \in C \}$$

    1. Note that $|G| = |C|^N$

    2. Note that $m_i \in G$ for all $i \in \{1, 2, ..., k\}$ by definition.

2. Define $T$ as the set of every possible result triplet 

    $$T := \{(t_1, t_2, t_3) \in \mathbb{N}_0^3 : t_1 + t_2 + t_3 = N\}$$

    1. Note that $r_i \in T$ for all $i \in \{1, 2, ..., k\}$ by definition.
    2. Note that $|T|=\binom{N+2}{2}$ (stars-and-bars)
    3. By default, $N = 4$ in the game so $|T|=15$

3. Define $f : G \times G \to T$ by $f(g_{\text{guess}}, g_{\text{truth}}) = t$ as the result triplet $(t_1, t_2, t_3)$ obtained when guessing $g_{\text{guess}}$ against ground truth $g_{\text{truth}}$. It is trivial to algorithmically make this function which simply counts from $g_1$ and $g_2.$ Look at the function `get_triplets` for a naive implementation of this.

4. Define $S$ as the subset of $G$ that is consistent with the previous guesses $m_i$ and results $r_i$

    $$
    S := \{g \in G : \forall i \in \{1, 2, ..., k\}, f(m_i, g) = r_i\}
    $$
    1. Note that if there aren't previous guesses ($\mathrm{MR} = \emptyset$) then $S = G$
    2. Note that if $S = \emptyset$ then something is wrong with the previous guesses $\mathrm{MR}$ and there is no possible solution to the puzzle. The algorithm stops here and informs the user that the puzzle is unsolvable with the given guesses $\mathrm{MR}$ and that this should never happen unless there is a typo in the guesses $\mathrm{MR}$ (which is usually the case).

5. For each possible guess $g \in G$ and each triplet $t \in T$, count the number of possible solutions $s \in S$ that result in the triplet $t$ when guessing $g$. i.e.

    $$D(g, t) := |\{s \in S: f(g, s) = t\}|$$

6. Calculate the entropy for each possible guess $g \in G$ as the sum of probability times the self-information for every triplet $t \in T$. i.e.

    $$H : G \to \mathbb{R}, \quad H(g) = -\sum_{t \in T} P(t \mid g) \log_2 P(t \mid g)$$

   1. where $P(t \mid g) = \frac{D(g, t)}{|S|}$
   2. By convention, terms with $P(t \mid g)=0$ contribute $0$ to the sum (interpreting $0\log 0 := 0)$.

7. Return the guess $g \in G$ that maximizes the entropy $H(g)$ (to break ties, choose $g$ that is also in $S$ such that it's possibly the correct solution as well, break further ties arbitrarily).
   1. i.e. return any $g^*\in (\mathrm{argmax}_{g\in G}\ H(g) \cap S)$ if exists
   2. otherwise return any $g\in \mathrm{argmax}_{g\in G}\ H(g)$.


If you are at all interested in the above steps and want to understand more, 
I highly recommend watching [this amazing 3Blue1Brown video](https://www.youtube.com/watch?v=v68zYyaEmEA) on solving Wordle using information theory where he describes the same steps but a bits more complicated problem to solve Wordle (a very similar game).

Below is an example of how to utilize the solver while in the middle of a puzzle.

(This is the only solver that under the hood does not utilize any packages besides numpy)

**Unsolved puzzle**

Let's say we start and made two guesses to end up with the following puzzle:

<img src="https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/guess_1.png" alt="Guess Pre Move" width="500">

Code to utilize this package and solve the puzzle:

We encode the puzzle as a Board object then retreive the optimal next guess:
```python
from puzzle_solver import guess_solver as solver
binst = solver.Board()
binst.add_guess(('R', 'Y', 'G', 'B'), (1, 1, 2))  # 1 black dot, 1 white dot, 2 grey dots
binst.add_guess(('R', 'G', 'O', 'P'), (0, 2, 2))  # 0 black dots, 2 white dots, 2 grey dots
binst.best_next_guess()
```

Note: the three numbers in each guess is the result of the guess: (# of black dots, # of white dots, # of grey dots)

Note: by default, the board will have 4 circles and 6 possible colors (R: Red, Y: Yellow, G: Green, B: Blue, O: Orange, P: Purple) but both of these are optional parameters to the Board to change behavior.

**Script Output 1/2**

Note that the output is next optimal guess that has the maximum Shannon entropy.
```python
out of 1296 possible ground truths, only 57 are still possible.
max entropy guess is: ['P', 'Y', 'Y', 'G'] with entropy 3.4511
```

So we make our next guess as (Purple, Yellow, Yellow, Green) and let's say we get this result: (2 black, 1 white, 1 grey)

<img src="https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/guess_2.png" alt="Guess Post 1 Move" width="500">

So we input that again to the solver to retreive the next optimal guess:

```python
from puzzle_solver import guess_solver as solver
binst = solver.Board()
binst.add_guess(('R', 'Y', 'G', 'B'), (1, 1, 2))  # 1 black dot, 1 white dot, 2 grey dots
binst.add_guess(('R', 'G', 'O', 'P'), (0, 2, 2))  # 0 black dots, 2 white dots, 2 grey dots
binst.add_guess(('P', 'Y', 'Y', 'G'), (2, 1, 1))  # 2 black dots, 1 white dot, 1 grey dot
binst.best_next_guess()
```

**Script Output 2/2**

```python
out of 1296 possible ground truths, only 3 are still possible.
max entropy guess is: ['G', 'Y', 'Y', 'O'] with entropy 1.5850
```

So we make our fourth guess as (Green, Yellow, Yellow, Orange) 

When we input the guess, we see that we correctly solve the puzzle!

<img src="https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/guess_3.png" alt="Guess Post 2 Moves" width="500">

Note that in this case, the correct guess was among multiple possible guesses

In the case when there's only one possible choice left, the solver will inform you that it's the garunteed solution.

---

## Chess Range (Puzzle Type #23)

* [**Play online**](https://www.puzzle-chess.com/chess-ranger-11/)

* [**Solver Code**][23]

<details>
  <summary><strong>Rules</strong></summary>

You are given a chess board with $N$ pieces distributed on it. Your aim is to make $N-1$ sequence of moves where each move is a legal chess move and captures another piece.

- Pieces move as standard chess pieces.
- You can perform only capture moves. A move that does not capture another piece is not allowed.
- You are allowed to capture the king.
- The goal is to end up with one single piece on the board. 

</details>

**Unsolved puzzle**

<img src="https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/chess_range_unsolved.png" alt="Chess range unsolved" width="500">

Code to utilize this package and solve the puzzle:

(Note that this puzzle does not typically have a unique solution. Thus, we specify here that we only want the first valid solution that the solver finds.)

```python
from puzzle_solver import chess_range_solver as solver
# algebraic notation
board = ['Qe7', 'Nc6', 'Kb6', 'Pb5', 'Nf5', 'Pg4', 'Rb3', 'Bc3', 'Pd3', 'Pc2', 'Rg2']
binst = solver.Board(board)
solutions = binst.solve_and_print(max_solutions=1)
```
**Script Output**

The output is in the form of "pos -> pos" where "pos" is the algebraic notation of the position.

```python
Solution found
['Rg2->Pc2', 'Rc2->Bc3', 'Rc3->Pd3', 'Kb6->Pb5', 'Pg4->Nf5', 'Rd3->Rb3', 'Rb3->Kb5', 'Nc6->Qe7', 'Ne7->Pf5', 'Rb5->Nf5']
Solutions found: 1
status: FEASIBLE
Time taken: 1.16 seconds
```

**Solved puzzle**

<img src="https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/chess_range_solved.png" alt="Chess range solved" width="500">

---

## Chess Solo (Puzzle Type #24)

* [**Play online**](https://www.puzzle-chess.com/solo-chess-11/)

* [**Solver Code**][24]

<details>
  <summary><strong>Rules</strong></summary>

You are given a chess board with $N$ pieces distributed on it. Your aim is to make $N-1$ sequence of moves where each move is a legal chess move and captures another piece and end up with the king as the only piece on the board. You are not allowed to move a piece more than twice.

- Pieces move as standard chess pieces.
- You can perform only capture moves. A move that does not capture another piece is not allowed.
- You can move a piece only twice.
- You are NOT allowed to capture the king.
- The goal is to end up with one single piece (the king) on the board. 

</details>

**Unsolved puzzle**

<img src="https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/chess_solo_unsolved.png" alt="Chess solo unsolved" width="500">

Code to utilize this package and solve the puzzle:

(Note that this puzzle does not typically have a unique solution. Thus, we specify here that we only want the first valid solution that the solver finds.)

```python
from puzzle_solver import chess_solo_solver as solver
# algebraic notation
board = ['Kc6', 'Rc5', 'Rc4', 'Pb3', 'Bd3', 'Pd2', 'Pe3', 'Nf2', 'Ng2', 'Qg3', 'Pg6']
binst = solver.Board(board)
solutions = binst.solve_and_print(max_solutions=1)
```
**Script Output**

The output is in the form of "pos -> pos" where "pos" is the algebraic notation of the position.

```python
Solution found
['Qg3->Pg6', 'Qg6->Bd3', 'Pd2->Pe3', 'Ng2->Pe3', 'Nf2->Qd3', 'Ne3->Rc4', 'Pb3->Nc4', 'Nd3->Rc5', 'Kc6->Nc5', 'Kc5->Pc4']
Solutions found: 1
status: FEASIBLE
Time taken: 0.47 seconds
```

**Solved puzzle**

<img src="https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/chess_solo_solved.png" alt="Chess solo solved" width="500">

---

## Chess Melee (Puzzle Type #25)

* [**Play online**](https://www.puzzle-chess.com/chess-melee-13/)

* [**Solver Code**][25]

<details>
  <summary><strong>Rules</strong></summary>

You are given a chess board with $N$ pieces distributed on it (equal white and black pieces, one more black if $N$ is odd). Your aim is to make $N-1$ sequence of moves where each move is a legal chess move and captures another piece of the opposite color and end up with a single piece on the board. White starts and colors alternate as usual.

- Pieces move as standard chess pieces.
- White moves first.
- You can perform only capture moves. A move that does not capture another piece of the opposite color is not allowed.
- The goal is to end up with one single piece on the board. 

</details>

**Unsolved puzzle**

<img src="https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/chess_melee_unsolved.png" alt="Chess melee unsolved" width="500">

Code to utilize this package and solve the puzzle:

(Note that this puzzle does not typically have a unique solution. Thus, we specify here that we only want the first valid solution that the solver finds.)

```python
from puzzle_solver import chess_melee_solver as solver
# algebraic notation
board = ['Pb7', 'Nc7', 'Bc6', 'Ne6', 'Pb5', 'Rc4', 'Qb3', 'Rf7', 'Rb6', 'Pe5', 'Nc3', 'Pd3', 'Nf3']
colors = ['B', 'B', 'B', 'B', 'B', 'B', 'B', 'W', 'W', 'W', 'W', 'W', 'W']
binst = solver.Board(board, colors)
solutions = binst.solve_and_print()
```
**Script Output**

The output is in the form of "pos -> pos" where "pos" is the algebraic notation of the position.

```python
Solution found
['Rf7->Nc7', 'Ne6->Rc7', 'Pd3->Rc4', 'Qb3->Nc3', 'Pc4->Pb5', 'Qc3->Pe5', 'Nf3->Qe5', 'Nc7->Pb5', 'Ne5->Bc6', 'Pb7->Nc6', 'Rb6->Nb5', 'Pc6->Rb5']
Solutions found: 1
status: OPTIMAL
Time taken: 6.24 seconds
```

**Solved puzzle**

<img src="https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/chess_melee_solved.png" alt="Chess melee solved" width="500">

---

## Thermometers (Puzzle Type #26)

* [**Play online**](https://www.puzzle-thermometers.com/)

* [**Solver Code**][26]

<details>
  <summary><strong>Rules</strong></summary>

You have to fill some thermometers with mercury starting from the bulb and going toward the end without gaps.

The numbers outside the grid show the number of filled cells horizontally and vertically. 

</details>

**Unsolved puzzle**

<img src="https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/thermometers_unsolved.png" alt="Thermometers unsolved" width="500">

Code to utilize this package and solve the puzzle:

```python
from puzzle_solver import thermometers_solver as solver
board = np.array([
  ['R', 'R', 'D', 'R', 'D', 'R', 'X', 'D', 'L', 'X', 'L', 'L', 'L', 'L', 'L'],
  ['D', 'D', 'D', 'U', 'X', 'U', 'X', 'R', 'R', 'R', 'R', 'D', 'D', 'R', 'U'],
  ['D', 'D', 'D', 'U', 'X', 'U', 'U', 'R', 'R', 'R', 'X', 'D', 'D', 'D', 'D'],
  ['X', 'D', 'D', 'U', 'U', 'U', 'L', 'U', 'R', 'R', 'D', 'X', 'D', 'X', 'X'],
  ['X', 'D', 'D', 'U', 'U', 'R', 'R', 'R', 'R', 'X', 'R', 'X', 'D', 'R', 'X'],
  ['U', 'D', 'D', 'U', 'U', 'R', 'X', 'R', 'R', 'R', 'R', 'D', 'D', 'R', 'D'],
  ['U', 'D', 'D', 'R', 'R', 'X', 'R', 'R', 'R', 'R', 'D', 'D', 'R', 'X', 'D'],
  ['U', 'D', 'D', 'U', 'X', 'L', 'X', 'L', 'R', 'X', 'X', 'R', 'X', 'X', 'L'],
  ['U', 'D', 'D', 'R', 'X', 'U', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L'],
  ['X', 'D', 'X', 'U', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'D', 'U'],
  ['U', 'D', 'X', 'U', 'R', 'R', 'X', 'R', 'R', 'R', 'R', 'X', 'X', 'L', 'U'],
  ['U', 'R', 'U', 'U', 'R', 'X', 'R', 'X', 'R', 'X', 'R', 'R', 'R', 'R', 'U'],
  ['U', 'R', 'X', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'X', 'X', 'L'],
  ['U', 'U', 'R', 'R', 'X', 'D', 'R', 'R', 'D', 'R', 'X', 'X', 'L', 'L', 'U'],
  ['U', 'U', 'U', 'L', 'L', 'R', 'X', 'X', 'L', 'U', 'R', 'R', 'R', 'U', 'U'],
])
top = np.array([7, 4, 12, 8, 4, 6, 5, 7, 5, 4, 8, 9, 13, 8, 12])
side = np.array([8, 10, 9, 10, 6, 10, 4, 6, 6, 10, 5, 7, 6, 6, 9])
binst = solver.Board(board=board, top=top, side=side)
solutions = binst.solve_and_print()
```
**Script Output**

```python
Solution found
[['X' 'X' 'X' ' ' ' ' ' ' ' ' 'X' 'X' ' ' ' ' ' ' 'X' 'X' 'X']
 ['X' ' ' 'X' ' ' ' ' ' ' ' ' 'X' 'X' 'X' 'X' 'X' 'X' 'X' 'X']
 ['X' ' ' 'X' 'X' ' ' 'X' 'X' 'X' ' ' ' ' ' ' 'X' 'X' ' ' 'X']
 [' ' ' ' 'X' 'X' ' ' 'X' 'X' 'X' 'X' 'X' 'X' ' ' 'X' ' ' 'X']
 [' ' ' ' 'X' 'X' ' ' ' ' ' ' ' ' ' ' ' ' 'X' ' ' 'X' 'X' 'X']
 [' ' ' ' 'X' 'X' ' ' ' ' ' ' 'X' 'X' 'X' 'X' 'X' 'X' 'X' 'X']
 [' ' ' ' 'X' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' 'X' 'X' ' ' 'X']
 [' ' ' ' 'X' ' ' ' ' ' ' 'X' 'X' ' ' ' ' ' ' 'X' 'X' ' ' 'X']
 [' ' ' ' 'X' ' ' ' ' ' ' ' ' ' ' ' ' ' ' 'X' 'X' 'X' 'X' 'X']
 [' ' ' ' ' ' ' ' 'X' 'X' 'X' 'X' 'X' 'X' 'X' 'X' 'X' ' ' 'X']
 [' ' ' ' ' ' 'X' 'X' 'X' 'X' ' ' ' ' ' ' ' ' ' ' ' ' ' ' 'X']
 ['X' ' ' ' ' 'X' ' ' ' ' ' ' ' ' ' ' ' ' 'X' 'X' 'X' 'X' 'X']
 ['X' 'X' 'X' 'X' 'X' 'X' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ']
 ['X' 'X' 'X' ' ' ' ' 'X' ' ' ' ' ' ' ' ' ' ' ' ' 'X' 'X' ' ']
 ['X' 'X' 'X' 'X' 'X' ' ' ' ' ' ' ' ' ' ' 'X' 'X' 'X' 'X' ' ']]
Solutions found: 1
status: OPTIMAL
Time taken: 0.01 seconds
```

**Solved puzzle**

<img src="https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/thermometers_solved.png" alt="Thermometers solved" width="500">

---

## Aquarium (Puzzle Type #27)

* [**Play online**](https://www.puzzle-aquarium.com/)

* [**Solver Code**][27]

<details>
  <summary><strong>Rules</strong></summary>

The puzzle is played on a rectangular grid divided into blocks called "aquariums"

You have to "fill" the aquariums with water up to a certain level or leave it empty.

The water level in each aquarium is one and the same across its full width

The numbers outside the grid show the number of filled cells horizontally and vertically. 

</details>

**Unsolved puzzle**

<img src="https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/aquarium_unsolved.png" alt="Aquarium unsolved" width="500">

Code to utilize this package and solve the puzzle:

```python
from puzzle_solver import aquarium_solver as solver
board = np.array([
  ['01', '01', '01', '01', '02', '02', '02', '03', '03', '03', '03', '04', '05', '05', '05'],
  ['01', '02', '02', '02', '02', '06', '07', '07', '03', '08', '03', '04', '04', '05', '09'],
  ['01', '01', '02', '11', '06', '06', '06', '12', '12', '08', '13', '13', '13', '09', '09'],
  ['01', '11', '11', '11', '14', '06', '06', '12', '12', '15', '15', '13', '09', '09', '09'],
  ['01', '01', '11', '11', '14', '12', '12', '12', '16', '16', '15', '13', '13', '17', '09'],
  ['45', '11', '11', '14', '14', '12', '42', '42', '42', '15', '15', '13', '13', '17', '18'],
  ['45', '11', '11', '14', '14', '12', '12', '43', '15', '15', '20', '13', '13', '17', '18'],
  ['46', '46', '11', '19', '19', '19', '43', '43', '44', '20', '20', '20', '13', '17', '18'],
  ['46', '22', '23', '23', '23', '19', '43', '21', '21', '24', '24', '24', '25', '17', '17'],
  ['22', '22', '22', '23', '19', '19', '26', '24', '24', '24', '28', '28', '25', '17', '33'],
  ['22', '22', '23', '23', '27', '27', '26', '26', '24', '24', '29', '29', '25', '25', '33'],
  ['22', '22', '35', '27', '27', '26', '26', '26', '26', '30', '30', '30', '25', '34', '34'],
  ['37', '22', '35', '35', '35', '35', '35', '26', '26', '30', '31', '31', '32', '32', '40'],
  ['37', '37', '37', '36', '36', '35', '26', '26', '26', '40', '40', '40', '40', '40', '40'],
  ['37', '37', '37', '37', '35', '35', '38', '38', '39', '39', '40', '40', '40', '41', '41'],
])
top = np.array([6, 6, 5, 3, 3, 4, 7, 6, 9, 6, 3, 4, 9, 6, 7])
side = np.array([3, 5, 1, 2, 5, 3, 10, 10, 5, 3, 7, 3, 7, 8, 12])
binst = solver.Board(board=board, top=top, side=side)
solutions = binst.solve_and_print()
```
**Script Output**

```python
Solution found
[['0' '0' '0' '0' '0' '0' '0' '0' '0' '0' '0' '0' '1' '1' '1']
 ['0' '0' '0' '0' '0' '0' '0' '0' '1' '0' '1' '1' '1' '1' '0']
 ['0' '0' '0' '0' '0' '0' '0' '0' '0' '1' '0' '0' '0' '0' '0']
 ['0' '0' '0' '0' '0' '1' '1' '0' '0' '0' '0' '0' '0' '0' '0']
 ['1' '1' '0' '0' '0' '0' '0' '0' '1' '1' '0' '0' '0' '0' '1']
 ['0' '0' '0' '0' '0' '0' '1' '1' '1' '0' '0' '0' '0' '0' '0']
 ['1' '1' '1' '0' '0' '1' '1' '0' '1' '1' '0' '1' '1' '0' '1']
 ['1' '1' '1' '0' '0' '0' '1' '1' '0' '1' '1' '1' '1' '0' '1']
 ['1' '0' '0' '0' '0' '0' '1' '1' '1' '0' '0' '0' '1' '0' '0']
 ['0' '0' '0' '0' '1' '1' '0' '0' '0' '0' '0' '0' '1' '0' '0']
 ['0' '0' '1' '1' '0' '0' '0' '0' '1' '1' '0' '0' '1' '1' '1']
 ['0' '0' '0' '0' '0' '0' '0' '0' '0' '0' '0' '0' '1' '1' '1']
 ['0' '1' '0' '0' '0' '0' '0' '1' '1' '0' '1' '1' '1' '1' '0']
 ['1' '1' '1' '1' '1' '0' '1' '1' '1' '0' '0' '0' '0' '0' '0']
 ['1' '1' '1' '1' '1' '1' '1' '1' '1' '1' '0' '0' '0' '1' '1']]
Solutions found: 1
status: OPTIMAL
Time taken: 0.02 seconds
```

**Solved puzzle**

<img src="https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/aquarium_solved.png" alt="Aquarium solved" width="500">

---

## Stitches (Puzzle Type #28)

* [**Play online**](https://www.puzzle-stitches.com/)

* [**Solver Code**][28]

<details>
  <summary><strong>Rules</strong></summary>

- Connect each block with ALL its neighbor blocks with exactly 1 "stitch" each.
- A "stitch" connects 2 orthogonally adjacent cells from different blocks.
- 2 stitches cannot share a hole.
- The clues outside the grid indicate the number of holes on that row/column
- For 2÷ puzzles, you have to use 2 stitches to connect neighbor blocks, for 3÷ puzzles - 3 stitches etc.

</details>

**Unsolved puzzle**

<img src="https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/stitches_unsolved.png" alt="Stitches unsolved" width="500">

Code to utilize this package and solve the puzzle:

```python
from puzzle_solver import stitches_solver as solver
board = np.array([
  ["00", "00", "00", "00", "00", "01", "01", "01", "01", "01", "01", "01", "01", "02", "02"],
  ["00", "03", "03", "04", "00", "00", "01", "05", "05", "05", "05", "05", "01", "01", "02"],
  ["00", "03", "04", "04", "04", "00", "05", "05", "05", "05", "05", "05", "05", "05", "02"],
  ["00", "03", "04", "04", "04", "04", "05", "05", "06", "05", "02", "02", "02", "02", "02"],
  ["07", "03", "03", "03", "03", "04", "06", "06", "06", "06", "06", "06", "06", "02", "02"],
  ["07", "07", "07", "03", "03", "04", "04", "06", "08", "08", "08", "06", "02", "02", "02"],
  ["07", "07", "03", "03", "03", "04", "04", "08", "08", "08", "08", "06", "06", "06", "02"],
  ["07", "07", "07", "07", "07", "08", "08", "08", "09", "09", "08", "06", "08", "06", "02"],
  ["10", "10", "07", "07", "09", "09", "09", "09", "09", "09", "08", "08", "08", "11", "02"],
  ["10", "10", "07", "09", "09", "09", "09", "09", "09", "09", "09", "08", "08", "11", "02"],
  ["10", "09", "09", "09", "12", "12", "12", "13", "09", "09", "11", "11", "11", "11", "11"],
  ["10", "10", "10", "09", "12", "12", "12", "13", "09", "11", "11", "11", "13", "13", "11"],
  ["14", "15", "10", "12", "12", "16", "17", "13", "13", "11", "13", "13", "13", "13", "11"],
  ["14", "15", "10", "12", "16", "16", "17", "17", "13", "13", "13", "13", "13", "13", "11"],
  ["14", "15", "15", "12", "16", "16", "17", "17", "17", "17", "17", "13", "13", "13", "13"]
])
top = np.array([6, 6, 9, 5, 3, 8, 9, 3, 1, 4, 4, 1, 4, 8, 5])
side = np.array([0, 10, 6, 4, 4, 1, 5, 8, 2, 6, 5, 11, 4, 3, 7])
binst = solver.Board(board=board, top=top, side=side)
solutions = binst.solve_and_print()
```

Note: `solver.Board` accepts an optional `connection_count=N` parameter to specify the (÷N) stitches puzzle (by default, 1 stitch).

**Script Output**

```python
Solution found
[[' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ']
 ['R' 'L' 'D' 'R' 'L' 'R' 'L' ' ' ' ' ' ' ' ' ' ' 'D' 'R' 'L']
 [' ' ' ' 'U' ' ' ' ' 'R' 'L' ' ' ' ' ' ' ' ' ' ' 'U' 'R' 'L']
 ['D' ' ' ' ' ' ' ' ' 'R' 'L' ' ' ' ' 'D' ' ' ' ' ' ' ' ' ' ']
 ['U' ' ' ' ' ' ' ' ' 'R' 'L' ' ' ' ' 'U' ' ' ' ' ' ' ' ' ' ']
 [' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' 'D' ' ']
 [' ' ' ' 'D' ' ' ' ' ' ' 'R' 'L' ' ' 'D' ' ' ' ' ' ' 'U' ' ']
 [' ' 'D' 'U' ' ' 'R' 'L' ' ' ' ' ' ' 'U' ' ' 'R' 'L' 'D' ' ']
 [' ' 'U' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' 'U' ' ']
 [' ' ' ' 'R' 'L' ' ' ' ' ' ' ' ' ' ' ' ' 'D' ' ' 'R' 'L' 'D']
 [' ' ' ' 'D' ' ' ' ' ' ' 'R' 'L' ' ' ' ' 'U' ' ' ' ' ' ' 'U']
 ['D' 'D' 'U' 'R' 'L' 'D' 'D' 'R' 'L' ' ' ' ' ' ' ' ' 'R' 'L']
 ['U' 'U' ' ' ' ' ' ' 'U' 'U' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ']
 [' ' ' ' 'R' 'L' ' ' ' ' ' ' ' ' ' ' ' ' 'D' ' ' ' ' ' ' ' ']
 ['R' 'L' 'R' 'L' ' ' 'R' 'L' ' ' ' ' ' ' 'U' ' ' ' ' ' ' ' ']]
Solutions found: 1
status: OPTIMAL
Time taken: 0.01 seconds
```

**Solved puzzle**

<img src="https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/stitches_solved.png" alt="Stitches solved" width="500">

---

## Battleships (Puzzle Type #29)

* [**Play online**](https://www.puzzle-battleships.com/)

* [**Solver Code**][29]

<details>
  <summary><strong>Rules</strong></summary>

- You have to find the location of the battleships hidden in the grid. Some battleships may be partially revealed.
- A battleship is a straight line of consecutive black cells.
- The number of the battleships from each size is shown in the legend.
- 2 battleships cannot touch each other (even diagonally)
- The numbers outside the grid show the number of cells occupied by battleships on that row/column.

</details>

**Unsolved puzzle**

<img src="https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/battleships_unsolved.png" alt="Battleships unsolved" width="500">

Code to utilize this package and solve the puzzle:

```python
from puzzle_solver import battleships_solver as solver
board = np.array([
  [' ', ' ', ' ', ' ', ' ', 'S', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '],
  [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'S', ' ', ' ', ' ', ' '],
  [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'O', ' ', ' '],
  [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '],
  [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '],
  ['W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '],
  [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '],
  [' ', ' ', ' ', ' ', ' ', ' ', 'O', ' ', ' ', ' ', ' ', 'W', ' ', ' ', 'R'],
  [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '],
  [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '],
  [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '],
  [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'U', ' ', ' ', ' ', ' '],
  [' ', ' ', ' ', ' ', ' ', ' ', ' ', 'L', ' ', ' ', ' ', ' ', ' ', ' ', ' '],
  [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '],
  [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'S'],
])
top = np.array([2, 2, 4, 2, 1, 2, 1, 2, 4, 1, 3, 2, 5, 2, 2])
side = np.array([1, 2, 1, 1, 0, 7, 0, 9, 2, 2, 5, 1, 3, 0, 1])
ship_counts = {1: 5, 2: 4, 3: 3, 4: 2, 5: 1}
binst = solver.Board(board=board, top=top, side=side, ship_counts=ship_counts)
solutions = binst.solve_and_print()
```


**Script Output**

```python
Solution found
[[' ' ' ' ' ' ' ' ' ' 'S' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ']
 [' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' 'S' ' ' 'S' ' ' ' ']
 [' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' 'S' ' ' ' ']
 [' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' 'S' ' ' ' ']
 [' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ']
 [' ' 'S' 'S' 'S' 'S' ' ' ' ' ' ' ' ' ' ' ' ' 'S' 'S' 'S' ' ']
 [' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ']
 [' ' ' ' 'S' ' ' ' ' 'S' 'S' 'S' 'S' 'S' ' ' ' ' 'S' 'S' 'S']
 ['S' ' ' 'S' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ']
 [' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' 'S' ' ' ' ' 'S' ' ' ' ' ' ']
 ['S' 'S' 'S' 'S' ' ' ' ' ' ' ' ' 'S' ' ' ' ' ' ' ' ' ' ' ' ']
 [' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' 'S' ' ' ' ' ' ' ' ']
 [' ' ' ' ' ' ' ' ' ' ' ' ' ' 'S' 'S' ' ' 'S' ' ' ' ' ' ' ' ']
 [' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ']
 [' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' 'S']]
Solutions found: 1
status: OPTIMAL
Time taken: 0.12 seconds
```

**Solved puzzle**

<img src="https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/battleships_solved.png" alt="Battleships solved" width="500">

---

## Kakurasu (Puzzle Type #30)

* [**Play online**](https://www.puzzle-kakurasu.com/)

* [**Solver Code**][30]

<details>
  <summary><strong>Rules</strong></summary>

The goal is to make some of the cells black in such a way that:

1. The black cells on each row sum up to the number on the right.

2. The black cells on each column sum up to the number on the bottom.

3. If a black cell is first on its row/column its value is 1. If it is second its value is 2 etc. 

</details>

**Unsolved puzzle**

<img src="https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/kakurasu_unsolved.png" alt="Kakurasu unsolved" width="500">

Code to utilize this package and solve the puzzle:

```python
from puzzle_solver import kakurasu_solver as solver
side = np.array([27, 6, 1, 12, 37, 37, 11, 4, 29, 23, 66, 55])
bottom = np.array([22, 1, 25, 36, 10, 22, 25, 35, 32, 28, 45, 45])
binst = solver.Board(side=side, bottom=bottom)
solutions = binst.solve_and_print()
```


**Script Output**

```python
Solution found
[['X' 'X' ' ' 'X' ' ' ' ' ' ' 'X' ' ' ' ' ' ' 'X']
 [' ' ' ' ' ' ' ' ' ' 'X' ' ' ' ' ' ' ' ' ' ' ' ']
 ['X' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ']
 [' ' ' ' ' ' ' ' 'X' ' ' 'X' ' ' ' ' ' ' ' ' ' ']
 [' ' ' ' 'X' 'X' ' ' ' ' ' ' 'X' ' ' 'X' ' ' 'X']
 ['X' ' ' ' ' ' ' 'X' ' ' ' ' 'X' ' ' ' ' 'X' 'X']
 [' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' 'X' ' ']
 [' ' ' ' ' ' 'X' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ']
 [' ' ' ' 'X' ' ' ' ' 'X' ' ' ' ' 'X' ' ' 'X' ' ']
 [' ' ' ' ' ' 'X' ' ' ' ' 'X' ' ' ' ' ' ' ' ' 'X']
 [' ' ' ' 'X' ' ' ' ' 'X' 'X' 'X' 'X' 'X' 'X' 'X']
 ['X' ' ' ' ' 'X' ' ' ' ' ' ' 'X' 'X' 'X' 'X' 'X']]
Solutions found: 1
status: OPTIMAL
Time taken: 0.00 seconds
```

**Solved puzzle**

<img src="https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/kakurasu_solved.png" alt="Kakurasu solved" width="500">

---

## Star Battle (Puzzle Type #31)

* [**Play online**](https://www.puzzle-star-battle.com/)

* [**Solver Code**][31]

<details>
  <summary><strong>Rules</strong></summary>

 You have to place stars on the grid according to the rules:
- 2 stars cannot be adjacent horizontally, vertically or diagonally.
- For 1★ puzzles, you have to place 1 star on each row, column and shape.
- For 2★ puzzles, the stars per row, column and shape must be 2 etc.

</details>

**Unsolved puzzle**

<img src="https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/star_battle_unsolved.png" alt="Star Battle unsolved" width="500">

Code to utilize this package and solve the puzzle:

Note that as usual the board is an id of the shape (id is meaningless, just used to identify one shape), and the `star_count` parameter depenends on the puzzle type.

```python
from puzzle_solver import star_battle_solver as solver
board = np.array([
  ['00', '00', '00', '00', '00', '01', '01', '01', '01', '01', '01', '01', '01', '01', '02', '02', '02', '03', '03', '03', '03', '03', '03', '03', '03'],
  ['00', '01', '00', '01', '01', '01', '01', '01', '01', '01', '04', '04', '01', '02', '02', '02', '02', '05', '05', '05', '05', '05', '05', '03', '03'],
  ['00', '01', '01', '01', '01', '01', '01', '01', '01', '04', '04', '04', '04', '04', '02', '02', '05', '05', '05', '05', '05', '05', '03', '03', '03'],
  ['00', '01', '06', '04', '04', '04', '04', '04', '04', '04', '04', '04', '04', '04', '02', '05', '05', '05', '05', '05', '05', '05', '03', '07', '03'],
  ['00', '01', '06', '06', '06', '06', '06', '06', '06', '04', '04', '04', '04', '02', '02', '02', '02', '02', '05', '05', '05', '05', '05', '07', '03'],
  ['00', '00', '08', '06', '09', '09', '09', '09', '06', '04', '04', '04', '04', '02', '02', '02', '02', '02', '05', '05', '05', '05', '07', '07', '07'],
  ['00', '08', '08', '08', '08', '09', '09', '06', '06', '06', '04', '04', '04', '04', '02', '02', '02', '05', '05', '05', '07', '07', '07', '07', '07'],
  ['00', '00', '08', '08', '08', '09', '09', '09', '09', '06', '10', '10', '10', '10', '02', '02', '02', '05', '11', '11', '11', '11', '07', '07', '07'],
  ['08', '08', '08', '08', '09', '09', '09', '09', '09', '09', '10', '10', '10', '02', '02', '02', '02', '11', '11', '11', '11', '11', '11', '07', '11'],
  ['08', '08', '08', '08', '09', '09', '09', '09', '09', '10', '10', '10', '10', '02', '02', '02', '11', '11', '11', '11', '11', '11', '11', '07', '11'],
  ['08', '08', '08', '09', '09', '09', '09', '09', '10', '10', '10', '10', '10', '12', '12', '12', '12', '11', '11', '11', '11', '11', '11', '11', '11'],
  ['08', '08', '09', '09', '09', '09', '09', '08', '10', '10', '10', '10', '10', '10', '10', '10', '12', '11', '11', '11', '11', '13', '11', '13', '11'],
  ['14', '08', '08', '08', '08', '08', '08', '08', '10', '10', '10', '10', '10', '12', '12', '12', '12', '12', '11', '11', '11', '13', '11', '13', '15'],
  ['14', '14', '14', '14', '16', '08', '16', '16', '17', '10', '10', '10', '10', '10', '10', '10', '10', '12', '13', '13', '13', '13', '13', '13', '15'],
  ['14', '14', '14', '14', '16', '16', '16', '16', '17', '10', '10', '18', '18', '10', '19', '10', '12', '12', '13', '15', '15', '15', '15', '15', '15'],
  ['14', '14', '14', '14', '14', '16', '16', '17', '17', '18', '18', '18', '19', '19', '19', '10', '10', '10', '13', '15', '15', '15', '15', '15', '15'],
  ['14', '14', '14', '16', '16', '16', '16', '17', '18', '18', '20', '20', '19', '21', '19', '19', '19', '19', '13', '15', '15', '15', '15', '15', '15'],
  ['14', '16', '16', '16', '16', '16', '16', '17', '18', '18', '20', '21', '21', '21', '21', '19', '21', '19', '15', '15', '21', '15', '15', '15', '15'],
  ['14', '14', '14', '16', '16', '17', '17', '17', '18', '20', '20', '21', '20', '21', '21', '19', '21', '19', '15', '21', '21', '15', '15', '15', '15'],
  ['14', '14', '14', '16', '16', '16', '17', '17', '18', '18', '20', '20', '20', '20', '21', '21', '21', '21', '21', '21', '15', '15', '22', '22', '15'],
  ['14', '14', '14', '14', '23', '16', '17', '20', '18', '20', '20', '20', '20', '20', '20', '21', '24', '24', '24', '21', '15', '15', '22', '15', '15'],
  ['14', '14', '14', '14', '23', '20', '17', '20', '18', '20', '20', '20', '20', '24', '24', '24', '24', '24', '24', '21', '15', '22', '22', '22', '15'],
  ['14', '23', '23', '14', '23', '20', '20', '20', '18', '20', '20', '20', '20', '24', '24', '24', '24', '24', '24', '24', '24', '24', '24', '22', '15'],
  ['14', '23', '14', '14', '23', '20', '23', '20', '18', '20', '20', '20', '20', '24', '24', '24', '24', '24', '24', '24', '22', '22', '22', '22', '22'],
  ['14', '23', '23', '23', '23', '23', '23', '20', '20', '20', '20', '20', '20', '24', '24', '24', '24', '24', '24', '24', '24', '24', '24', '24', '24']
])
binst = solver.Board(board=board, star_count=6)
solutions = binst.solve_and_print()
```


**Script Output**

```python
Solution found
[' ', ' ', ' ', ' ', '*', ' ', '*', ' ', '*', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '*', ' ', '*', ' ', '*', ' ', ' ', ' '],
['*', ' ', '*', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '*', ' ', '*', ' ', '*', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '*', ' '],
[' ', ' ', ' ', ' ', '*', ' ', '*', ' ', '*', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '*', ' ', '*', ' ', '*', ' ', ' ', ' ', ' '],
['*', ' ', '*', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '*', ' ', '*', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '*', ' ', '*'],
[' ', ' ', ' ', ' ', ' ', '*', ' ', '*', ' ', ' ', ' ', ' ', ' ', ' ', '*', ' ', '*', ' ', '*', ' ', '*', ' ', ' ', ' ', ' '],
[' ', '*', ' ', '*', ' ', ' ', ' ', ' ', ' ', '*', ' ', '*', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '*', ' ', '*'],
[' ', ' ', ' ', ' ', ' ', '*', ' ', '*', ' ', ' ', ' ', ' ', ' ', '*', ' ', '*', ' ', ' ', '*', ' ', '*', ' ', ' ', ' ', ' '],
[' ', '*', ' ', '*', ' ', ' ', ' ', ' ', ' ', '*', ' ', '*', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '*', ' ', '*'],
[' ', ' ', ' ', ' ', ' ', '*', ' ', '*', ' ', ' ', ' ', ' ', ' ', '*', ' ', '*', ' ', '*', ' ', '*', ' ', ' ', ' ', ' ', ' '],
[' ', '*', ' ', '*', ' ', ' ', ' ', ' ', ' ', '*', ' ', '*', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '*', ' ', '*', ' '],
[' ', ' ', ' ', ' ', ' ', '*', ' ', '*', ' ', ' ', ' ', ' ', ' ', '*', ' ', '*', ' ', '*', ' ', '*', ' ', ' ', ' ', ' ', ' '],
['*', ' ', '*', ' ', ' ', ' ', ' ', ' ', ' ', '*', ' ', '*', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '*', ' ', '*', ' '],
[' ', ' ', ' ', ' ', '*', ' ', '*', ' ', ' ', ' ', ' ', ' ', ' ', '*', ' ', '*', ' ', '*', ' ', '*', ' ', ' ', ' ', ' ', ' '],
['*', ' ', '*', ' ', ' ', ' ', ' ', ' ', '*', ' ', '*', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '*', ' ', '*', ' '],
[' ', ' ', ' ', ' ', '*', ' ', '*', ' ', ' ', ' ', ' ', ' ', '*', ' ', '*', ' ', '*', ' ', '*', ' ', ' ', ' ', ' ', ' ', ' '],
[' ', '*', ' ', ' ', ' ', ' ', ' ', ' ', '*', ' ', '*', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '*', ' ', '*', ' ', '*'],
[' ', ' ', ' ', '*', ' ', '*', ' ', ' ', ' ', ' ', ' ', ' ', '*', ' ', '*', ' ', '*', ' ', '*', ' ', ' ', ' ', ' ', ' ', ' '],
[' ', '*', ' ', ' ', ' ', ' ', ' ', '*', ' ', '*', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '*', ' ', '*', ' ', '*'],
[' ', ' ', ' ', '*', ' ', '*', ' ', ' ', ' ', ' ', ' ', '*', ' ', '*', ' ', '*', ' ', '*', ' ', ' ', ' ', ' ', ' ', ' ', ' '],
['*', ' ', ' ', ' ', ' ', ' ', ' ', '*', ' ', '*', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '*', ' ', '*', ' ', '*', ' '],
[' ', ' ', '*', ' ', '*', ' ', ' ', ' ', ' ', ' ', ' ', '*', ' ', '*', ' ', '*', ' ', '*', ' ', ' ', ' ', ' ', ' ', ' ', ' '],
['*', ' ', ' ', ' ', ' ', ' ', '*', ' ', '*', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '*', ' ', '*', ' ', '*', ' '],
[' ', ' ', '*', ' ', '*', ' ', ' ', ' ', ' ', ' ', '*', ' ', '*', ' ', '*', ' ', '*', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '],
[' ', ' ', ' ', ' ', ' ', ' ', '*', ' ', '*', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '*', ' ', '*', ' ', '*', ' ', '*'],
[' ', '*', ' ', '*', ' ', ' ', ' ', ' ', ' ', ' ', '*', ' ', '*', ' ', '*', ' ', '*', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ']
Solutions found: 1
status: OPTIMAL
Time taken: 0.38 seconds
```

**Solved puzzle**

<img src="https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/star_battle_solved.png" alt="Star Battle solved" width="500">

---

## Star Battle Shapeless (Puzzle Type #32)

* [**Play online**](https://www.puzzle-star-battle.com/?size=14)

* [**Solver Code**][32]

<details>
  <summary><strong>Rules</strong></summary>

 You have to place stars on the grid according to the rules:
- 2 stars cannot be adjacent horizontally, vertically or diagonally.
- For 1★ puzzles, you have to place 1 star on each row and column.
- For 2★ puzzles, the stars per row and column must be 2 etc.
- Some places begin with a black square and cannot have stars placed on them.

</details>

**Unsolved puzzle**

<img src="https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/star_battle_shapeless_unsolved.png" alt="Star Battle Shapeless unsolved" width="500">

Code to utilize this package and solve the puzzle:

The `star_count` parameter depenends on the puzzle type.

```python
from puzzle_solver import star_battle_shapeless_solver as shapeless_solver
board = np.array([
  [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '], 
  ['B', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '], 
  ['B', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'B', ' '], 
  ['B', 'B', ' ', ' ', 'B', ' ', ' ', ' ', ' ', ' '], 
  ['B', 'B', ' ', ' ', 'B', ' ', ' ', ' ', ' ', ' '], 
  [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '], 
  [' ', ' ', ' ', ' ', 'B', ' ', ' ', ' ', 'B', ' '], 
  ['B', ' ', ' ', ' ', ' ', ' ', ' ', 'B', 'B', ' '], 
  ['B', 'B', ' ', ' ', ' ', ' ', 'B', 'B', 'B', ' '], 
  ['B', ' ', ' ', ' ', 'B', ' ', ' ', ' ', ' ', ' '], 
])
binst = shapeless_solver.Board(board=board, star_count=2)
solutions = binst.solve_and_print()
```


**Script Output**

```python
Solution found
['*', ' ', ' ', ' ', '*', ' ', ' ', ' ', ' ', ' '],
[' ', ' ', ' ', ' ', ' ', ' ', '*', ' ', '*', ' '],
[' ', ' ', '*', ' ', '*', ' ', ' ', ' ', ' ', ' '],
[' ', ' ', ' ', ' ', ' ', ' ', ' ', '*', ' ', '*'],
[' ', ' ', '*', ' ', ' ', '*', ' ', ' ', ' ', ' '],
['*', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '*', ' '],
[' ', ' ', ' ', '*', ' ', ' ', '*', ' ', ' ', ' '],
[' ', '*', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '*'],
[' ', ' ', ' ', '*', ' ', '*', ' ', ' ', ' ', ' '],
[' ', '*', ' ', ' ', ' ', ' ', ' ', '*', ' ', ' ']
Solutions found: 1
status: OPTIMAL
Time taken: 0.02 seconds
```

**Solved puzzle**

<img src="https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/star_battle_shapeless_solved.png" alt="Star Battle Shapeless solved" width="500">

---

## Lits (Puzzle Type #33)

* [**Play online**](https://www.puzzle-lits.com/)

* [**Solver Code**][33]

<details>
  <summary><strong>Rules</strong></summary>

 You have to place one tetromino in each region in such a way that:
- 2 tetrominoes of matching types cannot touch each other horizontally or vertically. Rotations and reflections count as matching.
- The shaded cells should form a single connected area.
- 2x2 shaded areas are not allowed.

* Tetromino is a shape made of 4 connected cells. There are 5 types of tetrominoes, which are usually named L, I, T, S and O, based on their shape. The O tetromino is not used in this puzzle because it is a 2x2 shape, which is not allowed. 

</details>

Note: The solver is capable of solving variations where the puzzle pieces the made up of more than 4 cells (e.g., pentominoes for 5 with `polyomino_degrees=5`, or hexominoes for 6 with `polyomino_degrees=6`, etc.). By default the degree is set to 4 thus only tetrominoes are used.

**Unsolved puzzle**

<img src="https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/lits_unsolved.png" alt="Lits unsolved" width="500">

Code to utilize this package and solve the puzzle:

```python
board = np.array([
  ['00', '00', '00', '01', '01', '02', '02', '02', '03', '03', '03', '04', '04', '05', '06', '07', '07', '08', '08', '09'],
  ['00', '00', '00', '00', '01', '02', '03', '03', '03', '10', '04', '04', '05', '05', '06', '07', '08', '08', '09', '09'],
  ['11', '11', '11', '01', '01', '02', '02', '03', '10', '10', '04', '04', '05', '06', '06', '07', '07', '07', '09', '12'],
  ['11', '13', '13', '13', '01', '02', '03', '03', '03', '10', '04', '04', '06', '06', '06', '07', '12', '09', '09', '12'],
  ['11', '11', '11', '13', '14', '14', '03', '15', '15', '10', '04', '04', '06', '16', '16', '12', '12', '09', '12', '12'],
  ['17', '13', '13', '13', '14', '14', '03', '03', '15', '15', '04', '04', '16', '16', '16', '12', '12', '12', '12', '18'],
  ['17', '13', '19', '13', '20', '14', '03', '03', '15', '04', '04', '16', '16', '21', '21', '22', '23', '23', '23', '18'],
  ['17', '17', '19', '19', '20', '20', '03', '03', '24', '24', '24', '25', '25', '25', '21', '22', '23', '23', '18', '18'],
  ['17', '26', '19', '19', '20', '20', '20', '24', '24', '20', '20', '25', '25', '21', '21', '22', '22', '23', '23', '18'],
  ['26', '26', '26', '19', '19', '20', '20', '20', '20', '20', '25', '25', '21', '21', '21', '21', '21', '23', '27', '18'],
  ['28', '28', '28', '29', '29', '29', '29', '20', '20', '30', '30', '25', '31', '32', '32', '32', '21', '27', '27', '27'],
  ['28', '33', '28', '28', '28', '28', '29', '34', '34', '35', '30', '30', '31', '31', '31', '32', '32', '36', '36', '27'],
  ['28', '33', '33', '28', '28', '29', '29', '34', '34', '35', '35', '30', '31', '31', '31', '32', '36', '36', '27', '27'],
  ['28', '33', '37', '37', '28', '29', '34', '34', '35', '35', '38', '38', '39', '39', '40', '40', '40', '40', '27', '41'],
  ['28', '37', '37', '37', '42', '34', '34', '34', '43', '38', '38', '38', '39', '39', '44', '44', '40', '40', '27', '41'],
  ['37', '37', '42', '42', '42', '34', '34', '43', '43', '43', '38', '39', '39', '39', '44', '44', '27', '27', '27', '41'],
  ['45', '45', '45', '42', '46', '34', '34', '34', '34', '38', '38', '47', '47', '47', '44', '44', '44', '27', '27', '41'],
  ['48', '45', '45', '46', '46', '46', '46', '34', '49', '49', '49', '47', '44', '44', '44', '27', '44', '50', '27', '27'],
  ['48', '48', '45', '46', '46', '51', '46', '52', '52', '49', '49', '53', '44', '53', '44', '27', '50', '50', '50', '27'],
  ['48', '51', '51', '51', '51', '51', '52', '52', '52', '49', '53', '53', '53', '53', '44', '27', '27', '27', '27', '27']
])
binst = solver.Board(board)
solutions = binst.solve_then_constrain()  # solve_then_constrain NOT solve_and_print (to use #1 instead of #2 in https://github.com/google/or-tools/discussions/3347, its faster in this case)
```

**Script Output**

```python
Solution found
[
  ['X', 'X', 'X', ' ', ' ', 'X', 'X', 'X', ' ', ' ', ' ', ' ', ' ', 'X', ' ', 'X', ' ', 'X', 'X', ' '],
  [' ', 'X', ' ', ' ', 'X', 'X', ' ', ' ', ' ', 'X', ' ', ' ', 'X', 'X', ' ', 'X', 'X', 'X', ' ', ' '],
  ['X', 'X', 'X', 'X', 'X', ' ', ' ', 'X', 'X', 'X', ' ', ' ', 'X', ' ', ' ', 'X', ' ', ' ', 'X', ' '],
  ['X', ' ', ' ', ' ', 'X', ' ', 'X', 'X', ' ', 'X', 'X', ' ', 'X', 'X', 'X', 'X', ' ', 'X', 'X', ' '],
  [' ', ' ', ' ', 'X', 'X', 'X', 'X', ' ', 'X', ' ', 'X', ' ', 'X', ' ', ' ', ' ', ' ', 'X', ' ', ' '],
  ['X', ' ', 'X', 'X', ' ', 'X', ' ', ' ', 'X', 'X', 'X', ' ', 'X', 'X', ' ', 'X', 'X', 'X', 'X', ' '],
  ['X', ' ', ' ', 'X', ' ', 'X', ' ', ' ', 'X', ' ', 'X', 'X', 'X', ' ', ' ', 'X', ' ', ' ', ' ', 'X'],
  ['X', 'X', ' ', 'X', ' ', 'X', ' ', ' ', 'X', 'X', ' ', 'X', ' ', ' ', ' ', 'X', ' ', 'X', ' ', 'X'],
  [' ', 'X', ' ', 'X', ' ', 'X', 'X', 'X', 'X', ' ', ' ', 'X', ' ', 'X', 'X', 'X', 'X', 'X', 'X', 'X'],
  ['X', 'X', 'X', 'X', 'X', 'X', ' ', ' ', ' ', ' ', 'X', 'X', 'X', 'X', ' ', ' ', ' ', 'X', ' ', 'X'],
  [' ', ' ', ' ', ' ', ' ', 'X', 'X', ' ', ' ', ' ', 'X', ' ', ' ', 'X', 'X', 'X', ' ', ' ', ' ', ' '],
  [' ', 'X', ' ', 'X', 'X', ' ', 'X', ' ', ' ', 'X', 'X', 'X', ' ', 'X', ' ', 'X', ' ', 'X', 'X', ' '],
  [' ', 'X', 'X', ' ', 'X', ' ', 'X', ' ', ' ', 'X', ' ', 'X', 'X', 'X', 'X', ' ', 'X', 'X', ' ', ' '],
  [' ', 'X', ' ', ' ', 'X', ' ', 'X', ' ', 'X', 'X', 'X', ' ', 'X', ' ', 'X', 'X', 'X', ' ', ' ', 'X'],
  [' ', 'X', 'X', ' ', 'X', ' ', 'X', ' ', 'X', ' ', 'X', ' ', 'X', 'X', ' ', ' ', 'X', ' ', ' ', 'X'],
  ['X', 'X', ' ', 'X', 'X', ' ', 'X', 'X', 'X', 'X', 'X', ' ', ' ', 'X', ' ', ' ', 'X', 'X', ' ', 'X'],
  [' ', 'X', 'X', 'X', ' ', ' ', 'X', ' ', ' ', ' ', 'X', 'X', 'X', 'X', 'X', ' ', ' ', 'X', 'X', 'X'],
  ['X', ' ', 'X', ' ', 'X', 'X', 'X', ' ', 'X', 'X', ' ', 'X', ' ', ' ', 'X', ' ', ' ', 'X', ' ', ' '],
  ['X', 'X', 'X', ' ', 'X', ' ', ' ', 'X', ' ', 'X', ' ', 'X', ' ', ' ', 'X', ' ', 'X', 'X', 'X', ' '],
  ['X', ' ', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', ' ', 'X', ' ', ' ', ' ', ' ', ' '],
]
Solutions found: 1
Time taken: 0.38 seconds
```

**Solved puzzle**

<img src="https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/lits_solved.png" alt="Lits solved" width="500">

---

---

## Why SAT / CP-SAT?

Because it is extremely faster than naive solutions and many pencil puzzles can be modeled with:

- **Boolean decisions** (e.g., black/white, bulb/no-bulb)
- **Linear constraints** (counts, separations, adjacency)
- **All-different / visibility / reachability** constraints

This repo builds those constraints in Python and uses SAT/CP-SAT (e.g., OR-Tools) to search efficiently. It both demonstrates the modeling and provides usable solvers.

---

## Testing

To run the tests, simply run the following (to create a fresh conda environment and install the dev dependencies):

```bash
conda create -p ./env python=3.11
conda activate ./env
pip install -r requirements.txt
pip install -r requirements-dev.txt
pytest
```

the `pytest.ini` file is used to configure the pytest command to use `-n 4` to have 4 workers.

## Contributing

Issues and PRs welcome!


* Python version `>= 3.9` required.
* Keep puzzle folders self-contained (solver, README.md, other files if needed).
* Prefer small, readable encodings with comments explaining each constraint.
* If you add a new puzzle:

  1. Create a directory in `src/puzzle_solver/puzzles/<name>/`,
  2. Add a minimal test script in `tests/test_<name>.py`,
  3. Document the modeling in code comments,

### Build and push to PyPI

1. First make sure all the tests pass (see [Testing](#testing))
2. Update the version in `src/puzzle_solver/__init__.py`
3. Build and push:
   1. Bash: `rm dist/* && python -m build --sdist --wheel && python -m twine upload --repository pypi dist/*`
   2. Powershell: `rm dist/*; if ($?) { python -m build --sdist --wheel; if ($?) { python -m twine upload --repository pypi dist/* } }`


[1]: https://github.com/Ar-Kareem/puzzle_solver/tree/master/src/puzzle_solver/puzzles/nonograms "puzzle_solver/src/puzzle_solver/puzzles/nonograms at master · Ar-Kareem/puzzle_solver · GitHub"
[2]: https://github.com/Ar-Kareem/puzzle_solver/tree/master/src/puzzle_solver/puzzles/sudoku "puzzle_solver/src/puzzle_solver/puzzles/sudoku at master · Ar-Kareem/puzzle_solver · GitHub"
[3]: https://github.com/Ar-Kareem/puzzle_solver/tree/master/src/puzzle_solver/puzzles/minesweeper "puzzle_solver/src/puzzle_solver/puzzles/minesweeper at master · Ar-Kareem/puzzle_solver · GitHub"
[22]: https://github.com/Ar-Kareem/puzzle_solver/tree/master/src/puzzle_solver/puzzles/guess "puzzle_solver/src/puzzle_solver/puzzles/guess at master · Ar-Kareem/puzzle_solver · GitHub"
[4]: https://github.com/Ar-Kareem/puzzle_solver/tree/master/src/puzzle_solver/puzzles/dominosa "puzzle_solver/src/puzzle_solver/puzzles/dominosa at master · Ar-Kareem/puzzle_solver · GitHub"
[5]: https://github.com/Ar-Kareem/puzzle_solver/tree/master/src/puzzle_solver/puzzles/light_up "puzzle_solver/src/puzzle_solver/puzzles/light_up at master · Ar-Kareem/puzzle_solver · GitHub"
[18]: https://github.com/Ar-Kareem/puzzle_solver/tree/master/src/puzzle_solver/puzzles/map "puzzle_solver/src/puzzle_solver/puzzles/map at master · Ar-Kareem/puzzle_solver · GitHub"
[21]: https://github.com/Ar-Kareem/puzzle_solver/tree/master/src/puzzle_solver/puzzles/inertia "puzzle_solver/src/puzzle_solver/puzzles/inertia at master · Ar-Kareem/puzzle_solver · GitHub"
[6]: https://github.com/Ar-Kareem/puzzle_solver/tree/master/src/puzzle_solver/puzzles/tents "puzzle_solver/src/puzzle_solver/puzzles/tents at master · Ar-Kareem/puzzle_solver · GitHub"
[20]: https://github.com/Ar-Kareem/puzzle_solver/tree/master/src/puzzle_solver/puzzles/bridges "puzzle_solver/src/puzzle_solver/puzzles/bridges at master · Ar-Kareem/puzzle_solver · GitHub"
[7]: https://github.com/Ar-Kareem/puzzle_solver/tree/master/src/puzzle_solver/puzzles/filling "puzzle_solver/src/puzzle_solver/puzzles/filling at master · Ar-Kareem/puzzle_solver · GitHub"
[8]: https://github.com/Ar-Kareem/puzzle_solver/tree/master/src/puzzle_solver/puzzles/keen "puzzle_solver/src/puzzle_solver/puzzles/keen at master · Ar-Kareem/puzzle_solver · GitHub"
[9]: https://github.com/Ar-Kareem/puzzle_solver/tree/master/src/puzzle_solver/puzzles/towers "puzzle_solver/src/puzzle_solver/puzzles/towers at master · Ar-Kareem/puzzle_solver · GitHub"
[10]: https://github.com/Ar-Kareem/puzzle_solver/tree/master/src/puzzle_solver/puzzles/singles "puzzle_solver/src/puzzle_solver/puzzles/singles at master · Ar-Kareem/puzzle_solver · GitHub"
[11]: https://github.com/Ar-Kareem/puzzle_solver/tree/master/src/puzzle_solver/puzzles/magnets "puzzle_solver/src/puzzle_solver/puzzles/magnets at master · Ar-Kareem/puzzle_solver · GitHub"
[12]: https://github.com/Ar-Kareem/puzzle_solver/tree/master/src/puzzle_solver/puzzles/signpost "puzzle_solver/src/puzzle_solver/puzzles/signpost at master · Ar-Kareem/puzzle_solver · GitHub"
[13]: https://github.com/Ar-Kareem/puzzle_solver/tree/master/src/puzzle_solver/puzzles/range "puzzle_solver/src/puzzle_solver/puzzles/range at master · Ar-Kareem/puzzle_solver · GitHub"
[19]: https://github.com/Ar-Kareem/puzzle_solver/tree/master/src/puzzle_solver/puzzles/pearl "puzzle_solver/src/puzzle_solver/puzzles/pearl at master · Ar-Kareem/puzzle_solver · GitHub"
[14]: https://github.com/Ar-Kareem/puzzle_solver/tree/master/src/puzzle_solver/puzzles/undead "puzzle_solver/src/puzzle_solver/puzzles/undead at master · Ar-Kareem/puzzle_solver · GitHub"
[15]: https://github.com/Ar-Kareem/puzzle_solver/tree/master/src/puzzle_solver/puzzles/unruly "puzzle_solver/src/puzzle_solver/puzzles/unruly at master · Ar-Kareem/puzzle_solver · GitHub"
[16]: https://github.com/Ar-Kareem/puzzle_solver/tree/master/src/puzzle_solver/puzzles/tracks "puzzle_solver/src/puzzle_solver/puzzles/tracks at master · Ar-Kareem/puzzle_solver · GitHub"
[17]: https://github.com/Ar-Kareem/puzzle_solver/tree/master/src/puzzle_solver/puzzles/mosaic "puzzle_solver/src/puzzle_solver/puzzles/mosaic at master · Ar-Kareem/puzzle_solver · GitHub"
[23]: https://github.com/Ar-Kareem/puzzle_solver/tree/master/src/puzzle_solver/puzzles/chess_range "puzzle_solver/src/puzzle_solver/puzzles/chess_range at master · Ar-Kareem/puzzle_solver · GitHub"
[24]: https://github.com/Ar-Kareem/puzzle_solver/tree/master/src/puzzle_solver/puzzles/chess_range#chess-solo-puzzle-type-24 "puzzle_solver/src/puzzle_solver/puzzles/chess_range at master · Ar-Kareem/puzzle_solver · GitHub"
[25]: https://github.com/Ar-Kareem/puzzle_solver/tree/master/src/puzzle_solver/puzzles/chess_range#chess-melee-puzzle-type-25 "puzzle_solver/src/puzzle_solver/puzzles/chess_range at master · Ar-Kareem/puzzle_solver · GitHub"
[26]: https://github.com/Ar-Kareem/puzzle_solver/tree/master/src/puzzle_solver/puzzles/thermometers "puzzle_solver/src/puzzle_solver/puzzles/thermometers at master · Ar-Kareem/puzzle_solver · GitHub"
[27]: https://github.com/Ar-Kareem/puzzle_solver/tree/master/src/puzzle_solver/puzzles/aquarium "puzzle_solver/src/puzzle_solver/puzzles/aquarium at master · Ar-Kareem/puzzle_solver · GitHub"
[28]: https://github.com/Ar-Kareem/puzzle_solver/tree/master/src/puzzle_solver/puzzles/stitches "puzzle_solver/src/puzzle_solver/puzzles/stitches at master · Ar-Kareem/puzzle_solver · GitHub"
[29]: https://github.com/Ar-Kareem/puzzle_solver/tree/master/src/puzzle_solver/puzzles/battleships "puzzle_solver/src/puzzle_solver/puzzles/battleships at master · Ar-Kareem/puzzle_solver · GitHub"
[30]: https://github.com/Ar-Kareem/puzzle_solver/tree/master/src/puzzle_solver/puzzles/kakurasu "puzzle_solver/src/puzzle_solver/puzzles/kakurasu at master · Ar-Kareem/puzzle_solver · GitHub"
[31]: https://github.com/Ar-Kareem/puzzle_solver/tree/master/src/puzzle_solver/puzzles/star_battle "puzzle_solver/src/puzzle_solver/puzzles/star_battle at master · Ar-Kareem/puzzle_solver · GitHub"
[32]: https://github.com/Ar-Kareem/puzzle_solver/tree/master/src/puzzle_solver/puzzles/star_battle_shapeless "puzzle_solver/src/puzzle_solver/puzzles/star_battle_shapeless at master · Ar-Kareem/puzzle_solver · GitHub"
[33]: https://github.com/Ar-Kareem/puzzle_solver/tree/master/src/puzzle_solver/puzzles/lits "puzzle_solver/src/puzzle_solver/puzzles/lits at master · Ar-Kareem/puzzle_solver · GitHub"

            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "multi-puzzle-solver",
    "maintainer": null,
    "docs_url": null,
    "requires_python": ">=3.9",
    "maintainer_email": null,
    "keywords": "puzzle, solver, sat, cp-sat, google-or-tools, efficient, logic, puzzle-solver, puzzle-solver-python, sudoku, nonograms, minesweeper, dominosa, lightup, map, inertia, tents, towers, tracks, undead, unruly, bridges, pearl, range, signpost, singles, magnets",
    "author": "Ar-Kareem",
    "author_email": null,
    "download_url": "https://files.pythonhosted.org/packages/ad/7d/690f87f5f2e9bd81e672358b191a53393344c3f67893cc11c7723aa75f41/multi_puzzle_solver-0.9.13.tar.gz",
    "platform": null,
    "description": "# Python Puzzle Solver\r\n\r\nSolve numerous classical logic puzzles automatically in Python. \r\n\r\n## Quick Start\r\n\r\nInstall\r\n\r\n```bash\r\npip install multi-puzzle-solver\r\n```\r\n\r\nUse:\r\n\r\n```python\r\nfrom puzzle_solver import nonograms_solver\r\nsolver = nonograms_solver.Board(top=[[2], [3], [1], [1, 1]], side=[[3], [1], [2, 1], [1]])\r\nsolutions = solver.solve_and_print()\r\n```\r\n\r\nOutput:\r\n\r\n```python\r\nSolution found\r\n[[' ' 'B' 'B' 'B']\r\n [' ' 'B' ' ' ' ']\r\n ['B' 'B' ' ' 'B']\r\n ['B' ' ' ' ' ' ']]\r\nSolutions found: 1\r\nstatus: OPTIMAL\r\nTime taken: 0.00 seconds\r\n```\r\n(Note: Printing can be turned off by setting `verbose=False`)\r\n\r\n## Introduction\r\n\r\nThe aim of this repo is to provide very efficient solvers (i.e. not brute force solvers) for many popular pencil logic puzzles like Nonograms, Sudoku, Minesweeper, and many more lesser known ones.\r\n\r\nIf you happen to have a puzzle similar to the ones listed below and want to solve it (or see how many potential solutions a partially covered board has), then this repo is perfect for you.\r\n\r\nThe simple use-case of this repo is if you want to solve a puzzle given the state of the board. But the other interesting use-cases is if you want to check if removing a clue would still result in a unique solution or would make the puzzle ambiguous and have multiple solutions.\r\n\r\n**Why?** There are countless python packages that can solve the popular puzzles below, so a valid question to ask is **why would I want to use this package and why did you create it?**. The answer is that there are multiple problems with most of those packages which this package solves which are:\r\n\r\n1. **Sophisticated solvers:** A lot of available online solvers are incredibly inefficient as they implement naive algorithms that brute force and backtrack through all possible solutions. This package solves that issue as all the solvers included here never use naive algorithms and instead use a very efficient CP-SAT solver which is a more sophisticated solver than any one person could possibly write.\r\n2. **Numerous puzzles:** Most of the available python solvers are only designed for a single type of puzzle and each one requires a different way to encode the input and extract the solution. This package solves both those issues as this package provides solvers for many puzzles all with a similar interface that encodes the input and extracts the solution in a similar way.\r\n3. **Esoteric puzzles:** Most packages you can find online are only designed for popular puzzles. This package partially solves this issue by providing solvers for many puzzles. I'm open to suggestions for implementing solvers for more puzzles.\r\n4. **All possible solutions:** The available solvers often lack uniqueness checks and simply stop at the first possible solution without verifying uniqueness or completeness. This package supports checking whether puzzles are uniquely solvable, ambiguous, or unsolvable for all the puzzles.\r\n\r\nPlay the original puzzles online: https://www.chiark.greenend.org.uk/~sgtatham/puzzles\r\n\r\nAlmost all the solvers in this repo use the CP-SAT solver from Google OR-Tools.\r\n\r\n\r\n<div align=\"center\">\r\n\r\n\r\n## \ud83d\udd79\ufe0f Puzzle Gallery\r\n\r\nThese are all the puzzles that are implemented in this repo. <br> Click on any of them to go to that section of the README.\r\n\r\n<table>\r\n<tr>\r\n  <td align=\"center\">\r\n    <a href=\"#nonograms-puzzle-type-1\"><b>Nonograms</b><br><br>\r\n      <img src=\"https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/nonogram_solved.png\" alt=\"Nonograms\" width=\"140\">\r\n    </a>\r\n  </td>\r\n  <td align=\"center\">\r\n    <a href=\"#sudoku-puzzle-type-2\"><b>Sudoku</b><br><br>\r\n      <img src=\"https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/sudoku_solved.png\" alt=\"Sudoku\" width=\"140\">\r\n    </a>\r\n  </td>\r\n  <td align=\"center\">\r\n    <a href=\"#minesweeper-puzzle-type-3\"><b>Minesweeper</b><br><br>\r\n      <img src=\"https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/minesweeper_pre.png\" alt=\"Minesweeper\" width=\"140\">\r\n    </a>\r\n  </td>\r\n  <td align=\"center\">\r\n    <a href=\"#dominosa-puzzle-type-4\"><b>Dominosa</b><br><br>\r\n      <img src=\"https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/dominosa_solved.png\" alt=\"Dominosa\" width=\"140\">\r\n    </a>\r\n  </td>\r\n  <td align=\"center\">\r\n    <a href=\"#light-up-puzzle-type-5\"><b>Light Up</b><br><br>\r\n      <img src=\"https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/lightup_solved.png\" alt=\"Light Up\" width=\"140\">\r\n    </a>\r\n  </td>\r\n</tr>\r\n<tr>\r\n  <td align=\"center\">\r\n    <a href=\"#tents-puzzle-type-6\"><b>Tents</b><br><br>\r\n      <img src=\"https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/tents_solved.png\" alt=\"Tents\" width=\"140\">\r\n    </a>\r\n  </td>\r\n  <td align=\"center\">\r\n    <a href=\"#filling-puzzle-type-7\"><b>Filling</b><br><br>\r\n      <img src=\"https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/filling_solved.png\" alt=\"Filling\" width=\"140\">\r\n    </a>\r\n  </td>\r\n  <td align=\"center\">\r\n    <a href=\"#keen-puzzle-type-8\"><b>Keen</b><br><br>\r\n      <img src=\"https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/keen_solved.png\" alt=\"Keen\" width=\"140\">\r\n    </a>\r\n  </td>\r\n  <td align=\"center\">\r\n    <a href=\"#towers-puzzle-type-9\"><b>Towers</b><br><br>\r\n      <img src=\"https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/towers_solved.png\" alt=\"Towers\" width=\"140\">\r\n    </a>\r\n  </td>\r\n  <td align=\"center\">\r\n    <a href=\"#singles-puzzle-type-10\"><b>Singles</b><br><br>\r\n      <img src=\"https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/singles_solved.png\" alt=\"Singles\" width=\"140\">\r\n    </a>\r\n  </td>\r\n</tr>\r\n<tr>\r\n  <td align=\"center\">\r\n    <a href=\"#magnets-puzzle-type-11\"><b>Magnets</b><br><br>\r\n      <img src=\"https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/magnets_solved.png\" alt=\"Magnets\" width=\"140\">\r\n    </a>\r\n  </td>\r\n  <td align=\"center\">\r\n    <a href=\"#signpost-puzzle-type-12\"><b>Signpost</b><br><br>\r\n      <img src=\"https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/signpost_solved.png\" alt=\"Signpost\" width=\"140\">\r\n    </a>\r\n  </td>\r\n  <td align=\"center\">\r\n    <a href=\"#range-puzzle-type-13\"><b>Range</b><br><br>\r\n      <img src=\"https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/range_solved.png\" alt=\"Range\" width=\"140\">\r\n    </a>\r\n  </td>\r\n  <td align=\"center\">\r\n    <a href=\"#undead-puzzle-type-14\"><b>Undead</b><br><br>\r\n      <img src=\"https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/undead_solved.png\" alt=\"Undead\" width=\"140\">\r\n    </a>\r\n  </td>\r\n  <td align=\"center\">\r\n    <a href=\"#unruly-puzzle-type-15\"><b>Unruly</b><br><br>\r\n      <img src=\"https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/unruly_solved.png\" alt=\"Unruly\" width=\"140\">\r\n    </a>\r\n  </td>\r\n</tr>\r\n<tr>\r\n  <td align=\"center\">\r\n    <a href=\"#tracks-puzzle-type-16\"><b>Tracks</b><br><br>\r\n      <img src=\"https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/tracks_solved.png\" alt=\"Tracks\" width=\"140\">\r\n    </a>\r\n  </td>\r\n  <td align=\"center\">\r\n    <a href=\"#mosaic-puzzle-type-17\"><b>Mosaic</b><br><br>\r\n      <img src=\"https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/mosaic_solved.png\" alt=\"Mosaic\" width=\"140\">\r\n    </a>\r\n  </td>\r\n  <td align=\"center\">\r\n    <a href=\"#map-puzzle-type-18\"><b>Map</b><br><br>\r\n      <img src=\"https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/map_solved.png\" alt=\"Map\" width=\"140\">\r\n    </a>\r\n  </td>\r\n  <td align=\"center\">\r\n    <a href=\"#pearl-puzzle-type-19\"><b>Pearl</b><br><br>\r\n      <img src=\"https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/pearl_solved.png\" alt=\"Pearl\" width=\"140\">\r\n    </a>\r\n  </td>\r\n  <td align=\"center\">\r\n    <a href=\"#bridges-puzzle-type-20\"><b>Bridges</b><br><br>\r\n      <img src=\"https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/bridges_solved.png\" alt=\"Bridges\" width=\"140\">\r\n    </a>\r\n  </td>\r\n</tr>\r\n<tr>\r\n  <td align=\"center\">\r\n    <a href=\"#inertia-puzzle-type-21\"><b>Inertia</b><br><br>\r\n      <img src=\"https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/inertia_unsolved.png\" alt=\"Inertia\" width=\"140\">\r\n    </a>\r\n  </td>\r\n  <td align=\"center\">\r\n    <a href=\"#guess-puzzle-type-22\"><b>Guess</b><br><br>\r\n      <img src=\"https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/guess_3.png\" alt=\"Guess\" width=\"140\">\r\n    </a>\r\n  </td>\r\n  <td align=\"center\">\r\n    <a href=\"#chess-range-puzzle-type-23\"><b>Chess Range</b><br><br>\r\n      <img src=\"https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/chess_range_unsolved.png\" alt=\"Chess range\" width=\"140\">\r\n    </a>\r\n  </td>\r\n  <td align=\"center\">\r\n    <a href=\"#chess-solo-puzzle-type-24\"><b>Chess Solo</b><br><br>\r\n      <img src=\"https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/chess_solo_unsolved.png\" alt=\"Chess solo\" width=\"140\">\r\n    </a>\r\n  </td>\r\n  <td align=\"center\">\r\n    <a href=\"#chess-melee-puzzle-type-25\"><b>Chess Melee</b><br><br>\r\n      <img src=\"https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/chess_melee_unsolved.png\" alt=\"Chess melee\" width=\"140\">\r\n    </a>\r\n  </td>\r\n</tr>\r\n<tr>\r\n  <td align=\"center\">\r\n    <a href=\"#thermometers-puzzle-type-26\"><b>Thermometers</b><br><br>\r\n      <img src=\"https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/thermometers_solved.png\" alt=\"Thermometers\" width=\"140\">\r\n    </a>\r\n  </td>\r\n  <td align=\"center\">\r\n    <a href=\"#aquarium-puzzle-type-27\"><b>Aquarium</b><br><br>\r\n      <img src=\"https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/aquarium_solved.png\" alt=\"Aquarium\" width=\"140\">\r\n    </a>\r\n  </td>\r\n  <td align=\"center\">\r\n    <a href=\"#stitches-puzzle-type-28\"><b>Stitches</b><br><br>\r\n      <img src=\"https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/stitches_solved.png\" alt=\"Stitches\" width=\"140\">\r\n    </a>\r\n  </td>\r\n  <td align=\"center\">\r\n    <a href=\"#battleships-puzzle-type-29\"><b>Battleships</b><br><br>\r\n      <img src=\"https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/battleships_solved.png\" alt=\"Battleships\" width=\"140\">\r\n    </a>\r\n  </td>\r\n  <td align=\"center\">\r\n    <a href=\"#kakurasu-puzzle-type-30\"><b>Kakurasu</b><br><br>\r\n      <img src=\"https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/kakurasu_solved.png\" alt=\"Kakurasu\" width=\"140\">\r\n    </a>\r\n  </td>\r\n</tr>\r\n<tr>\r\n  <td align=\"center\">\r\n    <a href=\"#star-battle-puzzle-type-31\"><b>Star Battle</b><br><br>\r\n      <img src=\"https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/star_battle_solved.png\" alt=\"Star Battle\" width=\"140\">\r\n    </a>\r\n  </td>\r\n  <td align=\"center\">\r\n    <a href=\"#star-battle-shapeless-puzzle-type-32\"><b>Star Battle Shapeless</b><br><br>\r\n      <img src=\"https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/star_battle_shapeless_solved.png\" alt=\"Star Battle Shapeless\" width=\"140\">\r\n    </a>\r\n  </td>\r\n  <td align=\"center\">\r\n    <a href=\"#lits-puzzle-type-33\"><b>Lits</b><br><br>\r\n      <img src=\"https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/lits_solved.png\" alt=\"Lits\" width=\"140\">\r\n    </a>\r\n  </td>\r\n</tr>\r\n</table>\r\n\r\n</div>\r\n\r\n---\r\n\r\n## Table of Contents\r\n\r\n- [Python Puzzle Solver](#python-puzzle-solver)\r\n  - [Quick Start](#quick-start)\r\n  - [Introduction](#introduction)\r\n  - [\ud83d\udd79\ufe0f Puzzle Gallery](#\ufe0f-puzzle-gallery)\r\n  - [Table of Contents](#table-of-contents)\r\n- [Puzzles](#puzzles)\r\n  - [Nonograms (Puzzle Type #1)](#nonograms-puzzle-type-1)\r\n  - [Sudoku (Puzzle Type #2)](#sudoku-puzzle-type-2)\r\n  - [Minesweeper (Puzzle Type #3)](#minesweeper-puzzle-type-3)\r\n  - [Dominosa (Puzzle Type #4)](#dominosa-puzzle-type-4)\r\n  - [Light Up (Puzzle Type #5)](#light-up-puzzle-type-5)\r\n  - [Tents (Puzzle Type #6)](#tents-puzzle-type-6)\r\n  - [Filling (Puzzle Type #7)](#filling-puzzle-type-7)\r\n  - [Keen (Puzzle Type #8)](#keen-puzzle-type-8)\r\n  - [Towers (Puzzle Type #9)](#towers-puzzle-type-9)\r\n  - [Singles (Puzzle Type #10)](#singles-puzzle-type-10)\r\n  - [Magnets (Puzzle Type #11)](#magnets-puzzle-type-11)\r\n  - [Signpost (Puzzle Type #12)](#signpost-puzzle-type-12)\r\n  - [Range (Puzzle Type #13)](#range-puzzle-type-13)\r\n  - [UnDead (Puzzle Type #14)](#undead-puzzle-type-14)\r\n  - [Unruly (Puzzle Type #15)](#unruly-puzzle-type-15)\r\n  - [Tracks (Puzzle Type #16)](#tracks-puzzle-type-16)\r\n  - [Mosaic (Puzzle Type #17)](#mosaic-puzzle-type-17)\r\n  - [Map (Puzzle Type #18)](#map-puzzle-type-18)\r\n  - [Pearl (Puzzle Type #19)](#pearl-puzzle-type-19)\r\n  - [Bridges (Puzzle Type #20)](#bridges-puzzle-type-20)\r\n  - [Inertia (Puzzle Type #21)](#inertia-puzzle-type-21)\r\n  - [Guess (Puzzle Type #22)](#guess-puzzle-type-22)\r\n  - [Chess Range (Puzzle Type #23)](#chess-range-puzzle-type-23)\r\n  - [Chess Solo (Puzzle Type #24)](#chess-solo-puzzle-type-24)\r\n  - [Chess Melee (Puzzle Type #25)](#chess-melee-puzzle-type-25)\r\n  - [Thermometers (Puzzle Type #26)](#thermometers-puzzle-type-26)\r\n  - [Aquarium (Puzzle Type #27)](#aquarium-puzzle-type-27)\r\n  - [Stitches (Puzzle Type #28)](#stitches-puzzle-type-28)\r\n  - [Battleships (Puzzle Type #29)](#battleships-puzzle-type-29)\r\n  - [Kakurasu (Puzzle Type #30)](#kakurasu-puzzle-type-30)\r\n  - [Star Battle (Puzzle Type #31)](#star-battle-puzzle-type-31)\r\n  - [Star Battle Shapeless (Puzzle Type #32)](#star-battle-shapeless-puzzle-type-32)\r\n  - [Lits (Puzzle Type #33)](#lits-puzzle-type-33)\r\n  - [Why SAT / CP-SAT?](#why-sat--cp-sat)\r\n  - [Testing](#testing)\r\n  - [Contributing](#contributing)\r\n    - [Build and push to PyPI](#build-and-push-to-pypi)\r\n\r\n---\r\n\r\n# Puzzles\r\n\r\nThe puzzles that have solvers implemented are listed below. Each puzzle has a simple example input board followed by the code to utilize this package and solve the puzzle, followed by the scripts output, and finally the solved puzzle.\r\n\r\n## Nonograms (Puzzle Type #1)\r\n\r\nCalled \"Pattern\" in the website.\r\n\r\n* [**Play online**](https://www.chiark.greenend.org.uk/~sgtatham/puzzles/js/pattern.html)\r\n\r\n* [**Instructions**](https://www.chiark.greenend.org.uk/~sgtatham/puzzles/doc/pattern.html#pattern)\r\n\r\n* [**Solver Code**][1]\r\n\r\n<details>\r\n  <summary><strong>Rules</strong></summary>\r\nYou have a grid of squares, which must all be filled in either black or white. Beside each row of the grid are listed, in order, the lengths of the runs of black squares on that row; above each column are listed, in order, the lengths of the runs of black squares in that column. Your aim is to fill in the entire grid black or white. \r\n</details>\r\n\r\n**Unsolved puzzle**\r\n\r\n<img src=\"https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/nonogram_unsolved.png\" alt=\"Nonogram unsolved\" width=\"500\">\r\n\r\nCode to utilize this package and solve the puzzle:\r\n```python\r\nfrom puzzle_solver import nonograms_solver as solver\r\ntop_numbers = [\r\n  [8, 2],\r\n  [5, 4],\r\n  [2, 1, 4],\r\n  [2, 4],\r\n  [2, 1, 4],\r\n  [2, 5],\r\n  [2, 8],\r\n  [3, 2],\r\n  [1, 6],\r\n  [1, 9],\r\n  [1, 6, 1],\r\n  [1, 5, 3],\r\n  [3, 2, 1],\r\n  [4, 2],\r\n  [1, 5],\r\n]\r\nside_numbers = [\r\n  [7, 3],\r\n  [7, 1, 1],\r\n  [2, 3],\r\n  [2, 3],\r\n  [3, 2],\r\n  [1, 1, 1, 1, 2],\r\n  [1, 6, 1],\r\n  [1, 9],\r\n  [9],\r\n  [2, 4],\r\n  [8],\r\n  [11],\r\n  [7, 1, 1],\r\n  [4, 3],\r\n  [3, 2],\r\n]\r\nbinst = solver.Board(top=top_numbers, side=side_numbers)\r\nsolutions = binst.solve_and_print()\r\n```\r\n**Script Output**\r\n```python\r\nSolution found\r\nB B B B B B B . B B B . . . .\r\nB B B B B B B . . . . . B . B\r\nB B . . . . . . . . . B B B .\r\nB B . . . . . . . . . . B B B\r\nB B B . . . . . . . . . . B B\r\nB . . . B . B . . B . . . B B\r\nB . . . . . B B B B B B . . B\r\nB . . . . . B B B B B B B B B\r\n. . . . . B B B B B B B B B .\r\n. . . . . B B . B B B B . . .\r\n. . . . B B B B B B B B . . .\r\nB B B B B B B B B B B . . . .\r\nB B B B B B B . . B . B . . .\r\n. B B B B . . . . B B B . . .\r\n. B B B . . . . . . . B B . .\r\nSolutions found: 1\r\nstatus: OPTIMAL\r\nTime taken: 0.04 seconds\r\n```\r\n\r\n**Solved puzzle**\r\n\r\n<img src=\"https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/nonogram_solved.png\" alt=\"Nonogram solved\" width=\"500\">\r\n\r\n---\r\n\r\n## Sudoku (Puzzle Type #2)\r\n\r\nCalled \"Solo\" in the website.\r\n\r\n* [**Play online**](https://www.chiark.greenend.org.uk/~sgtatham/puzzles/js/solo.html)\r\n\r\n* [**Instructions**](https://www.chiark.greenend.org.uk/~sgtatham/puzzles/doc/solo.html#solo)\r\n\r\n* [**Solver Code**][2]\r\n\r\n<details>\r\n  <summary><strong>Rules</strong></summary>\r\nYou have a square grid, which is divided into as many equally sized sub-blocks as the grid has rows. Each square must be filled in with a digit from 1 to the size of the grid, in such a way that\r\n\r\n  - every row contains only one occurrence of each digit\r\n  - every column contains only one occurrence of each digit\r\n  - every block contains only one occurrence of each digit.\r\n\r\nYou are given some of the numbers as clues; your aim is to place the rest of the numbers correctly.\r\n</details>\r\n\r\n**Unsolved puzzle**\r\n\r\n<img src=\"https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/sudoku_unsolved.png\" alt=\"Sudoku unsolved\" width=\"500\">\r\n\r\nCode to utilize this package and solve the puzzle:\r\n```python\r\nimport numpy as np\r\nfrom puzzle_solver import sudoku_solver as solver\r\nboard = np.array([\r\n  [' ', '7', '5', '4',  '9', '1', 'c', 'e',  'd', 'f', ' ', ' ',  '2', ' ', '3', ' '],\r\n  [' ', ' ', ' ', ' ',  'f', 'a', ' ', ' ',  ' ', '6', ' ', 'c',  ' ', ' ', '8', 'b'],\r\n  [' ', ' ', '1', ' ',  ' ', '6', ' ', ' ',  ' ', '9', ' ', ' ',  ' ', 'g', ' ', 'd'],\r\n  [' ', '6', ' ', ' ',  ' ', ' ', ' ', ' ',  ' ', ' ', '5', 'g',  'c', '7', ' ', ' '],\r\n\r\n  ['4', 'a', ' ', ' ',  ' ', ' ', ' ', ' ',  ' ', ' ', ' ', '9',  ' ', ' ', ' ', ' '],\r\n  [' ', 'g', 'f', ' ',  'e', ' ', ' ', '5',  '4', ' ', ' ', '1',  ' ', '9', ' ', '8'],\r\n  [' ', ' ', ' ', ' ',  'a', '3', 'b', '7',  'c', 'g', ' ', '6',  ' ', ' ', ' ', '4'],\r\n  [' ', 'b', ' ', '7',  ' ', ' ', ' ', ' ',  'f', ' ', '3', ' ',  ' ', 'a', ' ', '6'],\r\n\r\n  ['2', ' ', 'a', ' ',  ' ', 'c', ' ', '1',  ' ', ' ', ' ', ' ',  '7', ' ', '6', ' '],\r\n  ['8', ' ', ' ', ' ',  '3', ' ', 'e', 'f',  '7', '5', 'c', 'd',  ' ', ' ', ' ', ' '],\r\n  ['9', ' ', '3', ' ',  '7', ' ', ' ', 'a',  '6', ' ', ' ', '2',  ' ', 'b', '1', ' '],\r\n  [' ', ' ', ' ', ' ',  '4', ' ', ' ', ' ',  ' ', ' ', ' ', ' ',  ' ', ' ', 'e', 'f'],\r\n\r\n  [' ', ' ', 'g', 'd',  '2', '9', ' ', ' ',  ' ', ' ', ' ', ' ',  ' ', ' ', '4', ' '],\r\n  ['a', ' ', 'b', ' ',  ' ', ' ', '5', ' ',  ' ', ' ', 'd', ' ',  ' ', '8', ' ', ' '],\r\n  ['e', '8', ' ', ' ',  '1', ' ', '4', ' ',  ' ', ' ', '6', '7',  ' ', ' ', ' ', ' '],\r\n  [' ', '3', ' ', '9',  ' ', ' ', 'f', '8',  'a', 'e', 'g', '5',  'b', 'c', 'd', ' '],\r\n])\r\nbinst = solver.Board(board=board)\r\nsolutions = binst.solve_and_print()\r\nassert len(solutions) == 1, f'unique solutions != 1, == {len(solutions)}'\r\n```\r\n**Script Output**\r\n```python\r\nSolution found\r\n[['g' '7' '5' '4' '9' '1' 'c' 'e' 'd' 'f' 'b' '8' '2' '6' '3' 'a']\r\n ['3' '9' 'd' 'e' 'f' 'a' '7' 'g' '2' '6' '4' 'c' '5' '1' '8' 'b']\r\n ['b' 'c' '1' '8' '5' '6' '3' '2' 'e' '9' '7' 'a' '4' 'g' 'f' 'd']\r\n ['f' '6' '2' 'a' 'b' '8' 'd' '4' '1' '3' '5' 'g' 'c' '7' '9' 'e']\r\n ['4' 'a' 'e' '3' '8' 'f' '1' '6' '5' 'b' '2' '9' 'g' 'd' 'c' '7']\r\n ['6' 'g' 'f' 'c' 'e' 'd' '2' '5' '4' '7' 'a' '1' '3' '9' 'b' '8']\r\n ['d' '1' '9' '2' 'a' '3' 'b' '7' 'c' 'g' '8' '6' 'e' 'f' '5' '4']\r\n ['5' 'b' '8' '7' 'g' '4' '9' 'c' 'f' 'd' '3' 'e' '1' 'a' '2' '6']\r\n ['2' 'e' 'a' 'b' 'd' 'c' 'g' '1' '3' '8' '9' 'f' '7' '4' '6' '5']\r\n ['8' '4' '6' '1' '3' 'b' 'e' 'f' '7' '5' 'c' 'd' 'a' '2' 'g' '9']\r\n ['9' 'f' '3' 'g' '7' '5' '8' 'a' '6' '4' 'e' '2' 'd' 'b' '1' 'c']\r\n ['c' 'd' '7' '5' '4' '2' '6' '9' 'g' 'a' '1' 'b' '8' '3' 'e' 'f']\r\n ['7' '5' 'g' 'd' '2' '9' 'a' 'b' '8' 'c' 'f' '3' '6' 'e' '4' '1']\r\n ['a' '2' 'b' '6' 'c' 'e' '5' '3' '9' '1' 'd' '4' 'f' '8' '7' 'g']\r\n ['e' '8' 'c' 'f' '1' 'g' '4' 'd' 'b' '2' '6' '7' '9' '5' 'a' '3']\r\n ['1' '3' '4' '9' '6' '7' 'f' '8' 'a' 'e' 'g' '5' 'b' 'c' 'd' '2']]\r\nSolutions found: 1\r\nstatus: OPTIMAL\r\nTime taken: 0.04 seconds\r\n```\r\n\r\n**Solved puzzle**\r\n\r\n<img src=\"https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/sudoku_solved.png\" alt=\"Sudoku solved\" width=\"500\">\r\n\r\n---\r\n\r\n## Minesweeper (Puzzle Type #3)\r\n\r\nThis Minesweeper solver is a bit different from the other solvers in this repo because Minesweeper is a uniquely different type of puzzle. \r\n\r\nIn Minesweeper, you don't solve the puzzle in one go. You need to partially solve the puzzle and get new information to continue. Thus the solver is designed to take the state of the board at any timestep and always gives the most amount of garunteed next steps to take (i.e. garunteed safe positions, garunteed mine positions, and even warns you if you placed a flag in a potentially wrong position).\r\n\r\nThen obviously, once the you act upon the guesses and get the new information, you simply put that new info back into the solver and repeat the process until the puzzle is fully solved. \r\n\r\nBelow is an example of how to utilize the solver while in the middle of a puzzle. (notice how there's an intentionally placed incorrect flag in the example and the solver will warn you about it)\r\n\r\n* [**Play online**](https://www.chiark.greenend.org.uk/~sgtatham/puzzles/js/mines.html)\r\n\r\n* [**Instructions**](https://www.chiark.greenend.org.uk/~sgtatham/puzzles/doc/mines.html#mines)\r\n\r\n* [**Solver Code**][3]\r\n\r\n<details>\r\n  <summary><strong>Rules</strong></summary>\r\nYou have a grid of covered squares, some of which contain mines, but you don't know which. Your job is to uncover every square which does not contain a mine. If you uncover a square containing a mine, you lose. If you uncover a square which does not contain a mine, you are told how many mines are contained within the eight surrounding squares.\r\n\r\nThis game needs no introduction; popularised by Windows, it is perhaps the single best known desktop puzzle game in existence.\r\n\r\nThis version of it has an unusual property. By default, it will generate its mine positions in such a way as to ensure that you never need to guess where a mine is: you will always be able to deduce it somehow. So you will never, as can happen in other versions, get to the last four squares and discover that there are two mines left but you have no way of knowing for sure where they are. \r\n</details>\r\n\r\n**Partially solved puzzle**\r\n\r\n<img src=\"https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/minesweeper_pre.png\" alt=\"Minesweeper partially solved\" width=\"500\">\r\n\r\nCode to utilize this package and solve the puzzle:\r\n```python\r\nimport numpy as np\r\nfrom puzzle_solver import minesweeper_solver as solver\r\nboard = np.array([\r\n  [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '1', '1', '1', '3', 'F', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '],\r\n  [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '2', '2', '1', 'F', '4', 'F', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '],\r\n  [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'F', '2', '1', '3', 'F', '5', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '],\r\n  [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '2', '4', 'F', '3', '0', '3', 'F', 'F', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '],\r\n  [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '3', '4', 'F', '3', '0', '2', 'F', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '],\r\n  [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'F', '4', 'F', '2', '0', '2', '3', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '],\r\n  [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'F', '4', '1', '1', '0', '1', 'F', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '],\r\n  [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'F', '4', '2', '1', '1', '2', '2', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '],\r\n  [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '],\r\n  [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '],\r\n  [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '],\r\n  [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '],\r\n  [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '],\r\n  [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '],\r\n  [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '],\r\n  [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '],\r\n])\r\nmine_count = 30\r\nsafe_positions, new_garuneed_mine_positions, wrong_flag_positions = solver.give_next_guess(board=board, mine_count=mine_count)\r\n```\r\n**Script Output**\r\n\r\nNotice how not only did it output all garunteed new safe and mine positions, it also outputs a warning about the incorrectly placed flag position.\r\n\r\n```python\r\nFound 8 new guaranteed safe positions\r\n{Pos(x=9, y=0), Pos(x=15, y=8), Pos(x=15, y=7), Pos(x=9, y=2), Pos(x=15, y=6), Pos(x=7, y=2), Pos(x=9, y=1), Pos(x=12, y=8)}\r\n----------\r\nFound 4 new guaranteed mine positions\r\n{Pos(x=8, y=2), Pos(x=7, y=5), Pos(x=10, y=0), Pos(x=9, y=8)}\r\n----------\r\nWARNING | WARNING | WARNING | WARNING | WARNING\r\nFound 1 wrong flag positions\r\n{Pos(x=15, y=3)}\r\n----------\r\nTime taken: 0.92 seconds\r\n```\r\n\r\n**Progressed puzzle**\r\n\r\n<img src=\"https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/minesweeper_post.png\" alt=\"Minesweeper progressed\" width=\"500\">\r\n\r\n---\r\n\r\n## Dominosa (Puzzle Type #4)\r\n\r\n* [**Play online**](https://www.chiark.greenend.org.uk/~sgtatham/puzzles/js/dominosa.html)\r\n\r\n* [**Instructions**](https://www.chiark.greenend.org.uk/~sgtatham/puzzles/doc/dominosa.html#dominosa)\r\n\r\n* [**Solver Code**][4]\r\n\r\n<details>\r\n  <summary><strong>Rules</strong></summary>\r\nA normal set of dominoes \u2013 that is, one instance of every (unordered) pair of numbers from 0 to N \u2013 has been arranged irregularly into a rectangle; then the number in each square has been written down and the dominoes themselves removed. \r\n\r\nYour task is to reconstruct the pattern by arranging the set of dominoes to match the provided array of numbers. \r\n</details>\r\n\r\n**Unsolved puzzle**\r\n\r\n<img src=\"https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/dominosa_unsolved.png\" alt=\"Dominosa unsolved\" width=\"500\">\r\n\r\nCode to utilize this package and solve the puzzle:\r\n```python\r\nimport numpy as np\r\nfrom puzzle_solver import dominosa_solver as solver\r\nboard = np.array([\r\n  [6, 8, 2, 7, 1, 3, 3, 4, 6, 6, 0],\r\n  [4, 9, 5, 6, 1, 0, 6, 1, 2, 2, 4],\r\n  [8, 2, 8, 9, 1, 9, 3, 3, 8, 8, 5],\r\n  [1, 1, 7, 3, 4, 7, 0, 8, 7, 7, 7],\r\n  [4, 5, 3, 9, 9, 3, 0, 1, 6, 1, 5],\r\n  [6, 9, 5, 8, 9, 2, 1, 2, 6, 7, 9],\r\n  [2, 7, 4, 3, 5, 5, 9, 6, 4, 0, 9],\r\n  [0, 7, 8, 0, 5, 4, 2, 7, 6, 7, 3],\r\n  [0, 4, 5, 2, 8, 6, 1, 0, 9, 0, 4],\r\n  [0, 8, 8, 3, 2, 1, 3, 2, 5, 5, 4],\r\n])\r\nbinst = solver.Board(board=board)\r\nsolutions = binst.solve_and_print()\r\nassert len(solutions) == 1, f'unique solutions != 1, == {len(solutions)}'\r\n```\r\n**Script Output**\r\n```python\r\nSolution found\r\n[['R' 'L' 'R' 'L' 'D' 'R' 'L' 'R' 'L' 'R' 'L']\r\n ['D' 'D' 'R' 'L' 'U' 'D' 'D' 'D' 'R' 'L' 'D']\r\n ['U' 'U' 'D' 'R' 'L' 'U' 'U' 'U' 'R' 'L' 'U']\r\n ['D' 'D' 'U' 'D' 'D' 'R' 'L' 'D' 'R' 'L' 'D']\r\n ['U' 'U' 'D' 'U' 'U' 'R' 'L' 'U' 'D' 'D' 'U']\r\n ['D' 'D' 'U' 'R' 'L' 'D' 'R' 'L' 'U' 'U' 'D']\r\n ['U' 'U' 'R' 'L' 'D' 'U' 'R' 'L' 'R' 'L' 'U']\r\n ['D' 'D' 'D' 'D' 'U' 'R' 'L' 'R' 'L' 'R' 'L']\r\n ['U' 'U' 'U' 'U' 'D' 'D' 'R' 'L' 'D' 'D' 'D']\r\n ['R' 'L' 'R' 'L' 'U' 'U' 'R' 'L' 'U' 'U' 'U']]\r\nSolutions found: 1\r\nstatus: OPTIMAL\r\nTime taken: 0.02 seconds\r\n```\r\n\r\n**Solved puzzle**\r\n\r\n<img src=\"https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/dominosa_solved.png\" alt=\"Dominosa solved\" width=\"500\">\r\n\r\n---\r\n\r\n## Light Up (Puzzle Type #5)\r\n\r\n* [**Play online**](https://www.chiark.greenend.org.uk/~sgtatham/puzzles/js/lightup.html)\r\n\r\n* [**Instructions**](https://www.chiark.greenend.org.uk/~sgtatham/puzzles/doc/lightup.html#lightup)\r\n\r\n* [**Solver Code**][5]\r\n\r\n<details>\r\n  <summary><strong>Rules</strong></summary>\r\nYou have a grid of squares. Some are filled in black; some of the black squares are numbered. Your aim is to \u2018light up\u2019 all the empty squares by placing light bulbs in some of them.\r\n\r\nEach light bulb illuminates the square it is on, plus all squares in line with it horizontally or vertically unless a black square is blocking the way.\r\n\r\nTo win the game, you must satisfy the following conditions:\r\n\r\n  - All non-black squares are lit.\r\n  - No light is lit by another light.\r\n  - All numbered black squares have exactly that number of lights adjacent to them (in the four squares above, below, and to the side).\r\n\r\nNon-numbered black squares may have any number of lights adjacent to them. \r\n</details>\r\n\r\n**Unsolved puzzle**\r\n\r\n<img src=\"https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/lightup_unsolved.png\" alt=\"Light Up unsolved\" width=\"500\">\r\n\r\nCode to utilize this package and solve the puzzle:\r\n```python\r\nimport numpy as np\r\nfrom puzzle_solver import light_up_solver as solver\r\nboard = np.array([\r\n  [' ', '0', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' '],\r\n  [' ', ' ', ' ', '0', ' ', ' ', ' ', ' ', ' ', '1'],\r\n  ['W', ' ', 'W', ' ', ' ', 'W', ' ', ' ', '0', ' '],\r\n  ['0', ' ', ' ', ' ', '3', ' ', 'W', ' ', '0', ' '],\r\n  [' ', ' ', ' ', ' ', 'W', ' ', '2', ' ', 'W', ' '],\r\n  [' ', '1', ' ', 'W', ' ', '2', ' ', ' ', ' ', ' '],\r\n  [' ', '0', ' ', 'W', ' ', 'W', ' ', ' ', ' ', 'W'],\r\n  [' ', '0', ' ', ' ', '1', ' ', ' ', '2', ' ', 'W'],\r\n  ['0', ' ', ' ', ' ', ' ', ' ', '1', ' ', ' ', ' '],\r\n  [' ', ' ', ' ', '2', ' ', ' ', ' ', ' ', 'W', ' '],\r\n])  # W is wall, ' ' is space, '0-9' is number\r\n\r\nbinst = solver.Board(board=board)\r\nsolutions = binst.solve_and_print()\r\n```\r\n**Script Output**\r\n```python\r\nSolution found\r\n[[' ' '0' ' ' ' ' ' ' 'L' 'W' ' ' ' ' 'L']\r\n ['L' ' ' ' ' '0' ' ' ' ' 'L' ' ' ' ' '1']\r\n ['W' 'L' 'W' ' ' 'L' 'W' ' ' ' ' '0' ' ']\r\n ['0' ' ' ' ' 'L' '3' 'L' 'W' ' ' '0' ' ']\r\n [' ' ' ' 'L' ' ' 'W' ' ' '2' 'L' 'W' 'L']\r\n ['L' '1' ' ' 'W' 'L' '2' 'L' ' ' ' ' ' ']\r\n [' ' '0' ' ' 'W' ' ' 'W' ' ' ' ' ' ' 'W']\r\n [' ' '0' ' ' ' ' '1' 'L' ' ' '2' 'L' 'W']\r\n ['0' ' ' ' ' 'L' ' ' ' ' '1' 'L' ' ' ' ']\r\n [' ' 'L' ' ' '2' 'L' ' ' ' ' ' ' 'W' 'L']]\r\nSolutions found: 1\r\nstatus: OPTIMAL\r\nTime taken: 0.01 seconds\r\n```\r\n\r\n**Solved puzzle**\r\n\r\nWhich exactly matches the true solutions (Remember, the goal of the puzzle is to find where to place the lights, marked as 'L' in the solution above):\r\n\r\n<img src=\"https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/lightup_solved.png\" alt=\"Light Up solved\" width=\"500\">\r\n\r\n---\r\n\r\n## Tents (Puzzle Type #6)\r\n\r\n* [**Play online**](https://www.chiark.greenend.org.uk/~sgtatham/puzzles/js/tents.html)\r\n\r\n* [**Instructions**](https://www.chiark.greenend.org.uk/~sgtatham/puzzles/doc/tents.html#tents)\r\n\r\n* [**Solver Code**][6]\r\n\r\n<details>\r\n  <summary><strong>Rules</strong></summary>\r\nYou have a grid of squares, some of which contain trees. Your aim is to place tents in some of the remaining squares, in such a way that the following conditions are met:\r\n\r\n  - There are exactly as many tents as trees.\r\n  - The tents and trees can be matched up in such a way that each tent is directly adjacent (horizontally or vertically, but not diagonally) to its own tree. However, a tent may be adjacent to other trees as well as its own.\r\n  - No two tents are adjacent horizontally, vertically or diagonally.\r\n  - The number of tents in each row, and in each column, matches the numbers given round the sides of the grid.\r\n</details>\r\n\r\n**Unsolved puzzle**\r\n\r\n<img src=\"https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/tents_unsolved.png\" alt=\"Tents unsolved\" width=\"500\">\r\n\r\nCode to utilize this package and solve the puzzle:\r\n```python\r\nimport numpy as np\r\nfrom puzzle_solver import tents_solver as solver\r\nboard = np.array([\r\n  [' ', 'T', ' ', ' ', ' ', ' ', ' ', ' ', 'T', ' ', 'T', ' ', 'T', ' ', ' '],\r\n  [' ', ' ', ' ', ' ', 'T', ' ', ' ', 'T', ' ', 'T', ' ', ' ', 'T', ' ', ' '],\r\n  [' ', 'T', ' ', 'T', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '],\r\n  [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'T', ' ', ' ', ' ', 'T', ' ', 'T'],\r\n  [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '],\r\n  [' ', 'T', ' ', ' ', 'T', ' ', 'T', ' ', ' ', 'T', ' ', ' ', 'T', 'T', ' '],\r\n  [' ', 'T', ' ', ' ', 'T', ' ', ' ', ' ', 'T', ' ', ' ', ' ', ' ', ' ', ' '],\r\n  [' ', ' ', ' ', ' ', ' ', ' ', 'T', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '],\r\n  [' ', ' ', ' ', ' ', ' ', 'T', ' ', ' ', ' ', ' ', 'T', ' ', ' ', 'T', ' '],\r\n  [' ', ' ', ' ', 'T', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'T', ' ', 'T'],\r\n  ['T', ' ', ' ', ' ', ' ', ' ', ' ', 'T', ' ', ' ', ' ', 'T', ' ', ' ', ' '],\r\n  ['T', ' ', ' ', ' ', 'T', ' ', 'T', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '],\r\n  [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'T', ' ', 'T', ' ', ' ', ' ', 'T'],\r\n  [' ', 'T', ' ', ' ', ' ', 'T', ' ', ' ', ' ', ' ', ' ', ' ', 'T', ' ', ' '],\r\n  [' ', 'T', ' ', ' ', 'T', ' ', ' ', ' ', ' ', 'T', ' ', 'T', ' ', ' ', ' '],\r\n])\r\nside = np.array([4, 1, 6, 0, 5, 2, 3, 1, 5, 2, 3, 2, 4, 3, 4])\r\ntop = np.array([4, 2, 4, 1, 3, 3, 3, 3, 3, 3, 2, 2, 6, 2, 4])\r\n\r\nbinst = solver.Board(board=board, sides={'top': top, 'side': side})\r\nsolutions = binst.solve_and_print()\r\n```\r\n**Script Output**\r\n```python\r\nSolution found\r\n[[' ' 'T' 'E' ' ' ' ' ' ' ' ' 'E' 'T' ' ' 'T' 'E' 'T' 'E' ' ']\r\n [' ' ' ' ' ' ' ' 'T' 'E' ' ' 'T' ' ' 'T' ' ' ' ' 'T' ' ' ' ']\r\n ['E' 'T' 'E' 'T' ' ' ' ' ' ' 'E' ' ' 'E' ' ' ' ' 'E' ' ' 'E']\r\n [' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' 'T' ' ' ' ' ' ' 'T' ' ' 'T']\r\n [' ' 'E' ' ' ' ' 'E' ' ' 'E' ' ' 'E' ' ' ' ' ' ' 'E' ' ' ' ']\r\n [' ' 'T' ' ' ' ' 'T' ' ' 'T' ' ' ' ' 'T' 'E' ' ' 'T' 'T' 'E']\r\n [' ' 'T' ' ' ' ' 'T' 'E' ' ' 'E' 'T' ' ' ' ' ' ' 'E' ' ' ' ']\r\n [' ' 'E' ' ' ' ' ' ' ' ' 'T' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ']\r\n [' ' ' ' ' ' ' ' 'E' 'T' 'E' ' ' ' ' 'E' 'T' ' ' 'E' 'T' 'E']\r\n ['E' ' ' 'E' 'T' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' 'T' ' ' 'T']\r\n ['T' ' ' ' ' ' ' ' ' ' ' ' ' 'T' 'E' ' ' ' ' 'T' 'E' ' ' 'E']\r\n ['T' ' ' ' ' 'E' 'T' 'E' 'T' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ']\r\n ['E' ' ' ' ' ' ' ' ' ' ' ' ' ' ' 'T' 'E' 'T' 'E' ' ' 'E' 'T']\r\n [' ' 'T' 'E' ' ' 'E' 'T' 'E' ' ' ' ' ' ' ' ' ' ' 'T' ' ' ' ']\r\n ['E' 'T' ' ' ' ' 'T' ' ' ' ' ' ' 'E' 'T' 'E' 'T' 'E' ' ' ' ']]\r\nSolutions found: 1\r\nstatus: OPTIMAL\r\nTime taken: 0.02 seconds\r\n```\r\n\r\n**Solved puzzle**\r\n\r\n<img src=\"https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/tents_solved.png\" alt=\"Tents solved\" width=\"500\">\r\n\r\n---\r\n\r\n## Filling (Puzzle Type #7)\r\n\r\n* [**Play online**](https://www.chiark.greenend.org.uk/~sgtatham/puzzles/js/filling.html)\r\n\r\n* [**Instructions**](https://www.chiark.greenend.org.uk/~sgtatham/puzzles/doc/filling.html#filling)\r\n\r\n* [**Solver Code**][7]\r\n\r\n<details>\r\n  <summary><strong>Rules</strong></summary>\r\nYou have a grid of squares, some of which contain digits, and the rest of which are empty. Your job is to fill in digits in the empty squares, in such a way that each connected region of squares all containing the same digit has an area equal to that digit.\r\n\r\n(\u2018Connected region\u2019, for the purposes of this game, does not count diagonally separated squares as adjacent.)\r\n\r\nFor example, it follows that no square can contain a zero, and that two adjacent squares can not both contain a one. No region has an area greater than 9 (because then its area would not be a single digit).\r\n</details>\r\n\r\n(Note: The solver for this puzzle is the only extremely slow solver in this repo and will take a minute to solve a simple 6x7 puzzle)\r\n\r\n**Unsolved puzzle**\r\n\r\n<img src=\"https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/filling_unsolved.png\" alt=\"Filling unsolved\" width=\"500\">\r\n\r\nCode to utilize this package and solve the puzzle:\r\n```python\r\nimport numpy as np\r\nfrom puzzle_solver import filling_solver as solver\r\nboard = np.array([\r\n  [' ', '4', '2', ' ', ' ', '2', ' '],\r\n  [' ', ' ', '7', ' ', ' ', '3', ' '],\r\n  [' ', ' ', ' ', ' ', '4', ' ', '3'],\r\n  [' ', '6', '6', ' ', '3', ' ', ' '],\r\n  [' ', '7', ' ', '6', '4', '5', ' '],\r\n  [' ', '6', ' ', ' ', ' ', ' ', '4'],\r\n])\r\nbinst = solver.Board(board=board)\r\nsolutions = binst.solve_and_print()\r\nassert len(solutions) == 1, f'unique solutions != 1, == {len(solutions)}'\r\n```\r\n**Script Output**\r\n```python\r\nSolution found\r\n[[4 4 2 2 4 2 2]\r\n [4 4 7 4 4 3 3]\r\n [7 7 7 3 4 5 3]\r\n [7 6 6 3 3 5 5]\r\n [7 7 6 6 4 5 5]\r\n [1 6 6 1 4 4 4]]\r\nSolutions found: 1\r\nstatus: OPTIMAL\r\nTime taken: 46.27 seconds\r\n```\r\n\r\n**Solved puzzle**\r\n\r\n<img src=\"https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/filling_solved.png\" alt=\"Filling solved\" width=\"500\">\r\n\r\n---\r\n\r\n## Keen (Puzzle Type #8)\r\n\r\n* [**Play online**](https://www.chiark.greenend.org.uk/~sgtatham/puzzles/js/keen.html)\r\n\r\n* [**Instructions**](https://www.chiark.greenend.org.uk/~sgtatham/puzzles/doc/keen.html#keen)\r\n\r\n* [**Solver Code**][8]\r\n\r\n<details>\r\n  <summary><strong>Rules</strong></summary>\r\nYou have a square grid; each square may contain a digit from 1 to the size of the grid. The grid is divided into blocks of varying shape and size, with arithmetic clues written in them. Your aim is to fully populate the grid with digits such that:\r\n\r\n  - Each row contains only one occurrence of each digit\r\n  - Each column contains only one occurrence of each digit\r\n  - The digits in each block can be combined to form the number stated in the clue, using the arithmetic operation given in the clue. That is:\r\n      - An addition clue means that the sum of the digits in the block must be the given number. For example, \u201815+\u2019 means the contents of the block adds up to fifteen.\r\n      - A multiplication clue (e.g. \u201860\u00d7\u2019), similarly, means that the product of the digits in the block must be the given number.\r\n      - A subtraction clue will always be written in a block of size two, and it means that one of the digits in the block is greater than the other by the given amount. For example, \u20182\u2212\u2019 means that one of the digits in the block is 2 more than the other, or equivalently that one digit minus the other one is 2. The two digits could be either way round, though.\r\n      - A division clue (e.g. \u20183\u00f7\u2019), similarly, is always in a block of size two and means that one digit divided by the other is equal to the given amount.\r\n\r\n  Note that a block may contain the same digit more than once (provided the identical ones are not in the same row and column).\r\n</details>\r\n\r\n**Unsolved puzzle**\r\n\r\n<img src=\"https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/keen_unsolved.png\" alt=\"Keen unsolved\" width=\"500\">\r\n\r\nCode to utilize this package and solve the puzzle:\r\n```python\r\nimport numpy as np\r\nfrom puzzle_solver import keen_solver as solver\r\n# tells the api the shape of the blocks in the board\r\nboard = np.array([\r\n  ['d01', 'd01', 'd03', 'd03', 'd05', 'd05', 'd08', 'd08', 'd10'],\r\n  ['d02', 'd02', 'd03', 'd04', 'd06', 'd06', 'd09', 'd09', 'd10'],\r\n  ['d12', 'd13', 'd14', 'd04', 'd07', 'd07', 'd07', 'd11', 'd11'],\r\n  ['d12', 'd13', 'd14', 'd14', 'd15', 'd16', 'd11', 'd11', 'd18'],\r\n  ['d19', 'd20', 'd24', 'd26', 'd15', 'd16', 'd16', 'd17', 'd18'],\r\n  ['d19', 'd20', 'd24', 'd26', 'd28', 'd28', 'd29', 'd17', 'd33'],\r\n  ['d21', 'd21', 'd24', 'd27', 'd30', 'd30', 'd29', 'd33', 'd33'],\r\n  ['d22', 'd23', 'd25', 'd27', 'd31', 'd32', 'd34', 'd34', 'd36'],\r\n  ['d22', 'd23', 'd25', 'd25', 'd31', 'd32', 'd35', 'd35', 'd36'],\r\n])\r\n# tells the api the operation and the result for each block\r\nblock_results = {\r\n  'd01': ('-', 1), 'd02': ('-', 1), 'd03': ('*', 378), 'd04': ('/', 4), 'd05': ('/', 2),\r\n  'd06': ('-', 2), 'd07': ('*', 6), 'd08': ('+', 9), 'd09': ('/', 2), 'd10': ('+', 9),\r\n  'd11': ('+', 22), 'd12': ('-', 1), 'd13': ('*', 30), 'd14': ('+', 12), 'd15': ('-', 1),\r\n  'd16': ('*', 196), 'd17': ('*', 63), 'd18': ('-', 1), 'd19': ('/', 3), 'd20': ('/', 3),\r\n  'd21': ('*', 21), 'd22': ('/', 4), 'd23': ('-', 7), 'd24': ('*', 64), 'd25': ('+', 15),\r\n  'd26': ('-', 1), 'd27': ('+', 11), 'd28': ('-', 4), 'd29': ('/', 4), 'd30': ('*', 54),\r\n  'd31': ('+', 11), 'd32': ('/', 4), 'd33': ('+', 16), 'd34': ('+', 15), 'd35': ('*', 30),\r\n  'd36': ('-', 7),\r\n}\r\nbinst = solver.Board(board=board, block_results=block_results)\r\nsolutions = binst.solve_and_print()\r\n```\r\n**Script Output**\r\n```python\r\nSolution found\r\n[[5 4 7 9 3 6 8 1 2]\r\n [9 8 6 1 5 3 2 4 7]\r\n [7 5 9 4 2 1 3 8 6]\r\n [8 6 1 2 9 7 5 3 4]\r\n [6 1 2 5 8 4 7 9 3]\r\n [2 3 8 6 1 5 4 7 9]\r\n [3 7 4 8 6 9 1 2 5]\r\n [4 2 5 3 7 8 9 6 1]\r\n [1 9 3 7 4 2 6 5 8]]\r\nSolutions found: 1\r\nstatus: OPTIMAL\r\nTime taken: 0.02 seconds\r\n```\r\n\r\n**Solved puzzle**\r\n\r\n<img src=\"https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/keen_solved.png\" alt=\"Keen solved\" width=\"500\">\r\n\r\n---\r\n\r\n## Towers (Puzzle Type #9)\r\n\r\n* [**Play online**](https://www.chiark.greenend.org.uk/~sgtatham/puzzles/js/towers.html)\r\n\r\n* [**Instructions**](https://www.chiark.greenend.org.uk/~sgtatham/puzzles/doc/towers.html#towers)\r\n\r\n* [**Solver Code**][9]\r\n\r\n<details>\r\n  <summary><strong>Rules</strong></summary>\r\nYou have a square grid. On each square of the grid you can build a tower, with its height ranging from 1 to the size of the grid. Around the edge of the grid are some numeric clues.\r\n\r\nYour task is to build a tower on every square, in such a way that:\r\n\r\n  - Each row contains every possible height of tower once\r\n  - Each column contains every possible height of tower once\r\n  - Each numeric clue describes the number of towers that can be seen if you look into the square from that direction, assuming that shorter towers are hidden behind taller ones. For example, in a 5\u00d75 grid, a clue marked \u20185\u2019 indicates that the five tower heights must appear in increasing order (otherwise you would not be able to see all five towers), whereas a clue marked \u20181\u2019 indicates that the tallest tower (the one marked 5) must come first.\r\n\r\nIn harder or larger puzzles, some towers will be specified for you as well as the clues round the edge, and some edge clues may be missing. \r\n</details>\r\n\r\n**Unsolved puzzle**\r\n\r\n<img src=\"https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/towers_unsolved.png\" alt=\"Towers unsolved\" width=\"500\">\r\n\r\nCode to utilize this package and solve the puzzle:\r\n```python\r\nimport numpy as np\r\nfrom puzzle_solver import towers_solver as solver\r\nboard = np.array([\r\n  [' ', ' ', ' ', ' ', ' ', ' '],\r\n  [' ', ' ', ' ', ' ', ' ', ' '],\r\n  [' ', ' ', '3', ' ', ' ', ' '],\r\n  [' ', ' ', ' ', ' ', ' ', ' '],\r\n  [' ', ' ', ' ', ' ', ' ', ' '],\r\n  [' ', ' ', ' ', ' ', ' ', ' '],\r\n])\r\nt = np.array([2, -1, 2, 2, 2, 3])\r\nb = np.array([2, 4, -1, 4, -1, -1])\r\nr = np.array([3, -1, 2, -1, -1, -1])\r\nl = np.array([-1, -1, -1, 2, -1, 4])\r\nbinst = solver.Board(board=board, sides={'top': t, 'bottom': b, 'right': r, 'left': l})\r\nsolutions = binst.solve_and_print()\r\n```\r\n**Script Output**\r\n```python\r\nSolution found\r\n[[5 6 4 1 2 3]\r\n [3 4 2 6 1 5]\r\n [4 5 3 2 6 1]\r\n [2 1 6 5 3 4]\r\n [6 3 1 4 5 2]\r\n [1 2 5 3 4 6]]\r\nSolutions found: 1\r\nstatus: OPTIMAL\r\nTime taken: 0.03 seconds\r\n```\r\n\r\n**Solved puzzle**\r\n\r\n<img src=\"https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/towers_solved.png\" alt=\"Towers solved\" width=\"500\">\r\n\r\n---\r\n\r\n## Singles (Puzzle Type #10)\r\n\r\n* [**Play online**](https://www.chiark.greenend.org.uk/~sgtatham/puzzles/js/singles.html)\r\n\r\n* [**Instructions**](https://www.chiark.greenend.org.uk/~sgtatham/puzzles/doc/singles.html#singles)\r\n\r\n* [**Solver Code**][10]\r\n\r\n<details>\r\n  <summary><strong>Rules</strong></summary>\r\nYou have a grid of white squares, all of which contain numbers. Your task is to colour some of the squares black (removing the number) so as to satisfy all of the following conditions:\r\n\r\n  - No number occurs more than once in any row or column.\r\n  - No black square is horizontally or vertically adjacent to any other black square.\r\n  - The remaining white squares must all form one contiguous region (connected by edges, not just touching at corners).\r\n</details>\r\n\r\n**Unsolved puzzle**\r\n\r\n<img src=\"https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/singles_unsolved.png\" alt=\"Singles unsolved\" width=\"500\">\r\n\r\nCode to utilize this package and solve the puzzle:\r\n```python\r\nimport numpy as np\r\nfrom puzzle_solver import singles_solver as solver\r\nboard = np.array([\r\n  [1, 6, 5, 4, 9, 8, 9, 3, 5, 1, 3, 7],\r\n  [2, 8, 5, 7, 1, 1, 4, 3, 6, 3, 10, 7],\r\n  [6, 7, 7, 11, 2, 6, 3, 10, 10, 2, 3, 3],\r\n  [11, 9, 4, 3, 6, 1, 2, 5, 3, 10, 7, 8], \r\n  [5, 5, 4, 9, 7, 9, 6, 6, 11, 5, 4, 11],\r\n  [1, 3, 7, 9, 12, 5, 4, 2, 9, 6, 12, 4],\r\n  [6, 11, 1, 3, 6, 4, 11, 2, 2, 10, 8, 10],\r\n  [3, 11, 12, 6, 2, 9, 9, 1, 4, 8, 12, 5],\r\n  [4, 8, 8, 5, 11, 3, 3, 6, 5, 9, 1, 4],\r\n  [2, 4, 6, 2, 1, 10, 1, 10, 8, 5, 4, 6],\r\n  [5, 1, 6, 10, 9, 4, 8, 4, 8, 3, 2, 12],\r\n  [11, 2, 12, 10, 8, 3, 5, 4, 10, 4, 8, 11],\r\n])\r\nbinst = solver.Board(board=board)\r\nsolutions = binst.solve_and_print()\r\n```\r\n**Script Output**\r\n```python\r\nSolution found\r\n[['B' ' ' 'B' ' ' 'B' ' ' ' ' 'B' ' ' ' ' ' ' ' ']\r\n [' ' ' ' ' ' ' ' ' ' 'B' ' ' ' ' ' ' 'B' ' ' 'B']\r\n ['B' ' ' 'B' ' ' 'B' ' ' 'B' ' ' 'B' ' ' 'B' ' ']\r\n [' ' ' ' ' ' 'B' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ']\r\n ['B' ' ' 'B' ' ' ' ' 'B' ' ' 'B' ' ' 'B' ' ' 'B']\r\n [' ' ' ' ' ' 'B' ' ' ' ' 'B' ' ' ' ' ' ' 'B' ' ']\r\n [' ' 'B' ' ' ' ' 'B' ' ' ' ' 'B' ' ' 'B' ' ' ' ']\r\n [' ' ' ' 'B' ' ' ' ' ' ' 'B' ' ' ' ' ' ' ' ' ' ']\r\n [' ' 'B' ' ' ' ' ' ' 'B' ' ' ' ' 'B' ' ' ' ' 'B']\r\n ['B' ' ' 'B' ' ' 'B' ' ' ' ' 'B' ' ' ' ' 'B' ' ']\r\n [' ' ' ' ' ' ' ' ' ' 'B' ' ' ' ' 'B' ' ' ' ' ' ']\r\n ['B' ' ' ' ' 'B' ' ' ' ' ' ' 'B' ' ' ' ' 'B' ' ']]\r\nSolutions found: 1\r\nstatus: OPTIMAL\r\nTime taken: 2.14 seconds\r\n```\r\n\r\n**Solved puzzle**\r\n\r\n<img src=\"https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/singles_solved.png\" alt=\"Singles solved\" width=\"500\">\r\n\r\n---\r\n\r\n## Magnets (Puzzle Type #11)\r\n\r\n* [**Play online**](https://www.chiark.greenend.org.uk/~sgtatham/puzzles/js/magnets.html)\r\n\r\n* [**Instructions**](https://www.chiark.greenend.org.uk/~sgtatham/puzzles/doc/magnets.html#magnets)\r\n\r\n* [**Solver Code**][11]\r\n\r\n<details>\r\n  <summary><strong>Rules</strong></summary>\r\nA rectangular grid has been filled with a mixture of magnets (that is, dominoes with one positive end and one negative end) and blank dominoes (that is, dominoes with two neutral poles). These dominoes are initially only seen in silhouette. Around the grid are placed a number of clues indicating the number of positive and negative poles contained in certain columns and rows.\r\n\r\nYour aim is to correctly place the magnets and blank dominoes such that all the clues are satisfied, with the additional constraint that no two similar magnetic poles may be orthogonally adjacent (since they repel). Neutral poles do not repel, and can be adjacent to any other pole. \r\n</details>\r\n\r\n**Unsolved puzzle**\r\n\r\n<img src=\"https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/magnets_unsolved.png\" alt=\"Magnets unsolved\" width=\"500\">\r\n\r\nCode to utilize this package and solve the puzzle:\r\n```python\r\nimport numpy as np\r\nfrom puzzle_solver import magnets_solver as solver\r\nboard = np.array([\r\n  ['H', 'H', 'H', 'H', 'V', 'V', 'V', 'V', 'H', 'H'],\r\n  ['H', 'H', 'H', 'H', 'V', 'V', 'V', 'V', 'V', 'V'],\r\n  ['H', 'H', 'H', 'H', 'H', 'H', 'H', 'H', 'V', 'V'],\r\n  ['V', 'V', 'V', 'H', 'H', 'H', 'H', 'H', 'H', 'V'],\r\n  ['V', 'V', 'V', 'V', 'V', 'V', 'V', 'H', 'H', 'V'],\r\n  ['V', 'H', 'H', 'V', 'V', 'V', 'V', 'V', 'V', 'V'],\r\n  ['V', 'V', 'V', 'V', 'V', 'H', 'H', 'V', 'V', 'V'],\r\n  ['V', 'V', 'V', 'V', 'V', 'V', 'H', 'H', 'H', 'H'],\r\n  ['V', 'H', 'H', 'H', 'H', 'V', 'H', 'H', 'H', 'H'],\r\n])\r\npos_v = np.array([-1, -1, 3, 5, 3, 3, -1, 3, -1, 4])\r\nneg_v = np.array([-1, 2, 3, 4, -1, 3, 4, 3, 4, 4])\r\npos_h = np.array([5, -1, -1, -1, 5, -1, 3, 1, -1])\r\nneg_h = np.array([4, -1, 4, -1, 5, 4, -1, 2, -1])\r\n\r\nbinst = solver.Board(board=board, sides={'pos_v': pos_v, 'neg_v': neg_v, 'pos_h': pos_h, 'neg_h': neg_h})\r\nsolutions = binst.solve_and_print()\r\n```\r\n**Script Output**\r\n```python\r\nSolution found\r\n[['-' '+' '-' '+' ' ' '+' '-' '+' '-' '+']\r\n [' ' ' ' '+' '-' ' ' '-' '+' '-' '+' '-']\r\n ['-' '+' '-' '+' ' ' ' ' '-' '+' '-' '+']\r\n ['+' '-' '+' '-' '+' '-' '+' '-' '+' '-']\r\n ['-' '+' '-' '+' '-' '+' '-' '+' '-' '+']\r\n [' ' '-' '+' '-' '+' '-' '+' ' ' '+' '-']\r\n [' ' ' ' ' ' '+' '-' '+' '-' ' ' '-' '+']\r\n ['-' ' ' ' ' '-' '+' ' ' ' ' ' ' ' ' ' ']\r\n ['+' ' ' ' ' '+' '-' ' ' '+' '-' '+' '-']]\r\nSolutions found: 1\r\nstatus: OPTIMAL\r\nTime taken: 0.02 seconds\r\n```\r\n\r\n**Solved puzzle**\r\n\r\n<img src=\"https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/magnets_solved.png\" alt=\"Magnets solved\" width=\"500\">\r\n\r\n\r\n---\r\n\r\n## Signpost (Puzzle Type #12)\r\n\r\n* [**Play online**](https://www.chiark.greenend.org.uk/~sgtatham/puzzles/js/signpost.html)\r\n\r\n* [**Instructions**](https://www.chiark.greenend.org.uk/~sgtatham/puzzles/doc/signpost.html#signpost)\r\n\r\n* [**Solver Code**][12]\r\n\r\n<details>\r\n  <summary><strong>Rules</strong></summary>\r\nYou have a grid of squares; each square (except the last one) contains an arrow, and some squares also contain numbers. Your job is to connect the squares to form a continuous list of numbers starting at 1 and linked in the direction of the arrows \u2013 so the arrow inside the square with the number 1 will point to the square containing the number 2, which will point to the square containing the number 3, etc. Each square can be any distance away from the previous one, as long as it is somewhere in the direction of the arrow.\r\n\r\nBy convention the first and last numbers are shown; one or more interim numbers may also appear at the beginning. \r\n</details>\r\n\r\n**Unsolved puzzle**\r\n\r\n<img src=\"https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/signpost_unsolved.png\" alt=\"Signpost unsolved\" width=\"500\">\r\n\r\nCode to utilize this package and solve the puzzle:\r\n```python\r\nimport numpy as np\r\nfrom puzzle_solver import signpost_solver as solver\r\n# Q = up-left, W = up, E = up-right, A = left, D = right, Z = down-left, X = down, C = down-right\r\nboard1 = np.array([\r\n  ['C', 'D', 'D', 'X', 'D', 'Z', 'X'],\r\n  ['D', 'C', 'D', 'X', 'X', 'A', 'A'],\r\n  ['X', 'X', 'D', 'Q', 'Z', 'W', 'A'],\r\n  ['W', 'D', 'W', 'W', 'X', 'Z', 'X'],\r\n  ['X', 'A', 'Q', 'Q', 'A', 'Q', 'X'],\r\n  ['D', 'W', 'W', 'A', 'E', 'A', 'Z'],\r\n  ['D', 'E', 'D', 'E', 'D', 'A', ' '],\r\n])\r\nboard2 = np.array([\r\n  [ 1,  0, 23,  0,  0,  0,  0],\r\n  [30, 32,  0,  0,  0,  0,  0],\r\n  [ 0,  0,  2,  0,  0,  0,  0],\r\n  [ 0,  0,  0,  0,  0,  0,  0],\r\n  [ 0, 45,  0,  0, 33,  0,  0],\r\n  [ 0,  0, 22,  8, 39, 10,  0],\r\n  [ 0,  0,  0,  0,  0, 20, 49],\r\n])\r\n\r\nbinst = solver.Board(board=board1, values=board2)\r\nsolutions = binst.solve_and_print()\r\n```\r\n**Script Output**\r\n```python\r\nSolution found\r\n[[1 42 23 7 43 44 24]\r\n [30 32 36 5 37 4 31]\r\n [28 12 2 41 26 3 25]\r\n [29 13 35 6 38 14 17]\r\n [46 45 27 34 33 40 18]\r\n [9 11 22 8 39 10 19]\r\n [47 21 15 16 48 20 49]]\r\nSolutions found: 1\r\nstatus: OPTIMAL\r\nTime taken: 0.03 seconds\r\n```\r\n\r\n**Solved puzzle**\r\n\r\n<img src=\"https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/signpost_solved.png\" alt=\"Signpost solved\" width=\"500\">\r\n\r\n\r\n---\r\n\r\n## Range (Puzzle Type #13)\r\n\r\n* [**Play online**](https://www.chiark.greenend.org.uk/~sgtatham/puzzles/js/range.html)\r\n\r\n* [**Instructions**](https://www.chiark.greenend.org.uk/~sgtatham/puzzles/doc/range.html#range)\r\n\r\n* [**Solver Code**][13]\r\n\r\n<details>\r\n  <summary><strong>Rules</strong></summary>\r\nYou have a grid of squares; some squares contain numbers. Your job is to colour some of the squares black, such that several criteria are satisfied:\r\n\r\n  - no square with a number is coloured black.\r\n  - no two black squares are adjacent (horizontally or vertically).\r\n  - for any two white squares, there is a path between them using only white squares.\r\n  - for each square with a number, that number denotes the total number of white squares reachable from that square going in a straight line in any horizontal or vertical direction until hitting a wall or a black square; the square with the number is included in the total (once).\r\n\r\nFor instance, a square containing the number one must have four black squares as its neighbours by the last criterion; but then it's impossible for it to be connected to any outside white square, which violates the second to last criterion. So no square will contain the number one. \r\n</details>\r\n\r\n(Note: The solver for this puzzle is slightly slower and could take several seconds to solve a 16x11 puzzle)\r\n\r\n**Unsolved puzzle**\r\n\r\n<img src=\"https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/range_unsolved.png\" alt=\"Range unsolved\" width=\"500\">\r\n\r\nCode to utilize this package and solve the puzzle:\r\n```python\r\nimport numpy as np\r\nfrom puzzle_solver import range_solver as solver\r\nclues = np.array([\r\n    [-1, 4, 2, -1, -1, 3, -1, -1, -1, 8, -1, -1, -1, -1, 6, -1],\r\n    [-1, -1, -1, -1, -1, 13, -1, 18, -1, -1, 14, -1, -1, 22, -1, -1],\r\n    [-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, 12, -1, -1, -1, -1],\r\n    [-1, -1, -1, -1, 12, -1, 11, -1, -1, -1, 9, -1, -1, -1, -1, -1],\r\n    [7, -1, -1, -1, -1, -1, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1],\r\n    [-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1],\r\n    [-1, -1, -1, -1, -1, -1, -1, -1, -1, 12, -1, -1, -1, -1, -1, 5],\r\n    [-1, -1, -1, -1, -1, 9, -1, -1, -1, 9, -1, 4, -1, -1, -1, -1],\r\n    [-1, -1, -1, -1, 6, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1],\r\n    [-1, -1, 10, -1, -1, 7, -1, -1, 13, -1, 10, -1, -1, -1, -1, -1],\r\n    [-1, 7, -1, -1, -1, -1, 6, -1, -1, -1, 6, -1, -1, 13, 5, -1],\r\n])\r\nbinst = solver.Board(clues)\r\nsolutions = binst.solve_and_print()\r\n```\r\n**Script Output**\r\n```python\r\nSolution:\r\nB . . B . . B . B . B . B . . .\r\n. . B . . . . . . . . . . . . B\r\nB . . . . B . . . . . . . . . .\r\n. B . B . . . . . . . B . . . .\r\n. . . . . B . . B . B . . . B .\r\n. . B . . . . . . . . B . . . B\r\nB . . . B . B . . . . . B . . .\r\n. . . . . . . B . . B . . . B .\r\n. B . . . B . . . B . B . . . .\r\n. . . . . . B . . . . . . . . B\r\nB . . . . . . B . . . . B . . .\r\nSolutions found: 1\r\nstatus: OPTIMAL\r\nTime taken: 3.32 seconds\r\n```\r\n\r\n**Solved puzzle**\r\n\r\n<img src=\"https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/range_solved.png\" alt=\"Range solved\" width=\"500\">\r\n\r\n---\r\n\r\n## UnDead (Puzzle Type #14)\r\n\r\n* [**Play online**](https://www.chiark.greenend.org.uk/~sgtatham/puzzles/js/undead.html)\r\n\r\n* [**Instructions**](https://www.chiark.greenend.org.uk/~sgtatham/puzzles/doc/undead.html#undead)\r\n\r\n* [**Solver Code**][14]\r\n\r\n<details>\r\n  <summary><strong>Rules</strong></summary>\r\nYou are given a grid of squares, some of which contain diagonal mirrors. Every square which is not a mirror must be filled with one of three types of undead monster: a ghost, a vampire, or a zombie.\r\n\r\nVampires can be seen directly, but are invisible when reflected in mirrors. Ghosts are the opposite way round: they can be seen in mirrors, but are invisible when looked at directly. Zombies are visible by any means.\r\n\r\nYou are also told the total number of each type of monster in the grid. Also around the edge of the grid are written numbers, which indicate how many monsters can be seen if you look into the grid along a row or column starting from that position. (The diagonal mirrors are reflective on both sides. If your reflected line of sight crosses the same monster more than once, the number will count it each time it is visible, not just once.) \r\n</details>\r\n\r\n**Unsolved puzzle**\r\n\r\n<img src=\"https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/undead_unsolved.png\" alt=\"UnDead unsolved\" width=\"500\">\r\n\r\nCode to utilize this package and solve the puzzle:\r\n```python\r\nimport numpy as np\r\nfrom puzzle_solver import undead_solver as solver\r\nboard = np.array([\r\n  ['  ', '//', '  ', '  ', '  ', '  ', '\\\\'],\r\n  ['  ', '  ', '  ', '//', '  ', '  ', '  '],\r\n  ['  ', '//', '//', '  ', '  ', '\\\\', '//'],\r\n  ['//', '\\\\', '//', '  ', '//', '\\\\', '  '],\r\n  ['//', '  ', '//', '\\\\', '  ', '//', '//'],\r\n  ['  ', '\\\\', '\\\\', '\\\\', '  ', '  ', '  '],\r\n  ['  ', '//', '  ', '  ', '  ', '  ', '  '],\r\n])\r\nt = np.array([3, 0, 3, 0, 5, 6, 0])\r\nb = np.array([5, 2, 1, 3, 8, 2, 0])\r\nr = np.array([0, 8, 0, 4, 2, 2, 4])\r\nl = np.array([1, 4, 8, 0, 0, 2, 2])\r\ncounts = {Monster.GHOST: 5, Monster.VAMPIRE: 12, Monster.ZOMBIE: 11}\r\n\r\n# create board and solve\r\nbinst = solver.Board(board=board, sides={'top': t, 'bottom': b, 'right': r, 'left': l}, monster_count=counts)\r\nsolutions = binst.solve_and_print()\r\n```\r\n**Script Output**\r\n```python\r\nSolution found\r\n[['VA' '//' 'GH' 'GH' 'ZO' 'GH' '\\\\']\r\n ['VA' 'VA' 'VA' '//' 'ZO' 'ZO' 'ZO']\r\n ['VA' '//' '//' 'ZO' 'ZO' '\\\\' '//']\r\n ['//' '\\\\' '//' 'VA' '//' '\\\\' 'VA']\r\n ['//' 'VA' '//' '\\\\' 'ZO' '//' '//']\r\n ['ZO' '\\\\' '\\\\' '\\\\' 'ZO' 'VA' 'GH']\r\n ['ZO' '//' 'VA' 'VA' 'ZO' 'VA' 'GH']]\r\nSolutions found: 1\r\nstatus: OPTIMAL\r\nTime taken: 0.01 seconds\r\n```\r\n\r\n**Solved puzzle**\r\n\r\n<img src=\"https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/undead_solved.png\" alt=\"UnDead solved\" width=\"500\">\r\n\r\n---\r\n\r\n## Unruly (Puzzle Type #15)\r\n\r\n* [**Play online**](https://www.chiark.greenend.org.uk/~sgtatham/puzzles/js/unruly.html)\r\n\r\n* [**Instructions**](https://www.chiark.greenend.org.uk/~sgtatham/puzzles/doc/unruly.html#unruly)\r\n\r\n* [**Solver Code**][15]\r\n\r\n<details>\r\n  <summary><strong>Rules</strong></summary>\r\nYou are given a grid of squares, which you must colour either black or white. Some squares are provided as clues; the rest are left for you to fill in. Each row and column must contain the same number of black and white squares, and no row or column may contain three consecutive squares of the same colour. \r\n</details>\r\n\r\n**Unsolved puzzle**\r\n\r\n<img src=\"https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/unruly_unsolved.png\" alt=\"Unruly unsolved\" width=\"500\">\r\n\r\nCode to utilize this package and solve the puzzle:\r\n```python\r\nimport numpy as np\r\nfrom puzzle_solver import unruly_solver as solver\r\nboard = np.array([\r\n  ['W', 'W', ' ', 'B', ' ', ' ', ' ', ' ', 'B', ' ', ' ', ' ', ' ', ' '],\r\n  [' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', 'W'],\r\n  [' ', ' ', ' ', ' ', ' ', 'B', ' ', 'W', ' ', ' ', 'B', ' ', ' ', ' '],\r\n  [' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', 'W', ' ', ' '],\r\n  ['B', ' ', ' ', 'W', ' ', ' ', ' ', ' ', 'B', ' ', ' ', ' ', ' ', ' '],\r\n  [' ', ' ', ' ', ' ', ' ', 'B', ' ', ' ', ' ', 'B', ' ', 'B', ' ', ' '],\r\n  [' ', 'B', 'B', ' ', ' ', 'B', ' ', ' ', ' ', ' ', ' ', 'B', ' ', ' '],\r\n  [' ', ' ', 'B', ' ', ' ', ' ', ' ', 'W', ' ', 'B', 'B', ' ', ' ', 'W'],\r\n  [' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', ' ', ' ', ' ', ' ', ' ', 'W'],\r\n  [' ', ' ', ' ', 'B', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '],\r\n  [' ', ' ', 'W', ' ', ' ', ' ', 'W', ' ', ' ', 'W', ' ', 'W', ' ', ' '],\r\n  [' ', 'W', ' ', 'W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'B'],\r\n  [' ', ' ', ' ', ' ', 'B', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '],\r\n  ['W', ' ', ' ', ' ', 'W', ' ', ' ', ' ', 'B', ' ', 'W', ' ', 'B', ' '],\r\n])\r\nbinst = solver.Board(board=board)\r\nsolutions = binst.solve_and_print()\r\n```\r\n**Script Output**\r\n```python\r\nSolution found\r\n[['W' 'W' 'B' 'B' 'W' 'B' 'W' 'B' 'B' 'W' 'B' 'W' 'W' 'B']\r\n ['B' 'B' 'W' 'W' 'B' 'W' 'B' 'W' 'W' 'B' 'W' 'B' 'B' 'W']\r\n ['W' 'W' 'B' 'W' 'W' 'B' 'B' 'W' 'B' 'W' 'B' 'B' 'W' 'B']\r\n ['W' 'B' 'W' 'B' 'B' 'W' 'W' 'B' 'W' 'W' 'B' 'W' 'B' 'B']\r\n ['B' 'W' 'B' 'W' 'B' 'W' 'B' 'W' 'B' 'B' 'W' 'W' 'B' 'W']\r\n ['B' 'W' 'W' 'B' 'W' 'B' 'B' 'W' 'B' 'B' 'W' 'B' 'W' 'W']\r\n ['W' 'B' 'B' 'W' 'W' 'B' 'W' 'B' 'W' 'W' 'B' 'B' 'W' 'B']\r\n ['B' 'W' 'B' 'W' 'B' 'W' 'B' 'W' 'W' 'B' 'B' 'W' 'B' 'W']\r\n ['B' 'B' 'W' 'B' 'B' 'W' 'B' 'W' 'B' 'W' 'W' 'B' 'W' 'W']\r\n ['W' 'W' 'B' 'B' 'W' 'B' 'W' 'B' 'W' 'B' 'W' 'W' 'B' 'B']\r\n ['B' 'B' 'W' 'W' 'B' 'W' 'W' 'B' 'B' 'W' 'B' 'W' 'B' 'W']\r\n ['B' 'W' 'B' 'W' 'W' 'B' 'B' 'W' 'W' 'B' 'W' 'B' 'W' 'B']\r\n ['W' 'B' 'W' 'B' 'B' 'W' 'W' 'B' 'W' 'B' 'B' 'W' 'W' 'B']\r\n ['W' 'B' 'W' 'B' 'W' 'B' 'W' 'B' 'B' 'W' 'W' 'B' 'B' 'W']]\r\nSolutions found: 1\r\nstatus: OPTIMAL\r\nTime taken: 0.01 seconds\r\n```\r\n\r\n**Solved puzzle**\r\n\r\n<img src=\"https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/unruly_solved.png\" alt=\"Unruly solved\" width=\"500\">\r\n\r\n---\r\n\r\n## Tracks (Puzzle Type #16)\r\n\r\n* [**Play online**](https://www.chiark.greenend.org.uk/~sgtatham/puzzles/js/tracks.html)\r\n\r\n* [**Instructions**](https://www.chiark.greenend.org.uk/~sgtatham/puzzles/doc/tracks.html#tracks)\r\n\r\n* [**Solver Code**][16]\r\n\r\n<details>\r\n  <summary><strong>Rules</strong></summary>\r\nComplete the track from A to B so that the rows and columns contain the same number of track segments as are indicated in the clues to the top and right of the grid. There are only straight and 90-degree curved rail sections, and the track may not cross itself. \r\n\r\n</details>\r\n\r\n(Note: The solver for this puzzle is slightly slower and could take several seconds to solve a large 15x15 puzzle)\r\n\r\n**Unsolved puzzle**\r\n\r\n<img src=\"https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/tracks_unsolved.png\" alt=\"Tracks unsolved\" width=\"500\">\r\n\r\nCode to utilize this package and solve the puzzle:\r\n```python\r\nimport numpy as np\r\nfrom puzzle_solver import tracks_solver as solver\r\nboard = np.array([\r\n  ['  ', '  ', '  ', '  ', '  ', '  ', '  ', '  ', '  ', '  ', '  ', '  ', 'LD', '  ', '  ', ], \r\n  ['  ', '  ', '  ', '  ', '  ', '  ', '  ', '  ', '  ', '  ', 'LD', '  ', '  ', '  ', '  ', ], \r\n  ['  ', '  ', '  ', '  ', '  ', '  ', '  ', '  ', '  ', '  ', '  ', '  ', '  ', '  ', '  ', ], \r\n  ['  ', '  ', '  ', '  ', '  ', 'LD', 'UD', 'DR', '  ', '  ', '  ', '  ', '  ', '  ', '  ', ], \r\n  ['DR', '  ', '  ', '  ', '  ', '  ', '  ', '  ', '  ', '  ', '  ', '  ', '  ', '  ', '  ', ], \r\n  ['  ', '  ', '  ', '  ', '  ', '  ', '  ', '  ', '  ', '  ', '  ', '  ', 'DR', '  ', '  ', ], \r\n  ['  ', '  ', '  ', '  ', '  ', '  ', '  ', '  ', 'DR', '  ', '  ', '  ', '  ', '  ', '  ', ], \r\n  ['  ', '  ', 'UL', '  ', '  ', '  ', '  ', '  ', '  ', '  ', '  ', '  ', '  ', '  ', '  ', ], \r\n  ['  ', '  ', '  ', '  ', '  ', '  ', '  ', '  ', '  ', '  ', 'LR', '  ', '  ', '  ', '  ', ], \r\n  ['  ', '  ', '  ', '  ', '  ', '  ', '  ', '  ', '  ', '  ', 'LD', '  ', '  ', '  ', 'UD', ], \r\n  ['  ', '  ', '  ', '  ', '  ', '  ', '  ', '  ', 'UR', '  ', '  ', '  ', '  ', 'UD', 'UD', ], \r\n  ['  ', '  ', '  ', '  ', '  ', '  ', '  ', '  ', '  ', 'LR', '  ', '  ', '  ', '  ', '  ', ], \r\n  ['UL', '  ', '  ', '  ', '  ', '  ', '  ', '  ', '  ', '  ', 'LR', 'LR', '  ', '  ', '  ', ], \r\n  ['  ', '  ', '  ', '  ', '  ', '  ', '  ', '  ', '  ', '  ', '  ', '  ', '  ', '  ', '  ', ], \r\n  ['  ', '  ', '  ', '  ', '  ', '  ', '  ', '  ', '  ', '  ', '  ', '  ', '  ', 'DR', '  ', ], \r\n])\r\nside = np.array([9, 7, 7, 7, 11, 10, 9, 8, 9, 10, 7, 9, 9, 2, 2])\r\ntop = np.array([6, 5, 7, 3, 3, 2, 7, 8, 13, 8, 9, 8, 10, 13, 14])\r\nbinst = solver.Board(board=board, top=top, side=side)\r\nsolutions = binst.solve_and_print()\r\n```\r\n**Script Output**\r\n```python\r\n[['  ' '  ' '  ' '  ' '  ' '  ' '\u250f\u2501' '\u2501\u2501' '\u2501\u2501' '\u2501\u2501' '\u2501\u2501' '\u2501\u2501' '\u2501\u2512' '\u250f\u2501' '\u2501\u2512']\r\n ['  ' '  ' '  ' '  ' '  ' '  ' '\u2503 ' '  ' '\u250f\u2501' '\u2501\u2501' '\u2501\u2512' '  ' '\u2517\u2501' '\u2501\u251b' '\u2503 ']\r\n ['  ' '  ' '  ' '  ' '  ' '  ' '\u2503 ' '  ' '\u2503 ' '  ' '\u2517\u2501' '\u2501\u2501' '\u2501\u2501' '\u2501\u2501' '\u2501\u251b']\r\n ['  ' '  ' '\u250f\u2501' '\u2501\u2512' '\u250f\u2501' '\u2501\u2512' '\u2503 ' '\u250f\u2501' '\u2501\u251b' '  ' '  ' '  ' '  ' '  ' '  ']\r\n ['\u250f\u2501' '\u2501\u2501' '\u2501\u251b' '\u2503 ' '\u2503 ' '\u2517\u2501' '\u2501\u251b' '\u2517\u2501' '\u2501\u2512' '  ' '  ' '  ' '  ' '\u250f\u2501' '\u2501\u2512']\r\n ['\u2517\u2501' '\u2501\u2501' '\u2501\u2512' '\u2517\u2501' '\u2501\u251b' '  ' '  ' '\u250f\u2501' '\u2501\u251b' '  ' '  ' '  ' '\u250f\u2501' '\u2501\u251b' '\u2503 ']\r\n ['  ' '  ' '\u2503 ' '  ' '  ' '  ' '\u250f\u2501' '\u2501\u251b' '\u250f\u2501' '\u2501\u2501' '\u2501\u2501' '\u2501\u2501' '\u2501\u251b' '  ' '\u2503 ']\r\n ['  ' '\u250f\u2501' '\u2501\u251b' '  ' '  ' '  ' '\u2517\u2501' '\u2501\u2501' '\u2501\u251b' '  ' '  ' '  ' '\u250f\u2501' '\u2501\u2501' '\u2501\u251b']\r\n ['  ' '\u2517\u2501' '\u2501\u2512' '  ' '  ' '  ' '  ' '  ' '\u250f\u2501' '\u2501\u2501' '\u2501\u2501' '\u2501\u2512' '\u2503 ' '\u250f\u2501' '\u2501\u2512']\r\n ['\u250f\u2501' '\u2501\u2501' '\u2501\u251b' '  ' '  ' '  ' '  ' '  ' '\u2503 ' '\u250f\u2501' '\u2501\u2512' '\u2517\u2501' '\u2501\u251b' '\u2503 ' '\u2503 ']\r\n ['\u2503 ' '  ' '  ' '  ' '  ' '  ' '  ' '  ' '\u2517\u2501' '\u2501\u251b' '\u2517\u2501' '\u2501\u2512' '  ' '\u2503 ' '\u2503 ']\r\n ['\u2503 ' '  ' '  ' '  ' '  ' '  ' '  ' '\u250f\u2501' '\u2501\u2501' '\u2501\u2501' '\u2501\u2501' '\u2501\u251b' '\u250f\u2501' '\u2501\u251b' '\u2503 ']\r\n ['\u2501\u251b' '  ' '  ' '  ' '  ' '  ' '  ' '\u2517\u2501' '\u2501\u2501' '\u2501\u2501' '\u2501\u2501' '\u2501\u2501' '\u2501\u251b' '\u250f\u2501' '\u2501\u251b']\r\n ['  ' '  ' '  ' '  ' '  ' '  ' '  ' '  ' '  ' '  ' '  ' '  ' '  ' '\u2517\u2501' '\u2501\u2512']\r\n ['  ' '  ' '  ' '  ' '  ' '  ' '  ' '  ' '  ' '  ' '  ' '  ' '  ' '\u250f\u2501' '\u2501\u251b']]\r\nSolutions found: 1\r\nstatus: OPTIMAL\r\nTime taken: 9.42 seconds\r\n```\r\n\r\n**Solved puzzle**\r\n\r\n<img src=\"https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/tracks_solved.png\" alt=\"Tracks solved\" width=\"500\">\r\n\r\n---\r\n\r\n## Mosaic (Puzzle Type #17)\r\n\r\n* [**Play online**](https://www.chiark.greenend.org.uk/~sgtatham/puzzles/js/mosaic.html)\r\n\r\n* [**Instructions**](https://www.chiark.greenend.org.uk/~sgtatham/puzzles/doc/mosaic.html#mosaic)\r\n\r\n* [**Solver Code**][17]\r\n\r\n<details>\r\n  <summary><strong>Rules</strong></summary>\r\nYou are given a grid of squares, which you must colour either black or white.\r\n\r\nSome squares contain clue numbers. Each clue tells you the number of black squares in the 3\u00d73 region surrounding the clue \u2013 including the clue square itself. \r\n</details>\r\n\r\n**Unsolved puzzle**\r\n\r\n<img src=\"https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/mosaic_unsolved.png\" alt=\"Mosaic unsolved\" width=\"500\">\r\n\r\nCode to utilize this package and solve the puzzle:\r\n```python\r\nimport numpy as np\r\nfrom puzzle_solver import mosaic_solver as solver\r\nboard = np.array([\r\n  [' ', ' ', '2', '1', ' ', ' ', ' ', '3', ' ', '4', '2', '2', ' ', ' ', '4'],\r\n  ['3', ' ', ' ', ' ', '4', ' ', ' ', ' ', ' ', ' ', '4', ' ', '2', ' ', ' '],\r\n  ['4', ' ', ' ', '5', ' ', '5', ' ', ' ', '5', ' ', '3', '3', '2', '5', ' '],\r\n  [' ', ' ', '7', ' ', '4', ' ', ' ', '5', ' ', ' ', ' ', ' ', ' ', '5', ' '],\r\n  [' ', '6', '7', ' ', ' ', '4', ' ', '7', ' ', ' ', ' ', ' ', '7', '7', ' '],\r\n  ['3', ' ', ' ', '3', ' ', '5', '7', '7', '6', '4', ' ', '4', ' ', '5', ' '],\r\n  [' ', ' ', '4', ' ', '5', '7', '8', ' ', '5', ' ', '1', '3', '4', '5', ' '],\r\n  [' ', '5', ' ', '4', '3', ' ', ' ', ' ', '7', ' ', '3', ' ', '3', ' ', ' '],\r\n  ['3', ' ', ' ', ' ', ' ', ' ', ' ', '5', ' ', '6', ' ', ' ', ' ', ' ', ' '],\r\n  ['4', ' ', '7', ' ', '5', ' ', ' ', '4', '6', '7', ' ', '3', ' ', '3', ' '],\r\n  ['5', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '6', ' ', ' ', '3', '5', ' ', ' '],\r\n  [' ', ' ', ' ', '5', '4', '5', '3', ' ', '7', ' ', ' ', '5', '6', '6', ' '],\r\n  ['2', ' ', ' ', ' ', '3', '4', ' ', ' ', ' ', '7', ' ', ' ', '7', ' ', '3'],\r\n  ['1', ' ', ' ', '5', ' ', ' ', ' ', '5', ' ', ' ', ' ', '6', ' ', '6', ' '],\r\n  [' ', ' ', '3', ' ', '2', ' ', '3', ' ', '2', ' ', ' ', ' ', ' ', ' ', ' ']\r\n])\r\nbinst = solver.Board(board=board)\r\nsolutions = binst.solve_and_print()\r\n```\r\n**Script Output**\r\n```python\r\nSolution found\r\n[[' ' 'B' ' ' ' ' ' ' ' ' ' ' ' ' 'B' ' ' 'B' ' ' ' ' 'B' 'B']\r\n [' ' 'B' ' ' ' ' 'B' 'B' ' ' 'B' 'B' ' ' 'B' ' ' ' ' 'B' 'B']\r\n [' ' 'B' 'B' ' ' 'B' 'B' ' ' ' ' ' ' 'B' 'B' ' ' ' ' ' ' 'B']\r\n ['B' 'B' 'B' 'B' ' ' ' ' 'B' 'B' 'B' ' ' ' ' ' ' 'B' ' ' 'B']\r\n [' ' 'B' 'B' ' ' ' ' 'B' ' ' 'B' 'B' 'B' ' ' 'B' 'B' 'B' ' ']\r\n [' ' 'B' ' ' 'B' ' ' 'B' 'B' ' ' 'B' ' ' ' ' 'B' 'B' 'B' 'B']\r\n ['B' ' ' 'B' ' ' ' ' 'B' 'B' 'B' 'B' ' ' ' ' ' ' ' ' ' ' ' ']\r\n [' ' ' ' 'B' ' ' 'B' 'B' 'B' 'B' 'B' ' ' ' ' ' ' 'B' ' ' 'B']\r\n [' ' 'B' 'B' ' ' ' ' ' ' ' ' 'B' 'B' 'B' 'B' 'B' ' ' 'B' ' ']\r\n ['B' 'B' 'B' 'B' 'B' 'B' ' ' ' ' ' ' 'B' 'B' ' ' ' ' 'B' ' ']\r\n [' ' 'B' ' ' 'B' 'B' ' ' 'B' ' ' 'B' 'B' ' ' ' ' ' ' 'B' ' ']\r\n ['B' 'B' ' ' ' ' 'B' ' ' ' ' 'B' 'B' 'B' ' ' 'B' 'B' 'B' 'B']\r\n [' ' ' ' 'B' ' ' 'B' ' ' 'B' ' ' 'B' 'B' 'B' 'B' 'B' ' ' 'B']\r\n [' ' ' ' 'B' 'B' ' ' ' ' 'B' ' ' 'B' 'B' ' ' 'B' 'B' ' ' ' ']\r\n ['B' ' ' 'B' ' ' ' ' 'B' 'B' ' ' ' ' ' ' ' ' ' ' 'B' 'B' 'B']]\r\nSolutions found: 1\r\nstatus: OPTIMAL\r\nTime taken: 0.01 seconds\r\n```\r\n\r\n**Solved puzzle**\r\n\r\n<img src=\"https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/mosaic_solved.png\" alt=\"Mosaic solved\" width=\"500\">\r\n\r\n---\r\n\r\n## Map (Puzzle Type #18)\r\n\r\n* [**Play online**](https://www.chiark.greenend.org.uk/~sgtatham/puzzles/js/map.html)\r\n\r\n* [**Instructions**](https://www.chiark.greenend.org.uk/~sgtatham/puzzles/doc/map.html#map)\r\n\r\n* [**Solver Code**][18]\r\n\r\n<details>\r\n  <summary><strong>Rules</strong></summary>\r\nYou are given a map consisting of a number of regions. Your task is to colour each region with one of four colours, in such a way that no two regions sharing a boundary have the same colour. You are provided with some regions already coloured, sufficient to make the remainder of the solution unique, and these cannot be changed.\r\n\r\nOnly regions which share a length of border are required to be different colours. Two regions which meet at only one point (i.e. are diagonally separated) may be the same colour. \r\n</details>\r\n\r\n**Unsolved puzzle**\r\n\r\n<img src=\"https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/map_unsolved.png\" alt=\"Map unsolved\" width=\"500\">\r\n\r\nCode to utilize this package and solve the puzzle:\r\n```python\r\nfrom puzzle_solver import map_solver as solver\r\nregions = {\r\n  0: {1, 11, 12, 27},\r\n  1: {11, 12, 13, 6, 2},\r\n  2: {3, 4, 6, 7, 9, 10},\r\n  # ...\r\n  # ...\r\n  37: {38, 46, 49, 51, 54, 59, 60, 61},\r\n  38: {44, 45, 49, 51, 53, 58, 59},\r\n  39: {40, 46},\r\n  40: {55, 56},\r\n  41: {42, 47},\r\n  42: {48},\r\n  # ...\r\n  # ...\r\n  # ommited for brevity ; this was a pain to type out by hand\r\n}\r\nfixed_colors = {\r\n  0: 'Y', 3: 'R', 7: 'Y', 14: 'Y', 15: 'R', 16: 'Y', 20: 'G', 32: 'B', 33: 'Y', 34: 'R', 35: 'G',\r\n  36: 'B', 39: 'G', 43: 'G', 47: 'R', 55: 'B', 60: 'R', 64: 'G', 66: 'Y', 67: 'G', 73: 'G', 74: 'G',\r\n}\r\nbinst = solver.Board(regions=regions, fixed_colors=fixed_colors)\r\nsolutions = binst.solve_and_print()\r\n```\r\n**Script Output**\r\n```python\r\nSolution found\r\n{0: 'Y', 1: 'R', 2: 'G', 3: 'R', 4: 'B', 5: 'G', 6: 'B', 7: 'Y', 8: 'R', 9: 'Y', 10: 'B', 11: 'G', 12: 'B', 13: 'G', 14: 'Y', 15: 'R', 16: 'Y', 17: 'R', 18: 'G', 19: 'B', 20: 'G', 21: 'Y', 22: 'R', 23: 'Y', 24: 'Y', 25: 'B', 26: 'R', 27: 'G', 28: 'G', 29: 'B', 30: 'B', 31: 'R', 32: 'B', 33: 'Y', 34: 'R', 35: 'G', 36: 'B', 37: 'G', 38: 'B', 39: 'G', 40: 'Y', 41: 'Y', 42: 'R', 43: 'G', 44: 'R', 45: 'Y', 46: 'Y', 47: 'R', 48: 'Y', 49: 'Y', 50: 'G', 51: 'R', 52: 'R', 53: 'Y', 54: 'B', 55: 'B', 56: 'G', 57: 'B', 58: 'R', 59: 'Y', 60: 'R', 61: 'B', 62: 'B', 63: 'Y', 64: 'G', 65: 'R', 66: 'Y', 67: 'G', 68: 'B', 69: 'R', 70: 'Y', 71: 'R', 72: 'B', 73: 'G', 74: 'G'}\r\nSolutions found: 1\r\nstatus: OPTIMAL\r\nTime taken: 0.01 seconds\r\n```\r\n\r\n**Solved puzzle**\r\n\r\n<img src=\"https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/map_solved.png\" alt=\"Map solved\" width=\"500\">\r\n\r\n---\r\n\r\n## Pearl (Puzzle Type #19)\r\n\r\n* [**Play online**](https://www.chiark.greenend.org.uk/~sgtatham/puzzles/js/pearl.html)\r\n\r\n* [**Instructions**](https://www.chiark.greenend.org.uk/~sgtatham/puzzles/doc/pearl.html#pearl)\r\n\r\n* [**Solver Code**][19]\r\n\r\n<details>\r\n  <summary><strong>Rules</strong></summary>\r\nYou have a grid of squares. Your job is to draw lines between the centres of horizontally or vertically adjacent squares, so that the lines form a single closed loop. In the resulting grid, some of the squares that the loop passes through will contain corners, and some will be straight horizontal or vertical lines. (And some squares can be completely empty \u2013 the loop doesn't have to pass through every square.)\r\n\r\nSome of the squares contain black and white circles, which are clues that the loop must satisfy.\r\n\r\nA black circle in a square indicates that that square is a corner, but neither of the squares adjacent to it in the loop is also a corner.\r\n\r\nA white circle indicates that the square is a straight edge, but at least one of the squares adjacent to it in the loop is a corner.\r\n\r\n(In both cases, the clue only constrains the two squares adjacent in the loop, that is, the squares that the loop passes into after leaving the clue square. The squares that are only adjacent in the grid are not constrained.)\r\n</details>\r\n\r\n**Unsolved puzzle**\r\n\r\n<img src=\"https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/pearl_unsolved.png\" alt=\"Pearl unsolved\" width=\"500\">\r\n\r\nCode to utilize this package and solve the puzzle:\r\n```python\r\nimport numpy as np\r\nfrom puzzle_solver import pearl_solver as solver\r\nboard = np.array([\r\n  ['B', ' ', ' ', 'W', ' ', ' ', 'W', ' ', 'B', ' ', ' ', 'B'],\r\n  [' ', ' ', ' ', 'B', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '],\r\n  [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'B', ' ', ' ', ' '],\r\n  [' ', 'B', ' ', 'B', ' ', 'W', ' ', 'B', ' ', 'B', 'W', ' '],\r\n  [' ', ' ', 'B', ' ', 'B', ' ', ' ', ' ', ' ', ' ', ' ', ' '],\r\n  [' ', ' ', ' ', ' ', ' ', ' ', ' ', 'W', 'W', ' ', ' ', 'B'],\r\n  [' ', ' ', 'B', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '],\r\n  ['B', ' ', ' ', ' ', ' ', 'B', 'B', ' ', ' ', ' ', ' ', 'B'],\r\n])\r\nbinst = solver.Board(board)\r\nsolutions = binst.solve_and_print()\r\n```\r\n**Script Output**\r\n```python\r\nSolution found\r\n[['\u250f\u2501' '\u2501\u2501' '\u2501\u2501' '\u2501\u2501' '\u2501\u2512' '\u250f\u2501' '\u2501\u2501' '\u2501\u2512' '\u250f\u2501' '\u2501\u2501' '\u2501\u2501' '\u2501\u2512']\r\n ['\u2503 ' '\u250f\u2501' '\u2501\u2501' '\u2501\u2512' '\u2517\u2501' '\u2501\u251b' '\u250f\u2501' '\u2501\u251b' '\u2503 ' '\u250f\u2501' '\u2501\u2512' '\u2503 ']\r\n ['\u2517\u2501' '\u2501\u251b' '  ' '\u2503 ' '\u250f\u2501' '\u2501\u2512' '\u2517\u2501' '\u2501\u2501' '\u2501\u251b' '\u2503 ' '\u2503 ' '\u2503 ']\r\n ['  ' '\u250f\u2501' '\u2501\u2501' '\u2501\u251b' '\u2503 ' '\u2503 ' '  ' '\u250f\u2501' '\u2501\u2501' '\u2501\u251b' '\u2503 ' '\u2503 ']\r\n ['  ' '\u2503 ' '\u250f\u2501' '\u2501\u2501' '\u2501\u251b' '\u2517\u2501' '\u2501\u2512' '\u2503 ' '\u250f\u2501' '\u2501\u2512' '\u2517\u2501' '\u2501\u251b']\r\n ['\u250f\u2501' '\u2501\u251b' '\u2503 ' '  ' '\u250f\u2501' '\u2501\u2512' '\u2503 ' '\u2503 ' '\u2503 ' '\u2517\u2501' '\u2501\u2501' '\u2501\u2512']\r\n ['\u2503 ' '  ' '\u2517\u2501' '\u2501\u2501' '\u2501\u251b' '\u2503 ' '\u2503 ' '\u2517\u2501' '\u2501\u251b' '  ' '  ' '\u2503 ']\r\n ['\u2517\u2501' '\u2501\u2501' '\u2501\u2501' '\u2501\u2501' '\u2501\u2501' '\u2501\u251b' '\u2517\u2501' '\u2501\u2501' '\u2501\u2501' '\u2501\u2501' '\u2501\u2501' '\u2501\u251b']]\r\nSolutions found: 1\r\nstatus: OPTIMAL\r\nTime taken: 0.98 seconds\r\n```\r\n\r\n**Solved puzzle**\r\n\r\n<img src=\"https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/pearl_solved.png\" alt=\"Pearl solved\" width=\"500\">\r\n\r\n---\r\n\r\n## Bridges (Puzzle Type #20)\r\n\r\n* [**Play online**](https://www.chiark.greenend.org.uk/~sgtatham/puzzles/js/bridges.html)\r\n\r\n* [**Instructions**](https://www.chiark.greenend.org.uk/~sgtatham/puzzles/doc/bridges.html#bridges)\r\n\r\n* [**Solver Code**][20]\r\n\r\n<details>\r\n  <summary><strong>Rules</strong></summary>\r\nYou have a set of islands distributed across the playing area. Each island contains a number. Your aim is to connect the islands together with bridges, in such a way that:\r\n\r\n  - Bridges run horizontally or vertically.\r\n  - The number of bridges terminating at any island is equal to the number written in that island.\r\n  - Two bridges may run in parallel between the same two islands, but no more than two may do so.\r\n  - No bridge crosses another bridge.\r\n  - All the islands are connected together.\r\n\r\nThere are some configurable alternative modes, which involve changing the parallel-bridge limit to something other than 2\r\n</details>\r\n\r\n**Unsolved puzzle**\r\n\r\n<img src=\"https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/bridges_unsolved.png\" alt=\"Bridges unsolved\" width=\"500\">\r\n\r\nCode to utilize this package and solve the puzzle:\r\n```python\r\nimport numpy as np\r\nfrom puzzle_solver import bridges_solver as solver\r\nboard = np.array([\r\n  [' ', ' ', ' ', ' ', ' ', '1', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '3'],\r\n  ['2', ' ', ' ', ' ', ' ', ' ', ' ', '4', ' ', ' ', '4', ' ', ' ', '2', ' '],\r\n  [' ', ' ', ' ', '2', ' ', '4', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '],\r\n  [' ', '1', ' ', ' ', ' ', ' ', ' ', ' ', '1', ' ', '2', ' ', ' ', ' ', '4'],\r\n  [' ', '2', ' ', '3', ' ', '6', ' ', '4', ' ', ' ', '3', ' ', '1', ' ', ' '],\r\n  ['2', ' ', ' ', ' ', '2', ' ', ' ', ' ', '1', ' ', ' ', '2', ' ', ' ', ' '],\r\n  [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '],\r\n  ['2', ' ', ' ', ' ', ' ', ' ', '5', ' ', ' ', '3', ' ', '4', ' ', ' ', ' '],\r\n  [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '],\r\n  [' ', '3', ' ', ' ', ' ', '3', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '],\r\n  [' ', ' ', '2', ' ', '2', ' ', ' ', ' ', ' ', ' ', ' ', '5', ' ', ' ', '4'],\r\n  [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '],\r\n  [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '],\r\n  [' ', ' ', '1', ' ', ' ', '2', ' ', ' ', ' ', '1', ' ', '2', ' ', ' ', ' '],\r\n  [' ', '4', ' ', ' ', '4', ' ', '3', ' ', ' ', ' ', '4', ' ', ' ', ' ', '4'],\r\n])\r\nbinst = solver.Board(board)\r\nsolutions = binst.solve_and_print()\r\n```\r\n**Script Output**\r\n\r\nNote that the four numbers indicate how many bridges in the 4 directions (right, left, down, up) respectively.\r\n```python\r\nSolution found\r\n|    |    |    |    |    |1000|    |    |    |    |    |    |    |    |0120|\r\n\r\n|1010|    |    |    |    |    |    |2110|    |    |2200|    |    |0200|    |\r\n\r\n|    |    |    |2000|    |0220|    |    |    |    |    |    |    |    |    |\r\n\r\n|    |0010|    |    |    |    |    |    |1000|    |1100|    |    |    |0112|\r\n\r\n|    |1001|    |2100|    |2202|    |1201|    |    |1110|    |0100|    |    |\r\n\r\n|1001|    |    |    |1100|    |    |    |0100|    |    |0020|    |    |    |\r\n\r\n|    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |\r\n\r\n|2000|    |    |    |    |    |2210|    |    |0210|    |0022|    |    |    |\r\n\r\n|    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |\r\n\r\n|    |1020|    |    |    |0120|    |    |    |    |    |    |    |    |    |\r\n\r\n|    |    |1010|    |0110|    |    |    |    |    |    |1022|    |    |0121|\r\n\r\n|    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |\r\n\r\n|    |    |    |    |    |    |    |    |    |    |    |    |    |    |    |\r\n\r\n|    |    |0001|    |    |0002|    |    |    |0001|    |0002|    |    |    |\r\n\r\n|    |2002|    |    |1201|    |1101|    |    |    |2101|    |    |    |0202|\r\n\r\nSolutions found: 1\r\nstatus: OPTIMAL\r\nTime taken: 0.01 seconds\r\n```\r\n\r\n**Solved puzzle**\r\n\r\n<img src=\"https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/bridges_solved.png\" alt=\"Bridges solved\" width=\"500\">\r\n\r\n---\r\n\r\n## Inertia (Puzzle Type #21)\r\n\r\nThis solver is a bit different from the other solvers in this repo because this game does not have a unique solution (you simply move the ball to collect all the gems).\r\n\r\nThus the solver was developed with the additional much harder goal of collecting all the gems with the least number of moves.\r\n\r\nIt does so using the following high level steps:\r\n\r\n1. Model the board as a directed graph where the cells are nodes and legal moves as directed edges with unit cost. Each gem has to a group of edges where traversing any one of them collects that gem.\r\n2. Model step (1) as a [Generalized Traveling Salesman Problem (GTSP)](https://en.wikipedia.org/wiki/Set_TSP_problem), where each gem\u2019s edge group forms a cluster.\r\n3. Apply the [Noon\u2013Bean transformation](https://deepblue.lib.umich.edu/bitstream/handle/2027.42/6834/ban3102.0001.001.pdf?sequence=5) **(Noon & Bean, 1991)** to convert the GTSP from step (2) into an equivalent Asymmetric TSP (ATSP) that can be solved with OR-Tools\u2019 routing solver. (Noon-Bean transformation is mentioned but not described in the [TSP wikipedia page](https://en.wikipedia.org/wiki/Travelling_salesman_problem).)\r\n4. Use a [Vehicle Routing Problem (VRP)](https://en.wikipedia.org/wiki/Vehicle_routing_problem) solver using the [OR-Tools VRP solver](https://developers.google.com/optimization/routing/routing_tasks) to solve the ATSP.\r\n\r\nThis achieves a final sequence of moves that is empirically always faster than the website's solution.\r\n\r\n* [**Play online**](https://www.chiark.greenend.org.uk/~sgtatham/puzzles/js/inertia.html)\r\n\r\n* [**Instructions**](https://www.chiark.greenend.org.uk/~sgtatham/puzzles/doc/inertia.html#inertia)\r\n\r\n* [**Solver Code**][21]\r\n\r\n<details>\r\n  <summary><strong>Rules</strong></summary>\r\nYou are a small green ball sitting in a grid full of obstacles. Your aim is to collect all the gems without running into any mines.\r\n\r\nYou can move the ball in any orthogonal or diagonal direction. Once the ball starts moving, it will continue until something stops it. A wall directly in its path will stop it (but if it is moving diagonally, it will move through a diagonal gap between two other walls without stopping). Also, some of the squares are \u2018stops\u2019; when the ball moves on to a stop, it will stop moving no matter what direction it was going in. Gems do not stop the ball; it picks them up and keeps on going.\r\n\r\nRunning into a mine is fatal. Even if you picked up the last gem in the same move which then hit a mine, the game will count you as dead rather than victorious. \r\n</details>\r\n\r\n**Unsolved puzzle**\r\n\r\n<img src=\"https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/inertia_unsolved.png\" alt=\"Inertia unsolved\" width=\"500\">\r\n\r\nCode to utilize this package and solve the puzzle:\r\n\r\n(Note: there is a script that parses a screenshot of the board and outputs the below array that the solver uses. The script uses classical computer vision techniques and is called `parse_map.py`)\r\n```python\r\nimport numpy as np\r\nfrom puzzle_solver import inertia_solver as solver\r\nboard = np.array([\r\n  ['O', 'O', 'M', ' ', 'G', 'O', 'G', 'O', ' ', ' ', 'M', ' ', ' ', 'O', 'G', 'G', 'W', 'O', 'O', 'O'],\r\n  ['O', ' ', 'W', ' ', 'W', 'O', 'G', 'M', ' ', ' ', ' ', 'G', 'M', 'O', 'W', 'G', ' ', 'M', 'M', 'O'],\r\n  ['O', 'M', 'O', 'O', ' ', 'M', ' ', 'W', 'W', 'M', 'G', 'W', ' ', ' ', 'G', ' ', 'W', 'G', 'O', 'G'],\r\n  ['O', ' ', 'O', 'M', 'G', 'O', 'W', 'G', 'M', 'O', ' ', ' ', 'G', 'G', 'G', ' ', 'M', 'W', 'M', 'O'],\r\n  ['M', 'M', 'O', 'G', ' ', 'W', ' ', ' ', 'O', 'G', ' ', 'M', 'M', ' ', 'W', 'W', ' ', 'W', 'W', 'O'],\r\n  ['G', ' ', 'G', 'W', 'M', 'W', 'W', ' ', 'G', 'G', 'W', 'M', 'G', 'G', ' ', 'G', 'O', 'O', 'M', 'M'],\r\n  ['M', ' ', 'M', ' ', 'W', 'W', 'M', 'M', 'M', 'O', 'M', 'G', 'O', 'M', 'M', 'W', 'B', 'O', 'W', 'M'],\r\n  ['G', 'G', ' ', 'W', 'M', 'M', 'W', 'O', 'W', 'G', 'W', 'O', 'O', 'M', ' ', 'W', 'W', 'G', 'G', 'M'],\r\n  [' ', 'M', 'M', ' ', ' ', ' ', 'G', 'G', 'M', 'O', 'M', 'O', 'M', 'G', 'W', 'M', 'W', ' ', 'O', ' '],\r\n  ['G', ' ', 'M', ' ', ' ', ' ', 'W', 'O', 'W', 'W', 'M', 'M', 'G', 'W', ' ', ' ', 'W', 'M', 'G', 'W'],\r\n  ['G', 'O', 'M', 'M', 'G', 'M', 'W', 'O', 'O', 'G', 'W', 'M', 'M', 'G', 'G', ' ', 'O', ' ', 'W', 'W'],\r\n  ['G', 'G', 'W', 'G', 'M', ' ', 'G', 'W', 'W', ' ', 'G', ' ', 'O', 'W', 'G', 'G', 'O', ' ', 'M', 'M'],\r\n  ['W', 'M', 'O', ' ', 'W', 'O', 'O', 'M', 'M', 'O', 'G', 'W', ' ', 'G', 'O', 'G', 'G', 'O', 'O', 'W'],\r\n  ['W', 'W', 'W', ' ', 'W', 'O', 'W', 'M', 'O', 'M', 'G', 'O', 'O', ' ', ' ', 'W', 'W', 'G', 'W', 'W'],\r\n  ['O', 'W', 'O', 'M', 'O', 'G', ' ', 'O', 'O', 'M', 'O', ' ', 'M', 'M', 'O', 'G', 'W', 'G', 'M', ' '],\r\n  ['M', 'G', 'O', 'G', 'O', 'G', 'O', 'G', ' ', 'W', 'W', 'G', 'O', ' ', 'W', 'M', 'G', ' ', 'W', ' ']\r\n])\r\nstart_pos, edges, edges_to_direction, gems_to_edges = solver.parse_nodes_and_edges(board)\r\noptimal_walk = solver.solve_optimal_walk(start_pos, edges, gems_to_edges)\r\nmoves = solver.get_moves_from_walk(optimal_walk, edges_to_direction, verbose=True)\r\n```\r\n**Script Output**\r\n\r\nNote that the output is the sequence of moves to collect all the gems. This particular solution is 106 moves, which is 15 moves better than the website's solution.\r\n```python\r\nnumber of moves 106\r\n\u2197 \u2196 \u2196 \u2199 \u2199 \u2196 \u2196 \u2199 \u2192 \u2198 \r\n\u2199 \u2192 \u2196 \u2192 \u2199 \u2193 \u2192 \u2198 \u2197 \u2193\r\n\u2198 \u2192 \u2198 \u2193 \u2197 \u2193 \u2191 \u2192 \u2197 \u2196\r\n\u2191 \u2197 \u2191 \u2197 \u2192 \u2193 \u2190 \u2199 \u2196 \u2197\r\n\u2193 \u2199 \u2199 \u2191 \u2190 \u2198 \u2199 \u2193 \u2192 \u2198\r\n\u2198 \u2199 \u2196 \u2199 \u2197 \u2198 \u2197 \u2198 \u2191 \u2198\r\n\u2196 \u2191 \u2197 \u2192 \u2192 \u2198 \u2192 \u2198 \u2197 \u2191\r\n\u2190 \u2191 \u2196 \u2196 \u2197 \u2192 \u2198 \u2193 \u2196 \u2190\r\n\u2196 \u2193 \u2190 \u2193 \u2193 \u2191 \u2196 \u2192 \u2197 \u2197\r\n\u2198 \u2198 \u2199 \u2198 \u2193 \u2197 \u2196 \u2198 \u2199 \u2190\r\n\u2198 \u2196 \u2197 \u2191 \u2197 \u2192\r\nTime taken: 13.92 seconds\r\n```\r\n\r\n**Solved puzzle**\r\n\r\nThis picture won't mean much as the game is about the sequence of moves not the final frame as shown here.\r\n\r\n<img src=\"https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/inertia_solved.png\" alt=\"Inertia solved\" width=\"500\">\r\n\r\n---\r\n\r\n## Guess (Puzzle Type #22)\r\n\r\n* [**Play online**](https://www.chiark.greenend.org.uk/~sgtatham/puzzles/js/guess.html)\r\n\r\n* [**Instructions**](https://www.chiark.greenend.org.uk/~sgtatham/puzzles/doc/guess.html#guess)\r\n\r\n* [**Solver Code**][22]\r\n\r\n<details>\r\n  <summary><strong>Rules</strong></summary>\r\nYou have a set of coloured pegs, and have to reproduce a predetermined sequence of them (chosen by the computer) within a certain number of guesses.\r\n\r\nEach guess gets marked with the number of correctly-coloured pegs in the correct places (in black), and also the number of correctly-coloured pegs in the wrong places (in white). \r\n</details>\r\n\r\n\r\nUnlike most other puzzles in this repo, 'Guess' is very different. Similar to minesweeper, Guess is a limited information dynamic puzzle where the next best move depends on information revealed by previous moves (The similarities to minesweeper stop here).\r\n\r\nThe solver is designed to take the state of the board at any timestep and always gives the next optimal guess. This might seem like an impossible task at first but it's actually not too bad. The optimal guess is defined to be the one that maximizes the Shannon entropy (i.e. maximizes the expected information gain).\r\n\r\nThe steps below formaly describe the algorithm that is also used in [this amazing 3Blue1Brown video](https://www.youtube.com/watch?v=v68zYyaEmEA) on solving Wordle using information theory. Where 3Blue1Browne describes the same steps below but for a slightly harder problem to solve wordle (a very similar game). The video intuitively justifies this algorithm and builds it from scratch using basic intuition.\r\n\r\nTo formalize the algorithm, let's first define our three inputs as\r\n- $N :=$ the number of pegs (the length of every guess) \r\n  - must have $N \\geq 1$ and by default $N = 4$ in the game\r\n- $C :=$ the set of possible colors \r\n  - what actually matters is $|C|$, the number of possible choices for each peg, i.e. the number of colors\r\n  - by default in the game, $C = \\{R,Y,G,B,O,P\\}$ (six distinct symbols; only $|C|$ matters) for Red, Yellow, Green, Blue, Orange, and Purple.\r\n- $\\mathrm{MR} := ((m_1, r_1), (m_2, r_2), ..., (m_k, r_k))$ be the sequence of previous guesses and results where $(m_i, r_i)$ is the previous guess and result at round $i$ and $k\\geq 0$ is the number of previous guesses the player has made \r\n  - Note that $m_i$ has length $N$ and each element is $\\in C$ by definition\r\n  - $r_i$ is a triplet of non-negative integers that sum to $N$ by definition. This corresponds to counts of exact-match positions, color-only matches, and non-matches (visualized as black, white, and grey dots)\r\n\r\nThe algorithm is as follows\r\n\r\n1. Define $G$ as the set of every possible guess that can be made\r\n\r\n   $$G := \\{(c_1, \\dots, c_N) \\mid \\forall i \\in \\{1, \\dots, N\\},\\ c_i \\in C \\}$$\r\n\r\n    1. Note that $|G| = |C|^N$\r\n\r\n    2. Note that $m_i \\in G$ for all $i \\in \\{1, 2, ..., k\\}$ by definition.\r\n\r\n2. Define $T$ as the set of every possible result triplet \r\n\r\n    $$T := \\{(t_1, t_2, t_3) \\in \\mathbb{N}_0^3 : t_1 + t_2 + t_3 = N\\}$$\r\n\r\n    1. Note that $r_i \\in T$ for all $i \\in \\{1, 2, ..., k\\}$ by definition.\r\n    2. Note that $|T|=\\binom{N+2}{2}$ (stars-and-bars)\r\n    3. By default, $N = 4$ in the game so $|T|=15$\r\n\r\n3. Define $f : G \\times G \\to T$ by $f(g_{\\text{guess}}, g_{\\text{truth}}) = t$ as the result triplet $(t_1, t_2, t_3)$ obtained when guessing $g_{\\text{guess}}$ against ground truth $g_{\\text{truth}}$. It is trivial to algorithmically make this function which simply counts from $g_1$ and $g_2.$ Look at the function `get_triplets` for a naive implementation of this.\r\n\r\n4. Define $S$ as the subset of $G$ that is consistent with the previous guesses $m_i$ and results $r_i$\r\n\r\n    $$\r\n    S := \\{g \\in G : \\forall i \\in \\{1, 2, ..., k\\}, f(m_i, g) = r_i\\}\r\n    $$\r\n    1. Note that if there aren't previous guesses ($\\mathrm{MR} = \\emptyset$) then $S = G$\r\n    2. Note that if $S = \\emptyset$ then something is wrong with the previous guesses $\\mathrm{MR}$ and there is no possible solution to the puzzle. The algorithm stops here and informs the user that the puzzle is unsolvable with the given guesses $\\mathrm{MR}$ and that this should never happen unless there is a typo in the guesses $\\mathrm{MR}$ (which is usually the case).\r\n\r\n5. For each possible guess $g \\in G$ and each triplet $t \\in T$, count the number of possible solutions $s \\in S$ that result in the triplet $t$ when guessing $g$. i.e.\r\n\r\n    $$D(g, t) := |\\{s \\in S: f(g, s) = t\\}|$$\r\n\r\n6. Calculate the entropy for each possible guess $g \\in G$ as the sum of probability times the self-information for every triplet $t \\in T$. i.e.\r\n\r\n    $$H : G \\to \\mathbb{R}, \\quad H(g) = -\\sum_{t \\in T} P(t \\mid g) \\log_2 P(t \\mid g)$$\r\n\r\n   1. where $P(t \\mid g) = \\frac{D(g, t)}{|S|}$\r\n   2. By convention, terms with $P(t \\mid g)=0$ contribute $0$ to the sum (interpreting $0\\log 0 := 0)$.\r\n\r\n7. Return the guess $g \\in G$ that maximizes the entropy $H(g)$ (to break ties, choose $g$ that is also in $S$ such that it's possibly the correct solution as well, break further ties arbitrarily).\r\n   1. i.e. return any $g^*\\in (\\mathrm{argmax}_{g\\in G}\\ H(g) \\cap S)$ if exists\r\n   2. otherwise return any $g\\in \\mathrm{argmax}_{g\\in G}\\ H(g)$.\r\n\r\n\r\nIf you are at all interested in the above steps and want to understand more, \r\nI highly recommend watching [this amazing 3Blue1Brown video](https://www.youtube.com/watch?v=v68zYyaEmEA) on solving Wordle using information theory where he describes the same steps but a bits more complicated problem to solve Wordle (a very similar game).\r\n\r\nBelow is an example of how to utilize the solver while in the middle of a puzzle.\r\n\r\n(This is the only solver that under the hood does not utilize any packages besides numpy)\r\n\r\n**Unsolved puzzle**\r\n\r\nLet's say we start and made two guesses to end up with the following puzzle:\r\n\r\n<img src=\"https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/guess_1.png\" alt=\"Guess Pre Move\" width=\"500\">\r\n\r\nCode to utilize this package and solve the puzzle:\r\n\r\nWe encode the puzzle as a Board object then retreive the optimal next guess:\r\n```python\r\nfrom puzzle_solver import guess_solver as solver\r\nbinst = solver.Board()\r\nbinst.add_guess(('R', 'Y', 'G', 'B'), (1, 1, 2))  # 1 black dot, 1 white dot, 2 grey dots\r\nbinst.add_guess(('R', 'G', 'O', 'P'), (0, 2, 2))  # 0 black dots, 2 white dots, 2 grey dots\r\nbinst.best_next_guess()\r\n```\r\n\r\nNote: the three numbers in each guess is the result of the guess: (# of black dots, # of white dots, # of grey dots)\r\n\r\nNote: by default, the board will have 4 circles and 6 possible colors (R: Red, Y: Yellow, G: Green, B: Blue, O: Orange, P: Purple) but both of these are optional parameters to the Board to change behavior.\r\n\r\n**Script Output 1/2**\r\n\r\nNote that the output is next optimal guess that has the maximum Shannon entropy.\r\n```python\r\nout of 1296 possible ground truths, only 57 are still possible.\r\nmax entropy guess is: ['P', 'Y', 'Y', 'G'] with entropy 3.4511\r\n```\r\n\r\nSo we make our next guess as (Purple, Yellow, Yellow, Green) and let's say we get this result: (2 black, 1 white, 1 grey)\r\n\r\n<img src=\"https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/guess_2.png\" alt=\"Guess Post 1 Move\" width=\"500\">\r\n\r\nSo we input that again to the solver to retreive the next optimal guess:\r\n\r\n```python\r\nfrom puzzle_solver import guess_solver as solver\r\nbinst = solver.Board()\r\nbinst.add_guess(('R', 'Y', 'G', 'B'), (1, 1, 2))  # 1 black dot, 1 white dot, 2 grey dots\r\nbinst.add_guess(('R', 'G', 'O', 'P'), (0, 2, 2))  # 0 black dots, 2 white dots, 2 grey dots\r\nbinst.add_guess(('P', 'Y', 'Y', 'G'), (2, 1, 1))  # 2 black dots, 1 white dot, 1 grey dot\r\nbinst.best_next_guess()\r\n```\r\n\r\n**Script Output 2/2**\r\n\r\n```python\r\nout of 1296 possible ground truths, only 3 are still possible.\r\nmax entropy guess is: ['G', 'Y', 'Y', 'O'] with entropy 1.5850\r\n```\r\n\r\nSo we make our fourth guess as (Green, Yellow, Yellow, Orange) \r\n\r\nWhen we input the guess, we see that we correctly solve the puzzle!\r\n\r\n<img src=\"https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/guess_3.png\" alt=\"Guess Post 2 Moves\" width=\"500\">\r\n\r\nNote that in this case, the correct guess was among multiple possible guesses\r\n\r\nIn the case when there's only one possible choice left, the solver will inform you that it's the garunteed solution.\r\n\r\n---\r\n\r\n## Chess Range (Puzzle Type #23)\r\n\r\n* [**Play online**](https://www.puzzle-chess.com/chess-ranger-11/)\r\n\r\n* [**Solver Code**][23]\r\n\r\n<details>\r\n  <summary><strong>Rules</strong></summary>\r\n\r\nYou are given a chess board with $N$ pieces distributed on it. Your aim is to make $N-1$ sequence of moves where each move is a legal chess move and captures another piece.\r\n\r\n- Pieces move as standard chess pieces.\r\n- You can perform only capture moves. A move that does not capture another piece is not allowed.\r\n- You are allowed to capture the king.\r\n- The goal is to end up with one single piece on the board. \r\n\r\n</details>\r\n\r\n**Unsolved puzzle**\r\n\r\n<img src=\"https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/chess_range_unsolved.png\" alt=\"Chess range unsolved\" width=\"500\">\r\n\r\nCode to utilize this package and solve the puzzle:\r\n\r\n(Note that this puzzle does not typically have a unique solution. Thus, we specify here that we only want the first valid solution that the solver finds.)\r\n\r\n```python\r\nfrom puzzle_solver import chess_range_solver as solver\r\n# algebraic notation\r\nboard = ['Qe7', 'Nc6', 'Kb6', 'Pb5', 'Nf5', 'Pg4', 'Rb3', 'Bc3', 'Pd3', 'Pc2', 'Rg2']\r\nbinst = solver.Board(board)\r\nsolutions = binst.solve_and_print(max_solutions=1)\r\n```\r\n**Script Output**\r\n\r\nThe output is in the form of \"pos -> pos\" where \"pos\" is the algebraic notation of the position.\r\n\r\n```python\r\nSolution found\r\n['Rg2->Pc2', 'Rc2->Bc3', 'Rc3->Pd3', 'Kb6->Pb5', 'Pg4->Nf5', 'Rd3->Rb3', 'Rb3->Kb5', 'Nc6->Qe7', 'Ne7->Pf5', 'Rb5->Nf5']\r\nSolutions found: 1\r\nstatus: FEASIBLE\r\nTime taken: 1.16 seconds\r\n```\r\n\r\n**Solved puzzle**\r\n\r\n<img src=\"https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/chess_range_solved.png\" alt=\"Chess range solved\" width=\"500\">\r\n\r\n---\r\n\r\n## Chess Solo (Puzzle Type #24)\r\n\r\n* [**Play online**](https://www.puzzle-chess.com/solo-chess-11/)\r\n\r\n* [**Solver Code**][24]\r\n\r\n<details>\r\n  <summary><strong>Rules</strong></summary>\r\n\r\nYou are given a chess board with $N$ pieces distributed on it. Your aim is to make $N-1$ sequence of moves where each move is a legal chess move and captures another piece and end up with the king as the only piece on the board. You are not allowed to move a piece more than twice.\r\n\r\n- Pieces move as standard chess pieces.\r\n- You can perform only capture moves. A move that does not capture another piece is not allowed.\r\n- You can move a piece only twice.\r\n- You are NOT allowed to capture the king.\r\n- The goal is to end up with one single piece (the king) on the board. \r\n\r\n</details>\r\n\r\n**Unsolved puzzle**\r\n\r\n<img src=\"https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/chess_solo_unsolved.png\" alt=\"Chess solo unsolved\" width=\"500\">\r\n\r\nCode to utilize this package and solve the puzzle:\r\n\r\n(Note that this puzzle does not typically have a unique solution. Thus, we specify here that we only want the first valid solution that the solver finds.)\r\n\r\n```python\r\nfrom puzzle_solver import chess_solo_solver as solver\r\n# algebraic notation\r\nboard = ['Kc6', 'Rc5', 'Rc4', 'Pb3', 'Bd3', 'Pd2', 'Pe3', 'Nf2', 'Ng2', 'Qg3', 'Pg6']\r\nbinst = solver.Board(board)\r\nsolutions = binst.solve_and_print(max_solutions=1)\r\n```\r\n**Script Output**\r\n\r\nThe output is in the form of \"pos -> pos\" where \"pos\" is the algebraic notation of the position.\r\n\r\n```python\r\nSolution found\r\n['Qg3->Pg6', 'Qg6->Bd3', 'Pd2->Pe3', 'Ng2->Pe3', 'Nf2->Qd3', 'Ne3->Rc4', 'Pb3->Nc4', 'Nd3->Rc5', 'Kc6->Nc5', 'Kc5->Pc4']\r\nSolutions found: 1\r\nstatus: FEASIBLE\r\nTime taken: 0.47 seconds\r\n```\r\n\r\n**Solved puzzle**\r\n\r\n<img src=\"https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/chess_solo_solved.png\" alt=\"Chess solo solved\" width=\"500\">\r\n\r\n---\r\n\r\n## Chess Melee (Puzzle Type #25)\r\n\r\n* [**Play online**](https://www.puzzle-chess.com/chess-melee-13/)\r\n\r\n* [**Solver Code**][25]\r\n\r\n<details>\r\n  <summary><strong>Rules</strong></summary>\r\n\r\nYou are given a chess board with $N$ pieces distributed on it (equal white and black pieces, one more black if $N$ is odd). Your aim is to make $N-1$ sequence of moves where each move is a legal chess move and captures another piece of the opposite color and end up with a single piece on the board. White starts and colors alternate as usual.\r\n\r\n- Pieces move as standard chess pieces.\r\n- White moves first.\r\n- You can perform only capture moves. A move that does not capture another piece of the opposite color is not allowed.\r\n- The goal is to end up with one single piece on the board. \r\n\r\n</details>\r\n\r\n**Unsolved puzzle**\r\n\r\n<img src=\"https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/chess_melee_unsolved.png\" alt=\"Chess melee unsolved\" width=\"500\">\r\n\r\nCode to utilize this package and solve the puzzle:\r\n\r\n(Note that this puzzle does not typically have a unique solution. Thus, we specify here that we only want the first valid solution that the solver finds.)\r\n\r\n```python\r\nfrom puzzle_solver import chess_melee_solver as solver\r\n# algebraic notation\r\nboard = ['Pb7', 'Nc7', 'Bc6', 'Ne6', 'Pb5', 'Rc4', 'Qb3', 'Rf7', 'Rb6', 'Pe5', 'Nc3', 'Pd3', 'Nf3']\r\ncolors = ['B', 'B', 'B', 'B', 'B', 'B', 'B', 'W', 'W', 'W', 'W', 'W', 'W']\r\nbinst = solver.Board(board, colors)\r\nsolutions = binst.solve_and_print()\r\n```\r\n**Script Output**\r\n\r\nThe output is in the form of \"pos -> pos\" where \"pos\" is the algebraic notation of the position.\r\n\r\n```python\r\nSolution found\r\n['Rf7->Nc7', 'Ne6->Rc7', 'Pd3->Rc4', 'Qb3->Nc3', 'Pc4->Pb5', 'Qc3->Pe5', 'Nf3->Qe5', 'Nc7->Pb5', 'Ne5->Bc6', 'Pb7->Nc6', 'Rb6->Nb5', 'Pc6->Rb5']\r\nSolutions found: 1\r\nstatus: OPTIMAL\r\nTime taken: 6.24 seconds\r\n```\r\n\r\n**Solved puzzle**\r\n\r\n<img src=\"https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/chess_melee_solved.png\" alt=\"Chess melee solved\" width=\"500\">\r\n\r\n---\r\n\r\n## Thermometers (Puzzle Type #26)\r\n\r\n* [**Play online**](https://www.puzzle-thermometers.com/)\r\n\r\n* [**Solver Code**][26]\r\n\r\n<details>\r\n  <summary><strong>Rules</strong></summary>\r\n\r\nYou have to fill some thermometers with mercury starting from the bulb and going toward the end without gaps.\r\n\r\nThe numbers outside the grid show the number of filled cells horizontally and vertically. \r\n\r\n</details>\r\n\r\n**Unsolved puzzle**\r\n\r\n<img src=\"https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/thermometers_unsolved.png\" alt=\"Thermometers unsolved\" width=\"500\">\r\n\r\nCode to utilize this package and solve the puzzle:\r\n\r\n```python\r\nfrom puzzle_solver import thermometers_solver as solver\r\nboard = np.array([\r\n  ['R', 'R', 'D', 'R', 'D', 'R', 'X', 'D', 'L', 'X', 'L', 'L', 'L', 'L', 'L'],\r\n  ['D', 'D', 'D', 'U', 'X', 'U', 'X', 'R', 'R', 'R', 'R', 'D', 'D', 'R', 'U'],\r\n  ['D', 'D', 'D', 'U', 'X', 'U', 'U', 'R', 'R', 'R', 'X', 'D', 'D', 'D', 'D'],\r\n  ['X', 'D', 'D', 'U', 'U', 'U', 'L', 'U', 'R', 'R', 'D', 'X', 'D', 'X', 'X'],\r\n  ['X', 'D', 'D', 'U', 'U', 'R', 'R', 'R', 'R', 'X', 'R', 'X', 'D', 'R', 'X'],\r\n  ['U', 'D', 'D', 'U', 'U', 'R', 'X', 'R', 'R', 'R', 'R', 'D', 'D', 'R', 'D'],\r\n  ['U', 'D', 'D', 'R', 'R', 'X', 'R', 'R', 'R', 'R', 'D', 'D', 'R', 'X', 'D'],\r\n  ['U', 'D', 'D', 'U', 'X', 'L', 'X', 'L', 'R', 'X', 'X', 'R', 'X', 'X', 'L'],\r\n  ['U', 'D', 'D', 'R', 'X', 'U', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L', 'L'],\r\n  ['X', 'D', 'X', 'U', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'D', 'U'],\r\n  ['U', 'D', 'X', 'U', 'R', 'R', 'X', 'R', 'R', 'R', 'R', 'X', 'X', 'L', 'U'],\r\n  ['U', 'R', 'U', 'U', 'R', 'X', 'R', 'X', 'R', 'X', 'R', 'R', 'R', 'R', 'U'],\r\n  ['U', 'R', 'X', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'R', 'X', 'X', 'L'],\r\n  ['U', 'U', 'R', 'R', 'X', 'D', 'R', 'R', 'D', 'R', 'X', 'X', 'L', 'L', 'U'],\r\n  ['U', 'U', 'U', 'L', 'L', 'R', 'X', 'X', 'L', 'U', 'R', 'R', 'R', 'U', 'U'],\r\n])\r\ntop = np.array([7, 4, 12, 8, 4, 6, 5, 7, 5, 4, 8, 9, 13, 8, 12])\r\nside = np.array([8, 10, 9, 10, 6, 10, 4, 6, 6, 10, 5, 7, 6, 6, 9])\r\nbinst = solver.Board(board=board, top=top, side=side)\r\nsolutions = binst.solve_and_print()\r\n```\r\n**Script Output**\r\n\r\n```python\r\nSolution found\r\n[['X' 'X' 'X' ' ' ' ' ' ' ' ' 'X' 'X' ' ' ' ' ' ' 'X' 'X' 'X']\r\n ['X' ' ' 'X' ' ' ' ' ' ' ' ' 'X' 'X' 'X' 'X' 'X' 'X' 'X' 'X']\r\n ['X' ' ' 'X' 'X' ' ' 'X' 'X' 'X' ' ' ' ' ' ' 'X' 'X' ' ' 'X']\r\n [' ' ' ' 'X' 'X' ' ' 'X' 'X' 'X' 'X' 'X' 'X' ' ' 'X' ' ' 'X']\r\n [' ' ' ' 'X' 'X' ' ' ' ' ' ' ' ' ' ' ' ' 'X' ' ' 'X' 'X' 'X']\r\n [' ' ' ' 'X' 'X' ' ' ' ' ' ' 'X' 'X' 'X' 'X' 'X' 'X' 'X' 'X']\r\n [' ' ' ' 'X' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' 'X' 'X' ' ' 'X']\r\n [' ' ' ' 'X' ' ' ' ' ' ' 'X' 'X' ' ' ' ' ' ' 'X' 'X' ' ' 'X']\r\n [' ' ' ' 'X' ' ' ' ' ' ' ' ' ' ' ' ' ' ' 'X' 'X' 'X' 'X' 'X']\r\n [' ' ' ' ' ' ' ' 'X' 'X' 'X' 'X' 'X' 'X' 'X' 'X' 'X' ' ' 'X']\r\n [' ' ' ' ' ' 'X' 'X' 'X' 'X' ' ' ' ' ' ' ' ' ' ' ' ' ' ' 'X']\r\n ['X' ' ' ' ' 'X' ' ' ' ' ' ' ' ' ' ' ' ' 'X' 'X' 'X' 'X' 'X']\r\n ['X' 'X' 'X' 'X' 'X' 'X' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ']\r\n ['X' 'X' 'X' ' ' ' ' 'X' ' ' ' ' ' ' ' ' ' ' ' ' 'X' 'X' ' ']\r\n ['X' 'X' 'X' 'X' 'X' ' ' ' ' ' ' ' ' ' ' 'X' 'X' 'X' 'X' ' ']]\r\nSolutions found: 1\r\nstatus: OPTIMAL\r\nTime taken: 0.01 seconds\r\n```\r\n\r\n**Solved puzzle**\r\n\r\n<img src=\"https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/thermometers_solved.png\" alt=\"Thermometers solved\" width=\"500\">\r\n\r\n---\r\n\r\n## Aquarium (Puzzle Type #27)\r\n\r\n* [**Play online**](https://www.puzzle-aquarium.com/)\r\n\r\n* [**Solver Code**][27]\r\n\r\n<details>\r\n  <summary><strong>Rules</strong></summary>\r\n\r\nThe puzzle is played on a rectangular grid divided into blocks called \"aquariums\"\r\n\r\nYou have to \"fill\" the aquariums with water up to a certain level or leave it empty.\r\n\r\nThe water level in each aquarium is one and the same across its full width\r\n\r\nThe numbers outside the grid show the number of filled cells horizontally and vertically. \r\n\r\n</details>\r\n\r\n**Unsolved puzzle**\r\n\r\n<img src=\"https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/aquarium_unsolved.png\" alt=\"Aquarium unsolved\" width=\"500\">\r\n\r\nCode to utilize this package and solve the puzzle:\r\n\r\n```python\r\nfrom puzzle_solver import aquarium_solver as solver\r\nboard = np.array([\r\n  ['01', '01', '01', '01', '02', '02', '02', '03', '03', '03', '03', '04', '05', '05', '05'],\r\n  ['01', '02', '02', '02', '02', '06', '07', '07', '03', '08', '03', '04', '04', '05', '09'],\r\n  ['01', '01', '02', '11', '06', '06', '06', '12', '12', '08', '13', '13', '13', '09', '09'],\r\n  ['01', '11', '11', '11', '14', '06', '06', '12', '12', '15', '15', '13', '09', '09', '09'],\r\n  ['01', '01', '11', '11', '14', '12', '12', '12', '16', '16', '15', '13', '13', '17', '09'],\r\n  ['45', '11', '11', '14', '14', '12', '42', '42', '42', '15', '15', '13', '13', '17', '18'],\r\n  ['45', '11', '11', '14', '14', '12', '12', '43', '15', '15', '20', '13', '13', '17', '18'],\r\n  ['46', '46', '11', '19', '19', '19', '43', '43', '44', '20', '20', '20', '13', '17', '18'],\r\n  ['46', '22', '23', '23', '23', '19', '43', '21', '21', '24', '24', '24', '25', '17', '17'],\r\n  ['22', '22', '22', '23', '19', '19', '26', '24', '24', '24', '28', '28', '25', '17', '33'],\r\n  ['22', '22', '23', '23', '27', '27', '26', '26', '24', '24', '29', '29', '25', '25', '33'],\r\n  ['22', '22', '35', '27', '27', '26', '26', '26', '26', '30', '30', '30', '25', '34', '34'],\r\n  ['37', '22', '35', '35', '35', '35', '35', '26', '26', '30', '31', '31', '32', '32', '40'],\r\n  ['37', '37', '37', '36', '36', '35', '26', '26', '26', '40', '40', '40', '40', '40', '40'],\r\n  ['37', '37', '37', '37', '35', '35', '38', '38', '39', '39', '40', '40', '40', '41', '41'],\r\n])\r\ntop = np.array([6, 6, 5, 3, 3, 4, 7, 6, 9, 6, 3, 4, 9, 6, 7])\r\nside = np.array([3, 5, 1, 2, 5, 3, 10, 10, 5, 3, 7, 3, 7, 8, 12])\r\nbinst = solver.Board(board=board, top=top, side=side)\r\nsolutions = binst.solve_and_print()\r\n```\r\n**Script Output**\r\n\r\n```python\r\nSolution found\r\n[['0' '0' '0' '0' '0' '0' '0' '0' '0' '0' '0' '0' '1' '1' '1']\r\n ['0' '0' '0' '0' '0' '0' '0' '0' '1' '0' '1' '1' '1' '1' '0']\r\n ['0' '0' '0' '0' '0' '0' '0' '0' '0' '1' '0' '0' '0' '0' '0']\r\n ['0' '0' '0' '0' '0' '1' '1' '0' '0' '0' '0' '0' '0' '0' '0']\r\n ['1' '1' '0' '0' '0' '0' '0' '0' '1' '1' '0' '0' '0' '0' '1']\r\n ['0' '0' '0' '0' '0' '0' '1' '1' '1' '0' '0' '0' '0' '0' '0']\r\n ['1' '1' '1' '0' '0' '1' '1' '0' '1' '1' '0' '1' '1' '0' '1']\r\n ['1' '1' '1' '0' '0' '0' '1' '1' '0' '1' '1' '1' '1' '0' '1']\r\n ['1' '0' '0' '0' '0' '0' '1' '1' '1' '0' '0' '0' '1' '0' '0']\r\n ['0' '0' '0' '0' '1' '1' '0' '0' '0' '0' '0' '0' '1' '0' '0']\r\n ['0' '0' '1' '1' '0' '0' '0' '0' '1' '1' '0' '0' '1' '1' '1']\r\n ['0' '0' '0' '0' '0' '0' '0' '0' '0' '0' '0' '0' '1' '1' '1']\r\n ['0' '1' '0' '0' '0' '0' '0' '1' '1' '0' '1' '1' '1' '1' '0']\r\n ['1' '1' '1' '1' '1' '0' '1' '1' '1' '0' '0' '0' '0' '0' '0']\r\n ['1' '1' '1' '1' '1' '1' '1' '1' '1' '1' '0' '0' '0' '1' '1']]\r\nSolutions found: 1\r\nstatus: OPTIMAL\r\nTime taken: 0.02 seconds\r\n```\r\n\r\n**Solved puzzle**\r\n\r\n<img src=\"https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/aquarium_solved.png\" alt=\"Aquarium solved\" width=\"500\">\r\n\r\n---\r\n\r\n## Stitches (Puzzle Type #28)\r\n\r\n* [**Play online**](https://www.puzzle-stitches.com/)\r\n\r\n* [**Solver Code**][28]\r\n\r\n<details>\r\n  <summary><strong>Rules</strong></summary>\r\n\r\n- Connect each block with ALL its neighbor blocks with exactly 1 \"stitch\" each.\r\n- A \"stitch\" connects 2 orthogonally adjacent cells from different blocks.\r\n- 2 stitches cannot share a hole.\r\n- The clues outside the grid indicate the number of holes on that row/column\r\n- For 2\u00f7 puzzles, you have to use 2 stitches to connect neighbor blocks, for 3\u00f7 puzzles - 3 stitches etc.\r\n\r\n</details>\r\n\r\n**Unsolved puzzle**\r\n\r\n<img src=\"https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/stitches_unsolved.png\" alt=\"Stitches unsolved\" width=\"500\">\r\n\r\nCode to utilize this package and solve the puzzle:\r\n\r\n```python\r\nfrom puzzle_solver import stitches_solver as solver\r\nboard = np.array([\r\n  [\"00\", \"00\", \"00\", \"00\", \"00\", \"01\", \"01\", \"01\", \"01\", \"01\", \"01\", \"01\", \"01\", \"02\", \"02\"],\r\n  [\"00\", \"03\", \"03\", \"04\", \"00\", \"00\", \"01\", \"05\", \"05\", \"05\", \"05\", \"05\", \"01\", \"01\", \"02\"],\r\n  [\"00\", \"03\", \"04\", \"04\", \"04\", \"00\", \"05\", \"05\", \"05\", \"05\", \"05\", \"05\", \"05\", \"05\", \"02\"],\r\n  [\"00\", \"03\", \"04\", \"04\", \"04\", \"04\", \"05\", \"05\", \"06\", \"05\", \"02\", \"02\", \"02\", \"02\", \"02\"],\r\n  [\"07\", \"03\", \"03\", \"03\", \"03\", \"04\", \"06\", \"06\", \"06\", \"06\", \"06\", \"06\", \"06\", \"02\", \"02\"],\r\n  [\"07\", \"07\", \"07\", \"03\", \"03\", \"04\", \"04\", \"06\", \"08\", \"08\", \"08\", \"06\", \"02\", \"02\", \"02\"],\r\n  [\"07\", \"07\", \"03\", \"03\", \"03\", \"04\", \"04\", \"08\", \"08\", \"08\", \"08\", \"06\", \"06\", \"06\", \"02\"],\r\n  [\"07\", \"07\", \"07\", \"07\", \"07\", \"08\", \"08\", \"08\", \"09\", \"09\", \"08\", \"06\", \"08\", \"06\", \"02\"],\r\n  [\"10\", \"10\", \"07\", \"07\", \"09\", \"09\", \"09\", \"09\", \"09\", \"09\", \"08\", \"08\", \"08\", \"11\", \"02\"],\r\n  [\"10\", \"10\", \"07\", \"09\", \"09\", \"09\", \"09\", \"09\", \"09\", \"09\", \"09\", \"08\", \"08\", \"11\", \"02\"],\r\n  [\"10\", \"09\", \"09\", \"09\", \"12\", \"12\", \"12\", \"13\", \"09\", \"09\", \"11\", \"11\", \"11\", \"11\", \"11\"],\r\n  [\"10\", \"10\", \"10\", \"09\", \"12\", \"12\", \"12\", \"13\", \"09\", \"11\", \"11\", \"11\", \"13\", \"13\", \"11\"],\r\n  [\"14\", \"15\", \"10\", \"12\", \"12\", \"16\", \"17\", \"13\", \"13\", \"11\", \"13\", \"13\", \"13\", \"13\", \"11\"],\r\n  [\"14\", \"15\", \"10\", \"12\", \"16\", \"16\", \"17\", \"17\", \"13\", \"13\", \"13\", \"13\", \"13\", \"13\", \"11\"],\r\n  [\"14\", \"15\", \"15\", \"12\", \"16\", \"16\", \"17\", \"17\", \"17\", \"17\", \"17\", \"13\", \"13\", \"13\", \"13\"]\r\n])\r\ntop = np.array([6, 6, 9, 5, 3, 8, 9, 3, 1, 4, 4, 1, 4, 8, 5])\r\nside = np.array([0, 10, 6, 4, 4, 1, 5, 8, 2, 6, 5, 11, 4, 3, 7])\r\nbinst = solver.Board(board=board, top=top, side=side)\r\nsolutions = binst.solve_and_print()\r\n```\r\n\r\nNote: `solver.Board` accepts an optional `connection_count=N` parameter to specify the (\u00f7N) stitches puzzle (by default, 1 stitch).\r\n\r\n**Script Output**\r\n\r\n```python\r\nSolution found\r\n[[' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ']\r\n ['R' 'L' 'D' 'R' 'L' 'R' 'L' ' ' ' ' ' ' ' ' ' ' 'D' 'R' 'L']\r\n [' ' ' ' 'U' ' ' ' ' 'R' 'L' ' ' ' ' ' ' ' ' ' ' 'U' 'R' 'L']\r\n ['D' ' ' ' ' ' ' ' ' 'R' 'L' ' ' ' ' 'D' ' ' ' ' ' ' ' ' ' ']\r\n ['U' ' ' ' ' ' ' ' ' 'R' 'L' ' ' ' ' 'U' ' ' ' ' ' ' ' ' ' ']\r\n [' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' 'D' ' ']\r\n [' ' ' ' 'D' ' ' ' ' ' ' 'R' 'L' ' ' 'D' ' ' ' ' ' ' 'U' ' ']\r\n [' ' 'D' 'U' ' ' 'R' 'L' ' ' ' ' ' ' 'U' ' ' 'R' 'L' 'D' ' ']\r\n [' ' 'U' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' 'U' ' ']\r\n [' ' ' ' 'R' 'L' ' ' ' ' ' ' ' ' ' ' ' ' 'D' ' ' 'R' 'L' 'D']\r\n [' ' ' ' 'D' ' ' ' ' ' ' 'R' 'L' ' ' ' ' 'U' ' ' ' ' ' ' 'U']\r\n ['D' 'D' 'U' 'R' 'L' 'D' 'D' 'R' 'L' ' ' ' ' ' ' ' ' 'R' 'L']\r\n ['U' 'U' ' ' ' ' ' ' 'U' 'U' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ']\r\n [' ' ' ' 'R' 'L' ' ' ' ' ' ' ' ' ' ' ' ' 'D' ' ' ' ' ' ' ' ']\r\n ['R' 'L' 'R' 'L' ' ' 'R' 'L' ' ' ' ' ' ' 'U' ' ' ' ' ' ' ' ']]\r\nSolutions found: 1\r\nstatus: OPTIMAL\r\nTime taken: 0.01 seconds\r\n```\r\n\r\n**Solved puzzle**\r\n\r\n<img src=\"https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/stitches_solved.png\" alt=\"Stitches solved\" width=\"500\">\r\n\r\n---\r\n\r\n## Battleships (Puzzle Type #29)\r\n\r\n* [**Play online**](https://www.puzzle-battleships.com/)\r\n\r\n* [**Solver Code**][29]\r\n\r\n<details>\r\n  <summary><strong>Rules</strong></summary>\r\n\r\n- You have to find the location of the battleships hidden in the grid. Some battleships may be partially revealed.\r\n- A battleship is a straight line of consecutive black cells.\r\n- The number of the battleships from each size is shown in the legend.\r\n- 2 battleships cannot touch each other (even diagonally)\r\n- The numbers outside the grid show the number of cells occupied by battleships on that row/column.\r\n\r\n</details>\r\n\r\n**Unsolved puzzle**\r\n\r\n<img src=\"https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/battleships_unsolved.png\" alt=\"Battleships unsolved\" width=\"500\">\r\n\r\nCode to utilize this package and solve the puzzle:\r\n\r\n```python\r\nfrom puzzle_solver import battleships_solver as solver\r\nboard = np.array([\r\n  [' ', ' ', ' ', ' ', ' ', 'S', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '],\r\n  [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'S', ' ', ' ', ' ', ' '],\r\n  [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'O', ' ', ' '],\r\n  [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '],\r\n  [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '],\r\n  ['W', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '],\r\n  [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '],\r\n  [' ', ' ', ' ', ' ', ' ', ' ', 'O', ' ', ' ', ' ', ' ', 'W', ' ', ' ', 'R'],\r\n  [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '],\r\n  [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '],\r\n  [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '],\r\n  [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'U', ' ', ' ', ' ', ' '],\r\n  [' ', ' ', ' ', ' ', ' ', ' ', ' ', 'L', ' ', ' ', ' ', ' ', ' ', ' ', ' '],\r\n  [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '],\r\n  [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'S'],\r\n])\r\ntop = np.array([2, 2, 4, 2, 1, 2, 1, 2, 4, 1, 3, 2, 5, 2, 2])\r\nside = np.array([1, 2, 1, 1, 0, 7, 0, 9, 2, 2, 5, 1, 3, 0, 1])\r\nship_counts = {1: 5, 2: 4, 3: 3, 4: 2, 5: 1}\r\nbinst = solver.Board(board=board, top=top, side=side, ship_counts=ship_counts)\r\nsolutions = binst.solve_and_print()\r\n```\r\n\r\n\r\n**Script Output**\r\n\r\n```python\r\nSolution found\r\n[[' ' ' ' ' ' ' ' ' ' 'S' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ']\r\n [' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' 'S' ' ' 'S' ' ' ' ']\r\n [' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' 'S' ' ' ' ']\r\n [' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' 'S' ' ' ' ']\r\n [' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ']\r\n [' ' 'S' 'S' 'S' 'S' ' ' ' ' ' ' ' ' ' ' ' ' 'S' 'S' 'S' ' ']\r\n [' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ']\r\n [' ' ' ' 'S' ' ' ' ' 'S' 'S' 'S' 'S' 'S' ' ' ' ' 'S' 'S' 'S']\r\n ['S' ' ' 'S' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ']\r\n [' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' 'S' ' ' ' ' 'S' ' ' ' ' ' ']\r\n ['S' 'S' 'S' 'S' ' ' ' ' ' ' ' ' 'S' ' ' ' ' ' ' ' ' ' ' ' ']\r\n [' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' 'S' ' ' ' ' ' ' ' ']\r\n [' ' ' ' ' ' ' ' ' ' ' ' ' ' 'S' 'S' ' ' 'S' ' ' ' ' ' ' ' ']\r\n [' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ']\r\n [' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' 'S']]\r\nSolutions found: 1\r\nstatus: OPTIMAL\r\nTime taken: 0.12 seconds\r\n```\r\n\r\n**Solved puzzle**\r\n\r\n<img src=\"https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/battleships_solved.png\" alt=\"Battleships solved\" width=\"500\">\r\n\r\n---\r\n\r\n## Kakurasu (Puzzle Type #30)\r\n\r\n* [**Play online**](https://www.puzzle-kakurasu.com/)\r\n\r\n* [**Solver Code**][30]\r\n\r\n<details>\r\n  <summary><strong>Rules</strong></summary>\r\n\r\nThe goal is to make some of the cells black in such a way that:\r\n\r\n1. The black cells on each row sum up to the number on the right.\r\n\r\n2. The black cells on each column sum up to the number on the bottom.\r\n\r\n3. If a black cell is first on its row/column its value is 1. If it is second its value is 2 etc. \r\n\r\n</details>\r\n\r\n**Unsolved puzzle**\r\n\r\n<img src=\"https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/kakurasu_unsolved.png\" alt=\"Kakurasu unsolved\" width=\"500\">\r\n\r\nCode to utilize this package and solve the puzzle:\r\n\r\n```python\r\nfrom puzzle_solver import kakurasu_solver as solver\r\nside = np.array([27, 6, 1, 12, 37, 37, 11, 4, 29, 23, 66, 55])\r\nbottom = np.array([22, 1, 25, 36, 10, 22, 25, 35, 32, 28, 45, 45])\r\nbinst = solver.Board(side=side, bottom=bottom)\r\nsolutions = binst.solve_and_print()\r\n```\r\n\r\n\r\n**Script Output**\r\n\r\n```python\r\nSolution found\r\n[['X' 'X' ' ' 'X' ' ' ' ' ' ' 'X' ' ' ' ' ' ' 'X']\r\n [' ' ' ' ' ' ' ' ' ' 'X' ' ' ' ' ' ' ' ' ' ' ' ']\r\n ['X' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ']\r\n [' ' ' ' ' ' ' ' 'X' ' ' 'X' ' ' ' ' ' ' ' ' ' ']\r\n [' ' ' ' 'X' 'X' ' ' ' ' ' ' 'X' ' ' 'X' ' ' 'X']\r\n ['X' ' ' ' ' ' ' 'X' ' ' ' ' 'X' ' ' ' ' 'X' 'X']\r\n [' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' 'X' ' ']\r\n [' ' ' ' ' ' 'X' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ' ']\r\n [' ' ' ' 'X' ' ' ' ' 'X' ' ' ' ' 'X' ' ' 'X' ' ']\r\n [' ' ' ' ' ' 'X' ' ' ' ' 'X' ' ' ' ' ' ' ' ' 'X']\r\n [' ' ' ' 'X' ' ' ' ' 'X' 'X' 'X' 'X' 'X' 'X' 'X']\r\n ['X' ' ' ' ' 'X' ' ' ' ' ' ' 'X' 'X' 'X' 'X' 'X']]\r\nSolutions found: 1\r\nstatus: OPTIMAL\r\nTime taken: 0.00 seconds\r\n```\r\n\r\n**Solved puzzle**\r\n\r\n<img src=\"https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/kakurasu_solved.png\" alt=\"Kakurasu solved\" width=\"500\">\r\n\r\n---\r\n\r\n## Star Battle (Puzzle Type #31)\r\n\r\n* [**Play online**](https://www.puzzle-star-battle.com/)\r\n\r\n* [**Solver Code**][31]\r\n\r\n<details>\r\n  <summary><strong>Rules</strong></summary>\r\n\r\n You have to place stars on the grid according to the rules:\r\n- 2 stars cannot be adjacent horizontally, vertically or diagonally.\r\n- For 1\u2605 puzzles, you have to place 1 star on each row, column and shape.\r\n- For 2\u2605 puzzles, the stars per row, column and shape must be 2 etc.\r\n\r\n</details>\r\n\r\n**Unsolved puzzle**\r\n\r\n<img src=\"https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/star_battle_unsolved.png\" alt=\"Star Battle unsolved\" width=\"500\">\r\n\r\nCode to utilize this package and solve the puzzle:\r\n\r\nNote that as usual the board is an id of the shape (id is meaningless, just used to identify one shape), and the `star_count` parameter depenends on the puzzle type.\r\n\r\n```python\r\nfrom puzzle_solver import star_battle_solver as solver\r\nboard = np.array([\r\n  ['00', '00', '00', '00', '00', '01', '01', '01', '01', '01', '01', '01', '01', '01', '02', '02', '02', '03', '03', '03', '03', '03', '03', '03', '03'],\r\n  ['00', '01', '00', '01', '01', '01', '01', '01', '01', '01', '04', '04', '01', '02', '02', '02', '02', '05', '05', '05', '05', '05', '05', '03', '03'],\r\n  ['00', '01', '01', '01', '01', '01', '01', '01', '01', '04', '04', '04', '04', '04', '02', '02', '05', '05', '05', '05', '05', '05', '03', '03', '03'],\r\n  ['00', '01', '06', '04', '04', '04', '04', '04', '04', '04', '04', '04', '04', '04', '02', '05', '05', '05', '05', '05', '05', '05', '03', '07', '03'],\r\n  ['00', '01', '06', '06', '06', '06', '06', '06', '06', '04', '04', '04', '04', '02', '02', '02', '02', '02', '05', '05', '05', '05', '05', '07', '03'],\r\n  ['00', '00', '08', '06', '09', '09', '09', '09', '06', '04', '04', '04', '04', '02', '02', '02', '02', '02', '05', '05', '05', '05', '07', '07', '07'],\r\n  ['00', '08', '08', '08', '08', '09', '09', '06', '06', '06', '04', '04', '04', '04', '02', '02', '02', '05', '05', '05', '07', '07', '07', '07', '07'],\r\n  ['00', '00', '08', '08', '08', '09', '09', '09', '09', '06', '10', '10', '10', '10', '02', '02', '02', '05', '11', '11', '11', '11', '07', '07', '07'],\r\n  ['08', '08', '08', '08', '09', '09', '09', '09', '09', '09', '10', '10', '10', '02', '02', '02', '02', '11', '11', '11', '11', '11', '11', '07', '11'],\r\n  ['08', '08', '08', '08', '09', '09', '09', '09', '09', '10', '10', '10', '10', '02', '02', '02', '11', '11', '11', '11', '11', '11', '11', '07', '11'],\r\n  ['08', '08', '08', '09', '09', '09', '09', '09', '10', '10', '10', '10', '10', '12', '12', '12', '12', '11', '11', '11', '11', '11', '11', '11', '11'],\r\n  ['08', '08', '09', '09', '09', '09', '09', '08', '10', '10', '10', '10', '10', '10', '10', '10', '12', '11', '11', '11', '11', '13', '11', '13', '11'],\r\n  ['14', '08', '08', '08', '08', '08', '08', '08', '10', '10', '10', '10', '10', '12', '12', '12', '12', '12', '11', '11', '11', '13', '11', '13', '15'],\r\n  ['14', '14', '14', '14', '16', '08', '16', '16', '17', '10', '10', '10', '10', '10', '10', '10', '10', '12', '13', '13', '13', '13', '13', '13', '15'],\r\n  ['14', '14', '14', '14', '16', '16', '16', '16', '17', '10', '10', '18', '18', '10', '19', '10', '12', '12', '13', '15', '15', '15', '15', '15', '15'],\r\n  ['14', '14', '14', '14', '14', '16', '16', '17', '17', '18', '18', '18', '19', '19', '19', '10', '10', '10', '13', '15', '15', '15', '15', '15', '15'],\r\n  ['14', '14', '14', '16', '16', '16', '16', '17', '18', '18', '20', '20', '19', '21', '19', '19', '19', '19', '13', '15', '15', '15', '15', '15', '15'],\r\n  ['14', '16', '16', '16', '16', '16', '16', '17', '18', '18', '20', '21', '21', '21', '21', '19', '21', '19', '15', '15', '21', '15', '15', '15', '15'],\r\n  ['14', '14', '14', '16', '16', '17', '17', '17', '18', '20', '20', '21', '20', '21', '21', '19', '21', '19', '15', '21', '21', '15', '15', '15', '15'],\r\n  ['14', '14', '14', '16', '16', '16', '17', '17', '18', '18', '20', '20', '20', '20', '21', '21', '21', '21', '21', '21', '15', '15', '22', '22', '15'],\r\n  ['14', '14', '14', '14', '23', '16', '17', '20', '18', '20', '20', '20', '20', '20', '20', '21', '24', '24', '24', '21', '15', '15', '22', '15', '15'],\r\n  ['14', '14', '14', '14', '23', '20', '17', '20', '18', '20', '20', '20', '20', '24', '24', '24', '24', '24', '24', '21', '15', '22', '22', '22', '15'],\r\n  ['14', '23', '23', '14', '23', '20', '20', '20', '18', '20', '20', '20', '20', '24', '24', '24', '24', '24', '24', '24', '24', '24', '24', '22', '15'],\r\n  ['14', '23', '14', '14', '23', '20', '23', '20', '18', '20', '20', '20', '20', '24', '24', '24', '24', '24', '24', '24', '22', '22', '22', '22', '22'],\r\n  ['14', '23', '23', '23', '23', '23', '23', '20', '20', '20', '20', '20', '20', '24', '24', '24', '24', '24', '24', '24', '24', '24', '24', '24', '24']\r\n])\r\nbinst = solver.Board(board=board, star_count=6)\r\nsolutions = binst.solve_and_print()\r\n```\r\n\r\n\r\n**Script Output**\r\n\r\n```python\r\nSolution found\r\n[' ', ' ', ' ', ' ', '*', ' ', '*', ' ', '*', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '*', ' ', '*', ' ', '*', ' ', ' ', ' '],\r\n['*', ' ', '*', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '*', ' ', '*', ' ', '*', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '*', ' '],\r\n[' ', ' ', ' ', ' ', '*', ' ', '*', ' ', '*', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '*', ' ', '*', ' ', '*', ' ', ' ', ' ', ' '],\r\n['*', ' ', '*', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '*', ' ', '*', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '*', ' ', '*'],\r\n[' ', ' ', ' ', ' ', ' ', '*', ' ', '*', ' ', ' ', ' ', ' ', ' ', ' ', '*', ' ', '*', ' ', '*', ' ', '*', ' ', ' ', ' ', ' '],\r\n[' ', '*', ' ', '*', ' ', ' ', ' ', ' ', ' ', '*', ' ', '*', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '*', ' ', '*'],\r\n[' ', ' ', ' ', ' ', ' ', '*', ' ', '*', ' ', ' ', ' ', ' ', ' ', '*', ' ', '*', ' ', ' ', '*', ' ', '*', ' ', ' ', ' ', ' '],\r\n[' ', '*', ' ', '*', ' ', ' ', ' ', ' ', ' ', '*', ' ', '*', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '*', ' ', '*'],\r\n[' ', ' ', ' ', ' ', ' ', '*', ' ', '*', ' ', ' ', ' ', ' ', ' ', '*', ' ', '*', ' ', '*', ' ', '*', ' ', ' ', ' ', ' ', ' '],\r\n[' ', '*', ' ', '*', ' ', ' ', ' ', ' ', ' ', '*', ' ', '*', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '*', ' ', '*', ' '],\r\n[' ', ' ', ' ', ' ', ' ', '*', ' ', '*', ' ', ' ', ' ', ' ', ' ', '*', ' ', '*', ' ', '*', ' ', '*', ' ', ' ', ' ', ' ', ' '],\r\n['*', ' ', '*', ' ', ' ', ' ', ' ', ' ', ' ', '*', ' ', '*', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '*', ' ', '*', ' '],\r\n[' ', ' ', ' ', ' ', '*', ' ', '*', ' ', ' ', ' ', ' ', ' ', ' ', '*', ' ', '*', ' ', '*', ' ', '*', ' ', ' ', ' ', ' ', ' '],\r\n['*', ' ', '*', ' ', ' ', ' ', ' ', ' ', '*', ' ', '*', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '*', ' ', '*', ' '],\r\n[' ', ' ', ' ', ' ', '*', ' ', '*', ' ', ' ', ' ', ' ', ' ', '*', ' ', '*', ' ', '*', ' ', '*', ' ', ' ', ' ', ' ', ' ', ' '],\r\n[' ', '*', ' ', ' ', ' ', ' ', ' ', ' ', '*', ' ', '*', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '*', ' ', '*', ' ', '*'],\r\n[' ', ' ', ' ', '*', ' ', '*', ' ', ' ', ' ', ' ', ' ', ' ', '*', ' ', '*', ' ', '*', ' ', '*', ' ', ' ', ' ', ' ', ' ', ' '],\r\n[' ', '*', ' ', ' ', ' ', ' ', ' ', '*', ' ', '*', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '*', ' ', '*', ' ', '*'],\r\n[' ', ' ', ' ', '*', ' ', '*', ' ', ' ', ' ', ' ', ' ', '*', ' ', '*', ' ', '*', ' ', '*', ' ', ' ', ' ', ' ', ' ', ' ', ' '],\r\n['*', ' ', ' ', ' ', ' ', ' ', ' ', '*', ' ', '*', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '*', ' ', '*', ' ', '*', ' '],\r\n[' ', ' ', '*', ' ', '*', ' ', ' ', ' ', ' ', ' ', ' ', '*', ' ', '*', ' ', '*', ' ', '*', ' ', ' ', ' ', ' ', ' ', ' ', ' '],\r\n['*', ' ', ' ', ' ', ' ', ' ', '*', ' ', '*', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '*', ' ', '*', ' ', '*', ' '],\r\n[' ', ' ', '*', ' ', '*', ' ', ' ', ' ', ' ', ' ', '*', ' ', '*', ' ', '*', ' ', '*', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '],\r\n[' ', ' ', ' ', ' ', ' ', ' ', '*', ' ', '*', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '*', ' ', '*', ' ', '*', ' ', '*'],\r\n[' ', '*', ' ', '*', ' ', ' ', ' ', ' ', ' ', ' ', '*', ' ', '*', ' ', '*', ' ', '*', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ']\r\nSolutions found: 1\r\nstatus: OPTIMAL\r\nTime taken: 0.38 seconds\r\n```\r\n\r\n**Solved puzzle**\r\n\r\n<img src=\"https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/star_battle_solved.png\" alt=\"Star Battle solved\" width=\"500\">\r\n\r\n---\r\n\r\n## Star Battle Shapeless (Puzzle Type #32)\r\n\r\n* [**Play online**](https://www.puzzle-star-battle.com/?size=14)\r\n\r\n* [**Solver Code**][32]\r\n\r\n<details>\r\n  <summary><strong>Rules</strong></summary>\r\n\r\n You have to place stars on the grid according to the rules:\r\n- 2 stars cannot be adjacent horizontally, vertically or diagonally.\r\n- For 1\u2605 puzzles, you have to place 1 star on each row and column.\r\n- For 2\u2605 puzzles, the stars per row and column must be 2 etc.\r\n- Some places begin with a black square and cannot have stars placed on them.\r\n\r\n</details>\r\n\r\n**Unsolved puzzle**\r\n\r\n<img src=\"https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/star_battle_shapeless_unsolved.png\" alt=\"Star Battle Shapeless unsolved\" width=\"500\">\r\n\r\nCode to utilize this package and solve the puzzle:\r\n\r\nThe `star_count` parameter depenends on the puzzle type.\r\n\r\n```python\r\nfrom puzzle_solver import star_battle_shapeless_solver as shapeless_solver\r\nboard = np.array([\r\n  [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '], \r\n  ['B', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '], \r\n  ['B', ' ', ' ', ' ', ' ', ' ', ' ', ' ', 'B', ' '], \r\n  ['B', 'B', ' ', ' ', 'B', ' ', ' ', ' ', ' ', ' '], \r\n  ['B', 'B', ' ', ' ', 'B', ' ', ' ', ' ', ' ', ' '], \r\n  [' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '], \r\n  [' ', ' ', ' ', ' ', 'B', ' ', ' ', ' ', 'B', ' '], \r\n  ['B', ' ', ' ', ' ', ' ', ' ', ' ', 'B', 'B', ' '], \r\n  ['B', 'B', ' ', ' ', ' ', ' ', 'B', 'B', 'B', ' '], \r\n  ['B', ' ', ' ', ' ', 'B', ' ', ' ', ' ', ' ', ' '], \r\n])\r\nbinst = shapeless_solver.Board(board=board, star_count=2)\r\nsolutions = binst.solve_and_print()\r\n```\r\n\r\n\r\n**Script Output**\r\n\r\n```python\r\nSolution found\r\n['*', ' ', ' ', ' ', '*', ' ', ' ', ' ', ' ', ' '],\r\n[' ', ' ', ' ', ' ', ' ', ' ', '*', ' ', '*', ' '],\r\n[' ', ' ', '*', ' ', '*', ' ', ' ', ' ', ' ', ' '],\r\n[' ', ' ', ' ', ' ', ' ', ' ', ' ', '*', ' ', '*'],\r\n[' ', ' ', '*', ' ', ' ', '*', ' ', ' ', ' ', ' '],\r\n['*', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '*', ' '],\r\n[' ', ' ', ' ', '*', ' ', ' ', '*', ' ', ' ', ' '],\r\n[' ', '*', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '*'],\r\n[' ', ' ', ' ', '*', ' ', '*', ' ', ' ', ' ', ' '],\r\n[' ', '*', ' ', ' ', ' ', ' ', ' ', '*', ' ', ' ']\r\nSolutions found: 1\r\nstatus: OPTIMAL\r\nTime taken: 0.02 seconds\r\n```\r\n\r\n**Solved puzzle**\r\n\r\n<img src=\"https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/star_battle_shapeless_solved.png\" alt=\"Star Battle Shapeless solved\" width=\"500\">\r\n\r\n---\r\n\r\n## Lits (Puzzle Type #33)\r\n\r\n* [**Play online**](https://www.puzzle-lits.com/)\r\n\r\n* [**Solver Code**][33]\r\n\r\n<details>\r\n  <summary><strong>Rules</strong></summary>\r\n\r\n You have to place one tetromino in each region in such a way that:\r\n- 2 tetrominoes of matching types cannot touch each other horizontally or vertically. Rotations and reflections count as matching.\r\n- The shaded cells should form a single connected area.\r\n- 2x2 shaded areas are not allowed.\r\n\r\n* Tetromino is a shape made of 4 connected cells. There are 5 types of tetrominoes, which are usually named L, I, T, S and O, based on their shape. The O tetromino is not used in this puzzle because it is a 2x2 shape, which is not allowed. \r\n\r\n</details>\r\n\r\nNote: The solver is capable of solving variations where the puzzle pieces the made up of more than 4 cells (e.g., pentominoes for 5 with `polyomino_degrees=5`, or hexominoes for 6 with `polyomino_degrees=6`, etc.). By default the degree is set to 4 thus only tetrominoes are used.\r\n\r\n**Unsolved puzzle**\r\n\r\n<img src=\"https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/lits_unsolved.png\" alt=\"Lits unsolved\" width=\"500\">\r\n\r\nCode to utilize this package and solve the puzzle:\r\n\r\n```python\r\nboard = np.array([\r\n  ['00', '00', '00', '01', '01', '02', '02', '02', '03', '03', '03', '04', '04', '05', '06', '07', '07', '08', '08', '09'],\r\n  ['00', '00', '00', '00', '01', '02', '03', '03', '03', '10', '04', '04', '05', '05', '06', '07', '08', '08', '09', '09'],\r\n  ['11', '11', '11', '01', '01', '02', '02', '03', '10', '10', '04', '04', '05', '06', '06', '07', '07', '07', '09', '12'],\r\n  ['11', '13', '13', '13', '01', '02', '03', '03', '03', '10', '04', '04', '06', '06', '06', '07', '12', '09', '09', '12'],\r\n  ['11', '11', '11', '13', '14', '14', '03', '15', '15', '10', '04', '04', '06', '16', '16', '12', '12', '09', '12', '12'],\r\n  ['17', '13', '13', '13', '14', '14', '03', '03', '15', '15', '04', '04', '16', '16', '16', '12', '12', '12', '12', '18'],\r\n  ['17', '13', '19', '13', '20', '14', '03', '03', '15', '04', '04', '16', '16', '21', '21', '22', '23', '23', '23', '18'],\r\n  ['17', '17', '19', '19', '20', '20', '03', '03', '24', '24', '24', '25', '25', '25', '21', '22', '23', '23', '18', '18'],\r\n  ['17', '26', '19', '19', '20', '20', '20', '24', '24', '20', '20', '25', '25', '21', '21', '22', '22', '23', '23', '18'],\r\n  ['26', '26', '26', '19', '19', '20', '20', '20', '20', '20', '25', '25', '21', '21', '21', '21', '21', '23', '27', '18'],\r\n  ['28', '28', '28', '29', '29', '29', '29', '20', '20', '30', '30', '25', '31', '32', '32', '32', '21', '27', '27', '27'],\r\n  ['28', '33', '28', '28', '28', '28', '29', '34', '34', '35', '30', '30', '31', '31', '31', '32', '32', '36', '36', '27'],\r\n  ['28', '33', '33', '28', '28', '29', '29', '34', '34', '35', '35', '30', '31', '31', '31', '32', '36', '36', '27', '27'],\r\n  ['28', '33', '37', '37', '28', '29', '34', '34', '35', '35', '38', '38', '39', '39', '40', '40', '40', '40', '27', '41'],\r\n  ['28', '37', '37', '37', '42', '34', '34', '34', '43', '38', '38', '38', '39', '39', '44', '44', '40', '40', '27', '41'],\r\n  ['37', '37', '42', '42', '42', '34', '34', '43', '43', '43', '38', '39', '39', '39', '44', '44', '27', '27', '27', '41'],\r\n  ['45', '45', '45', '42', '46', '34', '34', '34', '34', '38', '38', '47', '47', '47', '44', '44', '44', '27', '27', '41'],\r\n  ['48', '45', '45', '46', '46', '46', '46', '34', '49', '49', '49', '47', '44', '44', '44', '27', '44', '50', '27', '27'],\r\n  ['48', '48', '45', '46', '46', '51', '46', '52', '52', '49', '49', '53', '44', '53', '44', '27', '50', '50', '50', '27'],\r\n  ['48', '51', '51', '51', '51', '51', '52', '52', '52', '49', '53', '53', '53', '53', '44', '27', '27', '27', '27', '27']\r\n])\r\nbinst = solver.Board(board)\r\nsolutions = binst.solve_then_constrain()  # solve_then_constrain NOT solve_and_print (to use #1 instead of #2 in https://github.com/google/or-tools/discussions/3347, its faster in this case)\r\n```\r\n\r\n**Script Output**\r\n\r\n```python\r\nSolution found\r\n[\r\n  ['X', 'X', 'X', ' ', ' ', 'X', 'X', 'X', ' ', ' ', ' ', ' ', ' ', 'X', ' ', 'X', ' ', 'X', 'X', ' '],\r\n  [' ', 'X', ' ', ' ', 'X', 'X', ' ', ' ', ' ', 'X', ' ', ' ', 'X', 'X', ' ', 'X', 'X', 'X', ' ', ' '],\r\n  ['X', 'X', 'X', 'X', 'X', ' ', ' ', 'X', 'X', 'X', ' ', ' ', 'X', ' ', ' ', 'X', ' ', ' ', 'X', ' '],\r\n  ['X', ' ', ' ', ' ', 'X', ' ', 'X', 'X', ' ', 'X', 'X', ' ', 'X', 'X', 'X', 'X', ' ', 'X', 'X', ' '],\r\n  [' ', ' ', ' ', 'X', 'X', 'X', 'X', ' ', 'X', ' ', 'X', ' ', 'X', ' ', ' ', ' ', ' ', 'X', ' ', ' '],\r\n  ['X', ' ', 'X', 'X', ' ', 'X', ' ', ' ', 'X', 'X', 'X', ' ', 'X', 'X', ' ', 'X', 'X', 'X', 'X', ' '],\r\n  ['X', ' ', ' ', 'X', ' ', 'X', ' ', ' ', 'X', ' ', 'X', 'X', 'X', ' ', ' ', 'X', ' ', ' ', ' ', 'X'],\r\n  ['X', 'X', ' ', 'X', ' ', 'X', ' ', ' ', 'X', 'X', ' ', 'X', ' ', ' ', ' ', 'X', ' ', 'X', ' ', 'X'],\r\n  [' ', 'X', ' ', 'X', ' ', 'X', 'X', 'X', 'X', ' ', ' ', 'X', ' ', 'X', 'X', 'X', 'X', 'X', 'X', 'X'],\r\n  ['X', 'X', 'X', 'X', 'X', 'X', ' ', ' ', ' ', ' ', 'X', 'X', 'X', 'X', ' ', ' ', ' ', 'X', ' ', 'X'],\r\n  [' ', ' ', ' ', ' ', ' ', 'X', 'X', ' ', ' ', ' ', 'X', ' ', ' ', 'X', 'X', 'X', ' ', ' ', ' ', ' '],\r\n  [' ', 'X', ' ', 'X', 'X', ' ', 'X', ' ', ' ', 'X', 'X', 'X', ' ', 'X', ' ', 'X', ' ', 'X', 'X', ' '],\r\n  [' ', 'X', 'X', ' ', 'X', ' ', 'X', ' ', ' ', 'X', ' ', 'X', 'X', 'X', 'X', ' ', 'X', 'X', ' ', ' '],\r\n  [' ', 'X', ' ', ' ', 'X', ' ', 'X', ' ', 'X', 'X', 'X', ' ', 'X', ' ', 'X', 'X', 'X', ' ', ' ', 'X'],\r\n  [' ', 'X', 'X', ' ', 'X', ' ', 'X', ' ', 'X', ' ', 'X', ' ', 'X', 'X', ' ', ' ', 'X', ' ', ' ', 'X'],\r\n  ['X', 'X', ' ', 'X', 'X', ' ', 'X', 'X', 'X', 'X', 'X', ' ', ' ', 'X', ' ', ' ', 'X', 'X', ' ', 'X'],\r\n  [' ', 'X', 'X', 'X', ' ', ' ', 'X', ' ', ' ', ' ', 'X', 'X', 'X', 'X', 'X', ' ', ' ', 'X', 'X', 'X'],\r\n  ['X', ' ', 'X', ' ', 'X', 'X', 'X', ' ', 'X', 'X', ' ', 'X', ' ', ' ', 'X', ' ', ' ', 'X', ' ', ' '],\r\n  ['X', 'X', 'X', ' ', 'X', ' ', ' ', 'X', ' ', 'X', ' ', 'X', ' ', ' ', 'X', ' ', 'X', 'X', 'X', ' '],\r\n  ['X', ' ', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', 'X', ' ', 'X', ' ', ' ', ' ', ' ', ' '],\r\n]\r\nSolutions found: 1\r\nTime taken: 0.38 seconds\r\n```\r\n\r\n**Solved puzzle**\r\n\r\n<img src=\"https://raw.githubusercontent.com/Ar-Kareem/puzzle_solver/master/images/lits_solved.png\" alt=\"Lits solved\" width=\"500\">\r\n\r\n---\r\n\r\n---\r\n\r\n## Why SAT / CP-SAT?\r\n\r\nBecause it is extremely faster than naive solutions and many pencil puzzles can be modeled with:\r\n\r\n- **Boolean decisions** (e.g., black/white, bulb/no-bulb)\r\n- **Linear constraints** (counts, separations, adjacency)\r\n- **All-different / visibility / reachability** constraints\r\n\r\nThis repo builds those constraints in Python and uses SAT/CP-SAT (e.g., OR-Tools) to search efficiently. It both demonstrates the modeling and provides usable solvers.\r\n\r\n---\r\n\r\n## Testing\r\n\r\nTo run the tests, simply run the following (to create a fresh conda environment and install the dev dependencies):\r\n\r\n```bash\r\nconda create -p ./env python=3.11\r\nconda activate ./env\r\npip install -r requirements.txt\r\npip install -r requirements-dev.txt\r\npytest\r\n```\r\n\r\nthe `pytest.ini` file is used to configure the pytest command to use `-n 4` to have 4 workers.\r\n\r\n## Contributing\r\n\r\nIssues and PRs welcome!\r\n\r\n\r\n* Python version `>= 3.9` required.\r\n* Keep puzzle folders self-contained (solver, README.md, other files if needed).\r\n* Prefer small, readable encodings with comments explaining each constraint.\r\n* If you add a new puzzle:\r\n\r\n  1. Create a directory in `src/puzzle_solver/puzzles/<name>/`,\r\n  2. Add a minimal test script in `tests/test_<name>.py`,\r\n  3. Document the modeling in code comments,\r\n\r\n### Build and push to PyPI\r\n\r\n1. First make sure all the tests pass (see [Testing](#testing))\r\n2. Update the version in `src/puzzle_solver/__init__.py`\r\n3. Build and push:\r\n   1. Bash: `rm dist/* && python -m build --sdist --wheel && python -m twine upload --repository pypi dist/*`\r\n   2. Powershell: `rm dist/*; if ($?) { python -m build --sdist --wheel; if ($?) { python -m twine upload --repository pypi dist/* } }`\r\n\r\n\r\n[1]: https://github.com/Ar-Kareem/puzzle_solver/tree/master/src/puzzle_solver/puzzles/nonograms \"puzzle_solver/src/puzzle_solver/puzzles/nonograms at master \u00b7 Ar-Kareem/puzzle_solver \u00b7 GitHub\"\r\n[2]: https://github.com/Ar-Kareem/puzzle_solver/tree/master/src/puzzle_solver/puzzles/sudoku \"puzzle_solver/src/puzzle_solver/puzzles/sudoku at master \u00b7 Ar-Kareem/puzzle_solver \u00b7 GitHub\"\r\n[3]: https://github.com/Ar-Kareem/puzzle_solver/tree/master/src/puzzle_solver/puzzles/minesweeper \"puzzle_solver/src/puzzle_solver/puzzles/minesweeper at master \u00b7 Ar-Kareem/puzzle_solver \u00b7 GitHub\"\r\n[22]: https://github.com/Ar-Kareem/puzzle_solver/tree/master/src/puzzle_solver/puzzles/guess \"puzzle_solver/src/puzzle_solver/puzzles/guess at master \u00b7 Ar-Kareem/puzzle_solver \u00b7 GitHub\"\r\n[4]: https://github.com/Ar-Kareem/puzzle_solver/tree/master/src/puzzle_solver/puzzles/dominosa \"puzzle_solver/src/puzzle_solver/puzzles/dominosa at master \u00b7 Ar-Kareem/puzzle_solver \u00b7 GitHub\"\r\n[5]: https://github.com/Ar-Kareem/puzzle_solver/tree/master/src/puzzle_solver/puzzles/light_up \"puzzle_solver/src/puzzle_solver/puzzles/light_up at master \u00b7 Ar-Kareem/puzzle_solver \u00b7 GitHub\"\r\n[18]: https://github.com/Ar-Kareem/puzzle_solver/tree/master/src/puzzle_solver/puzzles/map \"puzzle_solver/src/puzzle_solver/puzzles/map at master \u00b7 Ar-Kareem/puzzle_solver \u00b7 GitHub\"\r\n[21]: https://github.com/Ar-Kareem/puzzle_solver/tree/master/src/puzzle_solver/puzzles/inertia \"puzzle_solver/src/puzzle_solver/puzzles/inertia at master \u00b7 Ar-Kareem/puzzle_solver \u00b7 GitHub\"\r\n[6]: https://github.com/Ar-Kareem/puzzle_solver/tree/master/src/puzzle_solver/puzzles/tents \"puzzle_solver/src/puzzle_solver/puzzles/tents at master \u00b7 Ar-Kareem/puzzle_solver \u00b7 GitHub\"\r\n[20]: https://github.com/Ar-Kareem/puzzle_solver/tree/master/src/puzzle_solver/puzzles/bridges \"puzzle_solver/src/puzzle_solver/puzzles/bridges at master \u00b7 Ar-Kareem/puzzle_solver \u00b7 GitHub\"\r\n[7]: https://github.com/Ar-Kareem/puzzle_solver/tree/master/src/puzzle_solver/puzzles/filling \"puzzle_solver/src/puzzle_solver/puzzles/filling at master \u00b7 Ar-Kareem/puzzle_solver \u00b7 GitHub\"\r\n[8]: https://github.com/Ar-Kareem/puzzle_solver/tree/master/src/puzzle_solver/puzzles/keen \"puzzle_solver/src/puzzle_solver/puzzles/keen at master \u00b7 Ar-Kareem/puzzle_solver \u00b7 GitHub\"\r\n[9]: https://github.com/Ar-Kareem/puzzle_solver/tree/master/src/puzzle_solver/puzzles/towers \"puzzle_solver/src/puzzle_solver/puzzles/towers at master \u00b7 Ar-Kareem/puzzle_solver \u00b7 GitHub\"\r\n[10]: https://github.com/Ar-Kareem/puzzle_solver/tree/master/src/puzzle_solver/puzzles/singles \"puzzle_solver/src/puzzle_solver/puzzles/singles at master \u00b7 Ar-Kareem/puzzle_solver \u00b7 GitHub\"\r\n[11]: https://github.com/Ar-Kareem/puzzle_solver/tree/master/src/puzzle_solver/puzzles/magnets \"puzzle_solver/src/puzzle_solver/puzzles/magnets at master \u00b7 Ar-Kareem/puzzle_solver \u00b7 GitHub\"\r\n[12]: https://github.com/Ar-Kareem/puzzle_solver/tree/master/src/puzzle_solver/puzzles/signpost \"puzzle_solver/src/puzzle_solver/puzzles/signpost at master \u00b7 Ar-Kareem/puzzle_solver \u00b7 GitHub\"\r\n[13]: https://github.com/Ar-Kareem/puzzle_solver/tree/master/src/puzzle_solver/puzzles/range \"puzzle_solver/src/puzzle_solver/puzzles/range at master \u00b7 Ar-Kareem/puzzle_solver \u00b7 GitHub\"\r\n[19]: https://github.com/Ar-Kareem/puzzle_solver/tree/master/src/puzzle_solver/puzzles/pearl \"puzzle_solver/src/puzzle_solver/puzzles/pearl at master \u00b7 Ar-Kareem/puzzle_solver \u00b7 GitHub\"\r\n[14]: https://github.com/Ar-Kareem/puzzle_solver/tree/master/src/puzzle_solver/puzzles/undead \"puzzle_solver/src/puzzle_solver/puzzles/undead at master \u00b7 Ar-Kareem/puzzle_solver \u00b7 GitHub\"\r\n[15]: https://github.com/Ar-Kareem/puzzle_solver/tree/master/src/puzzle_solver/puzzles/unruly \"puzzle_solver/src/puzzle_solver/puzzles/unruly at master \u00b7 Ar-Kareem/puzzle_solver \u00b7 GitHub\"\r\n[16]: https://github.com/Ar-Kareem/puzzle_solver/tree/master/src/puzzle_solver/puzzles/tracks \"puzzle_solver/src/puzzle_solver/puzzles/tracks at master \u00b7 Ar-Kareem/puzzle_solver \u00b7 GitHub\"\r\n[17]: https://github.com/Ar-Kareem/puzzle_solver/tree/master/src/puzzle_solver/puzzles/mosaic \"puzzle_solver/src/puzzle_solver/puzzles/mosaic at master \u00b7 Ar-Kareem/puzzle_solver \u00b7 GitHub\"\r\n[23]: https://github.com/Ar-Kareem/puzzle_solver/tree/master/src/puzzle_solver/puzzles/chess_range \"puzzle_solver/src/puzzle_solver/puzzles/chess_range at master \u00b7 Ar-Kareem/puzzle_solver \u00b7 GitHub\"\r\n[24]: https://github.com/Ar-Kareem/puzzle_solver/tree/master/src/puzzle_solver/puzzles/chess_range#chess-solo-puzzle-type-24 \"puzzle_solver/src/puzzle_solver/puzzles/chess_range at master \u00b7 Ar-Kareem/puzzle_solver \u00b7 GitHub\"\r\n[25]: https://github.com/Ar-Kareem/puzzle_solver/tree/master/src/puzzle_solver/puzzles/chess_range#chess-melee-puzzle-type-25 \"puzzle_solver/src/puzzle_solver/puzzles/chess_range at master \u00b7 Ar-Kareem/puzzle_solver \u00b7 GitHub\"\r\n[26]: https://github.com/Ar-Kareem/puzzle_solver/tree/master/src/puzzle_solver/puzzles/thermometers \"puzzle_solver/src/puzzle_solver/puzzles/thermometers at master \u00b7 Ar-Kareem/puzzle_solver \u00b7 GitHub\"\r\n[27]: https://github.com/Ar-Kareem/puzzle_solver/tree/master/src/puzzle_solver/puzzles/aquarium \"puzzle_solver/src/puzzle_solver/puzzles/aquarium at master \u00b7 Ar-Kareem/puzzle_solver \u00b7 GitHub\"\r\n[28]: https://github.com/Ar-Kareem/puzzle_solver/tree/master/src/puzzle_solver/puzzles/stitches \"puzzle_solver/src/puzzle_solver/puzzles/stitches at master \u00b7 Ar-Kareem/puzzle_solver \u00b7 GitHub\"\r\n[29]: https://github.com/Ar-Kareem/puzzle_solver/tree/master/src/puzzle_solver/puzzles/battleships \"puzzle_solver/src/puzzle_solver/puzzles/battleships at master \u00b7 Ar-Kareem/puzzle_solver \u00b7 GitHub\"\r\n[30]: https://github.com/Ar-Kareem/puzzle_solver/tree/master/src/puzzle_solver/puzzles/kakurasu \"puzzle_solver/src/puzzle_solver/puzzles/kakurasu at master \u00b7 Ar-Kareem/puzzle_solver \u00b7 GitHub\"\r\n[31]: https://github.com/Ar-Kareem/puzzle_solver/tree/master/src/puzzle_solver/puzzles/star_battle \"puzzle_solver/src/puzzle_solver/puzzles/star_battle at master \u00b7 Ar-Kareem/puzzle_solver \u00b7 GitHub\"\r\n[32]: https://github.com/Ar-Kareem/puzzle_solver/tree/master/src/puzzle_solver/puzzles/star_battle_shapeless \"puzzle_solver/src/puzzle_solver/puzzles/star_battle_shapeless at master \u00b7 Ar-Kareem/puzzle_solver \u00b7 GitHub\"\r\n[33]: https://github.com/Ar-Kareem/puzzle_solver/tree/master/src/puzzle_solver/puzzles/lits \"puzzle_solver/src/puzzle_solver/puzzles/lits at master \u00b7 Ar-Kareem/puzzle_solver \u00b7 GitHub\"\r\n",
    "bugtrack_url": null,
    "license": null,
    "summary": "Efficient solvers for numerous popular and esoteric logic puzzles using CP-SAT",
    "version": "0.9.13",
    "project_urls": {
        "Homepage": "https://github.com/Ar-Kareem/puzzle_solver",
        "Issues": "https://github.com/Ar-Kareem/puzzle_solver/issues",
        "Repository": "https://github.com/Ar-Kareem/puzzle_solver"
    },
    "split_keywords": [
        "puzzle",
        " solver",
        " sat",
        " cp-sat",
        " google-or-tools",
        " efficient",
        " logic",
        " puzzle-solver",
        " puzzle-solver-python",
        " sudoku",
        " nonograms",
        " minesweeper",
        " dominosa",
        " lightup",
        " map",
        " inertia",
        " tents",
        " towers",
        " tracks",
        " undead",
        " unruly",
        " bridges",
        " pearl",
        " range",
        " signpost",
        " singles",
        " magnets"
    ],
    "urls": [
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "67b7af4db41675f299b4624ea44a834c5231b9ffe0efd82cf1f2114772ced66d",
                "md5": "39952acefe349ae0c4fd14c4c2e8792f",
                "sha256": "b20d1c974aad48793f8ea18e06d653393346e0f877e1e39f5c1de935b7e930e2"
            },
            "downloads": -1,
            "filename": "multi_puzzle_solver-0.9.13-py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "39952acefe349ae0c4fd14c4c2e8792f",
            "packagetype": "bdist_wheel",
            "python_version": "py3",
            "requires_python": ">=3.9",
            "size": 115470,
            "upload_time": "2025-10-21T03:36:37",
            "upload_time_iso_8601": "2025-10-21T03:36:37.594826Z",
            "url": "https://files.pythonhosted.org/packages/67/b7/af4db41675f299b4624ea44a834c5231b9ffe0efd82cf1f2114772ced66d/multi_puzzle_solver-0.9.13-py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "ad7d690f87f5f2e9bd81e672358b191a53393344c3f67893cc11c7723aa75f41",
                "md5": "655325cac4638817607c10465d7a8f50",
                "sha256": "a977bf5361751534cc6db7f80f0d09d19cc979ccf456bd6f42a116b86cd1f3e3"
            },
            "downloads": -1,
            "filename": "multi_puzzle_solver-0.9.13.tar.gz",
            "has_sig": false,
            "md5_digest": "655325cac4638817607c10465d7a8f50",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": ">=3.9",
            "size": 153218,
            "upload_time": "2025-10-21T03:36:38",
            "upload_time_iso_8601": "2025-10-21T03:36:38.904549Z",
            "url": "https://files.pythonhosted.org/packages/ad/7d/690f87f5f2e9bd81e672358b191a53393344c3f67893cc11c7723aa75f41/multi_puzzle_solver-0.9.13.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2025-10-21 03:36:38",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "Ar-Kareem",
    "github_project": "puzzle_solver",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": false,
    "requirements": [
        {
            "name": "numpy",
            "specs": []
        },
        {
            "name": "ortools",
            "specs": []
        }
    ],
    "lcname": "multi-puzzle-solver"
}
        
Elapsed time: 1.69586s