Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Error handling in GeneralSolver #74

Merged
merged 3 commits into from
Dec 23, 2020
Merged
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
15 changes: 11 additions & 4 deletions puzzlesolver/solvers/generalsolver.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,21 @@
from .solver import Solver
from ..util import *
from ..util import PuzzleValue, PuzzleException
import queue as q
import progressbar

class GeneralSolver(Solver):

def __init__(self, puzzle, *args, **kwarg):
def __init__(self, puzzle):
self.remoteness = {}
self.puzzle = puzzle

def getRemoteness(self, puzzle, **kwargs):
def getRemoteness(self, puzzle):
"""Returns remoteness of puzzle. Automatically solves if memory isn't set"""
if not self.remoteness: print("Warning: No memory found. Please make sure that `solve` was called.")
if hash(puzzle) in self.remoteness: return self.remoteness[hash(puzzle)]
return PuzzleValue.UNSOLVABLE

def solve(self, *args, verbose=False, **kwargs):
def solve(self, verbose=False):
"""Traverse the entire puzzle tree and classifies all the
positions with values and remoteness
"""
Expand All @@ -27,6 +27,10 @@ def solve(self, *args, verbose=False, **kwargs):
else:
# Not a CSP - use generateSolutions()
for solution in solutions:
# Check if all the solutions are SOLVABLE
assert solution.primitive() == PuzzleValue.SOLVABLE, """
`generateSolutions` contains an UNSOLVABLE position
"""
self.remoteness[hash(solution)] = 0
queue.put(solution)

Expand All @@ -43,6 +47,9 @@ def solve(self, *args, verbose=False, **kwargs):
for move in puzzle.generateMoves('undo'):
nextPuzzle = puzzle.doMove(move)
if hash(nextPuzzle) not in self.remoteness:
assert nextPuzzle.primitive() != PuzzleValue.SOLVABLE, """
Found a state where primitive was SOLVABLE while traversing Puzzle tree
"""
self.remoteness[hash(nextPuzzle)] = self.remoteness[hash(puzzle)] + 1
queue.put(nextPuzzle)
if verbose: bar.finish()
Expand Down
2 changes: 1 addition & 1 deletion puzzlesolver/solvers/indexsolver.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ class IndexSolver(GeneralSolver):
the index of the chunk. Recommended for puzzles with tight hash functions.
"""
def __init__(self, puzzle, *args, dir_path='databases', **kwargs):
GeneralSolver.__init__(self, puzzle, *args, **kwargs)
if not os.path.exists(dir_path): os.makedirs(dir_path)
self.path = '{}/{}.txt'.format(dir_path, puzzle.getName())
GeneralSolver.__init__(self, puzzle, *args, **kwargs)

def getRemoteness(self, puzzle, *args, **kwargs):
if hash(puzzle) in self.remoteness: return self.remoteness[hash(puzzle)]
Expand Down
4 changes: 4 additions & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@
from puzzlesolver.util import PuzzleValue
from puzzlesolver.puzzles import puzzleList, GraphPuzzle

########################################################################
# Server Fixtures
########################################################################

@pytest.fixture
def client(tmpdir):
app = server.app
Expand Down
4 changes: 2 additions & 2 deletions tests/puzzles/test_Hanoi.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

from puzzlesolver.puzzles import Hanoi
from puzzlesolver.solvers import GeneralSolver
from puzzlesolver.util import *
from puzzlesolver.util import PuzzleValue, PuzzleException

def move(move0, move1):
return (move0, move1)
Expand All @@ -13,7 +13,7 @@ def testHash():
"""Tests the expected behavior of the hash function on the puzzle states. """
puzzle0 = Hanoi.deserialize('3_2_1--')
puzzle1 = Hanoi.deserialize('3_2_1--')
puzzle2 = Hanoi.deserialize('-3_2_1-')
#puzzle2 = Hanoi.deserialize('-3_2_1-')
puzzle3 = Hanoi.deserialize('--3_2_1')

# Checks if two of the exact same states have the same hash
Expand Down
32 changes: 31 additions & 1 deletion tests/solvers/test_GeneralSolver.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,38 @@

from puzzlesolver.puzzles import GraphPuzzle
from puzzlesolver.solvers import GeneralSolver
from puzzlesolver.util import *
from puzzlesolver.util import PuzzleValue

########################################################################
# Tests
########################################################################

def testSimple(simple):
simple(GeneralSolver, csp=False)
simple(GeneralSolver, csp=True)

def testSolutionsError():
record_sol = GraphPuzzle(0, value=PuzzleValue.SOLVABLE)
missed_sol = GraphPuzzle(1, value=PuzzleValue.SOLVABLE)
not_a_solu = GraphPuzzle(2)

record_sol.setMove(missed_sol, movetype="bi")
record_sol.setMove(not_a_solu, movetype="bi")

# Two solutions, but only one is called in generateSolutions
record_sol.solutions.remove(missed_sol)

try:
solver = GeneralSolver(record_sol)
solver.solve()
except AssertionError:
pass

# generateSolutions also includes a state that is not a solution
record_sol.solutions.update(set([record_sol, missed_sol, not_a_solu]))

try:
solver = GeneralSolver(record_sol)
solver.solve()
except AssertionError:
pass