Exemplo n.º 1
0
 def uniqueness_clauses(self, row, col):
     clauses = []
     for turn in range(2, self.num_turns):
         lits = [self.vpool.id((turn, row, col, state)) for state in STATES]
         clauses.extend(
             CardEnc.equals(lits, bound=1, vpool=self.vpool).clauses)
     return clauses
Exemplo n.º 2
0
 def second_turn_clauses(self, row, col):
     lits = [
         self.vpool.id((1, row, col, state)) for state in SECOND_TURN_STATES
     ]
     clauses = CardEnc.equals(lits, bound=1, vpool=self.vpool).clauses
     for state in STATES:
         if state not in SECOND_TURN_STATES:
             clauses.append([-self.vpool.id((0, row, col, state))])
     return clauses
Exemplo n.º 3
0
    def first_step(self, first_field=None):
        self.solver.append_formula(
            CardEnc.equals(lits=list(range(1, self.game.board_dim**2 + 1)),
                           bound=self.game.num_mines,
                           encoding=EncType.native))

        if first_field:
            row, column = first_field
            self.newly_opened = self.open(self.game.board[row][column])
        else:
            self.newly_opened = self.open(self.get_random_field())
Exemplo n.º 4
0
 def init_literal_meaning(self):
     counter = 1
     states = ["S2", "S1", "S0", "Q1", "Q0", "U", "I0", "I1", "H"]
     for turn, turn_state in enumerate(self.map):
         self.literal_meanings[turn] = {}
         for i, row in enumerate(turn_state):
             for j, item in enumerate(row):
                 for num in range(len(states)):
                     self.rev_literal_meanings[counter +
                                               num] = (turn, (i, j),
                                                       states[num])
                 self.literal_meanings[turn][(i, j)] = {
                     state: counter + num
                     for num, state in enumerate(states)
                 }
                 cnf = CardEnc.equals(lits=list(
                     self.literal_meanings[turn][(i, j)].values()),
                                      bound=1,
                                      encoding=EncType.pairwise)
                 for clause in cnf.clauses:
                     self.solver.add_clause(clause)
                 counter += len(states)
                 self.literal_meanings[turn][(i, j)]["D"] = counter
                 self.rev_literal_meanings[counter] = (turn, (i, j), "D")
                 counter += 1
                 self.literal_meanings[turn][(i, j)]["P"] = counter
                 self.rev_literal_meanings[counter] = (turn, (i, j), "P")
                 counter += 1
                 self.literal_meanings[turn][(i, j)]["Sick"] = counter
                 self.rev_literal_meanings[counter] = (turn, (i, j), "Sick")
                 counter += 1
                 not_s = ["Q1", "Q0", "U", "I0", "I1", "H", "Sick"]
                 literals = [
                     self.literal_meanings[turn][(i, j)][state]
                     for state in not_s
                 ]
                 cnf = CardEnc.equals(lits=literals,
                                      bound=1,
                                      encoding=EncType.pairwise)
                 for clause in cnf.clauses:
                     self.solver.add_clause(clause)
Exemplo n.º 5
0
 def equals(self, lits, bound=1, encoding=EncType.seqcounter):
     """
         **Added in SugarRush**\n
         Uses :meth:`pysat.card.CardEnc.equals`.
         Adds automatic bookkeeping of literals.
     """
     cnf = CardEnc.equals(lits=lits,
                          bound=bound,
                          encoding=encoding,
                          top_id=self._top_id())
     clauses = cnf.clauses
     self._add_lits_from(clauses)
     return clauses
