Пример #1
0
def solve_fillomino(height,
                    width,
                    problem,
                    checkered=False,
                    is_non_con=False,
                    is_anti_knight=False):
    solver = Solver()
    size = solver.int_array((height, width), 1, height * width)
    solver.add_answer_key(size)
    group_id = graph.division_connected_variable_groups(solver,
                                                        group_size=size)
    solver.ensure(
        (group_id[:, :-1] == group_id[:, 1:]) == (size[:, :-1] == size[:, 1:]))
    solver.ensure(
        (group_id[:-1, :] == group_id[1:, :]) == (size[:-1, :] == size[1:, :]))
    for y in range(height):
        for x in range(width):
            if problem[y][x] >= 1:
                solver.ensure(size[y, x] == problem[y][x])
    if checkered:
        color = solver.bool_array((height, width))
        solver.ensure(
            (group_id[:, :-1] == group_id[:,
                                          1:]) == (color[:, :-1] == color[:,
                                                                          1:]))
        solver.ensure((group_id[:-1, :] == group_id[1:, :]) == (
            color[:-1, :] == color[1:, :]))
    if is_non_con:
        graph.numbers_non_consecutive(solver, size)
    if is_anti_knight:
        graph.numbers_anti_knight(solver, size)
    is_sat = solver.solve()
    return is_sat, size
Пример #2
0
def solve_building(n, u, d, l, r):
    solver = Solver()
    answer = solver.int_array((n, n), 1, n)
    solver.add_answer_key(answer)

    for i in range(n):
        solver.ensure(alldifferent(answer[i, :]))
        solver.ensure(alldifferent(answer[:, i]))

    def num_visible_buildings(cells):
        cells = list(cells)
        res = 1
        for i in range(1, len(cells)):
            res += fold_and([cells[j] < cells[i] for j in range(i)]).cond(1, 0)
        return res

    for i in range(n):
        if u[i] >= 1:
            solver.ensure(num_visible_buildings(answer[:, i]) == u[i])
        if d[i] >= 1:
            solver.ensure(
                num_visible_buildings(reversed(list(answer[:, i]))) == d[i])
        if l[i] >= 1:
            solver.ensure(num_visible_buildings(answer[i, :]) == l[i])
        if r[i] >= 1:
            solver.ensure(
                num_visible_buildings(reversed(list(answer[i, :]))) == r[i])

    is_sat = solver.solve()
    return is_sat, answer
Пример #3
0
def generate_latin_square(size, is_anti_knight=False):
    solver = Solver()
    latin_array = solver.int_array((size, size), 1, size)

    line = random.sample(range(1, size + 1), size)
    row = random.randrange(size)

    for i in range(size):
        solver.ensure(latin_array[row, i] == line[i])
        solver.ensure(alldifferent(latin_array[i, :]))
        solver.ensure(alldifferent(latin_array[:, i]))

    if is_anti_knight:
        graph.numbers_anti_knight(solver, latin_array)

    solver.find_answer()

    latin = [[0 for _ in range(size)] for _ in range(size)]
    for y in range(size):
        for x in range(size):
            latin[x][y] = latin_array[y, x].sol

    if is_anti_knight:
        return latin

    for i in range(3):
        lines = random.sample(range(size), 2)
        latin = _swap_latin_square_row(size, latin, lines[0], lines[1])
        lines = random.sample(range(size), 2)
        latin = _swap_latin_square_column(size, latin, lines[0], lines[1])

    return latin
Пример #4
0
def solve_nurikabe(height, width, problem, unknown_low=None):
    solver = Solver()
    clues = []
    for y in range(height):
        for x in range(width):
            if problem[y][x] >= 1 or problem[y][x] == -1:
                clues.append((y, x, problem[y][x]))
    division = solver.int_array((height, width), 0, len(clues))

    roots = [None] + list(map(lambda x: (x[0], x[1]), clues))
    graph.division_connected(solver, division, len(clues) + 1, roots=roots)
    is_white = solver.bool_array((height, width))
    solver.ensure(is_white == (division != 0))
    solver.add_answer_key(is_white)

    solver.ensure(
        (is_white[:-1, :]
         & is_white[1:, :]).then(division[:-1, :] == division[1:, :]))
    solver.ensure((is_white[:, :-1]
                   & is_white[:, 1:]).then(division[:, :-1] == division[:,
                                                                        1:]))
    solver.ensure(is_white[:-1, :-1] | is_white[:-1, 1:] | is_white[1:, :-1]
                  | is_white[1:, 1:])
    for i, (y, x, n) in enumerate(clues):
        if n > 0:
            solver.ensure(count_true(division == (i + 1)) == n)
        elif n == -1 and unknown_low is not None:
            solver.ensure(count_true(division == (i + 1)) >= unknown_low)

    is_sat = solver.solve()

    return is_sat, is_white
