def add_rows(self, count: int, debugger: Debugger = Debugger(enabled=False)): """ >>> print("!" + Minefield.from_rows_text('..^^.').add_rows(2).show()) !..^^. .^^^^ ^^..^ >>> print("!" + Minefield.from_rows_text( ... '.^^.^.^^^^').add_rows(9).show()) !.^^.^.^^^^ ^^^...^..^ ^.^^.^.^^. ..^^...^^^ .^^^^.^^.^ ^^..^.^^.. ^^^^..^^^. ^..^^^^.^^ .^^^..^.^^ ^^.^^^..^^ """ debugger.reset() total_target = len(self.rows) + count for _ in range(count): self.add_row() debugger.step() if debugger.should_report(): debugger.default_report( f"rows: {len(self.rows)}/{total_target}") return self
def get_checksum(self, data: str, debugger: Debugger) -> str: """ >>> DataGenerator().get_checksum("110010110100") '100' >>> DataGenerator().get_checksum("110101") '100' >>> DataGenerator().get_checksum("100") '100' >>> DataGenerator().get_checksum("10000011110010000111") '01100' """ reduced = data while True: new_reduced = self.reduce_data(reduced) if new_reduced == reduced: break reduced = new_reduced if debugger.should_report(): debugger.report( f"Check summing, step: {debugger.step_count}, time: " f"{debugger.pretty_duration_since_start}, size: " f"{len(reduced)}/{len(data)}") return reduced
def solve( self, node_set: 'NodeSetExtended', target: Point2D = Point2D.ZERO_POINT, debugger: Debugger = Debugger(enabled=False), ) -> List['NodeSetExtended']: stack = [node_set] previous_map: PreviousMap = {node_set: None} duplicate_count = 0 debugger.reset() target_position_map = self.get_target_positions(node_set) debugger.reset() while debugger.step_if(stack): current_node_set = stack.pop(0) next_states = self.get_next_states( current_node_set, target_position_map) for next_node_set in next_states: if next_node_set in previous_map: duplicate_count += 1 continue previous_map[next_node_set] = current_node_set if next_node_set.position == target: return self.get_solution(next_node_set, previous_map) stack.append(next_node_set) if debugger.should_report(): debugger.default_report( f"stack: {len(stack)}, pruned: {duplicate_count}") raise Exception(f"Could not find solution")
def find_minimum_cost_to_organise( self, debugger: Debugger = Debugger(enabled=False), ) -> int: """ >>> Maze.from_maze_text(''' ... ############# ... #...........# ... ###B#C#B#D### ... #A#D#C#A# ... ######### ... ''').find_minimum_cost_to_organise() 12521 """ stack = [(0, 0, self)] seen_cost = {self: 0} min_cost: Optional[int] = None while debugger.step_if(stack): move_count, cost, maze = stack.pop(0) if seen_cost[maze] < cost: continue next_move_count = move_count + 1 next_stack = [] for move_cost, next_maze in maze.get_next_moves(): next_cost = cost + move_cost if seen_cost.get(next_maze, next_cost + 1) <= next_cost: continue if min_cost is not None and min_cost <= next_cost: continue next_stack.append((next_move_count, next_cost, next_maze)) if not next_stack: continue max_next_finish_count = max( next_maze.finish_count for _, _, next_maze in next_stack ) for next_move_count, next_cost, next_maze in next_stack: if next_maze.finish_count < max_next_finish_count: continue if next_maze.finished: min_cost = next_cost continue stack.append((next_move_count, next_cost, next_maze)) seen_cost[next_maze] = next_cost if debugger.should_report(): debugger.default_report( f"{len(stack)} in stack, seen {len(seen_cost)}, " f"last move count is {move_count}, last cost is {cost}, " f"min cost is {min_cost}, last maze is:\n{maze}\n" ) debugger.default_report( f"{len(stack)} in stack, seen {len(seen_cost)}, " f"min cost is {min_cost}" ) if min_cost is None: raise Exception(f"Could not find end state") return min_cost
def solve(self, _input, debugger: Debugger): """ >>> Challenge().default_solve() 22887907 """ firewall = Firewall.from_ranges_text(_input) if debugger.should_report(): debugger.report(f"Total range: {firewall.total_range}") for _range in firewall.blocked_ranges: debugger.report(f" {_range}") return firewall.get_lowest_non_blocked_value()
def fill_disk(self, size: int, initial: str, debugger: Debugger) -> str: """ >>> DataGenerator().fill_disk(20, "10000") '10000011110010000111' """ disk = initial while len(disk) < size: disk = self.increase_data(disk) if debugger.should_report(): debugger.report( f"Filling, step: {debugger.step_count}, time: " f"{debugger.pretty_duration_since_start}, size: " f"{len(disk)}/{size}") disk = disk[:size] return disk
def step_many( self, sequence: str, step_count: int = 40, debugger: Debugger = Debugger(enabled=False)) -> str: """ >>> LookAndSay().step_many("1", 5) '312211' """ debugger.reset() result = sequence for step in debugger.stepping(range(step_count)): result = self.step(result) if debugger.should_report(): debugger.default_report(f"step {step}/{step_count}") return result
def step_many(self, step_count: Optional[int] = None, debugger: Debugger = Debugger(enabled=False)): if step_count is None: steps = count() else: steps = range(step_count) debugger.reset() for _ in debugger.stepping(steps): self.step() if self.has_finished(): break if debugger.should_report(): debugger.default_report( f"remaining: {len(self.get_remaining_positions())}, " f"position: {self.position}") return self
def measure_distances( self, debugger: Debugger = Debugger(enabled=False), ) -> None: while debugger.step_if(self.stack): should_report = debugger.should_report() debugger.default_report_if( f"Seen {len(self.distances)}, {len(self.stack)} in stack, " f"target risk is {self.distances.get(self.target)}") if should_report: debugger.report(str(self)) state = self.stack.pop(0) if state.distance > self.distances[state.position]: continue for next_state in state.get_next_states(self): self.visit_state(next_state) if debugger.enabled: debugger.report(str(self))
def get_minimum_step_count( self, start: int = 0, return_to_start: bool = False, debugger: Debugger = Debugger(enabled=False), ) -> int: """ >>> Graph({ ... (0, 1): 2, (1, 0): 2, ... (0, 4): 2, (4, 0): 2, ... (1, 2): 6, (2, 1): 6, ... (2, 3): 2, (3, 2): 2, ... (3, 4): 8, (4, 3): 8, ... }).get_minimum_step_count() 14 """ nodes = self.get_nodes() if start not in nodes: raise Exception(f"Start {start} is not in nodes {nodes}") other_nodes = set(nodes) - {start} prefix = (start, ) if return_to_start: suffix = prefix else: suffix = () visit_orders = (prefix + permutation + suffix for permutation in itertools.permutations(other_nodes)) min_distance = None trip_distances_cache = {} for visit_order in debugger.stepping(visit_orders): distance = sum( self.get_shortest_distance( node_a, node_b, nodes, trip_distances_cache=trip_distances_cache) for node_a, node_b in get_windows(visit_order, 2)) if min_distance is None or distance < min_distance: min_distance = distance if debugger.should_report(): debugger.default_report(f"min distance: {min_distance}") return min_distance
def apply_extended( self, state: Optional[StateExtended] = None, debugger: Debugger = Debugger(enabled=False), ) -> StateExtended: """ >>> def check(instructions_text, state_values=None, program_counter=0): ... _state = InstructionSetExtended\\ ... .from_instructions_text(instructions_text)\\ ... .apply_extended(StateExtended( ... state_values or {}, program_counter)) ... # noinspection PyUnresolvedReferences ... values = { ... name: value ... for name, value in _state.values.items() ... if value ... } ... return values, _state.program_counter >>> check( ... "cpy 2 a\\n" ... "tgl a\\n" ... "tgl a\\n" ... "tgl a\\n" ... "cpy 1 a\\n" ... "dec a\\n" ... "dec a\\n" ... ) ({'a': 3}, 7) """ if state is None: state = StateExtended() state.instructions = list(self.instructions) debugger.reset() while debugger.step_if( 0 <= state.program_counter < len(state.instructions)): self.step(state) if debugger.should_report(): debugger.default_report( f"values: {state.values}, pc: {state.program_counter}") return state
def get_min_house_number_with_at_least_present_count( self, min_present_count: int, debugger: Debugger = Debugger(enabled=False) ) -> int: """ >>> Santa().get_min_house_number_with_at_least_present_count(100) 6 """ max_present_count_seen = None debugger.reset() for house_number in debugger.stepping(count(1)): present_count = self.get_house_present_count(house_number) if present_count >= min_present_count: return house_number if max_present_count_seen is None \ or present_count > max_present_count_seen: max_present_count_seen = present_count if debugger.should_report(): debugger.default_report( f"max presents: {max_present_count_seen}" f"/{min_present_count}")
def get_all_possible_chains( self, start: str, prune: Optional[Callable[[str], bool]] = None, debugger: Debugger = Debugger(enabled=False), ) -> Iterable[Tuple[str, ...]]: """ >>> list(MachineExtended().get_all_possible_chains("")) [] >>> list(MachineExtended({'H': ['O']}).get_all_possible_chains("HH")) [('HH', 'OH'), ('HH', 'HO'), ('HH', 'OH', 'OO'), ('HH', 'HO', 'OO')] >>> list(MachineExtended({'H': ['O']}).get_all_possible_chains( ... "HH", lambda step: step != 'HO')) [('HH', 'OH'), ('HH', 'OH', 'OO')] >>> list(MachineExtended({'H': ['OH']}).get_all_possible_chains( ... "HH", lambda step: len(step) <= 4)) [('HH', 'OHH'), ('HH', 'HOH'), ('HH', 'OHH', 'OOHH'), ('HH', 'OHH', 'OHOH'), ('HH', 'HOH', 'OHOH'), ('HH', 'HOH', 'HOOH')] >>> list(MachineExtended({'H': ['O'], 'O': ['H']}) ... .get_all_possible_chains("HH")) [('HH', 'OH'), ('HH', 'HO'), ('HH', 'OH', 'OO'), ('HH', 'HO', 'OO'), ('HH', 'OH', 'OO', 'HO'), ('HH', 'HO', 'OO', 'OH')] """ stack = [(start, ())] debugger.reset() while stack: current, chain = stack.pop() next_chain = chain + (current, ) for next_step in self.get_all_possible_next_steps(current): if next_step in next_chain: continue if prune and not prune(next_step): continue yield next_chain + (next_step, ) stack.append((next_step, next_chain)) if debugger.should_report(): debugger.default_report(f"stack size: {len(stack)}, " f"end length: {len(next_chain[-1])}, " f"chain length: {len(next_chain)}")
def find_paths( self, start: Point2D = Point2D(0, 0), finish: Point2D = Point2D(3, 3), debugger: Debugger = Debugger(enabled=False), ) -> Iterable[str]: if start == finish: yield '' stack = [('', Point2D(0, 0))] debugger.reset() path_count = 0 largest_path = None while stack: path, position = stack.pop(0) path_door_states = self.get_path_door_states(path) for direction, door_is_open in path_door_states.items(): if not door_is_open: continue next_position = position.offset(direction.offset) if not self.is_position_valid(next_position): continue next_path = f"{path}{direction.value}" if next_position == finish: path_count += 1 largest_path = next_path yield next_path else: stack.append((next_path, next_position)) debugger.step() if debugger.should_report(): debugger.report( f"Step: {debugger.step_count}, time: " f"{debugger.pretty_duration_since_start}, stack: " f"{len(stack)}, paths: {path_count}, largest path: " f"{largest_path and len(largest_path)}, average speed: " f"{debugger.step_frequency}, recent speed: " f"{debugger.step_frequency_since_last_report}")