Пример #1
0
def solve_magnets(height, width, to_right, to_down, cond_row, cond_col):
    solver = Solver()
    plus = solver.bool_array((height, width))
    minus = solver.bool_array((height, width))
    solver.add_answer_key(plus)
    solver.add_answer_key(minus)
    solver.ensure(~(plus & minus))
    solver.ensure(
        Array(to_right)[:, :-1].then((plus[:, :-1] == minus[:, 1:])
                                     & (minus[:, :-1] == plus[:, 1:])))
    solver.ensure(
        Array(to_down)[:-1, :].then((plus[:-1, :] == minus[1:, :])
                                    & (minus[:-1, :] == plus[1:, :])))
    solver.ensure(~(plus[:-1, :] & plus[1:, :]))
    solver.ensure(~(minus[:-1, :] & minus[1:, :]))
    solver.ensure(~(plus[:, :-1] & plus[:, 1:]))
    solver.ensure(~(minus[:, :-1] & minus[:, 1:]))
    for y in range(height):
        if cond_row[y][0] >= 0:
            solver.ensure(count_true(plus[y, :]) == cond_row[y][0])
        if cond_row[y][1] >= 0:
            solver.ensure(count_true(minus[y, :]) == cond_row[y][1])
    for x in range(width):
        if cond_col[x][0] >= 0:
            solver.ensure(count_true(plus[:, x]) == cond_col[x][0])
        if cond_col[x][1] >= 0:
            solver.ensure(count_true(minus[:, x]) == cond_col[x][1])
    is_sat = solver.solve()
    return is_sat, plus, minus
Пример #2
0
def solve_nurimaze(height, width, wall_vertical, wall_horizontal, mark, start,
                   goal):
    solver = Solver()
    is_white = solver.bool_array((height, width))
    graph.active_vertices_connected(solver, is_white, acyclic=True)
    solver.add_answer_key(is_white)

    solver.ensure(is_white[:-1, :-1] | is_white[:-1, 1:] | is_white[1:, :-1]
                  | is_white[1:, 1:])
    solver.ensure(~(is_white[:-1, :-1] & is_white[:-1, 1:] & is_white[1:, :-1]
                    & is_white[1:, 1:]))
    path = solver.bool_array((height, width))
    solver.ensure(path.then(is_white))
    for y in range(height):
        for x in range(width):
            if x < width - 1 and not wall_vertical[y][x]:
                solver.ensure(is_white[y, x] == is_white[y, x + 1])
            if y < height - 1 and not wall_horizontal[y][x]:
                solver.ensure(is_white[y, x] == is_white[y + 1, x])
            if (y, x) == start or (y, x) == goal:
                solver.ensure(path[y, x])
                solver.ensure(count_true(path.four_neighbors(y, x)) == 1)
            else:
                solver.ensure(
                    path[y,
                         x].then(count_true(path.four_neighbors(y, x)) == 2))
            if mark[y][x] != 0:
                solver.ensure(is_white[y, x])
            if mark[y][x] == 1:  # pass
                solver.ensure(path[y, x])
            elif mark[y][x] == 2:
                solver.ensure(~path[y, x])

    is_sat = solver.solve()
    return is_sat, is_white
Пример #3
0
def _active_vertices_connected(solver,
                               is_active,
                               graph,
                               acyclic=False,
                               use_graph_primitive=None):
    if use_graph_primitive is None:
        use_graph_primitive = config.use_graph_primitive
    if use_graph_primitive and not acyclic:
        solver.ensure(
            BoolExpr(Op.GRAPH_ACTIVE_VERTICES_CONNECTED,
                     [graph.num_vertices, len(graph)] + list(is_active) +
                     sum([[x, y] for x, y in graph.edges], [])))
        return

    n = graph.num_vertices

    ranks = solver.int_array(n, 0, n - 1)
    is_root = solver.bool_array(n)

    for i in range(n):
        less_ranks = [((ranks[j] < ranks[i]) & is_active[j])
                      for j, _ in graph.incident_edges[i]]
        if acyclic:
            for j, _ in graph.incident_edges[i]:
                if i < j:
                    solver.ensure(ranks[j] != ranks[i])
            solver.ensure(
                is_active[i].then(count_true(less_ranks + [is_root[i]]) == 1))
        else:
            solver.ensure(
                is_active[i].then(count_true(less_ranks + [is_root[i]]) >= 1))
    solver.ensure(count_true(is_root) <= 1)
Пример #4
0
def _division_connected(solver,
                        division,
                        num_regions,
                        graph,
                        roots=None,
                        allow_empty_group=False,
                        use_graph_primitive=None):
    if use_graph_primitive is None:
        use_graph_primitive = config.use_graph_primitive

    n = graph.num_vertices
    m = len(graph)

    if use_graph_primitive:
        for i in range(num_regions):
            region = solver.bool_array(n)
            solver.ensure(region == (division == i))
            _active_vertices_connected(solver,
                                       region,
                                       graph,
                                       use_graph_primitive=True)

            if not allow_empty_group:
                solver.ensure(count_true(region) >= 1)

        if roots is not None:
            for i, r in enumerate(roots):
                if r is not None:
                    solver.ensure(division[r] == i)
        return

    rank = solver.int_array(n, 0, n - 1)
    is_root = solver.bool_array(n)
    spanning_forest = solver.bool_array(m)

    for i in range(n):
        less_ranks = []
        for j, e in graph.incident_edges[i]:
            less_ranks.append(spanning_forest[e] & (rank[i] > rank[j]))
            if i < j:
                solver.ensure(
                    spanning_forest[e].then((division[i] == division[j])
                                            & (rank[i] != rank[j])))
        solver.ensure(count_true(less_ranks) == is_root[i].cond(0, 1))
    for i in range(num_regions):
        if allow_empty_group:
            solver.ensure(
                count_true([r & (n == i)
                            for r, n in zip(is_root, division)]) <= 1)
        else:
            solver.ensure(
                count_true([r & (n == i)
                            for r, n in zip(is_root, division)]) == 1)
    if roots is not None:
        for i, r in enumerate(roots):
            if r is not None:
                solver.ensure(division[r] == i)
                solver.ensure(is_root[r])
Пример #5
0
def solve_nurimisaki(height, width, problem):
    solver = Solver()
    is_white = solver.bool_array((height, width))
    solver.add_answer_key(is_white)

    graph.active_vertices_connected(solver, is_white)

    solver.ensure(is_white[:-1, :-1] | is_white[1:, :-1] | is_white[:-1, 1:]
                  | is_white[1:, 1:])
    solver.ensure(~(is_white[:-1, :-1] & is_white[1:, :-1] & is_white[:-1, 1:]
                    & is_white[1:, 1:]))

    for y in range(height):
        for x in range(width):
            if problem[y][x] == -1:
                solver.ensure(is_white[y, x].then(
                    count_true(is_white.four_neighbors(y, x)) != 1))
            else:
                solver.ensure(is_white[y, x])
                solver.ensure(count_true(is_white.four_neighbors(y, x)) == 1)
                if problem[y][x] != 0:
                    n = problem[y][x]
                    cand = []
                    if y == n - 1:
                        cand.append(fold_and(is_white[(y - n + 1):y, x]))
                    elif y > n - 1:
                        cand.append(
                            fold_and(is_white[(y - n + 1):y, x],
                                     ~is_white[y - n, x]))
                    if y == height - n:
                        cand.append(fold_and(is_white[(y + 1):(y + n), x]))
                    elif y < height - n:
                        cand.append(
                            fold_and(is_white[(y + 1):(y + n), x],
                                     ~is_white[y + n, x]))
                    if x == n - 1:
                        cand.append(fold_and(is_white[y, (x - n + 1):x]))
                    elif x > n - 1:
                        cand.append(
                            fold_and(is_white[y, (x - n + 1):x],
                                     ~is_white[y, x - n]))
                    if x == width - n:
                        cand.append(fold_and(is_white[y, (x + 1):(x + n)]))
                    elif x < width - n:
                        cand.append(
                            fold_and(is_white[y, (x + 1):(x + n)],
                                     ~is_white[y, x + n]))
                    solver.ensure(fold_or(cand))

    is_sat = solver.solve()
    return is_sat, is_white
