예제 #1
0
def snakes_find_the_head(g):
    # We'll want a type that represents a cell in the grid. We use a datatype because we can make it a sum type -
    # that is, its 0-arity constructors represent a closed enumeration of distinct objects.
    Cell = Datatype("Cell")

    for x, y in g.coords():
        if g.snake(x, y) is not None:
            Cell.declare("cell_{}_{}".format(x, y))
    Cell = Cell.create()

    # We'll have two functions that we explicitly declare. One is the short-distance "connected" relationship.
    # The other is a convenience function for turning the coordinates of a cell into the datatype member that
    # represents that position.
    Connected = Function("Connected", Cell, Cell, BoolSort())
    XYToCell = Function("XYToCell", IntSort(), IntSort(), Cell)

    cell = {}
    for x, y in g.coords():
        if g.snake(x, y) is not None:
            cell[x, y] = getattr(Cell, "cell_{}_{}".format(x, y))
            g.add(XYToCell(x, y) == cell[x, y])

    # Two cells are connected *if and only* if they are adjacent and both hold snakes
    # We need to be really clear about the "and only if" part; a naive implementation here will let the
    # solver fill in other arbitrary values for `Connected` in order to make the desired outcome true.
    # We do this by ensuring there's a value declared for our Connected relationship between every pair
    # of potential arguments.

    for x1, y1 in g.coords():
        c1 = g.snake(x1, y1)
        if c1 is None:
            continue
        # If there's a snake here, the cell is connected to itself
        g.add(Connected(cell[x1, y1], cell[x1, y1]) == c1)
        for x2, y2 in g.coords():
            c2 = g.snake(x2, y2)
            if c2 is None or (x1, y1) == (x2, y2):
                continue
            if manh(x1, y1, x2, y2) == 1:
                g.add(Connected(cell[x1, y1], cell[x2, y2]) == And(c1, c2))
            else:
                # Without this, our function declaration is only partial. The solver can fill in missing values
                # in order to scupper our good intentions.
                g.add(Not(Connected(cell[x1, y1], cell[x2, y2])))

    # The transitive closure of Connectedness is Reaches
    Reaches = TransitiveClosure(Connected)

    # For every cell in the grid, if it's a snake then we can connect it to the head position
    hx, hy = g.head()
    for x, y in g.coords():
        c = g.snake(x, y)
        if c is None:
            continue

        g.add(Implies(c, Reaches(cell[x, y], XYToCell(hx, hy))))
예제 #2
0
def main():
    """Skyscraper solver example."""
    lattice = grilops.get_square_lattice(SIZE)
    directions = {d.name: d for d in lattice.edge_sharing_directions()}
    sg = grilops.SymbolGrid(lattice, SYM)

    # Each row and each column contains each building height exactly once.
    for y in range(SIZE):
        sg.solver.add(Distinct(*[sg.grid[Point(y, x)] for x in range(SIZE)]))
    for x in range(SIZE):
        sg.solver.add(Distinct(*[sg.grid[Point(y, x)] for y in range(SIZE)]))

    # We'll use the sightlines accumulator to keep track of a tuple storing:
    #   the tallest building we've seen so far
    #   the number of visible buildings we've encountered
    Acc = Datatype("Acc")  # pylint: disable=C0103
    Acc.declare("acc", ("tallest", IntSort()), ("num_visible", IntSort()))
    Acc = Acc.create()  # pylint: disable=C0103

    def accumulate(a, height):
        return Acc.acc(
            If(height > Acc.tallest(a), height, Acc.tallest(a)),
            If(height > Acc.tallest(a),
               Acc.num_visible(a) + 1, Acc.num_visible(a)))

    for x, c in enumerate(GIVEN_TOP):
        sg.solver.add(c == Acc.num_visible(
            grilops.sightlines.reduce_cells(sg, Point(0, x), directions["S"],
                                            Acc.acc(0, 0), accumulate)))
    for y, c in enumerate(GIVEN_LEFT):
        sg.solver.add(c == Acc.num_visible(
            grilops.sightlines.reduce_cells(sg, Point(y, 0), directions["E"],
                                            Acc.acc(0, 0), accumulate)))
    for y, c in enumerate(GIVEN_RIGHT):
        sg.solver.add(c == Acc.num_visible(
            grilops.sightlines.reduce_cells(sg, Point(
                y, SIZE - 1), directions["W"], Acc.acc(0, 0), accumulate)))
    for x, c in enumerate(GIVEN_BOTTOM):
        sg.solver.add(c == Acc.num_visible(
            grilops.sightlines.reduce_cells(sg, Point(
                SIZE - 1, x), directions["N"], Acc.acc(0, 0), accumulate)))

    if sg.solve():
        sg.print()
        print()
        if sg.is_unique():
            print("Unique solution")
        else:
            print("Alternate solution")
            sg.print()
    else:
        print("No solution")
