def inplace(): self.metrics.add_event(('sub_collapse', 'inplace')) move_space = calculate_moves(task, len(field_line)) if len(task) == 1 and move_space == 0: return CollapseResult.filled(len(field_line), 1) count = calculate_count(task, len(field_line)) if move_space >= max(task): return CollapseResult.empty(len(field_line), count) def stack_left_ends(): start = 0 for block in task: yield start + block start += block + MIN_BLOCK_SPACE def stack_right_starts(): start = move_space for block in task: yield start start += block + MIN_BLOCK_SPACE filled_indecies = [ i for start, end in zip(stack_right_starts(), stack_left_ends()) for i in range(start, end) ] line = np.full(len(field_line), Cell.EMPTY, Cell.dtype) line[filled_indecies] = Cell.FILLED return CollapseResult(line, count)
def test_5_results(self): reducer = SlimReducer() result = reducer.reduce([ CollapseResult(stoline('|001110 |'), 2), None, CollapseResult(stoline('| 00111|'), 5), None, CollapseResult(stoline('| 111 |'), 7) ], 7) self.assertArrayEqual(result.line, stoline('| 1 |')) self.assertEqual(result.count, 14)
def task_division(block_index, block_pos): def bum(cond): return MIN_BLOCK_SPACE if cond else 0 block = task[block_index] block_end = block_pos + block left_bum = bum(block_index > 0) right_bum = bum(block_index < (len(task) - 1)) left_edge = block_pos - left_bum right_edge = block_end + right_bum if (left_edge < 0 or right_edge > len(field_line) or (field_line[left_edge:block_pos] == Cell.FILLED).any() or (field_line[block_end:right_edge] == Cell.FILLED).any()): return None left_result, right_result, total_count = self.run_sides( left=CollapseRun(task[:block_index], field_line[:left_edge]), right=CollapseRun(task[block_index + 1:], field_line[right_edge:])) if total_count == 0: return None return CollapseResult.join(*left_result.line, *Cells.x(left_bum), *Cells.f(block), *Cells.x(right_bum), *right_result.line, count=total_count)
def reduce(self, results, length): '''Combine results from multiple divisions Args: combinations (Iterable[CollapsedResult|None]): iterable of all division results length (int): length of a line/section Returns: CollapseResult|None: a reduction result ''' non_empty_results = [ *filter(lambda result: result is not None, results) ] if len(non_empty_results) == 0: return None count = sum(l.count for l in non_empty_results) collapsed = np.array([l.line for l in non_empty_results], Cell.dtype) reduced = (Cell.FILLED if (column == Cell.FILLED).all() else Cell.CROSSED if (column == Cell.CROSSED).all() else Cell.EMPTY for column in collapsed.T) return CollapseResult(np.fromiter(reduced, Cell.dtype, length), count)
def reduce(self, results, length): '''Combine results from multiple divisions as steam on each cycle to a single accumulated value Args: combinations (Iterable[CollapsedResult|None]): iterable of all division results length (int): length of a line/section Returns: CollapseResult|None: a reduction result ''' accumulated = None count = 0 for result in results: if result is None: continue count += result.count if accumulated is None: accumulated = result.line.copy() continue for i in range(length): if accumulated[i] != result.line[i]: accumulated[i] = Cell.EMPTY return CollapseResult(accumulated, count) if accumulated is not None else None
def test_2_results(self): reducer = SlimReducer() result = reducer.reduce( [CollapseResult(stoline('|0011100|'), 1), None], 7) self.assertArrayEqual(result.line, stoline('|0011100|')) self.assertEqual(result.count, 1)
def task_division(i): left_result, right_result, total_count = self.run_sides( left=CollapseRun(task[:i], field_line[:x_start]), right=CollapseRun(task[i:], field_line[x_end:])) if total_count == 0: return None return CollapseResult.join(*left_result.line, *Cells.x(x_end - x_start), *right_result.line, count=total_count)
def __collapse(self, task: Task, field_line: FieldLine): """Recursive function that calculates (collapses) all combinations of a segment of tasks on a specific segment of field line. Args: task (Task): line task or a segment of the task field_line (FieldLine): line on the field or its continuos segment Returns: CollapsedResult: a new state of a line or its segment """ def divide_by_crossed(x_start, x_end): self.metrics.add_event(('sub_collapse', 'divide_by_crossed')) def task_division(i): left_result, right_result, total_count = self.run_sides( left=CollapseRun(task[:i], field_line[:x_start]), right=CollapseRun(task[i:], field_line[x_end:])) if total_count == 0: return None return CollapseResult.join(*left_result.line, *Cells.x(x_end - x_start), *right_result.line, count=total_count) division_results = (task_division(i) for i in range(len(task), -1, -1)) return self.__reduce(division_results, len(field_line)) def divide_by_filled(f_start, f_end): self.metrics.add_event(('sub_collapse', 'divide_by_filled')) def task_division(block_index, block_pos): def bum(cond): return MIN_BLOCK_SPACE if cond else 0 block = task[block_index] block_end = block_pos + block left_bum = bum(block_index > 0) right_bum = bum(block_index < (len(task) - 1)) left_edge = block_pos - left_bum right_edge = block_end + right_bum if (left_edge < 0 or right_edge > len(field_line) or (field_line[left_edge:block_pos] == Cell.FILLED).any() or (field_line[block_end:right_edge] == Cell.FILLED).any()): return None left_result, right_result, total_count = self.run_sides( left=CollapseRun(task[:block_index], field_line[:left_edge]), right=CollapseRun(task[block_index + 1:], field_line[right_edge:])) if total_count == 0: return None return CollapseResult.join(*left_result.line, *Cells.x(left_bum), *Cells.f(block), *Cells.x(right_bum), *right_result.line, count=total_count) blocks_placement = [(i, f_end - block, f_start) for i, block in enumerate(task) if block >= (f_end - f_start)] division_results = (task_division(i, pos) for i, pos_start, pos_end in blocks_placement for pos in range(pos_start, pos_end + 1)) return self.__reduce(division_results, len(field_line)) def inplace(): self.metrics.add_event(('sub_collapse', 'inplace')) move_space = calculate_moves(task, len(field_line)) if len(task) == 1 and move_space == 0: return CollapseResult.filled(len(field_line), 1) count = calculate_count(task, len(field_line)) if move_space >= max(task): return CollapseResult.empty(len(field_line), count) def stack_left_ends(): start = 0 for block in task: yield start + block start += block + MIN_BLOCK_SPACE def stack_right_starts(): start = move_space for block in task: yield start start += block + MIN_BLOCK_SPACE filled_indecies = [ i for start, end in zip(stack_right_starts(), stack_left_ends()) for i in range(start, end) ] line = np.full(len(field_line), Cell.EMPTY, Cell.dtype) line[filled_indecies] = Cell.FILLED return CollapseResult(line, count) if len(task) == 0 or (len(task) == 1 and task[0] == 0): return CollapseResult.crossed(len(field_line), 1) if (pos_x := field_line.find_center_crossed()) != (None, None): return divide_by_crossed(*pos_x)