Пример #5
0
def solve_ripple(height, width, problem):
    solver = Solver()
    numbers = solver.int_array((height, width), 1, height * width + 1)
    solver.add_answer_key(numbers)

    block_num = max(sum(problem[0:height], [])) + 1
    blocks = [[] for _ in range(block_num)]
    for y in range(height):
        for x in range(width):
            blocks[problem[y][x]].append([y, x])
            if problem[y + height][x] > 0:
                solver.ensure(numbers[y, x] == problem[y + height][x])

    for block in blocks:
        solver.ensure(alldifferent([numbers[y, x] for y, x in block]))
        for y, x in block:
            solver.ensure(numbers[y, x] <= len(block))

    for y in range(height):
        for x in range(width):
            for i in range(1, width - x):
                solver.ensure(
                    (numbers[y, x] == numbers[y,
                                              x + i]).then(numbers[y, x] < i))
            for i in range(1, height - y):
                solver.ensure(
                    (numbers[y, x] == numbers[y + i,
                                              x]).then(numbers[y, x] < i))

    is_sat = solver.solve()
    return is_sat, numbers
Пример #6
0
def solve_doppelblock(n, clue_row, clue_column):
    solver = Solver()
    answer = solver.int_array((n, n), 0, n - 2)
    solver.add_answer_key(answer)

    def sequence_constraint(cells, v):
        s = 0
        for i in range(n):
            s += (fold_or(cells[:i] == 0) & fold_or(cells[i+1:] == 0)).cond(cells[i], 0)
        return s == v

    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)

    for i in range(n):
        occurrence_constraint(answer[i, :])
        occurrence_constraint(answer[:, i])
        if clue_row[i] >= 0:
            solver.ensure(sequence_constraint(answer[i, :], clue_row[i]))
        if clue_column[i] >= 0:
            solver.ensure(sequence_constraint(answer[:, i], clue_column[i]))

    is_sat = solver.solve()
    return is_sat, answer
Пример #7
0
def solve_koutano(height, width, problem):
    solver = Solver()
    col = solver.int_array(width, 0, width - 1)
    row = solver.int_array(height, 0, height - 1)

    solver.add_answer_key(col)
    solver.add_answer_key(row)

    solver.ensure(alldifferent(col))
    solver.ensure(alldifferent(row))

    for y in range(height):
        for x in range(width):
            if problem[y][x] >= 0:
                solver.ensure(col[x] + row[y] == problem[y][x])

    is_sat = solver.solve()
    return is_sat, col, row
Пример #8
0
def solve_view(height, width, problem, is_non_con=False, 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), 0, height + width)
    solver.add_answer_key(nums)
    solver.add_answer_key(has_number)

    to_up = solver.int_array((height, width), 0, height - 1)
    solver.ensure(to_up[0, :] == 0)
    solver.ensure(to_up[1:, :] == has_number[:-1, :].cond(0, to_up[:-1, :] + 1))

    to_down = solver.int_array((height, width), 0, height - 1)
    solver.ensure(to_down[-1, :] == 0)
    solver.ensure(to_down[:-1, :] == has_number[1:, :].cond(0, to_down[1:, :] + 1))

    to_left = solver.int_array((height, width), 0, width - 1)
    solver.ensure(to_left[:, 0] == 0)
    solver.ensure(to_left[:, 1:] == has_number[:, :-1].cond(0, to_left[:, :-1] + 1))

    to_right = solver.int_array((height, width), 0, width - 1)
    solver.ensure(to_right[:, -1] == 0)
    solver.ensure(to_right[:, :-1] == has_number[:, 1:].cond(0, to_right[:, 1:] + 1))

    solver.ensure(has_number.then(nums == to_up + to_left + to_down + to_right))
    solver.ensure((has_number[:-1, :] & has_number[1:, :]).then(nums[:-1, :] != nums[1:, :]))
    solver.ensure((has_number[:, :-1] & has_number[:, 1:]).then(nums[:, :-1] != nums[:, 1:]))
    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_non_con:
        graph.numbers_non_consecutive(solver, nums, has_number)

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

    is_sat = solver.solve()

    return is_sat, nums, has_number