예제 #3
0
def skyscraper(givens: Dict[Direction, List[int]]) -> str:
    """Solver for Skyscraper minipuzzles."""
    sym = grilops.make_number_range_symbol_set(1, SIZE)
    sg = grilops.SymbolGrid(LATTICE, sym)
    shifter = Shifter(sg.solver)

    # Each row and each column contains each building height exactly once.
    for y in range(SIZE):
        sg.solver.add(Distinct(*[sg.grid[Point(y, x)] for x in range(SIZE)]))
    for x in range(SIZE):
        sg.solver.add(Distinct(*[sg.grid[Point(y, x)] for y in range(SIZE)]))

    # We'll use the sightlines accumulator to keep track of a tuple storing:
    #   the tallest building we've seen so far
    #   the number of visible buildings we've encountered
    Acc = Datatype("Acc")  # pylint: disable=C0103
    Acc.declare("acc", ("tallest", IntSort()), ("num_visible", IntSort()))
    Acc = Acc.create()  # pylint: disable=C0103

    def accumulate(a, height):
        return Acc.acc(
            If(height > Acc.tallest(a), height, Acc.tallest(a)),
            If(height > Acc.tallest(a),
               Acc.num_visible(a) + 1, Acc.num_visible(a)))

    for d, gs in givens.items():
        for i, g in enumerate(gs):
            if d.vector.dy != 0:
                g = g - shifter.col_shifts.get(i, 0)
                p = Point(0 if d.vector.dy < 0 else SIZE - 1, i)
            elif d.vector.dx != 0:
                g = g - shifter.row_shifts.get(i, 0)
                p = Point(i, 0 if d.vector.dx < 0 else SIZE - 1)
            sg.solver.add(g == Acc.num_visible(  # type: ignore[attr-defined]
                grilops.sightlines.reduce_cells(
                    sg,
                    p,
                    LATTICE.opposite_direction(d),
                    Acc.acc(0, 0),  # type: ignore[attr-defined]
                    accumulate)))

    assert sg.solve()
    sg.print()
    print()
    shifter.print_shifts()
    print()
    return shifter.eval_binary()
예제 #4
0
파일: shapes.py 프로젝트: obijywk/grilops
    def test_datatype_payloads(self):
        lattice = get_square_lattice(3)
        sym = make_number_range_symbol_set(0, 2)
        row_grid = SymbolGrid(lattice, sym)
        col_grid = SymbolGrid(lattice, sym, solver=row_grid.solver)

        RowCol = Datatype("RowCol")
        RowCol.declare("row_col", ("row", IntSort()), ("col", IntSort()))
        RowCol = RowCol.create()

        sc = ShapeConstrainer(lattice, [
            Shape([
                (Vector(0, 0), RowCol.row_col(0, 0)),
                (Vector(0, 1), RowCol.row_col(0, 1)),
                (Vector(1, 0), RowCol.row_col(1, 0)),
            ]),
            Shape([
                (Vector(0, 1), RowCol.row_col(0, 2)),
                (Vector(1, 0), RowCol.row_col(1, 1)),
                (Vector(1, 1), RowCol.row_col(1, 2)),
                (Vector(2, 1), RowCol.row_col(2, 2)),
            ]),
            Shape([
                (Vector(0, 0), RowCol.row_col(2, 0)),
                (Vector(0, 1), RowCol.row_col(2, 1)),
            ]),
        ],
                              solver=row_grid.solver,
                              complete=True)

        for p in lattice.points:
            row_grid.solver.add(
                row_grid.cell_is(p, RowCol.row(sc.shape_payload_grid[p])))
            col_grid.solver.add(
                col_grid.cell_is(p, RowCol.col(sc.shape_payload_grid[p])))

        self.assertTrue(row_grid.solve())
        solved_row_grid = row_grid.solved_grid()
        solved_col_grid = col_grid.solved_grid()
        for p in lattice.points:
            self.assertEqual(solved_row_grid[p], p.y)
            self.assertEqual(solved_col_grid[p], p.x)
        self.assertTrue(row_grid.is_unique())
