Skip to content

Commit

Permalink
v0.4.4
Browse files Browse the repository at this point in the history
  • Loading branch information
oprypin committed Sep 17, 2014
1 parent f8da560 commit 1bdd1ed
Show file tree
Hide file tree
Showing 4 changed files with 59 additions and 106 deletions.
4 changes: 2 additions & 2 deletions common.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
# along with SixCells. If not, see <http://www.gnu.org/licenses/>.


__version__ = '0.4.3'
__version__ = '0.4.4'

import sys
import os.path
Expand Down Expand Up @@ -163,7 +163,7 @@ def upd(self, first=True):
fit_inside(self, self.text, 0.5)

if highlight:
self.setBrush(Color.yellow_border)
self.setBrush(Color.proven)

self.update()

Expand Down
106 changes: 23 additions & 83 deletions player.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import sys
import itertools
import collections
import time

import common
from common import *
Expand Down Expand Up @@ -139,7 +140,7 @@ def remaining(self):
def mistakes(self):
self.text_changed.emit()

@lazy_property
@cached_property
def _flower_poly(self):
result = QPolygonF()
hex1 = QPolygonF()
Expand Down Expand Up @@ -184,79 +185,13 @@ def drawForeground(self, g, rect):
g.drawConvexPolygon(poly)


def solve_simple(self):
cells, columns, known, related = solve_assist(self)

for cur in itertools.chain(known, columns):
if not any(x.kind is Cell.unknown for x in cur.members):
continue
if cur.value is not None:
# Fill up remaining fulls
if cur.value==sum(1 for x in cur.members if x.kind is not Cell.empty):
for x in cur.members:
if x.kind is Cell.unknown:
x.proven(Cell.full)
if isinstance(cur, Column):
cur.hidden = True
yield
# Fill up remaining empties
if len(cur.members)-cur.value==sum(1 for x in cur.members if x.kind is not Cell.full):
for x in cur.members:
if x.kind is Cell.unknown:
x.proven(Cell.empty)
if isinstance(cur, Column):
cur.hidden = True
yield

#def is_possible(self, assumed, max_depth=None, depth=0):
#cells, columns, known, related = self.solve_assist()

#def kind(x):
#try:
#return assumed[x]
#except KeyError:
#return x.kind

#all_related = set(itertools.chain.from_iterable((x for x in related[cur] if isinstance(x, Column) or (x.kind is not Cell.unknown and x.value is not None)) for cur in assumed))

#for cur in all_related:
#if sum(1 for x in cur.members if kind(x) is Cell.full)>cur.value:
#return False
#if sum(1 for x in cur.members if kind(x) is Cell.empty)>len(cur.members)-cur.value:
#return False
#if cur.together is not None and cur.value>1:
#if isinstance(cur, Cell):
#together = all_grouped({x for x in cur.members if kind(x) is Cell.full}, key=Cell.is_neighbor)
#else:
#groups = list(itertools.groupby(cur.members, key=lambda x: kind(x) is Cell.full))
#together = sum(1 for k, gr in groups if k)<=1
#if not cur.together and together and cur.value>=#TODO
#return True


#def solve_negative_proof(self):
#cells, columns, known, related = self.solve_assist()

#for cur in itertools.chain(known, columns):
#if not any(x.kind is Cell.unknown for x in cur.members):
#continue
#if cur.value is not None:
## Fill up remaining fulls
#if cur.value==sum(1 for x in cur.members if x.kind is not Cell.empty):
#for x in cur.members:
#if x.kind is Cell.unknown:
#x.proven(Cell.full)
#if isinstance(cur, Column):
#cur.hidden = True
#yield
## Fill up remaining empties
#if len(cur.members)-cur.value==sum(1 for x in cur.members if x.kind is not Cell.full):
#for x in cur.members:
#if x.kind is Cell.unknown:
#x.proven(Cell.empty)
#if isinstance(cur, Column):
#cur.hidden = True
#yield
@cached_property
def all_cells(self):
return list(self.all(Cell))

@cached_property
def all_columns(self):
return list(self.all(Column))


def solve_step(self):
Expand All @@ -280,20 +215,25 @@ def solve_step(self):

