CLI and library for solving nonograms (aka griddlers, picross). Supports puzzles with definite and deterministic solutions.
Before you start, check requirements.
# clone and install
git clone git@github.com:dpetrovych/nonopy.git
cd nonopy
python -m pip install -r requirements.txt
# run
python cli.py ./data/hard/tiger.non -i
# also run
python cli.py ./data/hard/sun.non -G --solvers AC --linestats -3
# run tests
python -m unittest -v
Don't want to mess up your environment setup? You can touch-and-go with a Docker image.
python cli.py [-h] [--solvers ABC] [--verbose] [--interactive] [--nogrid] [--linestats [SORTBY]] path
path | path to .non format file |
--help, -h | show this help message and exit |
--solvers ABC, -s ABC | solvers to run (one-letter code for solver algorithm) |
--verbose, -v | shows actions log |
--interactive, -i | shows grid while solving |
--nogrid, -G | hides grid in final output (for comparing stats only) |
--linestats [SORTBY] | shows statistics per line sorted by column 1-based index (negative values for sorting in descending order) |
wget -P ./data https://github.com/dpetrovych/nonopy/main/data/hard/sun.non
docker run -t --rm -v "${PWD}/data:/data" dpetrovych/nonopy:latest /data/sun.non
--rm
flag removes container when command is complete.
-v
argument mounts a data directory with a puzzle file into container.
A downside of this method is that you can't use --interactive
flag of Nonopy CLI, since it depends on terminal resize (curses).
- Python 3.8 (check with
python -v
) - Pip (check with
pip --version
)
If you using any modern Linux distributions, you probably have one already. Here is an instruction if you need to install Python.
- Field - 2d grid where a solved image is constructed
- Line - column or row on the field
- Field line - representation of the current line state on the field
- Block - consecutive filled cells in line (two blocks should be separated by 1+ crossed cells)
- Task - numerical cues on top of the column (right to the row), represent block length
- Task line - abstraction that contains task and data/methods assosiated with row/column
- Hot task - task that has definite cells on empty line (can be solved in-place)
- Collapse - operation of determining definite cell values based on the task and current line state
r10 - row with an index 10
c4 - column with an index 4
| - line boundary
· - empty cell
x - crossed cell
1 - filled cell
Calculates number of combinations of positionment of the blocks. Add lines with hot tasks to the hot heap - heap ordered desc by number of combinations.
Pop the line from the top of the hot heap (thus with min combinations).
To speed up collapse of combinations the solver uses some divide & conquere strategies.
If a line has already crossed boxes, it can solve 2 halfs of a line as separate lines.
line: |···xx···|
task: [2, 1, 1]
---
left: |···| right: |···|
task: [] task: [2, 1, 1]
rslt: |xxxx| rslt: None -- Eliminated
left: |···| right: |···|
task: [2] task: [1, 1]
rslt: |·1·| rslt: |1x1| -> |·1·xx1x1|
left: |····| right: |···|
task: [2, 1] task: [1]
rslt: |11x1| rslt: None -- Eliminated
left: |····| right: |···|
task: [2, 1, 1] task: [1]
rslt: None rslt: None -- Eliminated
---
result: |·1·xx1x1|
If a line has already filled boxes, it can fit one of the block on to the filled one and solve 2 parts with remaining blocks.
line: |··1····|
task: [2, 2]
---
Fit block 0 at index 1: |x11x···|
left: || right: |···|
task: [] task: [2]
rslt: || rslt: | 1 | -> |x11x 1 |
Fit block 0 at index 2: |·x11x··|
left: |·| right: |··|
task: [] task: [2]
rslt: |x| rslt: |11| -> |xx11x11|
Fit block 1 at index 1: |x11x···|
left: || right: |···|
task: [2] task: []
rslt: None rslt: |xxx| -- Eliminated
Fit block 1 at index 2: |·x11x··|
left: |·| right: |··|
task: [2] task: []
rslt: None rslt: |xx| -- Eliminated
---
result: |x·1··1·|
INPLACE runs when all line cells are empty. In the logic of the move all blocks tightly to the left boudary, then to the right and mark the intersection of the same blocks.
This technick is also known as Simple Blocks. (wiki)
task: [3, 2]
line: |·······|
---
lshift: |111·11·|
rshift: |·111·11|
---
result: |·11··1·|
Hightlight cells where new solution emerged from collapse operation.
Example (from DIVIDE_BY_CROSSED):
line: |···xx···|
result: |·1·xx1x1|
---
diff: |·1···1x1|
For each highlighted diff cell mark an opposite direction (column if diff line is a row and vice versa) as hot and put to a _hot heap.
Example:
diff r2: |·1···1x1|
marked hot: c1, c5, c6, c7