Exemplo n.º 6
0
    def generate_medic_clauses(self):
        clauses = []

        for turn in range(self.num_turns):
            lits = [
                self.vpool.id((turn, row, col, IMMUNE_RECENTLY))
                for row in range(self.height) for col in range(self.width)
            ]
            clauses.extend(
                CardEnc.atmost(lits, bound=self.num_medics,
                               vpool=self.vpool).clauses)
        # TODO check case of 0 medics
        if self.num_medics == 0:
            return clauses

        for turn in range(self.num_turns - 1):
            for num_healthy in range(self.width * self.height):
                for healthy_tiles in itertools.combinations(
                        self.tiles, num_healthy):
                    sick_tiles = [
                        tile for tile in self.tiles
                        if tile not in healthy_tiles
                    ]
                    clause = []

                    for row, col in healthy_tiles:
                        clause.append(-self.vpool.id((turn, row, col,
                                                      HEALTHY)))

                    for row, col in sick_tiles:
                        clause.append(self.vpool.id((turn, row, col, HEALTHY)))

                    lits = [
                        self.vpool.id((turn + 1, row, col, IMMUNE_RECENTLY))
                        for row, col in healthy_tiles
                    ]
                    equals_clauses = CardEnc.equals(lits,
                                                    bound=min(
                                                        self.num_medics,
                                                        num_healthy),
                                                    vpool=self.vpool).clauses
                    for sub_clause in equals_clauses:
                        temp_clause = deepcopy(clause)
                        temp_clause += sub_clause
                        clauses.append(temp_clause)

        return clauses
Exemplo n.º 7
0
def test_equals1():
    encs = list(
        filter(lambda name: not name.startswith('__') and name != 'native',
               dir(EncType)))
    for l in range(10, 20):
        for e in encs:
            for b in (1, l - 1):
                cnf = CardEnc.equals(lits=list(range(1, l + 1)),
                                     bound=b,
                                     encoding=getattr(EncType, e))

                # enumerating all models
                with MinisatGH(bootstrap_with=cnf) as solver:
                    for num, model in enumerate(solver.enum_models(), 1):
                        solver.add_clause([-l for l in model[:l]])

                assert num == l, 'wrong number of models for Equals-1-of-{0} ({1})'.format(
                    l, e)
Exemplo n.º 8
0
    def coloring(self, n_color):
        """
        Returns whether or not there exists a vertex coloring
        of, at most, n_color colors.

        Accepts one param:
        - n_color: number of color to check

        Might raise ValueError exception.
        """
        if n_color < 0:
            raise ValueError('Number of colors must be positive integer')

        if n_color == 0:
            return not bool(self.vertices())

        logging.info('\nCodifying SAT Solver...')
        solver = Solver(name='cd')
        vpool = IDPool()

        logging.info(
            ' -> Codifying: Every vertex must have a color, and only one')
        for vertex in self.vertices():
            cnf = CardEnc.equals(lits=[
                vpool.id('{}color{}'.format(vertex, color))
                for color in range(n_color)
            ],
                                 vpool=vpool,
                                 encoding=0)

            solver.append_formula(cnf)

        logging.info(
            ' -> Codifying: No two neighbours can have the same color')
        for vertex in self.vertices():
            for neighbour in self[vertex]:
                for color in range(n_color):
                    solver.add_clause([
                        -vpool.id('{}color{}'.format(vertex, color)),
                        -vpool.id('{}color{}'.format(neighbour, color))
                    ])

        logging.info('Running SAT Solver...')
        return solver.solve()
Exemplo n.º 9
0
 def unique_tile_dynamics(self):
     clauses = []
     for i in range(self.rows):
         for j in range(self.cols):
             for t in range(self.t_max + 1):  # including all
                 legal_states = [
                     self.pool.obj2id[f"U_{i}_{j}^{t}"],
                     self.pool.obj2id[f"I0_{i}_{j}^{t}"],
                     self.pool.obj2id[f"I_{i}_{j}^{t}"],
                     self.pool.obj2id[f"S0_{i}_{j}^{t}"],
                     self.pool.obj2id[f"S1_{i}_{j}^{t}"],
                     self.pool.obj2id[f"S2_{i}_{j}^{t}"],
                     self.pool.obj2id[f"Q0_{i}_{j}^{t}"],
                     self.pool.obj2id[f"Q1_{i}_{j}^{t}"],
                     self.pool.obj2id[f"H_{i}_{j}^{t}"],
                 ]
                 clauses.extend(
                     CardEnc.equals(legal_states, 1, vpool=self.pool))
     return CNF(from_clauses=clauses)