self.solving = False

for cell in self.all(Cell):
try:
cell.proven
cell.setBrush(Color.proven)
except AttributeError:
pass

return progress

def solve_complete(self):
"""Continue solving until stuck.
Return whether the entire level could be uncovered."""
while self.solve_step():
continue
while True:
self.confirm_proven()
app.processEvents()
for cell, value in solve_simple(self):
try:
assert cell.actual==value
except AssertionError:
cell.setPen(QPen(qt.red, 0.2))
raise
cell.kind = cell.actual
cell.upd()
if not self.solve_step():
break


# If it identified all blue cells, it'll have the rest uncovered as well
return self.remaining == 0
Expand Down
50 changes: 30 additions & 20 deletions solver.py
Original file line number Diff line number Diff line change
Expand Up @@ -65,24 +65,20 @@ def get_solver():
return None


def solve_assist(scene):
cells = list(scene.all(Cell))
columns = list(scene.all(Column))

# related contains all the active conditions
# a cell is a part of
related = collections.defaultdict(set)
for cur in itertools.chain(cells, columns):
# Ignore unrevealed or uninformative cells
if isinstance(cur, Cell) and (cur.kind is Cell.unknown or cur.value is None):
continue
for x in cur.members:
related[x].add(cur)

known = {it: it.kind for it in cells if it.kind is not Cell.unknown}
unknown = [cell for cell in cells if cell.kind is Cell.unknown]
solver = get_solver()
return cells, columns, known, unknown, related, solver

def solve_simple(scene):
for cur in itertools.chain(scene.all_cells, scene.all_columns):
if cur.value is not None and any(x.kind is Cell.unknown for x in cur.members):
# Fill up remaining fulls
if cur.value==sum(1 for x in cur.members if x.kind is not Cell.empty):
for x in cur.members:
if x.kind is Cell.unknown:
yield x, Cell.full
# Fill up remaining empties
if len(cur.members)-cur.value==sum(1 for x in cur.members if x.kind is not Cell.full):
for x in cur.members:
if x.kind is Cell.unknown:
yield x, Cell.empty


def solve(scene):
Expand All @@ -93,7 +89,21 @@ def solve(scene):
# unknown: unrevealed cells
# related: Maps a cell to all relevant constraints (cells and columns) that it is a member of
# solver: MILP program to use
cells, columns, known, unknown, related, solver = solve_assist(scene)

cells = scene.all_cells
columns = scene.all_columns
known = [cell for cell in cells if cell.kind is not Cell.unknown]
unknown = [cell for cell in cells if cell.kind is Cell.unknown]

related = collections.defaultdict(set)
for cur in itertools.chain(cells, columns):
# Ignore unrevealed or uninformative cells
if isinstance(cur, Cell) and (cur.kind is Cell.unknown or cur.value is None):
continue
for x in cur.members:
related[x].add(cur)

solver = get_solver()

# The MILP Problem (managed by PuLP)
# all variables and constraint will be added to this problem
Expand All @@ -104,7 +114,7 @@ def solve(scene):
# The value of 1 means the cell is blue, 0 means the cell is black.
# The eventual goal is to determine combinations of values fulfilling all constraints.
dic = LpVariable.dicts('v', [str(cell.id) for cell in cells if cell.kind is Cell.unknown], 0, 1, 'Binary')

# Convenience: Maps a cell to the corresponding variable (or constant if its known already):
def get_var(cell):
return dic[str(cell.id)] if cell.kind is Cell.unknown else cell.kind
Expand Down
5 changes: 4 additions & 1 deletion util.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@
# along with SixCells. If not, see <http://www.gnu.org/licenses/>.


import collections as _collections


def count(it, condition=None):
"Count the number of elements in an iterable (that satisfy the condition, if the function is provided)"
if condition is None:
Expand Down Expand Up @@ -47,7 +50,7 @@ def all_grouped(items, key):
return len(grouped)==len(items)


class lazy_property(object):
class cached_property(object):
"Attribute that is calculated upon first access and then stored"
def __init__(self, fget):
self.__doc__ = fget.__doc__
Expand Down

0 comments on commit 1bdd1ed

Please sign in to comment.