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
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
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
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
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
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
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
def solve_geradeweg(height, width, problem): def line_length(edges): edges = list(edges) if len(edges) == 0: return 0 ret = edges[-1].cond(1, 0) for i in range(len(edges) - 2, -1, -1): ret = edges[i].cond(1 + ret, 0) return ret solver = Solver() grid_frame = BoolGridFrame(solver, height - 1, width - 1) solver.add_answer_key(grid_frame) is_passed = graph.active_edges_single_cycle(solver, grid_frame) for y in range(height): for x in range(width): if problem[y][x] >= 1: solver.ensure(is_passed[y, x]) solver.ensure(fold_or(([grid_frame.horizontal[y, x - 1]] if x > 0 else []) + ([grid_frame.horizontal[y, x]] if x < width - 1 else [])).then( line_length(reversed(list(grid_frame.horizontal[y, :x]))) + line_length(grid_frame.horizontal[y, x:]) == problem[y][x] )) solver.ensure(fold_or(([grid_frame.vertical[y - 1, x]] if y > 0 else []) + ([grid_frame.vertical[y, x]] if y < height - 1 else [])).then( line_length(reversed(list(grid_frame.vertical[:y, x]))) + line_length(grid_frame.vertical[y:, x]) == problem[y][x] )) is_sat = solver.solve() return is_sat, grid_frame
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
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
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
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
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
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
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
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
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
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
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
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
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
def solve_star_battle(n, blocks, k, is_anti_knight=False): if not isinstance(blocks, Array): blocks = Array(blocks) solver = Solver() has_star = solver.bool_array((n, n)) solver.add_answer_key(has_star) for i in range(n): solver.ensure(sum(has_star[i, :].cond(1, 0)) == k) solver.ensure(sum(has_star[:, i].cond(1, 0)) == k) solver.ensure(~(has_star[:-1, :] & has_star[1:, :])) solver.ensure(~(has_star[:, :-1] & has_star[:, 1:])) solver.ensure(~(has_star[:-1, :-1] & has_star[1:, 1:])) solver.ensure(~(has_star[:-1, 1:] & has_star[1:, :-1])) for i in range(n): solver.ensure(sum((has_star & (blocks == i)).cond(1, 0)) == k) if is_anti_knight: graph.active_vertices_anti_knight(solver, has_star) is_sat = solver.solve() return is_sat, has_star
def solve_simpleloop(height, width, blocked, pivot): solver = Solver() grid_frame = BoolGridFrame(solver, height - 1, width - 1) solver.add_answer_key(grid_frame) is_passed = graph.active_edges_single_cycle(solver, grid_frame) for y in range(height): for x in range(width): if (y, x) != pivot: solver.ensure(is_passed[y, x] == (blocked[y][x] == 0)) py, px = pivot n_pass = 0 for y in range(height): for x in range(width): if (y, x) != pivot and blocked[y][x] == 0: n_pass += 1 solver.ensure(is_passed[py, px] == (n_pass % 2 == 1)) is_sat = solver.solve() return is_sat, grid_frame
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
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
def solve_masyu(height, width, problem): solver = Solver() grid_frame = BoolGridFrame(solver, height - 1, width - 1) solver.add_answer_key(grid_frame) graph.active_edges_single_cycle(solver, grid_frame) def get_edge(y, x, neg=False): if 0 <= y <= 2 * (height - 1) and 0 <= x <= 2 * (width - 1): if y % 2 == 0: r = grid_frame.horizontal[y // 2][x // 2] else: r = grid_frame.vertical[y // 2][x // 2] if neg: return ~r else: return r else: return neg for y in range(height): for x in range(width): if problem[y][x] == 1: solver.ensure( (get_edge(y * 2, x * 2 - 1) & get_edge(y * 2, x * 2 + 1) & (get_edge(y * 2, x * 2 - 3, True) | get_edge(y * 2, x * 2 + 3, True))) | (get_edge(y * 2 - 1, x * 2) & get_edge(y * 2 + 1, x * 2) & (get_edge(y * 2 - 3, x * 2, True) | get_edge(y * 2 + 3, x * 2, True)))) elif problem[y][x] == 2: dirs = [ get_edge(y * 2, x * 2 - 1) & get_edge(y * 2, x * 2 - 3), get_edge(y * 2 - 1, x * 2) & get_edge(y * 2 - 3, x * 2), get_edge(y * 2, x * 2 + 1) & get_edge(y * 2, x * 2 + 3), get_edge(y * 2 + 1, x * 2) & get_edge(y * 2 + 3, x * 2), ] solver.ensure((dirs[0] | dirs[2]) & (dirs[1] | dirs[3])) is_sat = solver.solve() return is_sat, grid_frame
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
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
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
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