Exemplo n.º 10
0
    def hadar_dynamics(self):
        clauses = []

        for t in range(self.num_observations):
            clauses.extend(
                CardEnc.atmost(self.__get_I0_predicates(t),
                               bound=self.medics,
                               vpool=self.pool).clauses)

        if self.medics == 0:
            return clauses

        for t in range(self.num_observations - 1):
            for num_healthy in range(self.cols * self.rows):
                for healthy_tiles in itertools.combinations(
                        self.tiles, num_healthy):
                    sick_tiles = [
                        tile for tile in self.tiles
                        if tile not in healthy_tiles
                    ]
                    clause = []

                    for i, j in healthy_tiles:
                        clause.append(-self.obj2id[f"H_{i}_{j}^{t}"])

                    for i, j in sick_tiles:
                        clause.append(self.obj2id[f"H_{i}_{j}^{t}"])

                    lits = [
                        self.obj2id[f"I0_{i}_{j}^{t + 1}"]
                        for i, j in healthy_tiles
                    ]
                    equals_clauses = CardEnc.equals(lits,
                                                    bound=min(
                                                        self.medics,
                                                        num_healthy),
                                                    vpool=self.pool).clauses
                    for sub_clause in equals_clauses:
                        temp_clause = copy.deepcopy(clause)
                        temp_clause += sub_clause
                        clauses.append(temp_clause)

        return CNF(from_clauses=clauses)
Exemplo n.º 11
0
    def naveh_dynamics(self):
        clauses = []

        for t in range(1, self.num_observations):
            clauses.extend(
                CardEnc.atmost(self.__get_Q0_predicates(t),
                               bound=self.police,
                               vpool=self.pool).clauses)

        if self.police == 0:
            return clauses

        for t in range(self.num_observations - 1):
            for num_sick in range(self.cols * self.rows):
                for sick_tiles in itertools.combinations(self.tiles, num_sick):
                    healthy_tiles = [
                        tile for tile in self.tiles if tile not in sick_tiles
                    ]
                    for sick_state_perm in itertools.combinations_with_replacement(
                            self.possible_sick_states(t), num_sick):
                        clause = []

                        for (i, j), state in zip(sick_tiles, sick_state_perm):
                            clause.append(-self.obj2id[f"{state}_{i}_{j}^{t}"])
                        for i, j in healthy_tiles:
                            for state in self.possible_sick_states(t):
                                clause.append(
                                    self.obj2id[f"{state}_{i}_{j}^{t}"])

                        equals_clauses = CardEnc.equals(
                            lits=self.__get_Q0_predicates(t + 1),
                            bound=min(self.police, num_sick),
                            vpool=self.pool).clauses
                        for sub_clause in equals_clauses:
                            temp_clause = copy.deepcopy(clause)
                            temp_clause += sub_clause
                            clauses.append(temp_clause)

        return CNF(from_clauses=clauses)
Exemplo n.º 12
0
    def make_constraint(self, row_index, col_index):
        """
        Compute equations for field (row_index, col_index)
        """

        adjacent_fields = self.game.get_adjacent_fields(row_index, col_index)
        adjacent_mines = self.game.get_adjacent_mines(row_index, col_index)

        assert adjacent_mines
        for field in adjacent_fields:
            if field not in self.current_adjacent_fields \
                    and self.game.board[field.row][field.column].covered \
                    and field not in self.mines:
                self.current_adjacent_fields.append(field)

        literals = []
        for field in adjacent_fields:
            if field.covered:
                literals.append(field.id)

        return CardEnc.equals(lits=literals,
                              bound=adjacent_mines,
                              encoding=EncType.native)