예제 #5
0
파일: program.py 프로젝트: fuurin/meyer
from .meyer import U
from .util.z3py_set import Set
from .util.z3py_rel import Rel
from .util.z3py_util import const, consts, show_record_element

## @file program.py
#  This module can be used for definition of specification/program instance
#
#

SET = ArraySort(U, BoolSort())
# OOPSet = ArraySort(IntSort(), ArraySort(U, BoolSort()))
PRE = ArraySort(U, BoolSort())
POST = ArraySort(U, ArraySort(U, BoolSort()))

PROG = Datatype('Prog')
PROG.declare('mk_prog', ('set', SET), ('pre', PRE), ('post', POST))
PROG = PROG.create()
set_ = PROG.set
pre_ = PROG.pre
post_ = PROG.post


class Program():
    """Base class for Program instance."""

    #  @param p A program instance created by Z3.py.
    def __init__(self, p):
        self.p = p

    #  @param x An element that is included in Set of this program.
예제 #6
0
"""Z3 implementation of L - the meta-Kappa devised by Adrien Husson and Jean
Krivine - using the Python bindings for Z3.
"""

from z3 import Datatype, ArraySort, IntSort, BoolSort, Function, Const
from z3_helpers import Iff, Equals

# Node is a datatype representing a vertex or node in a Kappa graph.
Node = Datatype('Node')
Node.declare('node', ('unique_identifier', IntSort()))
Node = Node.create()

# A datatype for storing a pair of edges
Edge = Datatype('Edge')
Edge.declare('edge', ('node1', Node), ('node2', Node))
Edge = Edge.create()

Nodeset = ArraySort(Node, BoolSort())
Edgeset = ArraySort(Edge, BoolSort())

Labelset = ArraySort(IntSort(), BoolSort())
Labelmap = ArraySort(Node, Labelset)

# Graph, before a rule or action has applied. Merged Pregraph and Postgraph
# into a single datatype.
Graph = Datatype('Graph')
Graph.declare('graph', ('has', Nodeset), ('links', Edgeset),
              ('parents', Edgeset), ('labelmap', Labelmap))
Graph = Graph.create()

# Atomic action. An Action is comprised of a set of these.
예제 #7
0
HEIGHT, WIDTH = 6, 10
LATTICE = grilops.get_rectangle_lattice(HEIGHT, WIDTH)

SYM = grilops.make_letter_range_symbol_set("A", "Z")


class Color(IntEnum):
    """The color of a section of a piece."""
    YELLOW = 1
    BLUE = 2
    RED = 3
    WHITE = 4


LetterColor = Datatype("LetterColor")
LetterColor.declare("letter_color", ("letter", IntSort()),
                    ("color", IntSort()))
LetterColor = LetterColor.create()


def letter_color(letter: str, color: Color) -> ExprRef:
    """Creates a LetterColor z3 constant."""
    return LetterColor.letter_color(SYM[letter],
                                    color.value)  # type: ignore[attr-defined]


SHAPES: List[Shape] = [
    Shape([
        (Vector(0, 1), letter_color("R", Color.BLUE)),
        (Vector(1, 0), letter_color("R", Color.BLUE)),
예제 #8
0
from z3 import (BoolSort, Const, Datatype, Exists, ForAll, Function, Implies,
                Not, Solver, unsat)

problem = """Someone who lived in Dreadbury Mansion killed Aunt Agatha. Agatha,
the Butler and Charles were the only people who lived in Dreadbury
Mansion. A killer always hates his victim, and is never richer than
his victim. Charles hates no one that aunt Agatha hates. Agatha hates
everyone except the butler. The butler hates everyone not richer than
Aunt Agatha. The butler also hates everyone Agatha hates. No one hates
everyone. Agatha is not the butler.  Who killed Aunt Agatha?
"""

print(problem)

# declare finite data type mansion
MansionDT = Datatype("Mansion")
MansionDT.declare("Agatha")
MansionDT.declare("Butler")
MansionDT.declare("Charles")

# create finite sort Mansion
Mansion = MansionDT.create()

# constants for ease of reference
a, b, c = Mansion.Agatha, Mansion.Butler, Mansion.Charles

# declare predicates
killed = Function("killed", Mansion, Mansion, BoolSort())
hates = Function("hates", Mansion, Mansion, BoolSort())
richer = Function("richer", Mansion, Mansion, BoolSort())