def gen_contiguous_constraints_single(index_, count_, *, width, height, board, puzzle): at_ = lambda l, c : board[l][c] at_puzzle = lambda l, c : puzzle[l][c] def is_valid_cell(ind, current_index=index_, height=height, width=width): if ind == current_index: # redundant check at_puzzle(ind) == count_ return True if not inside_board(ind, height=height, width=width): return False # a cell is considered valid neighbour (to spawn) if it is empty # or it has the same count_ return at_puzzle(*ind) == 0 or at_puzzle(*ind) == count_ possibs = [] # possible configurations given the count_ of contiguous cells in the block blocks = get_block(index_, nb_cells=count_, is_valid_cell=is_valid_cell) for block in blocks: block_vars = [ at_(*ind) for ind in block ] # fence can be thought of as completely encircling the block fence = get_fence(enclosure=block) geom = { 'width': width, 'height': height } fence_inside_board = [ ind for ind in fence if inside_board(ind, **geom) ] fence_var = [ at_(*ind) for ind in fence_inside_board ] fenced_enclosure = And( coerce_eq(block_vars, [count_] * len(block_vars)), And([var != count_ for var in fence_var])) possibs.append(simplify(fenced_enclosure)) return Exactly(*possibs, 1) #only one configuration would prevail
def is_valid_cell(ind, current_index=index_, height=height, width=width): if ind == current_index: # redundant check at_puzzle(ind) == count_ return True if not inside_board(ind, height=height, width=width): return False # a cell is considered valid neighbour (to spawn) if it is empty # or it has the same count_ return at_puzzle(*ind) == 0 or at_puzzle(*ind) == count_
def gen_consecutive_nums_constraint(l, c): val_neighs = [ neigh for neigh in neighbours(l, c) if inside_board(neigh, height=order, width=order) ] neighs = get_vars_at(val_neighs) occupied_neighs = set(neighs).intersection(occupied_cells) var = at_(l, c) #content of current cell one_adj_cell_is_consec_c = Exactly(*[ adj == var + 1 for adj in occupied_neighs ], 1) current_cell_is_max_c = var == maximum return Or(current_cell_is_max_c, one_adj_cell_is_consec_c)
def gen_coupling_constraints(l, c): val_neighs = [ neigh for neigh in neighbours((l, c)) if inside_board(neigh, height=height, width=width) ] couplings = [X[l][c] == -at_(*cell) for cell in val_neighs] # we don't add constraint that it is unoccupied by a tree # it's taken care by another set of constraints # return Exactly(*couplings, 1) return Exactly(*couplings, 1)
def gen_proximity_constraints(l, c): square = [ (l, c), (l, c + 1), # towards right (l + 1, c), (l + 1, c + 1) ] # towards bottom and right val_cells_in_square = [ cell for cell in square if inside_board(cell, height=height, width=width) ] # cell < 0 : we have encoded as tent tents_in_a_square = [at_(*cell) < 0 for cell in val_cells_in_square] return AtMost(*tents_in_a_square, 1)
def gen_contiguous_constraints_single(index_, count_, *, width, height, board): at_ = lambda l, c: board[l][c] # count_ - 1 because the current square is always counted possibs = [] #possible configurations given the count_ of white squares for distrib in distribute_in_4_directions(count_ - 1): strict_boundaries = boundaries(index_, spawn_direction=distrib) geom = {'width': width, 'height': height} can_spawn = all( (inside_board(ind, **geom) for ind in strict_boundaries)) if can_spawn: enclosure = get_enclosed_space(index_, spawn_direction=distrib) encloure_vars = [at_(*ind) for ind in enclosure] direction_plus_one = tuple(d + 1 for d in distrib) # walls can be thought of as surrounding boundaries walls = boundaries(index_, spawn_direction=direction_plus_one) walls_within_board = [ ind for ind in walls if inside_board(ind, **geom) ] walls_var = [at_(*ind) for ind in walls_within_board] walled_enclosure = And( coerce_eq(encloure_vars, [WHITE] * len(encloure_vars)), coerce_eq(walls_var, [BLACK] * len(walls_var))) possibs.append(simplify(walled_enclosure)) return Exactly(*possibs, 1) #only one configuration would prevail
def gen_consecutive_nums_constraint(l, c): geom = {'height': height, 'width': width} val_neighs = [ neigh for neigh in neighbours((l, c)) if inside_board(neigh, **geom) ] neighs = get_vars_at(val_neighs) var = at_(l, c) #content of current cell one_adj_cell_is_consec_c = Exactly(*[adj == var + 1 for adj in neighs], 1) current_cell_is_max_c = var == MAX unoccupied_c = var == 0 return Or(unoccupied_c, current_cell_is_max_c, one_adj_cell_is_consec_c)
def valid_neighbours(l, c): return [ neigh for neigh in neighbours((l, c)) if inside_board(neigh, **geom) ]
def valid_cells_in_disk(l, c): geom = {'height': height, 'width': width} cells = cells_in_disk((l, c), radius=1) return [cell for cell in cells if inside_board(cell, **geom)]