Exemplo n.º 13
0
 def __call__(self, k, vars_):
     assert max(map(abs, vars_)) < self.s1, 'max vars exceeded nvars_total'
     assert len(vars_) == len(set(vars_)), 'vars_ element must be unique'
     assert all(x > 0 for x in vars_), 'vars_ must be greater than 0'
     n = len(vars_)
     cnf = CardEnc.equals(range(1, n + 1), k)
     C = cnf.clauses
     # ns: number of auxiliary varibales in C
     ns = len(set(map(abs, itertools.chain.from_iterable(C)))) - n
     assert ns >= 0, (ns, n, C, k, vars_)
     sdiff = self.s1 - (n + 1)
     assert sdiff >= 0, (self.s1, n, sdiff, k, vars_)
     if sdiff:
         C = [[(x + sdiff) if x > n else x for x in r] for r in C]
         C = [[(x - sdiff) if x < -n else x for x in r] for r in C]
     tr = dict(zip(range(1, n + 1), vars_))
     tr.update(dict(zip(range(-1, -n - 2, -1), [-x for x in vars_])))
     C = [[tr.get(x, x) for x in r] for r in C]
     self.s1 += ns
     self.logger.debug('Updated self.s1 from %d to %d', self.s1 - ns,
                       self.s1)
     C = [[int(x) for x in r] for r in C]
     return C
Exemplo n.º 14
0
    def find_hamiltonian_path(self, check_cycle=False):
        """
        should it exists, find a Hamiltonian on
        current self. Otherwise return empty list.
        """
        if not self.edges():
            return []

        logging.info('Codifying SAT Solver...')
        length = len(self.vertices())
        solver = Solver(name='cd')
        names = {}
        vpool = IDPool()

        for integer, vertex in enumerate(self.vertices()):
            names[integer + 1] = vertex
        names[0] = names[length]

        for position_in_path in range(length):
            for vertex in range(1, length + 1):
                vpool.id(xvar(vertex, position_in_path))

        logging.info(' -> Codifying: All Positions occupied')
        for position_in_path in range(length):
            var_list = [
                vpool.id(xvar(vertex, position_in_path))
                for vertex in range(1, length + 1)
            ]

            cnf = CardEnc.equals(lits=var_list, vpool=vpool)
            solver.append_formula(cnf)

        logging.info(' -> Codifying: All vertex visited')
        for vertex in range(1, length + 1):
            var_list = [
                vpool.id(xvar(vertex, position_in_path))
                for position_in_path in range(length)
            ]

            cnf = CardEnc.equals(lits=var_list, vpool=vpool)
            solver.append_formula(cnf)

        logging.info(' -> Codifying: Adjacency Matrix')
        edges = self.edges()
        for vertex_a in range(1, length + 1):
            for vertex_b in range(vertex_a + 1, length + 1):
                if (names[vertex_a], names[vertex_b]) not in edges:
                    for position_in_path in range(length - 1):
                        solver.add_clause([
                            -vpool.id(xvar(vertex_a, position_in_path)),
                            -vpool.id(xvar(vertex_b, position_in_path + 1)),
                        ])
                        solver.add_clause([
                            -vpool.id(xvar(vertex_b, position_in_path)),
                            -vpool.id(xvar(vertex_a, position_in_path + 1)),
                        ])

                    if check_cycle:
                        solver.add_clause([
                            vpool.id(xvar(vertex_b, length - 1)),
                            vpool.id(xvar(vertex_a, 0))
                        ])
                        solver.add_clause([
                            vpool.id(xvar(vertex_b, 0)),
                            vpool.id(xvar(vertex_a, length - 1))
                        ])

        logging.info('Running SAT Solver...')
        solution = []
        if solver.solve():
            for index, variable in enumerate(solver.get_model()):
                if variable > 0 and index < length**2:
                    solution.append(names[variable % length])

        return solution
