示例#1
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
示例#2
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
示例#3
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
示例#4
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