def get_index_at_position_after_shuffle_many( self, index, size, count, debugger: Debugger = Debugger(enabled=False), ): total_index = index shuffles = [ shuffle.make_get_index_at_position_after_shuffle(size) for shuffle in reversed(self.shuffles) ] for step in debugger.stepping(range(count)): for shuffle in shuffles: total_index = shuffle(total_index) if total_index == index: break debugger.default_report_if( f"Looking {int(step / count * 10000) / 100}% " f"(current is {total_index})...") else: return total_index cycle_length = step + 1 debugger.default_report(f"Found cycle of length {cycle_length}") for _ in debugger.stepping(range(count % cycle_length)): for shuffle in shuffles: total_index = shuffle(total_index) debugger.default_report_if( f"Finishing after cycle (current is {total_index})...") return total_index
def get_nth_phase_message( initial: Union[str, Iterable[int]], length: Optional[int] = None, count: int = 100, message_offset: int = 0, debugger: Debugger = Debugger(enabled=False), ) -> str: """ >>> get_nth_phase_message('12345678', count=4) '01029498' >>> get_nth_phase_message('80871224585914546619083218645595') '24176176' >>> get_nth_phase_message('19617804207202209144916044189917') '73745418' >>> get_nth_phase_message('69317163492948606335995924319873') '52432133' """ if length is None: if not hasattr(initial, '__str__'): raise Exception( f"Initial has no length capability, and no length was passed") length = len(initial) if isinstance(initial, str): initial = list(map(int, initial)) result = np.array(initial) # phase_patterns = list(map(list, get_phase_patterns(length))) debugger.default_report(f"Doing {count} iterations") for iteration in debugger.stepping(range(1, count + 1)): result = get_next_phase(result, length, debugger=debugger) debugger.default_report_if(f"Done {iteration}/{count}") return "".join(map(str, result[message_offset:message_offset + 8]))
def step_many(self, step_count: int, debugger: Debugger = Debugger(enabled=False)): """ >>> print("!" + Life.from_life_text( ... ".#.#.#\\n" ... "...##.\\n" ... "#....#\\n" ... "..#...\\n" ... "#.#..#\\n" ... "####..\\n" ... ).step_many(4).show()) !...... ...... ..##.. ..##.. ...... ...... """ current = self debugger.reset() for _ in debugger.stepping(range(step_count)): current = current.step() debugger.default_report_if() return current
def move_until_stop( self, debugger: Debugger = Debugger(enabled=False), ) -> Tuple[int, "Herd"]: """ >>> _step_count, final = Herd.from_herd_text(''' ... v...>>.vv> ... .vv>>.vv.. ... >>.>v>...v ... >>v>>.>.v. ... v>v.vv.v.. ... >.>>..v... ... .vv..>.>v. ... v.v..>>v.v ... ....v..v.> ... ''').move_until_stop() >>> _step_count 58 >>> print(final) :.>>v>vv.. ..v.>>vv.. ..>>v>>vv. ..>>>>>vv. v......>vv v>v....>>v vvv.....>> >vv......> .>v.vv.v.. """ moved = self for step_count in debugger.stepping(count(1)): previous = moved moved = moved.move() debugger.default_report_if("Searching...") if moved == previous: return step_count, previous
def find_first_synchronised_flash_step_index( self, debugger: Debugger = Debugger(enabled=False), ) -> int: """ >>> GridExtended.from_grid_text(''' ... 5483143223 ... 2745854711 ... 5264556173 ... 6141336146 ... 6357385478 ... 4167524645 ... 2176841721 ... 6882881134 ... 4846848554 ... 5283751526 ... ''').find_first_synchronised_flash_step_index() 195 """ grid = deepcopy(self) for step_index in debugger.stepping(count(1)): debugger.report_if(step_index) grid.step() if grid.did_all_flash: return step_index raise Exception("Run out of numbers!")
def get_all_possible_chains_to_target( self, start: str, target: str, allow_reverse: bool = True, debugger: Debugger = Debugger(enabled=False), ) -> Iterable[Tuple[str, ...]]: """ >>> list(MachineExtended().get_all_possible_chains_to_target( ... "a", "b", allow_reverse=False)) [] >>> list(MachineExtended({'H': ['O']}) ... .get_all_possible_chains_to_target( ... "HH", "OO", allow_reverse=False)) [('HH', 'OH', 'OO'), ('HH', 'HO', 'OO')] >>> list(MachineExtended({'H': ['OH']}) ... .get_all_possible_chains_to_target( ... "HH", "OHOH", allow_reverse=False)) [('HH', 'OHH', 'OHOH'), ('HH', 'HOH', 'OHOH')] >>> list(MachineExtended({'H': ['O'], 'O': ['H']}) ... .get_all_possible_chains_to_target( ... "HH", "OH", allow_reverse=False)) [('HH', 'OH'), ('HH', 'HO', 'OO', 'OH')] >>> list(MachineExtended({'OH': ['H']}) ... .get_all_possible_chains_to_target( ... "OHOH", "HH", allow_reverse=False)) [('OHOH', 'HOH', 'HH'), ('OHOH', 'OHH', 'HH')] >>> list(MachineExtended().get_all_possible_chains_to_target("a", "b")) [] >>> list(MachineExtended({'H': ['O']}) ... .get_all_possible_chains_to_target("HH", "OO")) [('HH', 'OH', 'OO'), ('HH', 'HO', 'OO')] >>> list(MachineExtended({'H': ['OH']}) ... .get_all_possible_chains_to_target("HH", "OHOH")) [('OHOH', 'HOH', 'HH'), ('OHOH', 'OHH', 'HH')] >>> list(MachineExtended({'H': ['O'], 'O': ['H']}) ... .get_all_possible_chains_to_target("HH", "OH")) [('HH', 'OH'), ('HH', 'HO', 'OO', 'OH')] """ if len(target) < len(start): if self.are_substitutions_not_decreasing(): raise Exception( f"Cannot go from length {len(start)} to length " f"{len(target)}, as substitutions don't decrease") elif len(target) > len(start): if self.are_substitutions_not_increasing(): raise Exception( f"Cannot go from length {len(start)} to length " f"{len(target)}, as substitutions don't increase") if allow_reverse: if len(target) > len(start): yield from self.reverse().get_all_possible_chains_to_target( start=target, target=start, debugger=debugger) return prune = self.get_prune(target) for chain in debugger.stepping( self.get_all_possible_chains(start, prune, debugger=debugger)): if chain[-1] == target: yield chain
def step_many( self, algorithm: IEAlgorithm, count: int, debugger: Debugger = Debugger(enabled=False), ) -> "Image": """ >>> _algorithm = IEAlgorithm.from_mapping_text( ... "..#.#..#####.#.#.#.###.##.....###.##.#..###.####..#####..#...." ... "#..#..##..###..######.###...####..#..#####..##..#.#####...##.#" ... ".#..#.##..#.#......#.###.######.###.####...#.##.##..#..#..####" ... "#.....#.#....###..#.##......#.....#..#..#..##..#...##.######.#" ... "###.####.#.#...#.......#..#.#.#...####.##.#......#..#...##.#.#" ... "#..#...##.#.##..###.#......#.#.......#.#.#.####.###.##...#...." ... ".####.#..#..#.##.#....##..#.####....##...##..#...#......#.#..." ... "....#.......##..####..#...#.#.#...##..#.#..###..#####........#" ... "..####......#..#" ... ) >>> image = Image.from_image_text(''' ... #..#. ... #.... ... ##..# ... ..#.. ... ..### ... ''') >>> print(":", image.step_many(_algorithm, 1)) : ......... ..##.##.. .#..#.#.. .##.#..#. .####..#. ..#..##.. ...##..#. ....#.#.. ......... >>> print(":", image.step_many(_algorithm, 2)) : ........... ........#.. ..#..#.#... .#.#...###. .#...##.#.. .#.....#.#. ..#.#####.. ...#.#####. ....##.##.. .....###... ........... """ result = self debugger.default_report(f"Stepping {count} times") for step in debugger.stepping(range(count)): result = result.step(algorithm) debugger.default_report_if( f"Stepped {step}/{count} times, {result.light_pixel_count} " f"lights in result, size of {tuple(result.boundary.size)}" ) return result
def apply( self, reactor: Reactor, debugger: Debugger = Debugger(enabled=False), ) -> Reactor: for step_index, step in debugger.stepping(enumerate(self.steps, 1)): step.apply(reactor) debugger.default_report_if( f"After {step_index} steps: {reactor.cube_count} cubes are on") return reactor
def get_next_password( self, debugger: Debugger = Debugger(enabled=False), ) -> 'Password': """ >>> Password('abcdefgh').get_next_password() 'abcdffaa' >>> Password('ghijklmn').get_next_password() 'ghjaabcc' """ debugger.reset() for password in debugger.stepping(self.get_next_password_candidates()): if password.is_valid(): return password debugger.default_report_if(password)
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 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 get_lowest_index_with_zeros( self, start: int = 1, zero_count: int = 5, debugger: Debugger = Debugger(enabled=False)) -> int: """ >>> Hasher("abcdef").get_lowest_index_with_zeros() 609043 >>> Hasher("pqrstuv").get_lowest_index_with_zeros() 1048970 """ prefix = '0' * zero_count debugger.reset() for index in debugger.stepping(count(start)): if self.does_index_give_hash_prefix(index, prefix): return index debugger.default_report_if(index)
def apply( self, step_set: RebootStepSet, debugger: Debugger = Debugger(enabled=False), ) -> "RegionSet": region_class = self.get_region_class() for index, step in debugger.stepping(enumerate(step_set.steps, 1)): step_region = region_class.from_step(step) if step.set_to_on: self.add(step_region) else: self.remove(step_region) debugger.default_report_if( f"Done {index}/{len(step_set.steps)}, " f"{len(self.regions)} regions, {len(self)} total cubes") return self
def get_code( self, index: int, debugger: Debugger = Debugger(enabled=False), ) -> int: """ >>> list(map(Grid().get_code, range(1, 5))) [20151125, 31916031, 18749137, 16080970] """ max_precomputed_index = self.get_max_precomputed_index(index) code = self.pre_computed_codes[max_precomputed_index] for computing_index \ in debugger.stepping(range(index - max_precomputed_index)): debugger.default_report_if(computing_index) code *= self.multiplier code %= self.modulo return code
def apply( self, matrix: Optional[MatrixT] = None, debugger: Debugger = Debugger(enabled=False) ) -> MatrixT: """ >>> InstructionSet.from_instructions_text( ... "turn on 0,0 through 999,999").apply().get_brightness_total() 2000000 """ if matrix is None: matrix_class = self.get_matrix_class() matrix = matrix_class() debugger.reset() for instruction in debugger.stepping(self.instructions): instruction.apply(matrix) debugger.default_report_if() return matrix
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 get_message_after_index( initial: str, debugger: Debugger = Debugger(enabled=False), ) -> str: """ >>> get_message_after_index("03036732577212944063491565474664") 84462026 """ start_index = int(initial[:7]) original_length = len(initial) length = original_length * 10000 skip_lengths_count = math.floor(start_index / original_length) lengths_count = int(length / original_length - skip_lengths_count) skipped_phase = list(map(int, initial)) * lengths_count skipped_offset = start_index - skip_lengths_count * original_length skipped_phase = skipped_phase[skipped_offset:] result = skipped_phase debugger.default_report(f"Looking for message skipping {start_index}") for _ in debugger.stepping(range(100)): result = get_next_phase_after_index(result, start_index) debugger.default_report_if(f"So far: {''.join(map(str, result[:8]))}") return "".join(map(str, result[:8]))
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_next_phase( phase: np.ndarray, length: int, phase_patterns: Optional[List[np.ndarray]] = None, debugger: Debugger = Debugger(enabled=False), ) -> List[int]: """ >>> "".join(map(str, get_next_phase(np.array(list(map(int, '12345678'))), 8))) '48226158' >>> "".join(map(str, get_next_phase(np.array(list(map(int, '48226158'))), 8))) '34040438' >>> "".join(map(str, get_next_phase(np.array(list(map(int, '34040438'))), 8))) '03415518' >>> "".join(map(str, get_next_phase(np.array(list(map(int, '03415518'))), 8))) '01029498' """ # if phase_patterns is None: # phase_patterns = get_phase_patterns(length) if phase_patterns is None: indexes_and_phase_patterns = zip_longest( range(length), [], fillvalue=None, ) else: indexes_and_phase_patterns = enumerate(phase_patterns) next_phase = [] for index, phase_pattern in debugger.stepping(indexes_and_phase_patterns): next_phase.append( get_element_for_next_phase(phase, length, index, phase_pattern=phase_pattern)) debugger.default_report_if( f"{index}/{length} ({index * 100 // length}%)") return next_phase