Exemplo n.º 15
0
    def solve(self):
        n_orange = len([h for h in self.hexagons if h.tpe == HexagonTypes.ORANGE])
        use_total = False
        while True:
            with Solver() as s:

                if use_total:
                    assert self.total_n_blue is not None, 'Need total number of blue to solve'
                    is_orange = [h.var_id for h in self.hexagons if h.tpe == HexagonTypes.ORANGE]
                    s.append_formula(CardEnc.equals(is_orange, self.total_n_blue - self.n_blue, vpool=id_pool).clauses)
                    print('Total active')

                assumptions = []
                formulas = []
                for h in self.hexagons:
                    formulas.append((h.var_id, h.constraint))
                    if h.constraint is not None:
                        s.append_formula(h.constraint)
                    if h.tpe == HexagonTypes.DARK:
                        assumptions.append(-h.var_id)
                    elif h.tpe in [HexagonTypes.BLUE, HexagonTypes.BLUE_W_NUMBER]:
                        assumptions.append(h.var_id)
                    # s.solve(assumptions=assumptions)

                for l in self.lines:
                    formulas.append((l.detection, h.constraint))
                    if l.constraint is not None:
                        s.append_formula(l.constraint)
                        # assert s.solve(assumptions=assumptions)

                if not s.solve(assumptions=assumptions):
                    raise RuntimeError('The boolean formula is unsatisfiable. This usually occurs when a game element is detected wrongly.')

                hexagons_to_update = []

                for h in reversed(self.hexagons):
                    if h.tpe != HexagonTypes.ORANGE:
                        continue
                    if not s.solve(assumptions=assumptions + [h.var_id]):
                        print(-h.var_id)
                        pyautogui.rightClick(*tuple(h.center + self.window_rect[:2]))
                        hexagons_to_update.append(h)
                    elif not s.solve(assumptions=assumptions + [-h.var_id]):
                        print(h.var_id)
                        pyautogui.leftClick(*tuple(h.center + self.window_rect[:2]))
                        hexagons_to_update.append(h)
                        self.n_blue += 1

                n_orange -= len(hexagons_to_update)
                if n_orange == 0:
                    break
                if self.total_n_blue is not None:
                    print('REMAINING', self.total_n_blue - self.n_blue)

                if len(hexagons_to_update) == 0:
                    if use_total:
                        raise RuntimeError('Can\'t solve the puzzle! Solution is ambiguous. Maybe a game element was detected wrongly.')
                    use_total = True
                    continue

                sleep(.3)

                start_time = time()

                while len(hexagons_to_update) > 0:
                    img = self.take_screenshot()
                    failed = []
                    for h in hexagons_to_update:
                        print('updating', h.var_id)
                        failed.append(not h.update(img))
                    hexagons_to_update = list(compress(hexagons_to_update, failed))
                    if time() - start_time >= 3:
                        raise RuntimeError('Failed to update state of game elements after 3 seconds of trying')

                for x in self.hexagons + self.lines:
                    x.check_if_constraint_trivial()
