Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 8 additions & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,22 @@ This project provides a general nonogram solver. It can determine if a puzzle is

This solver can be used to create nonogram puzzles given a successful final solution. If the given solution is not solvable, the solver will suggest "hint" squares to be filled in when the nonogram is given to a human solver.

Input from JSON file possible, as generation of SVGs.

.. |ci-status| image:: https://travis-ci.org/mprat/nonogram-solver.svg?branch=master
:target: https://travis-ci.org/mprat/nonogram-solver
:alt: Build status


Installation
--------
To install, run `pip install nonogram-solver`.

Example
--------

.. image:: https://github.com/paulwuertz/nonogram-solver/blob/master/tests/test.svg
:height: 400px
:width: 300px

Project Website
---------
Expand Down
1 change: 1 addition & 0 deletions contributors.txt
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
Michele Pratusevich
Paul Wurtz
73 changes: 69 additions & 4 deletions nonogram_solver/nonogram.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
Defines the nonogram datatype.
"""
import numpy as np

import svgwrite
import json

class Nonogram(object):
def __init__(self, ordered=True):
Expand All @@ -14,6 +15,12 @@ def __init__(self, ordered=True):
self.solution_state = None
self.solution_list = None
self.ordered = ordered
#svg constants
self.mcols,self.mrows=[None,None] #max num of constraints
self.wBox,self.margin=[30,30] #dimensions
self.numBoxX,self.numBoxY=[0,0] #constraints+fields
self.wXstroke,self.wYstroke=[0,0] #length of strokes
self.xySize=[0,0] #canvas size

def _init_puzzle(self):
self.puzzle_state = -1 * np.ones((self.n_rows, self.n_cols))
Expand Down Expand Up @@ -69,9 +76,67 @@ def init_from_matrix(self, matrix):
self.solution_state = matrix
self.solution_list = zip(np.where(matrix == 1))
# TODO: finish this function
def init_from_json(self, jfile):
"""
Args:
the path to a json file:
json containing the row and col constraints as arrays of arrays i.e:
{
"ver":[[1],[]],
"hor":[[1],[]]
}
dimensions are deductable by len(json["ver"])/len(json["hor"])
"""
j=json.loads(open(jfile).read())
self.n_rows = len(j["ver"])
self.n_cols = len(j["hor"])

def display_puzzle_svg(self):
pass
filled_positions = []
self.solution_list = filled_positions
self.rows_constraints = j["ver"]
self.cols_constraints = j["hor"]
self._init_puzzle()
self.solution_state = np.zeros((self.n_rows, self.n_cols))

def display_number(self,svg,x,y,num):
if num>9: cor=.15
else: cor=0
svg.add(svg.text(num, insert=(self.margin+(x-2/3-cor)*self.wBox, self.margin+(y+2/3)*self.wBox), font_size=16, fill='black'))

def display_puzzle_svg(self,toSVG="test.svg"):
#init SVG
dwg = svgwrite.Drawing(toSVG, profile='tiny')
#get row/col constraints and max numbers of hints
rows,cols=[self.rows_constraints,self.cols_constraints]
self.mcols,self.mrows=[max([len(e) for e in cols]),max([len(e) for e in rows])]
#set constants
self.numBoxX,self.numBoxY=[self.mrows+self.n_cols,self.mcols+self.n_rows] #constraints+fields
self.wXstroke,self.wYstroke=[self.numBoxX*self.wBox,self.wBox*self.numBoxY] #length of strokes
self.xySize=[self.wXstroke+2*self.margin,self.wYstroke+2*self.margin]
#print gray raster
for i in range(self.numBoxX+1):
dwg.add(dwg.line((self.margin+i*self.wBox,self.margin), (self.margin+i*self.wBox,self.margin+self.wYstroke), stroke_width=5 ,stroke=svgwrite.rgb(70, 70, 70, '%')))
for i in range(self.numBoxY+1):
dwg.add(dwg.line((self.margin,self.margin+i*self.wBox), (self.margin+self.wXstroke,self.margin+i*self.wBox), stroke_width=5 ,stroke=svgwrite.rgb(70, 70, 70, '%')))
#print black raster
for i in [0,self.numBoxX]+[b for b in range(self.mrows,self.numBoxX,5)]:
dwg.add(dwg.line((self.margin+i*self.wBox,self.margin), (self.margin+i*self.wBox,self.margin+self.wYstroke), stroke_width=5 ,stroke=svgwrite.rgb(0, 0, 0, '%')))
for i in [0,self.numBoxY]+[b for b in range(self.mcols,self.numBoxY,5)]:
dwg.add(dwg.line((self.margin,self.margin+i*self.wBox), (self.margin+self.wXstroke,self.margin+i*self.wBox), stroke_width=5 ,stroke=svgwrite.rgb(0, 0, 0, '%')))
#print row constraints
for r in range(len(rows)):
ei=self.mrows
for e in range(1,1+len(rows[r])):
self.display_number(dwg,ei,self.mcols+r,rows[r][-e])
ei-=1
#print col constraints
for c in range(len(cols)):
ei=self.mcols-1
for e in range(1,1+len(cols[c])):
self.display_number(dwg,self.mrows+c+1,ei,cols[c][-e])
ei-=1
#set viewbox and export
dwg.viewbox(minx=0, miny=0, width=self.xySize[0], height=self.xySize[1])
dwg.save()
def display_solution_svg(self):
pass
pass
3 changes: 2 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
numpy
numpy
svgwrite
2 changes: 1 addition & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@

setup(
name='nonogram-solver',
version='0.1',
version='0.2',
description='A nonogram puzzle solver.',
long_description=long_description,
url='https://github.com/mprat/nonogram-solver',
Expand Down
1 change: 1 addition & 0 deletions tests/rose_nonogram.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"ver":[[4],[2,1],[1,4,2],[3,2,3,1,3],[2,1,2,3,2],[1,1,1,6,1,1],[2,2,2,2,2,1],[1,3,3,2,1],[2,6,5,2],[1,2,5,2],[3,3,5],[1,2,2,2],[8,1,1],[1,2,2],[1,4],[4,3],[5,4],[4,9],[1,2,6,1,1],[7,1],[5,5,3],[8,4,7],[1,11,5,2],[1,7,3,3,2],[7,12],[2,4,4],[2,3],[2,4],[5],[2,5],[3,4,2,1],[10,2],[4,4],[4],[2]],"hor":[[4],[2,4],[2,2,2,1],[3,2,5],[2,1,1,5],[1,2,2,1,5],[1,2,1,2,1,5,1],[1,3,1,3,3,1,1],[1,4,1,3,3,1,5],[2,2,1,5,1,9],[1,1,1,1,1,15],[1,1,1,2,2,2,10,2],[1,2,2,1,1,1,7,1,2],[1,1,1,1,2,6,2,2],[1,2,1,1,2,5,1,3],[1,2,1,4,4,2,3],[1,2,2,1,3,3,3,1,2],[1,3,1,6,5,1,2],[4,1,1,2,2,2,2,2,1],[1,2,2,1,2,1,2,2,3],[1,2,1,2,1,2,2,1],[1,2,5,1,2,1],[2,4,4],[5,1,3],[2]]}
2 changes: 2 additions & 0 deletions tests/test.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
9 changes: 8 additions & 1 deletion tests/test_solver.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ def simple_nonogram_from_string_box():
'..oo.',
'.o..o',
'o...o']

nonogram = Nonogram()
nonogram.init_from_solution_string(string_box)
return nonogram
Expand Down Expand Up @@ -73,3 +73,10 @@ def test_nonogram_solver_manual(simple_nonogram_from_string_box):
[0, 0, 1, 1, 0],
[-1, -1, -1, 0, 1],
[-1, -1, 0, 0, 1]]))


def svgGen():
nonogram = Nonogram()
nonogram.init_from_json("rose_nonogram.json")
nonogram.display_puzzle_svg()
svgGen()