Пример #6
0
def check_problem_constraints(height, width, problem, flg, circ=-1):
    if flg is None and circ == -1:
        return True
    for clue in problem:
        a = 0
        for i in range(2, 6):
            if clue[i] >= 0:
                a += 1
    solver = Solver()
    roots = map(lambda x: (x[0], x[1]), problem)
    division = solver.int_array((height, width), 0, len(problem) - 1)
    graph.division_connected(solver, division, len(problem), roots=roots)
    solver.add_answer_key(division)
    for i, (y, x, u, l, d, r) in enumerate(problem):
        solver.ensure(division[y, x] == i)
        if flg is not None and flg[i]:
            solver.ensure(count_true(division == i) >= 4)
        if u >= 0:
            solver.ensure(count_true(division[:y, :] == i) == u)
        if d >= 0:
            solver.ensure(count_true(division[(y + 1):, :] == i) == d)
        if l >= 0:
            solver.ensure(count_true(division[:, :x] == i) == l)
        if r >= 0:
            solver.ensure(count_true(division[:, (x + 1):] == i) == r)

    # encircling constraint
    if circ != -1:
        col = solver.bool_array((height, width))
        solver.ensure(col[0, :])
        solver.ensure(col[-1, :])
        solver.ensure(col[:, 0])
        solver.ensure(col[:, -1])
        solver.ensure(
            ((division[1:, :] != circ) &
             (division[:-1, :] != circ)).then(col[1:, :] == col[:-1, :]))
        solver.ensure(
            ((division[:, 1:] != circ) &
             (division[:, :-1] != circ)).then(col[:, 1:] == col[:, :-1]))
        solver.ensure(
            ((division[1:, 1:] != circ) &
             (division[:-1, :-1] != circ)).then(col[1:, 1:] == col[:-1, :-1]))
        solver.ensure(
            ((division[:-1, 1:] != circ) &
             (division[1:, :-1] != circ)).then(col[:-1, 1:] == col[1:, :-1]))
        solver.ensure(fold_or(col & (division != circ)))
        solver.ensure(fold_or((~col) & (division != circ)))

    sat = solver.find_answer()
    return sat
Пример #7
0
def solve_norinori(height, width, blocks):
    solver = Solver()
    is_black = solver.bool_array((height, width))
    solver.add_answer_key(is_black)

    for y in range(height):
        for x in range(width):
            solver.ensure(is_black[y, x].then(
                count_true(is_black.four_neighbors(y, x)) == 1))

    for block in blocks:
        solver.ensure(count_true(map(lambda p: is_black[p], block)) == 2)

    is_sat = solver.solve()
    return is_sat, is_black
Пример #8
0
def solve_putteria(height, width, blocks):
    solver = Solver()
    has_number = solver.bool_array((height, width))
    solver.add_answer_key(has_number)

    solver.ensure((~has_number[:, :-1]) | (~has_number[:, 1:]))
    solver.ensure((~has_number[:-1, :]) | (~has_number[1:, :]))
    for block in blocks:
        solver.ensure(count_true([has_number[y, x] for y, x in block]) == 1)

    block_size = [[0 for _ in range(width)] for _ in range(height)]
    for block in blocks:
        for y, x in block:
            block_size[y][x] = len(block)
    for y in range(height):
        for x1 in range(width):
            for x2 in range(x1 + 1, width):
                if block_size[y][x1] == block_size[y][x2]:
                    solver.ensure(~(has_number[y, x1] & has_number[y, x2]))
    for x in range(width):
        for y1 in range(height):
            for y2 in range(y1 + 1, height):
                if block_size[y1][x] == block_size[y2][x]:
                    solver.ensure(~(has_number[y1, x] & has_number[y2, x]))

    is_sat = solver.solve()
    return is_sat, has_number
