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")
def main(): """Sudoku solver example.""" givens = [ [5, 3, 0, 0, 7, 0, 0, 0, 0], [6, 0, 0, 1, 9, 5, 0, 0, 0], [0, 9, 8, 0, 0, 0, 0, 6, 0], [8, 0, 0, 0, 6, 0, 0, 0, 3], [4, 0, 0, 8, 0, 3, 0, 0, 1], [7, 0, 0, 0, 2, 0, 0, 0, 6], [0, 6, 0, 0, 0, 0, 2, 8, 0], [0, 0, 0, 4, 1, 9, 0, 0, 5], [0, 0, 0, 0, 8, 0, 0, 7, 9], ] sym = grilops.make_number_range_symbol_set(1, 9) sg = grilops.SymbolGrid(grilops.get_square_lattice(9), sym) for y, given_row in enumerate(givens): for x, given in enumerate(given_row): if given != 0: sg.solver.add(sg.cell_is(Point(y, x), sym[given])) for y in range(9): sg.solver.add(Distinct(*[sg.grid[Point(y, x)] for x in range(9)])) for x in range(9): sg.solver.add(Distinct(*[sg.grid[Point(y, x)] for y in range(9)])) for z in range(9): top = (z // 3) * 3 left = (z % 3) * 3 cells = [sg.grid[Point(y, x)] for y in range(top, top + 3) for x in range(left, left + 3)] sg.solver.add(Distinct(*cells)) if sg.solve(): sg.print() print() if sg.is_unique(): print("Unique solution") else: print("Alternate solution") sg.print() else: print("No solution")
def main(): """Heteromino solver example.""" lattice = grilops.get_square_lattice(SIZE) sg = grilops.SymbolGrid(lattice, SYM) rc = grilops.regions.RegionConstrainer(lattice, solver=sg.solver, complete=False) def constrain_neighbor(p, np, is_root, shape, has_neighbor): sg.solver.add(Implies(And(is_root, has_neighbor), sg.grid[np] == shape)) sg.solver.add( Implies(rc.region_id_grid[p] != rc.region_id_grid[np], sg.grid[np] != shape)) for p in lattice.points: if p in BLACK_CELLS: sg.solver.add(sg.cell_is(p, SYM.BL)) sg.solver.add(rc.region_id_grid[p] == -1) continue sg.solver.add(Not(sg.cell_is(p, SYM.BL))) # All regions have size 3. sg.solver.add(rc.region_size_grid[p] == 3) # Force the root of each region subtree to be in the middle of the # region, by not allowing non-root cells to have children. sg.solver.add( Implies(rc.parent_grid[p] != grilops.regions.R, rc.subtree_size_grid[p] == 1)) # All cells in the same region must have the same shape symbol. Cells in # different regions must not have the same shape symbol. shape = sg.grid[p] is_root = rc.parent_grid[p] == grilops.regions.R has_north = False if p.y > 0: np = Point(p.y - 1, p.x) has_north = rc.parent_grid[np] == rc.parent_type_to_index("S") constrain_neighbor(p, np, is_root, shape, has_north) has_south = False if p.y < SIZE - 1: np = Point(p.y + 1, p.x) has_south = rc.parent_grid[np] == rc.parent_type_to_index("N") constrain_neighbor(p, np, is_root, shape, has_south) has_west = False if p.x > 0: np = Point(p.y, p.x - 1) has_west = rc.parent_grid[np] == rc.parent_type_to_index("E") constrain_neighbor(p, np, is_root, shape, has_west) has_east = False if p.x < SIZE - 1: np = Point(p.y, p.x + 1) has_east = rc.parent_grid[np] == rc.parent_type_to_index("W") constrain_neighbor(p, np, is_root, shape, has_east) # Constrain the shape symbol based on adjacent cell relationships. for shape_symbol, region_presence in [ (SYM.NS, (has_north, has_south)), (SYM.EW, (has_east, has_west)), (SYM.NE, (has_north, has_east)), (SYM.SE, (has_south, has_east)), (SYM.SW, (has_south, has_west)), (SYM.NW, (has_north, has_west)), ]: sg.solver.add(Implies(And(*region_presence), shape == shape_symbol)) if sg.solve(): sg.print() print() if sg.is_unique(): print("Unique solution") else: print("Alternate solution") sg.print() else: print("No solution")
import grilops import grilops.loops from grilops.geometry import Point ANSWERS = [ "HATAMOTO", "IONESKYE", "JARGONIC", "KLONDIKE", "LUCHADOR", "MOCKTAIL", "NURSEJOY", "OMOPLATE", ] LATTICE = grilops.get_square_lattice(8) SYM = grilops.loops.LoopSymbolSet(LATTICE) TURN_SYMBOLS = [SYM.NE, SYM.SE, SYM.SW, SYM.NW] def extract_answer(sg, loop_order_grid): """Extract the metapuzzle answer from the grids.""" model = sg.solver.model() loop_order_to_point = { model.eval(loop_order_grid[p]).as_long(): p for p in sg.lattice.points } ordered_points = sorted(list(loop_order_to_point.items())) solved_grid = sg.solved_grid() answer = "" for _, p in ordered_points:
def main(): """Greater Than Killer Sudoku solver example.""" cages = [ "AABCCDDEE", "FFBGGHIJK", "FLMNHHIJK", "LLMNNOIPP", "QQRNSOTTU", "VWRSSXTUU", "VWYYSXZaa", "bccddXZee", "bbbdffZgg", ] cage_sums = { "B": 6, "D": 16, "F": 14, "H": 17, "I": 9, "J": 12, "K": 9, "L": 20, "M": 13, "N": 29, "O": 4, "R": 8, "S": 12, "V": 8, "W": 14, "Y": 17, "b": 11, "d": 11, "e": 8, } sym = grilops.make_number_range_symbol_set(1, 9) lattice = grilops.get_square_lattice(9) sg = grilops.SymbolGrid(lattice, sym) add_sudoku_constraints(sg) # Build a map from each cage label to the cells within that cage. cage_cells = defaultdict(list) for p in lattice.points: cage_cells[cages[p.y][p.x]].append(sg.grid[p]) # The digits used in each cage must be unique. for cells_in_cage in cage_cells.values(): sg.solver.add(Distinct(*cells_in_cage)) # Add constraints for cages with given sums. for cage_label, cage_sum in cage_sums.items(): sg.solver.add(Sum(*cage_cells[cage_label]) == cage_sum) # Add constraints between cage sums. def cage_sum_greater(a, b): sg.solver.add(Sum(*cage_cells[a]) > Sum(*cage_cells[b])) def cage_sum_equal(a, b): sg.solver.add(Sum(*cage_cells[a]) == Sum(*cage_cells[b])) cage_sum_equal("C", "G") cage_sum_greater("J", "E") cage_sum_greater("E", "K") cage_sum_greater("W", "c") cage_sum_greater("c", "b") cage_sum_greater("f", "d") cage_sum_greater("X", "f") if sg.solve(): sg.print() print() if sg.is_unique(): print("Unique solution") else: print("Alternate solution") sg.print() else: print("No solution")
def main(): """Akari solver example.""" size = 10 lattice = grilops.get_square_lattice(size) black_cells = { (0, 0): None, (0, 3): None, (0, 9): None, (1, 7): None, (2, 1): 3, (2, 6): 0, (3, 2): 2, (3, 5): None, (3, 9): 1, (4, 3): 1, (4, 4): 0, (4, 5): None, (5, 4): 1, (5, 5): None, (5, 6): None, (6, 0): None, (6, 4): 2, (6, 7): 2, (7, 3): None, (7, 8): None, (8, 2): 1, (9, 0): 0, (9, 6): 1, (9, 9): 0, } def print_given(point): if point in black_cells: v = black_cells.get(point) if v is None: return chr(0x2588) return str(v) return None lattice.print(print_given) print() sym = grilops.SymbolSet([ ("BLACK", chr(0x2588)), ("EMPTY", " "), ("LIGHT", "*"), ]) sg = grilops.SymbolGrid(lattice, sym) for point in lattice.points: if point in black_cells: sg.solver.add(sg.cell_is(point, sym.BLACK)) light_count = black_cells[point] if light_count is not None: sg.solver.add( PbEq([(n.symbol == sym.LIGHT, 1) for n in sg.edge_sharing_neighbors(point)], light_count)) else: # All black cells are given; don't allow this cell to be black. sg.solver.add(sg.cell_is_one_of(point, [sym.EMPTY, sym.LIGHT])) def is_black(c): return c == sym.BLACK def count_light(c): return If(c == sym.LIGHT, 1, 0) for point in lattice.points: if point in black_cells: continue visible_light_count = sum( grilops.sightlines.count_cells( sg, n.location, n.direction, count=count_light, stop=is_black) for n in sg.edge_sharing_neighbors(point)) # Ensure that each light cannot see any other lights, and that each cell # is lit by at least one light. sg.solver.add( If(sg.cell_is(point, sym.LIGHT), visible_light_count == 0, visible_light_count > 0)) if sg.solve(): sg.print() print() if sg.is_unique(): print("Unique solution") else: print("Alternate solution") sg.print() else: print("No solution")
def main(): """Outflight Entertainment sudoku solver example.""" cages = [ "AABCCD", "EFBCGD", "EFFHGI", "EJJHGI", "EKJHGL", "KKJLLL", ] peaks = [ (0, 1), (0, 2), (1, 3), (1, 4), (1, 5), (2, 1), (2, 3), (3, 0), (3, 5), (4, 2), (5, 1), (5, 4), ] extract = { "A": (5, 2), "B": (0, 3), "C": (3, 2), "D": (1, 4), "E": (0, 1), "F": (1, 5), "G": (4, 2), "H": (3, 5), "I": (1, 0), "J": (5, 5), "K": (3, 4), "L": (5, 1), "M": (0, 5), "N": (4, 4), } def answer(sg): solved_grid = sg.solved_grid() s = "" s += chr(64 + solved_grid[extract["A"]] + solved_grid[extract["B"]]) s += chr(64 + solved_grid[extract["C"]] + solved_grid[extract["D"]]) s += chr(64 + solved_grid[extract["E"]] + solved_grid[extract["F"]] + solved_grid[extract["G"]]) s += chr(64 + solved_grid[extract["H"]] + solved_grid[extract["I"]]) s += chr(64 + solved_grid[extract["J"]] + solved_grid[extract["K"]] + solved_grid[extract["L"]]) s += chr(64 + solved_grid[extract["M"]] + solved_grid[extract["N"]]) return s sym = grilops.make_number_range_symbol_set(1, 6) lattice = grilops.get_square_lattice(6) sg = grilops.SymbolGrid(lattice, sym) add_sudoku_constraints(sg) # Constrain regions to match the cages and be rooted at the peaks. cage_label_to_region_id = {} for py, px in peaks: cage_label_to_region_id[cages[py][px]] = lattice.point_to_index( (py, px)) rc = grilops.regions.RegionConstrainer(lattice, sg.solver) for y, x in lattice.points: sg.solver.add( rc.region_id_grid[(y, x)] == cage_label_to_region_id[cages[y][x]]) # Within each region, a parent cell must have a greater value than a child # cell, so that the values increase as you approach the root cell (the peak). for p in lattice.points: for n in sg.edge_sharing_neighbors(p): sg.solver.add( Implies( rc.edge_sharing_direction_to_index( n.direction) == rc.parent_grid[p], n.symbol > sg.grid[p])) if sg.solve(): sg.print() print() print(answer(sg)) while not sg.is_unique(): print() print("Alternate solution") sg.print() print() print(answer(sg)) else: print("No solution")