Exemplo n.º 16
0
    def generate_police_clauses(self):
        clauses = []

        for turn in range(1, self.num_turns):
            lits = [
                self.vpool.id((turn, row, col, QUARANTINED_1))
                for row in range(self.height) for col in range(self.width)
            ]
            clauses.extend(
                CardEnc.atmost(lits, bound=self.num_police,
                               vpool=self.vpool).clauses)
        # TODO check case of 0 policemen
        if self.num_police == 0:
            return clauses

        for turn in range(self.num_turns - 1):
            for num_sick in range(self.width * self.height):
                for sick_tiles in itertools.combinations(self.tiles, num_sick):
                    healthy_tiles = [
                        tile for tile in self.tiles if tile not in sick_tiles
                    ]
                    # TODO don't iterate over all sick states
                    for sick_state_perm in \
                            itertools.combinations_with_replacement(self.possible_sick_states(turn), num_sick):
                        clause = []

                        for (row, col), state in zip(sick_tiles,
                                                     sick_state_perm):
                            clause.append(-self.vpool.id((turn, row, col,
                                                          state)))
                        for row, col in healthy_tiles:
                            for state in self.possible_sick_states(turn):
                                clause.append(
                                    self.vpool.id((turn, row, col, state)))

                        lits = [
                            self.vpool.id((turn + 1, row, col, QUARANTINED_1))
                            for row, col in sick_tiles
                        ]
                        equals_clauses = CardEnc.equals(
                            lits,
                            bound=min(self.num_police, num_sick),
                            vpool=self.vpool).clauses
                        for sub_clause in equals_clauses:
                            temp_clause = deepcopy(clause)
                            temp_clause += sub_clause
                            clauses.append(temp_clause)

                        # if num_sick <= self.num_police:
                        #     for (row, col) in sick_tiles:
                        #         temp_clause = deepcopy(clause)
                        #         temp_clause.append(self.vpool.id((turn+1, row, col, QUARANTINED_1)))
                        #         clauses.extend(temp_clause)
                        #
                        #     # for (row, col) in healthy_tiles:
                        #     #     temp_clause = deepcopy(clause)
                        #     #     temp_clause.append(-self.vpool.id((turn+1, row, col, QUARANTINED_1)))
                        #     #     clauses.extend(temp_clause)
                        #
                        # else:
                        #     lits = [self.vpool.id((turn+1, row, col, QUARANTINED_1))
                        #             for row in range(self.height)
                        #             for col in range(self.width)]
                        #     equals_clauses = CardEnc.equals(lits, bound=self.num_police, vpool=self.vpool)
                        #
                        #     for sub_clause in equals_clauses.clauses():
                        #         temp_clause = deepcopy(clause)
                        #         temp_clause += sub_clause
                        #         clauses.extend(temp_clause)
        return clauses
Exemplo n.º 17
0
def encode_constraint(
        number,  # (int) number of hexagons in influence which should be blue
        curly,  # (bool) is curly
        dashed,  # (bool) is dashed
        influence,  # (List[Hexagon]) list of hexagons which are affected by the constraint
        circle  # (bool) if influenced hexagons are in a circle
):
    assert not circle or len(influence) <= 6
    assert len(influence) >= number

    # extract boolean variables
    literals = [h.var_id for h in influence]

    # if just a number
    if not curly and not dashed:
        return CardEnc.equals(lits=literals, bound=number, vpool=id_pool)

    # if dashed or curly
    # encode {number} in dnf
    n_vars = len(influence)
    dnf = []

    # this case needs special considerations since for hexagons gaps to count as such
    if (dashed or curly) and circle and n_vars < 6:
        # first find partitions
        # find start
        # remember: influence is sorted
        start = None
        for i in range(n_vars):
            # if there is a gap between influence[i] and influence[i - 1]
            if influence[i - 1] not in influence[i].influence or influence[i - 1].distance(influence[i]) > 1:
                start = i
                break
        assert start is not None

        # rotate such that start is at the beginning of influence
        influence = influence[start:] + influence[:start]
        partitions = [[influence[0]]]
        for i in influence[1:]:
            # if there is no gap between i and partitions[-1][-1]
            if i in partitions[-1][-1].influence and partitions[-1][-1].distance(i) == 1:
                partitions[-1].append(i)
            else:
                partitions.append([i])

        # encode {number} in dnf
        all_not = [-h.var_id for h in influence]
        start = 0
        for p in partitions:
            if len(p) < number:
                start += len(p)
                continue
            for i in range((len(p) - number + 1)):
                formula = all_not.copy()
                for j in range(number):
                    formula[start + i + j] = -formula[start + i + j]
                dnf.append(formula)
            start += len(p)

    else:
        # encode {number} in dnf
        all_not = [-h.var_id for h in influence]
        for i in range(n_vars if circle else (n_vars - number + 1)):
            formula = all_not.copy()
            for j in range(number):
                formula[(i + j) % n_vars] = -formula[(i + j) % n_vars]
            dnf.append(formula)

    if dashed:
        # -number- == not {number} and number
        cnf = CardEnc.equals(lits=literals, bound=number, vpool=id_pool)
        cnf.extend([[-v for v in c] for c in dnf])  # not {number} with DeMorgan
        return cnf

    if curly:
        # dnf to cnf
        cnf = [[]]
        for c in dnf:
            aux_var = id_pool.id(id_pool.top + 1)
            cnf[0].append(aux_var)
            for v in c:
                cnf.append([-aux_var, v])
        return CNF(from_clauses=cnf)