Пример #9
0
def active_vertices_not_adjacent_and_not_segmenting(solver,
                                                    is_active,
                                                    graph=None):
    if graph is None:
        if not _check_array_shape(is_active, bool, 2):
            raise TypeError(
                '`is_active` should be a 2-D bool Array if graph is not specified'
            )
        active_vertices_not_adjacent(solver, is_active)
        height, width = is_active.shape
        ranks = solver.int_array((height, width), 0, (height * width - 1) // 2)
        for y in range(height):
            for x in range(width):
                less_ranks = []
                nonzero = False
                for dy in [-1, 1]:
                    for dx in [-1, 1]:
                        y2 = y + dy
                        x2 = x + dx
                        if 0 <= y2 < height and 0 <= x2 < width:
                            less_ranks.append((ranks[y2, x2] < ranks[y, x])
                                              & is_active[y2, x2])
                            if (y2, x2) < (y, x):
                                solver.ensure(ranks[y2, x2] != ranks[y, x])
                        else:
                            nonzero = True
                solver.ensure(is_active[y, x].then(
                    count_true(less_ranks) <= (0 if nonzero else 1)))
    else:
        active_vertices_not_adjacent(solver, is_active, graph)
        active_vertices_connected(solver, ~is_active,
                                  graph)  # TODO: is_active may not be an Array
Пример #10
0
def solve_gokigen(height, width, problem):
    solver = Solver()
    edge_type = solver.bool_array((height, width))  # false: /, true: \
    solver.add_answer_key(edge_type)

    g = graph.Graph((height + 1) * (width + 1))
    edge_list = []
    for y in range(height):
        for x in range(width):
            g.add_edge(y * (width + 1) + x, (y + 1) * (width + 1) + (x + 1))
            edge_list.append(edge_type[y, x])
            g.add_edge(y * (width + 1) + (x + 1), (y + 1) * (width + 1) + x)
            edge_list.append(~edge_type[y, x])
    graph.active_edges_acyclic(solver, Array(edge_list), g)

    for y in range(height + 1):
        for x in range(width + 1):
            if problem[y][x] >= 0:
                related = []
                if 0 < y and 0 < x:
                    related.append(edge_type[y - 1, x - 1])
                if 0 < y and x < width:
                    related.append(~edge_type[y - 1, x])
                if y < height and 0 < x:
                    related.append(~edge_type[y, x - 1])
                if y < height and x < width:
                    related.append(edge_type[y, x])
                solver.ensure(count_true(related) == problem[y][x])

    is_sat = solver.solve()
    return is_sat, edge_type
Пример #11
0
def _active_edges_single_cycle(solver,
                               is_active_edge,
                               graph,
                               use_graph_primitive=None):
    if use_graph_primitive is None:
        use_graph_primitive = config.use_graph_primitive
    n = graph.num_vertices
    m = len(graph)

    rank = solver.int_array(n, 0, n - 1)
    is_passed = solver.bool_array(n)

    if use_graph_primitive:
        for i in range(n):
            degree = count_true(
                [is_active_edge[e] for j, e in graph.incident_edges[i]])
            solver.ensure(degree == is_passed[i].cond(2, 0))
        edge_graph = set()
        for v in range(n):
            for i in range(len(graph.incident_edges[v])):
                for j in range(i):
                    x = graph.incident_edges[v][i][1]
                    y = graph.incident_edges[v][j][1]
                    if x < y:
                        edge_graph.add((x, y))
                    else:
                        edge_graph.add((y, x))
        solver.ensure(
            BoolExpr(Op.GRAPH_ACTIVE_VERTICES_CONNECTED,
                     [len(graph), len(edge_graph)] + list(is_active_edge) +
                     sum([[x, y] for x, y in edge_graph], [])))
    else:
        is_root = solver.bool_array(n)

        for i in range(n):
            degree = count_true(
                [is_active_edge[e] for j, e in graph.incident_edges[i]])
            solver.ensure(degree == is_passed[i].cond(2, 0))
            solver.ensure(is_passed[i].then(
                count_true([
                    is_active_edge[e] & (rank[j] >= rank[i])
                    for j, e in graph.incident_edges[i]
                ]) <= is_root[i].cond(2, 1)))
        solver.ensure(count_true(is_root) == 1)
    return is_passed
Пример #12
0
def solve_fivecells(height, width, problem):
    vertex_id = [[-1 for _ in range(width)] for _ in range(height)]
    id_last = 0
    for y in range(height):
        for x in range(width):
            if problem[y][x] >= -1:
                vertex_id[y][x] = id_last
                id_last += 1
    g = graph.Graph(id_last)
    for y in range(height):
        for x in range(width):
            if problem[y][x] >= -1:
                if y < height - 1 and problem[y + 1][x] >= -1:
                    g.add_edge(vertex_id[y][x], vertex_id[y + 1][x])
                if x < width - 1 and problem[y][x + 1] >= -1:
                    g.add_edge(vertex_id[y][x], vertex_id[y][x + 1])
    solver = Solver()
    group_id = graph.division_connected_variable_groups(solver,
                                                        graph=g,
                                                        group_size=5)
    is_invalid = False
    for y in range(height):
        for x in range(width):
            if problem[y][x] >= 0:
                borders = []
                if y > 0 and problem[y - 1][x] >= -1:
                    borders.append(
                        group_id[vertex_id[y][x]] != group_id[vertex_id[y -
                                                                        1][x]])
                if y < height - 1 and problem[y + 1][x] >= -1:
                    borders.append(
                        group_id[vertex_id[y][x]] != group_id[vertex_id[y +
                                                                        1][x]])
                if x > 0 and problem[y][x - 1] >= -1:
                    borders.append(
                        group_id[vertex_id[y][x]] != group_id[vertex_id[y][x -
                                                                           1]])
                if x < width - 1 and problem[y][x + 1] >= -1:
                    borders.append(
                        group_id[vertex_id[y][x]] != group_id[vertex_id[y][x +
                                                                           1]])
                always_border = 4 - len(borders)
                solver.ensure(
                    count_true(borders) == problem[y][x] - always_border)
                if problem[y][x] - always_border < 0:
                    is_invalid = True

    is_border = solver.bool_array(len(g))
    for i, (u, v) in enumerate(g):
        solver.ensure(is_border[i] == (group_id[u] != group_id[v]))
    solver.add_answer_key(is_border)

    if is_invalid:
        is_sat = False
    else:
        is_sat = solver.solve()
    return is_sat, is_border
Пример #13
0
def solve_compass(height, width, problem):
    solver = Solver()
    roots = map(lambda x: (x[0], x[1]), problem)
    division = solver.int_array((height, width), 0, len(problem) - 1)
    graph.division_connected(solver, division, len(problem), roots=roots)
    solver.add_answer_key(division)
    for i, (y, x, u, l, d, r) in enumerate(problem):
        solver.ensure(division[y, x] == i)
        if u >= 0:
            solver.ensure(count_true(division[:y, :] == i) == u)
        if d >= 0:
            solver.ensure(count_true(division[(y + 1):, :] == i) == d)
        if l >= 0:
            solver.ensure(count_true(division[:, :x] == i) == l)
        if r >= 0:
            solver.ensure(count_true(division[:, (x + 1):] == i) == r)
    is_sat = solver.solve()

    return is_sat, division
Пример #14
0
def active_edges_acyclic(solver, is_active_edge, graph):
    n = graph.num_vertices

    ranks = solver.int_array(n, 0, n - 1)

    for i in range(n):
        less_ranks = []
        for j, e in graph.incident_edges[i]:
            less_ranks.append((ranks[j] < ranks[i]) & is_active_edge[e])
            if i < j:
                solver.ensure(ranks[i] != ranks[j])
        solver.ensure(count_true(less_ranks) <= 1)
Пример #15
0
def solve_slitherlink(height, width, problem):
    solver = Solver()
    grid_frame = BoolGridFrame(solver, height, width)
    solver.add_answer_key(grid_frame)
    graph.active_edges_single_cycle(solver, grid_frame)
    for y in range(height):
        for x in range(width):
            if problem[y][x] >= 0:
                solver.ensure(
                    count_true(grid_frame.cell_neighbors(y, x)) == problem[y]
                    [x])
    is_sat = solver.solve()
    return is_sat, grid_frame
Пример #16
0
def solve_creek(height, width, problem):
    solver = Solver()
    is_white = solver.bool_array((height, width))
    solver.add_answer_key(is_white)
    graph.active_vertices_connected(solver, is_white)
    for y in range(0, height + 1):
        for x in range(0, width + 1):
            if problem[y][x] >= 0:
                solver.ensure(
                    count_true(~is_white[max(y - 1, 0):min(y + 1, height),
                                         max(x - 1, 0):min(x + 1, width)]) ==
                    problem[y][x])
    is_sat = solver.solve()
    return is_sat, is_white
Пример #17
0
def solve_yajilin(height, width, problem):
    solver = Solver()
    grid_frame = BoolGridFrame(solver, height - 1, width - 1)
    is_passed = graph.active_edges_single_cycle(solver, grid_frame)
    black_cell = solver.bool_array((height, width))
    graph.active_vertices_not_adjacent(solver, black_cell)
    solver.add_answer_key(grid_frame)
    solver.add_answer_key(black_cell)

    for y in range(height):
        for x in range(width):
            if problem[y][x] != '..':
                # clue
                solver.ensure(~is_passed[y, x])
                solver.ensure(~black_cell[y, x])

                if problem[y][x][0] == '^':
                    solver.ensure(
                        count_true(black_cell[0:y,
                                              x]) == int(problem[y][x][1:]))
                elif problem[y][x][0] == 'v':
                    solver.ensure(
                        count_true(black_cell[(y + 1):height,
                                              x]) == int(problem[y][x][1:]))
                elif problem[y][x][0] == '<':
                    solver.ensure(
                        count_true(black_cell[y,
                                              0:x]) == int(problem[y][x][1:]))
                elif problem[y][x][0] == '>':
                    solver.ensure(
                        count_true(black_cell[y, (
                            x + 1):width]) == int(problem[y][x][1:]))
            else:
                solver.ensure(is_passed[y, x] != black_cell[y, x])

    is_sat = solver.solve()
    return is_sat, grid_frame, black_cell
Пример #18
0
def solve_darts(target, darts, board):
    solver = Solver()
    is_dart = solver.bool_array(len(board))
    solver.add_answer_key(is_dart)

    solver.ensure(count_true(is_dart) == darts)

    def _sum(d, b):
        sums = 0
        for i in range(len(board)):
            sums += d[i].cond(b[i], 0)
        return sums

    solver.ensure(_sum(is_dart, board) == target)

    is_sat = solver.solve()
    return is_sat, is_dart
Пример #19
0
def _division_connected_variable_groups(solver, graph, group_size=None):
    n = graph.num_vertices
    m = len(graph)

    group_id = solver.int_array(n, 0, n - 1)
    rank = solver.int_array(n, 0, n - 1)
    is_root = solver.bool_array(n)
    is_active_edge = solver.bool_array(m)
    solver.ensure(is_root == (rank == 0))

    for i in range(n):
        solver.ensure(is_root[i].then(group_id[i] == i))
        for j, e in graph.incident_edges[i]:
            solver.ensure(is_active_edge[e].then(rank[j] != rank[i]))
        solver.ensure(
            count_true([
                is_active_edge[e] & (rank[j] < rank[i])
                for j, e in graph.incident_edges[i]
            ]) == is_root[i].cond(0, 1))
    for i, (u, v) in enumerate(graph):
        solver.ensure(is_active_edge[i].then(group_id[u] == group_id[v]))
    if group_size is not None:
        downstream_size = solver.int_array(n, 1, n)
        total_size = solver.int_array(n, 1, n)
        solver.ensure(downstream_size <= total_size)
        solver.ensure(is_root.then(downstream_size == total_size))
        for i in range(n):
            solver.ensure(
                sum([(is_active_edge[e]
                      & (rank[j] > rank[i])).cond(downstream_size[j], 0)
                     for j, e in graph.incident_edges[i]]) +
                1 == downstream_size[i])

            if isinstance(group_size, (int, IntVar, IntExpr)):
                s = group_size
            else:
                s = group_size[i]
            if s is not None:
                solver.ensure(total_size[i] == s)
        if not isinstance(group_size, (int, IntVar, IntExpr)):
            for i, (u, v) in enumerate(graph):
                s = total_size[u]
                t = total_size[v]
                solver.ensure(is_active_edge[i].then(s == t))
    return group_id
Пример #20
0
def solve_castle_wall(height, width, arrow, inside):
    solver = Solver()
    grid_frame = BoolGridFrame(solver, height - 1, width - 1)
    solver.add_answer_key(grid_frame)
    passed = graph.active_edges_single_cycle(solver, grid_frame)

    # arrow constraints
    for y in range(height):
        for x in range(width):
            if arrow[y][x] == '..':
                continue
            solver.ensure(~passed[y, x])
            if arrow[y][x][0] == '^':
                related_edges = grid_frame.vertical[:y, x]
            elif arrow[y][x][0] == 'v':
                related_edges = grid_frame.vertical[y:, x]
            elif arrow[y][x][0] == '<':
                related_edges = grid_frame.horizontal[y, :x]
            elif arrow[y][x][0] == '>':
                related_edges = grid_frame.horizontal[y, x:]
            else:
                continue
            solver.ensure(count_true(related_edges) == int(arrow[y][x][1:]))

    # inout constraints
    is_inside = solver.bool_array((height - 1, width - 1))
    for y in range(height - 1):
        for x in range(width - 1):
            if y == 0:
                solver.ensure(is_inside[y, x] == grid_frame[0, x * 2 + 1])
            else:
                solver.ensure(is_inside[y, x] == (
                    is_inside[y - 1, x] != grid_frame[y * 2, x * 2 + 1]))
    for y in range(height):
        for x in range(width):
            if inside[y][x] is True:
                solver.ensure(is_inside[max(0, y - 1), max(0, x - 1)])
            elif inside[y][x] is False:
                solver.ensure(~is_inside[max(0, y - 1), max(0, x - 1)])
    is_sat = solver.solve()

    return is_sat, grid_frame
Пример #21
0
def solve_yinyang(height, width, problem):
    solver = Solver()
    is_black = solver.bool_array((height, width))
    solver.add_answer_key(is_black)

    graph.active_vertices_connected(solver, is_black)
    graph.active_vertices_connected(solver, ~is_black)
    solver.ensure(is_black[:-1, :-1] | is_black[:-1, 1:] | is_black[1:, :-1]
                  | is_black[1:, 1:])
    solver.ensure(~(is_black[:-1, :-1] & is_black[:-1, 1:] & is_black[1:, :-1]
                    & is_black[1:, 1:]))

    # auxiliary constraint
    solver.ensure(~(is_black[:-1, :-1] & is_black[1:, 1:] & ~is_black[1:, :-1]
                    & ~is_black[:-1, 1:]))
    solver.ensure(~(~is_black[:-1, :-1] & ~is_black[1:, 1:] & is_black[1:, :-1]
                    & is_black[:-1, 1:]))

    circ = []
    for y in range(height):
        circ.append(is_black[y, 0])
    for x in range(1, width):
        circ.append(is_black[-1, x])
    for y in reversed(range(0, height - 1)):
        circ.append(is_black[y, -1])
    for x in reversed(range(1, width - 1)):
        circ.append(is_black[0, x])
    circ_switching = []
    for i in range(len(circ)):
        circ_switching.append(circ[i] != circ[(i + 1) % len(circ)])
    solver.ensure(count_true(circ_switching) <= 2)

    for y in range(height):
        for x in range(width):
            if problem[y][x] == 1:
                solver.ensure(~is_black[y, x])
            elif problem[y][x] == 2:
                solver.ensure(is_black[y, x])

    is_sat = solver.solve()
    return is_sat, is_black
Пример #22
0
def solve_nanro(height, width, blocks, num):
    block_id = [[-1 for _ in range(width)] for _ in range(height)]
    for i, block in enumerate(blocks):
        for y, x in block:
            block_id[y][x] = i
    solver = Solver()
    answer = []
    has_num = solver.bool_array((height, width))
    for y in range(height):
        row = []
        for x in range(width):
            v = solver.int_var(0, len(blocks[block_id[y][x]]))
            solver.add_answer_key(v)
            solver.ensure(has_num[y, x] == (v != 0))
            row.append(v)
        answer.append(row)
    graph.active_vertices_connected(solver, has_num)

    for i, block in enumerate(blocks):
        nonempty = solver.int_var(1, len(block))
        solver.ensure(nonempty == count_true(answer[y][x] != 0
                                             for y, x in block))
        for y, x in block:
            solver.ensure((answer[y][x] == 0) | (answer[y][x] == nonempty))
    for y in range(height):
        for x in range(width):
            if num[y][x] > 0:
                solver.ensure(answer[y][x] == num[y][x])
            if y < height - 1 and x < width - 1:
                solver.ensure((answer[y][x] == 0) | (answer[y][x + 1] == 0)
                              | (answer[y + 1][x] == 0)
                              | (answer[y + 1][x + 1] == 0))
            if y < height - 1 and block_id[y][x] != block_id[y + 1][x]:
                solver.ensure((answer[y][x] == 0) | (answer[y + 1][x] == 0)
                              | (answer[y][x] != answer[y + 1][x]))
            if x < width - 1 and block_id[y][x] != block_id[y][x + 1]:
                solver.ensure((answer[y][x] == 0) | (answer[y][x + 1] == 0)
                              | (answer[y][x] != answer[y][x + 1]))

    is_sat = solver.solve()
    return is_sat, answer
Пример #23
0
def solve_sukoro(height, width, problem, is_anti_knight=False):
    solver = Solver()

    has_number = solver.bool_array((height, width))
    graph.active_vertices_connected(solver, has_number)
    nums = solver.int_array((height, width), -1, 4)
    solver.add_answer_key(nums)
    solver.add_answer_key(has_number)

    for y in range(height):
        for x in range(width):
            neighbors = []
            if y > 0:
                neighbors.append(has_number[y-1, x])
                solver.ensure((has_number[y, x] & has_number[y-1, x]).then(nums[y, x] != nums[y-1, x]))
            if y < height - 1:
                neighbors.append(has_number[y+1, x])
                solver.ensure((has_number[y, x] & has_number[y+1, x]).then(nums[y, x] != nums[y+1, x]))
            if x > 0:
                neighbors.append(has_number[y, x-1])
                solver.ensure((has_number[y, x] & has_number[y, x-1]).then(nums[y, x] != nums[y, x-1]))
            if x < width - 1:
                neighbors.append(has_number[y, x+1])
                solver.ensure((has_number[y, x] & has_number[y, x+1]).then(nums[y, x] != nums[y, x+1]))
            solver.ensure(has_number[y, x].then(count_true(neighbors) == nums[y, x]))

    solver.ensure((~has_number).then(nums < 0))

    for y in range(height):
        for x in range(width):
            if problem[y][x] >= 0:
                solver.ensure(nums[y, x] == problem[y][x])
                solver.ensure(has_number[y, x])

    if is_anti_knight:
        graph.numbers_anti_knight(solver, nums, has_number)

    is_sat = solver.solve()

    return is_sat, nums, has_number
Пример #24
0
def generate_slalom_initial_placement(height,
                                      width,
                                      n_min_gates=None,
                                      n_max_gates=None,
                                      n_max_isolated_black_cells=None,
                                      no_adjacent_black_cell=False,
                                      no_facing_length_two=False,
                                      no_space_2x2=False,
                                      black_cell_in_every_3x3=False,
                                      min_go_through=0):
    solver = Solver()
    loop = BoolGridFrame(solver, height - 1, width - 1)
    is_black = solver.bool_array((height, width))
    is_horizontal = solver.bool_array((height, width))
    is_vertical = solver.bool_array((height, width))

    solver.ensure(~(is_black & is_horizontal))
    solver.ensure(~(is_black & is_vertical))
    solver.ensure(~(is_horizontal & is_vertical))
    solver.ensure(~(is_horizontal[0, :]))
    solver.ensure(~(is_horizontal[-1, :]))
    solver.ensure(~(is_vertical[:, 0]))
    solver.ensure(~(is_vertical[:, -1]))

    is_passed = graph.active_edges_single_cycle(solver, loop)

    # --------- board must be valid as a problem ---------

    # loop constraints
    for y in range(height):
        for x in range(width):
            if y > 0:
                solver.ensure(is_black[y, x].then(~loop.vertical[y - 1, x]))
                solver.ensure(is_vertical[y, x].then(~loop.vertical[y - 1, x]))
            if y < height - 1:
                solver.ensure(is_black[y, x].then(~loop.vertical[y, x]))
                solver.ensure(is_vertical[y, x].then(~loop.vertical[y, x]))
            if x > 0:
                solver.ensure(is_black[y, x].then(~loop.horizontal[y, x - 1]))
                solver.ensure(
                    is_horizontal[y, x].then(~loop.horizontal[y, x - 1]))
            if x < width - 1:
                solver.ensure(is_black[y, x].then(~loop.horizontal[y, x]))
                solver.ensure(is_horizontal[y, x].then(~loop.horizontal[y, x]))

    # gates must be closed
    solver.ensure(is_vertical[1:, :].then(is_vertical[:-1, :]
                                          | is_black[:-1, :]))
    solver.ensure(is_vertical[:-1, :].then(is_vertical[1:, :]
                                           | is_black[1:, :]))
    solver.ensure(is_horizontal[:, 1:].then(is_horizontal[:, :-1]
                                            | is_black[:, :-1]))
    solver.ensure(is_horizontal[:, :-1].then(is_horizontal[:, 1:]
                                             | is_black[:, 1:]))
    # each horizontal gate must be passed exactly once
    for y in range(1, height - 1):
        for x in range(width):
            on_loop = []
            for x2 in range(width):
                cond = [is_passed[y, x2]]
                if x2 < x:
                    cond += [is_horizontal[y, i] for i in range(x2, x)]
                elif x < x2:
                    cond += [is_horizontal[y, i] for i in range(x + 1, x2 + 1)]
                on_loop.append(fold_and(cond))
            solver.ensure(is_horizontal[y, x].then(count_true(on_loop) == 1))
    # each vertical gate must be passed exactly once
    for y in range(height):
        for x in range(1, width - 1):
            on_loop = []
            for y2 in range(width):
                cond = [is_passed[y2, x]]
                if y2 < y:
                    cond += [is_vertical[i, x] for i in range(y2, y)]
                elif y < y2:
                    cond += [is_vertical[i, x] for i in range(y + 1, y2 + 1)]
                on_loop.append(fold_and(cond))
            solver.ensure(is_vertical[y, x].then(count_true(on_loop) == 1))

    # --------- loop must be canonical ---------

    # for simplicity, no stacked gates (although this is not necessary for the canonicity)
    solver.ensure(~(is_horizontal[:-1, :] & is_horizontal[1:, :]))
    solver.ensure(~(is_vertical[:, :-1] & is_vertical[:, 1:]))
    for y in range(height):
        for x in range(width):
            if 0 < y < height - 1:
                if x == 0 or x == width - 1:
                    solver.ensure(is_horizontal[y,
                                                x].then(~is_black[y - 1, x]
                                                        & ~is_black[y + 1, x]))
                else:
                    solver.ensure((is_horizontal[y, x] &
                                   (is_black[y - 1, x] | is_black[y + 1, x])
                                   ).then(is_horizontal[y, x - 1]
                                          & is_horizontal[y, x + 1]
                                          & ~is_black[y - 1, x - 1]
                                          & ~is_black[y + 1, x - 1]
                                          & ~is_black[y + 1, x - 1]
                                          & ~is_black[y + 1, x + 1]))
            if 0 < x < width - 1:
                if y == 0 or y == height - 1:
                    solver.ensure(is_vertical[y,
                                              x].then(~is_black[y, x - 1]
                                                      & ~is_black[y, x + 1]))
                else:
                    solver.ensure(
                        (is_vertical[y, x] &
                         (is_black[y, x - 1] | is_black[y, x + 1])
                         ).then(is_vertical[y - 1, x] & is_vertical[y + 1, x]
                                & ~is_black[y - 1, x - 1]
                                & ~is_black[y + 1, x - 1]
                                & ~is_black[y + 1, x - 1]
                                & ~is_black[y + 1, x + 1]))

    # no detour
    for y in range(height - 1):
        for x in range(width - 1):
            solver.ensure(count_true(loop.cell_neighbors(y, x)) <= 2)
            solver.ensure(
                fold_and(~is_black[y:y + 2, x:x + 2],
                         ~is_horizontal[y:y + 2, x:x + 2],
                         ~is_vertical[y:y + 2, x:x + 2]).then(
                             count_true(loop.cell_neighbors(y, x)) +
                             1 < count_true(is_passed[y:y + 2, x:x + 2])))

    # no ambiguous L-shaped turning
    for y in range(height - 1):
        for x in range(width - 1):
            for dy in [0, 1]:
                for dx in [0, 1]:
                    solver.ensure(~fold_and([
                        loop.horizontal[y + dy, x], loop.vertical[
                            y, x + dx], ~is_vertical[y + dy, x + 1 - dx],
                        ~is_horizontal[y + 1 - dx, x +
                                       dx], ~is_black[y + 1 - dy, x + 1 - dx],
                        count_true(is_passed[y:y + 2, x:x + 2]) == 3
                    ]))

    # no ambiguous L-shaped turning involving gates
    for y in range(height - 1):
        for x in range(width - 2):
            solver.ensure(
                fold_and(
                    is_vertical[y:y + 2, x + 1],
                    ~is_black[y:y + 2, x:x + 3]).then(
                        count_true(loop.horizontal[y, x], loop.horizontal[
                            y + 1,
                            x], loop.vertical[y, x], loop.vertical[y, x + 2]) +
                        1 < count_true(is_passed[y:y + 2,
                                                 x], is_passed[y:y + 2,
                                                               x + 2])))
    for y in range(height - 2):
        for x in range(width - 1):
            solver.ensure(
                fold_and(is_horizontal[y + 1, x:x + 2],
                         ~is_black[y:y + 3, x:x + 2]).
                then(
                    count_true(loop.vertical[y, x], loop.vertical[y, x + 1],
                               loop.horizontal[y, x], loop.horizontal[y + 2,
                                                                      x]) +
                    1 < count_true(is_passed[y, x:x + 2], is_passed[y + 2,
                                                                    x:x + 2])))

    # no dead ends
    for y in range(height):
        for x in range(width):
            solver.ensure((~is_black[y, x]).then(
                count_true(~is_black.four_neighbors(y, x)) >= 2))

    # --------- avoid "trivial" problems ---------
    solver.ensure(count_true(is_vertical) > 5)
    solver.ensure(count_true(is_horizontal) > 4)

    if n_max_isolated_black_cells is not None:
        lonely_black_cell = []
        for y in range(height):
            for x in range(width):
                cond = [is_black[y, x]]
                if y > 0:
                    cond.append(~is_vertical[y - 1, x])
                if y < height - 1:
                    cond.append(~is_vertical[y + 1, x])
                if x > 0:
                    cond.append(~is_horizontal[y, x - 1])
                if x < width - 1:
                    cond.append(~is_horizontal[y, x + 1])
                lonely_black_cell.append(fold_and(cond))
        solver.ensure(
            count_true(lonely_black_cell) <= n_max_isolated_black_cells)

    short_gates = []
    for y in range(height):
        for x in range(width):
            g1 = fold_and([
                is_vertical[y, x], ~is_vertical[y - 1, x] if y > 0 else True,
                ~is_vertical[y + 1, x] if y < height - 1 else True
            ])
            g2 = fold_and([
                is_horizontal[y,
                              x], ~is_horizontal[y, x - 1] if x > 0 else True,
                ~is_horizontal[y, x + 1] if x < width - 1 else True
            ])
            if 0 < y < height - 1 and 0 < x < width - 1:
                short_gates.append(g1)
                short_gates.append(g2)
                solver.ensure((g1 | g2).then(~is_black[y - 1, x - 1]
                                             & ~is_black[y - 1, x + 1]
                                             & ~is_black[y + 1, x - 1]
                                             & ~is_black[y + 1, x + 1]))
            else:
                solver.ensure(~g1)
                solver.ensure(~g2)
    solver.ensure(count_true(short_gates) <= 0)
    for y in range(1, height - 1):
        for x in range(1, width - 1):
            solver.ensure(
                count_true(
                    is_horizontal[y - 1, x] & is_black[y - 1, x - 1]
                    & is_black[y - 1, x + 1],
                    is_horizontal[y + 1, x] & is_black[y + 1, x - 1]
                    & is_black[y + 1, x + 1],
                    is_vertical[y, x - 1] & is_black[y - 1, x - 1]
                    & is_black[y + 1, x - 1],
                    is_vertical[y, x + 1] & is_black[y - 1, x + 1]
                    & is_black[y + 1, x + 1],
                ) <= 1)
    # --------- ensure randomness ---------

    passed_constraints = [[0 for _ in range(width)] for _ in range(height)]
    for y in range(height):
        for x in range(width):
            if (y > 0 and passed_constraints[y - 1][x] != 0) or (
                    x > 0 and passed_constraints[y][x - 1] != 0):
                continue
            passed_constraints[y][x] = max(0, random.randint(-20, 2))
    for y in range(height):
        for x in range(width):
            if passed_constraints[y][x] == 1:
                solver.ensure(is_passed[y, x])
            elif passed_constraints[y][x] == 2:
                solver.ensure(~is_passed[y, x])

    # --------- extra constraints ---------
    if n_min_gates is not None or n_max_gates is not None:
        gate_representative = []
        for y in range(height):
            for x in range(width):
                gate_representative.append(is_horizontal[y, x] & (
                    ~is_horizontal[y, x - 1] if x > 0 else True))
                gate_representative.append(is_vertical[y, x] & (
                    ~is_vertical[y - 1, x] if y > 0 else True))
        if n_min_gates is not None:
            solver.ensure(n_min_gates <= count_true(gate_representative))
        if n_max_gates is not None:
            solver.ensure(count_true(gate_representative) <= n_max_gates)

    if min_go_through > 0:
        go_through = []
        for y in range(height):
            for x in range(width):
                if y < height - 4 and 0 < x < width - 1:
                    go_through.append(
                        fold_and(
                            is_horizontal[y + 1,
                                          x], is_horizontal[y + 1, x - 1]
                            | is_horizontal[y + 1, x + 1],
                            ~is_black[y + 2, x - 1], ~is_black[y + 2, x + 1],
                            is_horizontal[y + 3,
                                          x], is_horizontal[y + 3, x - 1]
                            | is_horizontal[y + 3, x + 1],
                            loop.vertical[y:y + 4, x]))
                if x < width - 4 and 0 < y < height - 1:
                    go_through.append(
                        fold_and(
                            is_vertical[y, x + 1], is_vertical[y - 1, x + 1]
                            | is_vertical[y + 1, x + 1],
                            ~is_black[y - 1, x + 2], ~is_black[y + 1, x + 2],
                            is_vertical[y, x + 3], is_vertical[y - 1, x + 3]
                            | is_vertical[y + 1, x + 3],
                            loop.horizontal[y, x:x + 4]))
        solver.ensure(count_true(go_through) >= 2)

    if no_adjacent_black_cell:
        solver.ensure(~(is_black[:-1, :] & is_black[1:, :]))
        solver.ensure(~(is_black[:, :-1] & is_black[:, 1:]))
        solver.ensure(~(is_black[:-1, :-1] & is_black[1:, 1:]))
        solver.ensure(~(is_black[:-1, 1:] & is_black[1:, :-1]))

    if no_facing_length_two:
        for y in range(height):
            for x in range(width):
                if y <= height - 3 and x <= width - 4:
                    solver.ensure(~fold_and(
                        is_black[y, x], is_black[y + 2, x], is_black[y, x + 3],
                        is_black[y + 2, x + 3], is_horizontal[
                            y, x + 1], is_horizontal[y, x + 2], is_horizontal[
                                y + 2, x + 1], is_horizontal[y + 2, x + 2]))
                if y <= height - 4 and x <= width - 3:
                    solver.ensure(
                        ~fold_and(is_black[y, x], is_black[y, x + 2], is_black[
                            y + 3, x], is_black[y + 3, x + 2], is_vertical[
                                y + 1, x], is_vertical[y + 2, x], is_vertical[
                                    y + 1, x + 2], is_vertical[y + 2, x + 2]))

    if no_space_2x2:
        has_some = is_black | is_vertical | is_horizontal
        solver.ensure(has_some[:-1, :-1] | has_some[1:, :-1]
                      | has_some[:-1, 1:] | has_some[1:, 1:])

    if black_cell_in_every_3x3:
        for y in range(-1, height - 2):
            for x in range(-1, width - 2):
                solver.ensure(
                    fold_or(is_black[max(0, y):min(height, y + 3),
                                     max(0, x):min(width, x + 3)]))

    is_sat = solver.find_answer()
    if not is_sat:
        return None
    return loop, is_passed, is_black, is_horizontal, is_vertical
Пример #25
0
def solve_firefly(height, width, problem):
    solver = Solver()

    has_line = BoolGridFrame(solver, height - 1, width - 1)
    solver.add_answer_key(has_line)

    line_ul = BoolGridFrame(solver, height - 1, width - 1)
    line_dr = BoolGridFrame(solver, height - 1, width - 1)
    solver.ensure(
        Array(list(has_line)) == (Array(list(line_ul)) | Array(list(line_dr))))
    solver.ensure(~(Array(list(line_ul)) & Array(list(line_dr))))

    # unicyclic (= connected)
    ignored_edge = BoolGridFrame(solver, height - 1, width - 1)
    solver.ensure(count_true(ignored_edge) == 1)
    rank = solver.int_array((height, width), 0, height * width - 1)
    solver.ensure(
        (line_ul.horizontal
         & ~ignored_edge.horizontal).then(rank[:, :-1] < rank[:, 1:]))
    solver.ensure((line_ul.vertical
                   & ~ignored_edge.vertical).then(rank[:-1, :] < rank[1:, :]))
    solver.ensure(
        (line_dr.horizontal
         & ~ignored_edge.horizontal).then(rank[:, :-1] > rank[:, 1:]))
    solver.ensure((line_dr.vertical
                   & ~ignored_edge.vertical).then(rank[:-1, :] > rank[1:, :]))

    max_n_turn = 0
    for y in range(height):
        for x in range(width):
            if problem[y][x][0] != '.' and problem[y][x][1] != '?':
                max_n_turn = max(max_n_turn, int(problem[y][x][1:]))
    n_turn_unknown = max_n_turn + 1
    n_turn_horizontal = solver.int_array((height, width - 1), 0,
                                         max_n_turn + 1)
    n_turn_vertical = solver.int_array((height - 1, width), 0, max_n_turn + 1)

    for y in range(height):
        for x in range(width):
            # u, d, l, r
            adj = []  # (line_in, line_out, # turn)

            if y > 0:
                adj.append((line_dr.vertical[y - 1, x],
                            line_ul.vertical[y - 1, x], n_turn_vertical[y - 1,
                                                                        x]))
            else:
                adj.append(None)
            if y < height - 1:
                adj.append((line_ul.vertical[y, x], line_dr.vertical[y, x],
                            n_turn_vertical[y, x]))
            else:
                adj.append(None)
            if x > 0:
                adj.append(
                    (line_dr.horizontal[y, x - 1],
                     line_ul.horizontal[y, x - 1], n_turn_horizontal[y,
                                                                     x - 1]))
            else:
                adj.append(None)
            if x < width - 1:
                adj.append((line_ul.horizontal[y, x], line_dr.horizontal[y, x],
                            n_turn_horizontal[y, x]))
            else:
                adj.append(None)

            if problem[y][x][0] != '.':
                if problem[y][x][0] == '^':
                    out_idx = 0
                elif problem[y][x][0] == 'v':
                    out_idx = 1
                elif problem[y][x][0] == '<':
                    out_idx = 2
                elif problem[y][x][0] == '>':
                    out_idx = 3
                else:
                    raise ValueError('invalid direction: {}'.format(
                        problem[y][x][0]))
                if adj[out_idx] is None:
                    solver.ensure(False)
                    break
                solver.ensure(adj[out_idx][1])
                if problem[y][x][1] != '?':
                    solver.ensure(adj[out_idx][2] == int(problem[y][x][1:]))
                else:
                    solver.ensure(adj[out_idx][2] == n_turn_unknown)
                for i in range(4):
                    if adj[i] is not None and i != out_idx:
                        solver.ensure(~adj[i][1])
                        solver.ensure(
                            adj[i][0].then((adj[i][2] == 0)
                                           | (adj[i][2] == n_turn_unknown)))
            else:
                adj_present = list(filter(lambda x: x is not None, adj))
                solver.ensure(
                    count_true(map(lambda x: x[0], adj_present)) <= 1)
                solver.ensure(
                    count_true(map(lambda x: x[0], adj_present)) == count_true(
                        map(lambda x: x[1], adj_present)))

                for i in range(4):
                    for j in range(4):
                        if adj[i] is not None and adj[j] is not None and i != j:
                            if (i // 2) == (j // 2):  # straight
                                solver.ensure(
                                    (adj[i][0]
                                     & adj[j][1]).then(adj[i][2] == adj[j][2]))
                            else:
                                solver.ensure(
                                    (adj[i][0] & adj[j][1]
                                     ).then(((adj[i][2] == n_turn_unknown)
                                             & (adj[j][2] == n_turn_unknown))
                                            | (adj[i][2] == adj[j][2] + 1)))
    is_sat = solver.solve()
    return is_sat, has_line
Пример #26
0
def solve_shakashaka(height, width, problem):
    # 1   2   3   4
    # +-+ +     + +-+
    # |/  |\   /|  \|
    # +   +-+ +-+   +
    solver = Solver()
    answer = solver.int_array((height, width), 0, 4)
    solver.add_answer_key(answer)

    for y in range(height):
        for x in range(width):
            if problem[y][x] is not None:
                solver.ensure(answer[y, x] == 0)
                if problem[y][x] >= 0:
                    solver.ensure(count_true(answer.four_neighbors(y, x) != 0) == problem[y][x])
    for y in range(height + 1):
        for x in range(width + 1):
            diagonals = []
            is_empty = []
            is_white_angle = []
            if y > 0 and x > 0:
                diagonals.append(answer[y - 1, x - 1] == 4)
                diagonals.append(answer[y - 1, x - 1] == 2)
                is_empty.append(answer[y - 1, x - 1] == 0 if problem[y - 1][x - 1] is None else False)
                if problem[y - 1][x - 1] is None:
                    is_white_angle.append((answer[y - 1, x - 1] == 0) | (answer[y - 1, x - 1] == 1))
            else:
                diagonals += [False, False]
                is_empty.append(False)
            if y < height and x > 0:
                diagonals.append(answer[y, x - 1] == 1)
                diagonals.append(answer[y, x - 1] == 3)
                is_empty.append(answer[y, x - 1] == 0 if problem[y][x - 1] is None else False)
                if problem[y][x - 1] is None:
                    is_white_angle.append((answer[y, x - 1] == 0) | (answer[y, x - 1] == 2))
            else:
                diagonals += [False, False]
                is_empty.append(False)
            if y < height and x < width:
                diagonals.append(answer[y, x] == 2)
                diagonals.append(answer[y, x] == 4)
                is_empty.append(answer[y, x] == 0 if problem[y][x] is None else False)
                if problem[y][x] is None:
                    is_white_angle.append((answer[y, x] == 0) | (answer[y, x] == 3))
            else:
                diagonals += [False, False]
                is_empty.append(False)
            if y > 0 and x < width:
                diagonals.append(answer[y - 1, x] == 3)
                diagonals.append(answer[y - 1, x] == 1)
                is_empty.append(answer[y - 1, x] == 0 if problem[y - 1][x] is None else False)
                if problem[y - 1][x] is None:
                    is_white_angle.append((answer[y - 1, x] == 0) | (answer[y - 1, x] == 4))
            else:
                diagonals += [False, False]
                is_empty.append(False)
            for i in range(8):
                if diagonals[i] is False:
                    continue
                if i % 2 == 0:
                    solver.ensure(diagonals[i].then(
                        diagonals[(i + 3) % 8] | (is_empty[(i + 3) % 8 // 2] & diagonals[(i + 5) % 8])
                    ))
                else:
                    solver.ensure(diagonals[i].then(
                        diagonals[(i + 5) % 8] | (is_empty[(i + 5) % 8 // 2] & diagonals[(i + 3) % 8])
                    ))
            solver.ensure(count_true(is_white_angle) != 3)
    is_sat = solver.solve()
    return is_sat, answer
Пример #27
0
def solve_akari(height, width, problem, is_anti_knight=True):
    solver = Solver()
    has_light = solver.bool_array((height, width))
    solver.add_answer_key(has_light)

    for y in range(height):
        for x in range(width):
            if problem[y][x] >= -1:
                continue
            if y == 0 or problem[y - 1][x] >= -1:
                group = []
                for y2 in range(y, height):
                    if problem[y2][x] < -1:
                        group.append((y2, x))
                    else:
                        break
                solver.ensure(count_true([has_light[p] for p in group]) <= 1)
            if x == 0 or problem[y][x - 1] >= -1:
                group = []
                for x2 in range(x, width):
                    if problem[y][x2] < -1:
                        group.append((y, x2))
                    else:
                        break
                solver.ensure(count_true([has_light[p] for p in group]) <= 1)

    for y in range(height):
        for x in range(width):
            if problem[y][x] < -1:
                sight = [(y, x)]
                for y2 in range(y - 1, -1, -1):
                    if problem[y2][x] < -1:
                        sight.append((y2, x))
                    else:
                        break
                for y2 in range(y + 1, height, 1):
                    if problem[y2][x] < -1:
                        sight.append((y2, x))
                    else:
                        break
                for x2 in range(x - 1, -1, -1):
                    if problem[y][x2] < -1:
                        sight.append((y, x2))
                    else:
                        break
                for x2 in range(x + 1, width, 1):
                    if problem[y][x2] < -1:
                        sight.append((y, x2))
                    else:
                        break
                solver.ensure(fold_or([has_light[p] for p in sight]))
            else:
                solver.ensure(~has_light[y, x])
                if problem[y][x] >= 0:
                    neighbors = []
                    if y > 0 and problem[y - 1][x] < -1:
                        neighbors.append((y - 1, x))
                    if y < height - 1 and problem[y + 1][x] < -1:
                        neighbors.append((y + 1, x))
                    if x > 0 and problem[y][x - 1] < -1:
                        neighbors.append((y, x - 1))
                    if x < width - 1 and problem[y][x + 1] < -1:
                        neighbors.append((y, x + 1))
                    solver.ensure(count_true([has_light[p] for p in neighbors]) == problem[y][x])

    if is_anti_knight:
        graph.active_vertices_anti_knight(solver, has_light)

    is_sat = solver.solve()
    return is_sat, has_light
Пример #28
0
def solve_slalom(height,
                 width,
                 origin,
                 is_black,
                 gates,
                 reference_sol_loop=None):
    solver = Solver()
    loop = BoolGridFrame(solver, height - 1, width - 1)
    loop_dir = BoolGridFrame(solver, height - 1, width - 1)
    solver.add_answer_key(loop.all_edges())

    graph.active_edges_single_cycle(solver, loop)

    gate_ord = solver.int_array((height, width), 0, len(gates))
    passed = solver.bool_array((height, width))

    gate_id = [[None for _ in range(width)] for _ in range(height)]
    for y, x, d, l, n in gates:
        if d == 0:  # horizontal
            gate_cells = [(y, x + i) for i in range(l)]
        elif d == 1:  # vertical
            gate_cells = [(y + i, x) for i in range(l)]
        for y2, x2 in gate_cells:
            gate_id[y2][x2] = n
        solver.ensure(
            count_true([passed[y2, x2] for y2, x2 in gate_cells]) == 1)
    solver.ensure(passed[origin])
    for y in range(height):
        for x in range(width):
            neighbors = []
            if y > 0:
                neighbors.append((y - 1, x))
            if y < height - 1:
                neighbors.append((y + 1, x))
            if x > 0:
                neighbors.append((y, x - 1))
            if x < width - 1:
                neighbors.append((y, x + 1))

            # in-degree, out-degree
            solver.ensure(
                count_true([
                    loop[y + y2, x + x2]
                    & (loop_dir[y + y2, x + x2] != ((y2, x2) < (y, x)))
                    for y2, x2 in neighbors
                ]) == passed[y, x].cond(1, 0))
            solver.ensure(
                count_true([
                    loop[y + y2, x + x2]
                    & (loop_dir[y + y2, x + x2] == ((y2, x2) < (y, x)))
                    for y2, x2 in neighbors
                ]) == passed[y, x].cond(1, 0))

            if is_black[y][x]:
                solver.ensure(~passed[y, x])
                continue
            if (y, x) == origin:
                continue
            if gate_id[y][x] is None:
                for y2, x2 in neighbors:
                    solver.ensure((loop[y + y2, x + x2] &
                                   (loop_dir[y + y2, x + x2] !=
                                    ((y2, x2) < (y, x)))).then(
                                        (gate_ord[y2, x2] == gate_ord[y, x])))
            else:
                for y2, x2 in neighbors:
                    solver.ensure((loop[y + y2, x + x2] &
                                   (loop_dir[y + y2, x + x2] !=
                                    ((y2, x2) < (y, x)))).then(
                                        (gate_ord[y2,
                                                  x2] == gate_ord[y, x] - 1)))
                if gate_id[y][x] >= 1:
                    solver.ensure(passed[y,
                                         x].then(gate_ord[y,
                                                          x] == gate_id[y][x]))

    # auxiliary constraint
    for y0 in range(height):
        for x0 in range(width):
            for y1 in range(height):
                for x1 in range(width):
                    if (y0, x0) < (y1, x1) and gate_id[y0][
                            x0] is not None and gate_id[y1][x1] is not None:
                        solver.ensure((passed[y0, x0] & passed[y1, x1]).then(
                            gate_ord[y0, x0] != gate_ord[y1, x1]))

    if reference_sol_loop is not None:
        avoid_reference_sol = []
        for y in range(height):
            for x in range(width):
                if y < height - 1:
                    avoid_reference_sol.append(
                        loop.vertical[y,
                                      x] != reference_sol_loop.vertical[y,
                                                                        x].sol)
                if x < width - 1:
                    avoid_reference_sol.append(loop.horizontal[
                        y, x] != reference_sol_loop.horizontal[y, x].sol)
        solver.ensure(fold_or(avoid_reference_sol))

        is_sat = solver.find_answer()
        return is_sat, loop
    else:
        is_sat = solver.solve()
        return is_sat, loop
Пример #29
0
 def occurrence_constraint(cells):
     solver.ensure(count_true(cells == 0) == 2)
     for i in range(1, n - 1):
         solver.ensure(count_true(cells == i) == 1)
Пример #30
0
def solve_lits(height, width, blocks):
    solver = Solver()
    is_black = solver.bool_array((height, width))
    solver.add_answer_key(is_black)

    # black cells are connected
    graph.active_vertices_connected(solver, is_black)

    # no 2x2 black cells
    solver.ensure(~(is_black[1:, 1:] & is_black[1:, :-1] & is_black[:-1, 1:] & is_black[:-1, :-1]))

    block_id = [[-1 for _ in range(width)] for _ in range(height)]
    for i, block in enumerate(blocks):
        for y, x in block:
            block_id[y][x] = i

    num_straight = solver.int_array(len(blocks), 0, 2)
    has_t = solver.bool_array(len(blocks))

    for i in range(len(blocks)):
        solver.ensure(count_true([is_black[p] for p in blocks[i]]) == 4)
        adjacent_pairs = []
        is_straight = []
        is_t = []
        for y, x in blocks[i]:
            neighbor_same_block = []
            for y2, x2 in is_black.four_neighbor_indices(y, x):
                if block_id[y2][x2] == i:
                    neighbor_same_block.append((y2, x2))
                    if (y, x) < (y2, x2):
                        adjacent_pairs.append(is_black[y, x] & is_black[y2, x2])
            solver.ensure(is_black[y, x].then(fold_or([is_black[p] for p in neighbor_same_block])))

            tmp = []
            if 0 < y < height - 1 and block_id[y - 1][x] == i and block_id[y + 1][x] == i:
                tmp.append(fold_and(is_black[y-1:y+2, x]))
            if 0 < x < width - 1 and block_id[y][x - 1] == i and block_id[y][x + 1] == i:
                tmp.append(fold_and(is_black[y, x-1:x+2]))
            if len(tmp) >= 1:
                is_straight.append(fold_or(tmp))

            if len(neighbor_same_block) >= 3:
                is_t.append(count_true([is_black[p] for p in neighbor_same_block]) >= 3)
        solver.ensure(count_true(adjacent_pairs) == 3)

        solver.ensure(num_straight[i] == count_true(is_straight))
        solver.ensure(has_t[i] == fold_or(is_t))

    for y in range(height):
        for x in range(width):
            if y < height - 1 and block_id[y][x] != block_id[y + 1][x]:
                i = block_id[y][x]
                j = block_id[y + 1][x]
                solver.ensure((is_black[y, x] & is_black[y + 1, x]).then(
                    (num_straight[i] != num_straight[j]) | (has_t[i] != has_t[j])
                ))
            if x < width - 1 and block_id[y][x] != block_id[y][x + 1]:
                i = block_id[y][x]
                j = block_id[y][x + 1]
                solver.ensure((is_black[y, x] & is_black[y, x + 1]).then(
                    (num_straight[i] != num_straight[j]) | (has_t[i] != has_t[j])
                ))
    is_sat = solver.solve()
    return is_sat, is_black