Пример #9
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
Пример #10
0
def solve_amarune(height, width, problem):
    solver = Solver()
    numbers = solver.int_array((height, width), 1, height * width)
    solver.add_answer_key(numbers)

    solver.ensure(alldifferent(numbers))

    for y in range(height):
        for x in range(width - 1):
            solver.ensure(numbers[y, x] > numbers[y, x + 1])

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

    for y in range(height):
        for x in range(width - 1):
            for i in range(height * width):
                if problem[y + height][x] in [0, 1, 2]:
                    solver.ensure((numbers[y, x + 1] == i).then(
                        numbers[y, x] % i == problem[y + height][x]))
                elif problem[y + height][x] == 3:
                    solver.ensure(
                        (numbers[y, x + 1] == i).then(numbers[y, x] % i >= 3))

    for y in range(height - 1):
        for x in range(width):
            for i in range(height * width):
                if problem[y + height * 2][x] in [0, 1, 2]:
                    solver.ensure(
                        ((numbers[y, x] > numbers[y + 1, x]) &
                         (numbers[y + 1, x] == i)).then(
                             numbers[y, x] % i == problem[y + height * 2][x]))
                    solver.ensure(((numbers[y + 1, x] > numbers[y, x]) &
                                   (numbers[y, x] == i)).then(
                                       numbers[y + 1, x] %
                                       i == problem[y + height * 2][x]))
                elif problem[y + height * 2][x] == 3:
                    solver.ensure(((numbers[y, x] > numbers[y + 1, x]) &
                                   (numbers[y + 1, x] == i)).then(
                                       numbers[y, x] % i >= 3))
                    solver.ensure(((numbers[y + 1, x] > numbers[y, x]) &
                                   (numbers[y, x] == i)).then(
                                       numbers[y + 1, x] % i >= 3))

    is_sat = solver.solve()
    return is_sat, numbers
Пример #11
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
Пример #12
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
Пример #13
0
def solve_sudoku(problem,
                 n=3,
                 is_non_con=False,
                 is_anti_knight=False,
                 is_non_dicon=False,
                 is_anti_alfil=False):
    size = n * n
    solver = Solver()
    answer = solver.int_array((size, size), 1, size)
    solver.add_answer_key(answer)
    for i in range(size):
        solver.ensure(alldifferent(answer[i, :]))
        solver.ensure(alldifferent(answer[:, i]))
    for y in range(n):
        for x in range(n):
            solver.ensure(
                alldifferent(answer[y * n:(y + 1) * n, x * n:(x + 1) * n]))
    for y in range(size):
        for x in range(size):
            if problem[y][x] >= 1:
                solver.ensure(answer[y, x] == problem[y][x])

    if is_non_con:
        graph.numbers_non_consecutive(solver, answer)

    if is_anti_knight:
        graph.numbers_anti_knight(solver, answer)

    if is_non_dicon:
        graph.numbers_non_diagonally_consecutive(solver, answer)

    if is_anti_alfil:
        graph.numbers_anti_alfil(solver, answer)

    is_sat = solver.solve()

    return is_sat, answer
Пример #14
0
def solve_simplegako(height, width, problem):
    solver = Solver()
    numbers = solver.int_array((height, width), 1, height + width - 1)
    solver.add_answer_key(numbers)

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

    def _count(y0, x0, n):
        sum = -1
        for y in range(height):
            sum += (numbers[y, x0] == n).cond(1, 0)
        for x in range(width):
            sum += (numbers[y0, x] == n).cond(1, 0)
        return sum

    for y in range(height):
        for x in range(width):
            solver.ensure(numbers[y, x] == _count(y, x, numbers[y, x]))

    is_sat = solver.solve()
    return is_sat, numbers
Пример #15
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
Пример #16
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
Пример #17
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
Пример #18
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