Exemplo n.º 18
0
def make_formula(n_police, n_medics, n_rows, n_cols, n_time):
    states = {'U', 'H', 'S', 'I', 'Q'}
    variables = {}
    formula = CNF()
    var_pool = IDPool()
    for t in range(n_time):
        for r in range(n_rows):
            for c in range(n_cols):
                for s in states:
                    variables[(r, c), t,
                              s] = var_pool.id(f'({r}, {c}), {t}, {s}')
                variables[(r, c), t, 'P'] = var_pool.id(
                    f'({r}, {c}), {t}, P')  # Police were used
                variables[(r, c), t, 'M'] = var_pool.id(
                    f'({r}, {c}), {t}, M')  # Medics were used
                variables[(r, c), t, 'SS'] = var_pool.id(
                    f'({r}, {c}), {t}, SS')  # Stayed sick from last time
    for t in range(n_time):
        formula.extend(
            CardEnc.atmost([
                variables[(r, c), t, 'P'] for r in range(n_rows)
                for c in range(n_cols)
            ],
                           bound=n_police,
                           vpool=var_pool))
        formula.extend(
            CardEnc.atmost([
                variables[(r, c), t, 'M'] for r in range(n_rows)
                for c in range(n_cols)
            ],
                           bound=n_medics,
                           vpool=var_pool))
        for r in range(n_rows):
            for c in range(n_cols):
                formula.extend(
                    CardEnc.equals([variables[(r, c), t, s] for s in states],
                                   vpool=var_pool))
                if t > 0:
                    formula.extend(
                        req_equiv([
                            -variables[(r, c), t - 1, 'Q'], variables[(r, c),
                                                                      t, 'Q']
                        ], [variables[(r, c), t, 'P']]))
                    formula.extend(
                        req_equiv([
                            -variables[(r, c), t - 1, 'I'], variables[(r, c),
                                                                      t, 'I']
                        ], [variables[(r, c), t, 'M']]))
                    formula.extend(
                        req_equiv([
                            variables[(r, c), t - 1, 'S'], variables[(r, c), t,
                                                                     'S']
                        ], [variables[(r, c), t, 'SS']]))
                    nearby_sick_condition = []
                    for r_, c_ in nearby(r, c, n_rows, n_cols):
                        nearby_sick_condition.append(variables[(r_, c_), t,
                                                               'SS'])
                        formula.extend(
                            req_imply([
                                variables[(r, c), t, 'SS'],
                                variables[(r_, c_), t - 1, 'H']
                            ], [
                                variables[(r_, c_), t, 'S'],
                                variables[(r_, c_), t, 'I']
                            ]))
                        # formula.extend(req_imply([variables[(r, c), t, 'SS']], [-variables[(r_, c_), t, 'H']]))
                    formula.extend(
                        req_imply([
                            variables[(r, c), t - 1, 'H'], variables[(r, c), t,
                                                                     'S']
                        ], nearby_sick_condition))
                if t + 1 < n_time:
                    formula.extend(
                        req_equiv([variables[(r, c), t, 'U']],
                                  [variables[(r, c), t + 1, 'U']]))
                    formula.extend(
                        req_imply([variables[(r, c), t, 'I']],
                                  [variables[(r, c), t + 1, 'I']]))
                    formula.extend(
                        req_imply([variables[(r, c), t + 1, 'S']], [
                            variables[(r, c), t, 'S'], variables[(r, c), t,
                                                                 'H']
                        ]))
                    formula.extend(
                        req_imply([variables[(r, c), t + 1, 'Q']], [
                            variables[(r, c), t, 'Q'], variables[(r, c), t,
                                                                 'S']
                        ]))
                if t == 0:
                    formula.append([-variables[(r, c), t, 'Q']])
                    formula.append([-variables[(r, c), t, 'I']])
                    if t + 1 < n_time:
                        formula.extend(
                            req_imply([variables[(r, c), t, 'S']], [
                                variables[(r, c), t + 1, 'S'],
                                variables[(r, c), t + 1, 'Q']
                            ]))
                        formula.extend(
                            req_imply([variables[(r, c), t, 'Q']],
                                      [variables[(r, c), t + 1, 'Q']]))
                    if t + 2 < n_time:
                        formula.extend(
                            req_imply([
                                variables[(r, c), t, 'S'],
                                variables[(r, c), t + 1, 'S']
                            ], [
                                variables[(r, c), t + 2, 'S'],
                                variables[(r, c), t + 2, 'Q']
                            ]))
                        formula.extend(
                            req_imply([
                                variables[(r, c), t, 'S'],
                                variables[(r, c), t + 1, 'Q']
                            ], [variables[(r, c), t + 2, 'Q']]))
                        formula.extend(
                            req_imply([variables[(r, c), t, 'Q']],
                                      [variables[(r, c), t + 2, 'H']]))
                    if t + 3 < n_time:
                        formula.extend(
                            req_imply([
                                variables[(r, c), t,
                                          'S'], variables[(r, c), t + 1, 'S'],
                                variables[(r, c), t + 2, 'S']
                            ], [variables[(r, c), t + 3, 'H']]))
                if 0 < t and t + 1 < n_time:
                    formula.extend(
                        req_imply([
                            -variables[(r, c), t - 1, 'S'], variables[(r, c),
                                                                      t, 'S']
                        ], [
                            variables[(r, c), t + 1, 'S'],
                            variables[(r, c), t + 1, 'Q']
                        ]))
                    formula.extend(
                        req_imply([
                            -variables[(r, c), t - 1, 'Q'], variables[(r, c),
                                                                      t, 'Q']
                        ], [variables[(r, c), t + 1, 'Q']]))
                if 0 < t and t + 2 < n_time:
                    formula.extend(
                        req_imply([
                            -variables[(r, c), t - 1, 'S'],
                            variables[(r, c), t, 'S'], variables[(r, c), t + 1,
                                                                 'S']
                        ], [
                            variables[(r, c), t + 2, 'S'],
                            variables[(r, c), t + 2, 'Q']
                        ]))
                    formula.extend(
                        req_imply([
                            -variables[(r, c), t - 1, 'S'],
                            variables[(r, c), t, 'S'], variables[(r, c), t + 1,
                                                                 'Q']
                        ], [variables[(r, c), t + 2, 'Q']]))
                    formula.extend(
                        req_imply([
                            -variables[(r, c), t - 1, 'Q'], variables[(r, c),
                                                                      t, 'Q']
                        ], [variables[(r, c), t + 2, 'H']]))
                if 0 < t and t + 3 < n_time:
                    formula.extend(
                        req_imply([
                            -variables[(r, c), t - 1, 'S'], variables[(r, c),
                                                                      t, 'S'],
                            variables[(r, c), t + 1,
                                      'S'], variables[(r, c), t + 2, 'S']
                        ], [variables[(r, c), t + 3, 'H']]))
    return var_pool, formula