示例#1
0
 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
示例#2
0
    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!")
示例#3
0
    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 get_min_step_count_to_target(
        self,
        start: str,
        target: str,
        debugger: Debugger = Debugger(enabled=False)) -> int:
        """
        >>> MachineExtended.from_substitutions_text(
        ...     "e => H\\n"
        ...     "e => O\\n"
        ...     "H => HO\\n"
        ...     "H => OH\\n"
        ...     "O => HH\\n"
        ... ).get_min_step_count_to_target("e", "HOH")
        3
        >>> MachineExtended.from_substitutions_text(
        ...     "e => H\\n"
        ...     "e => O\\n"
        ...     "H => HO\\n"
        ...     "H => OH\\n"
        ...     "O => HH\\n"
        ... ).get_min_step_count_to_target("e", "HOHOHO")
        6
        """
        def min_length_report_format(_debugger, message):
            return f"min length: {min_length}, {message}"

        min_length = None
        with debugger.adding_extra_report_format(min_length_report_format):
            for chain in self.get_all_possible_chains_to_target(
                    start, target, debugger=debugger):
                chain_length = len(chain) - 1
                if min_length is None or chain_length < min_length:
                    min_length = chain_length

        return min_length
    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 get_index_cycle(
         self,
         index,
         size,
         max_count=None,
         debugger: Debugger = Debugger(enabled=False),
 ):
     """
     >>> list(ShufflesExtended.parse(
     ...         "deal into new stack\\n"
     ...         "cut -2\\n"
     ...         "deal with increment 7\\n"
     ...         "cut 8\\n"
     ...         "cut -4\\n"
     ...         "deal with increment 7\\n"
     ...         "cut 3\\n"
     ...         "deal with increment 9\\n"
     ...         "deal with increment 3\\n"
     ...         "cut -1\\n"
     ... ).get_index_cycle(0, 10))
     [9, 6, 7, 0]
     >>> list(ShufflesExtended.parse(
     ...         "deal into new stack\\n"
     ...         "cut -2\\n"
     ...         "deal with increment 7\\n"
     ...         "cut 8\\n"
     ...         "cut -4\\n"
     ...         "deal with increment 7\\n"
     ...         "cut 3\\n"
     ...         "deal with increment 9\\n"
     ...         "deal with increment 3\\n"
     ...         "cut -1\\n"
     ... ).get_index_cycle(1, 10))
     [2, 5, 4, 1]
     >>> list(ShufflesExtended.parse(
     ...         "deal into new stack\\n"
     ...         "cut -2\\n"
     ...         "deal with increment 7\\n"
     ...         "cut 8\\n"
     ...         "cut -4\\n"
     ...         "deal with increment 7\\n"
     ...         "cut 3\\n"
     ...         "deal with increment 9\\n"
     ...         "deal with increment 3\\n"
     ...         "cut -1\\n"
     ... ).get_index_cycle(3, 10))
     [8, 3]
     """
     if max_count is None:
         max_count = size
     search_index = index
     for step in range(max_count):
         search_index = self.get_index_at_position_after_shuffle(
             search_index, size)
         yield search_index
         if search_index == index:
             break
         debugger.default_report_if("Looking...")
 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_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
示例#9
0
    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(self, debugger: Debugger = Debugger(enabled=False)) -> Paths:
        def reporting_format(_: Debugger, message: str) -> str:
            return (
                f"{message} ({len(paths)} found, {len(seen)} seen, "
                f"{len(stack)} in stack)"
            )

        paths = []
        stack: List[CaveFinderStateT] = [self.get_state_class().make_initial()]
        seen = {stack[0]}
        with debugger.adding_extra_report_format(reporting_format):
            debugger.default_report("Looking...")
            while debugger.step_if(stack):
                debugger.default_report_if("Looking...")
                state = stack.pop(0)
                for next_state in state.get_next_states(self.system.graph):
                    if next_state in seen:
                        continue
                    seen.add(next_state)
                    if next_state.is_terminal:
                        paths.append(next_state.path)
                        continue
                    stack.append(next_state)
            debugger.default_report("Finished looking")

        return paths
 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()
示例#12
0
    def solve(
            self,
            debugger: Debugger = Debugger(enabled=False),
    ) -> "QuantumGameSearch":
        while not debugger.step_if(self.finished):
            self.advance_once()
            debugger.default_report_if(
                f"Done {self.move_count} moves, with "
                f"{len(self.residual_state_counts)} remaining states, "
                f"currently {self.player_1_wins} vs {self.player_2_wins}")

        return self
示例#13
0
 def get_disk_checksum(
     self,
     size: int,
     initial: str,
     debugger: Debugger,
 ) -> str:
     """
     >>> DataGenerator().get_disk_checksum(20, "10000")
     '01100'
     """
     debugger.report_if("Getting disk checksum")
     disk = self.fill_disk(size, initial, debugger=debugger.sub_debugger())
     return self.get_checksum(disk, debugger=debugger.sub_debugger())
    def __add__(
            self,
            other: "ModuloShuffle",
            debugger: Debugger = Debugger(enabled=False),
    ) -> "ModuloShuffle":
        """
        >>> ShufflesExtended.parse('''
        ...     deal with increment 7
        ...     deal into new stack
        ...     deal into new stack
        ... ''').to_modulo_shuffle_for_size(10).shuffle_deck(tuple(range(10)))
        (0, 3, 6, 9, 2, 5, 8, 1, 4, 7)
        >>> ShufflesExtended.parse('''
        ...     cut 6
        ...     deal with increment 7
        ...     deal into new stack
        ... ''').to_modulo_shuffle_for_size(10).shuffle_deck(tuple(range(10)))
        (3, 0, 7, 4, 1, 8, 5, 2, 9, 6)
        >>> ShufflesExtended.parse('''
        ...     deal with increment 7
        ...     deal with increment 9
        ...     cut -2
        ... ''').to_modulo_shuffle_for_size(10).shuffle_deck(tuple(range(10)))
        (6, 3, 0, 7, 4, 1, 8, 5, 2, 9)
        >>> ShufflesExtended.parse(
        ...     "deal into new stack\\n"
        ...     "cut -2\\n"
        ...     "deal with increment 7\\n"
        ...     "cut 8\\n"
        ...     "cut -4\\n"
        ...     "deal with increment 7\\n"
        ...     "cut 3\\n"
        ...     "deal with increment 9\\n"
        ...     "deal with increment 3\\n"
        ...     "cut -1\\n"
        ... ).to_modulo_shuffle_for_size(10).shuffle_deck(tuple(range(10)))
        (9, 2, 5, 8, 1, 4, 7, 0, 3, 6)
        """
        if self.size != other.size:
            raise Exception(f"Cannot combine shuffles with different sizes")

        debugger.report(f"Add {self} + {other}")

        cls = type(self)
        # noinspection PyArgumentList
        return cls(
            factor=(other.factor * self.factor) % self.size,
            offset=(other.offset * self.factor + self.offset) % self.size,
            size=self.size,
        )
示例#15
0
    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 find_first_valid_input(
        digits: Iterable[int],
        debugger: Debugger = Debugger(enabled=False),
) -> int:
    def get_next_chains(
        _chains: Iterable[Tuple[InputList, int]],
        _index: int,
    ) -> Iterable[Tuple[InputList, int]]:
        for previous_inputs, previous_state in debugger.stepping(_chains):
            if debugger.should_report():
                debugger.default_report(
                    f"Looking at {''.join(map(str, previous_inputs))}, giving "
                    f"{run_program_optimised(previous_inputs, True)}/"
                    f"{run_program_optimised_list(previous_inputs, True)}", )
            for next_input in digits:
                state = get_program_optimised_state(
                    previous_state,
                    _index,
                    next_input,
                )
                if state >= AAC[_index]:
                    continue
                yield previous_inputs + [next_input], state

    chains: Iterable[Tuple[InputList, int]] = (([], 0) for _ in range(1))
    for index in range(14):
        chains = get_next_chains(chains, index)

    valid_inputs = (_input for _input, state in chains if not state)

    valid_input = next(iter(valid_inputs), None)
    if valid_input is None:
        raise Exception(f"Could not a valid input")

    return int("".join(map(str, valid_input)))
示例#17
0
 def from_steps_text(
         cls,
         steps_text: str,
         debugger: Debugger = Debugger(enabled=False),
 ) -> "Region2DSet":
     steps = RebootStepSet.from_steps_text(steps_text)
     return cls().apply(steps, debugger=debugger)
示例#18
0
    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 find_min_target_distance_for_cavern(
         cls,
         cavern: Cavern,
         debugger: Debugger = Debugger(enabled=False),
 ) -> int:
     measurer = cls.from_cavern(cavern)
     return measurer.find_min_target_distance(debugger=debugger)
示例#20
0
    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 apply(
            self,
            steps: RebootStepSetT,
            debugger: Debugger = Debugger(enabled=False),
    ) -> "Reactor":
        steps.apply(self, debugger=debugger)

        return self
 def get_index_at_position_after_shuffle_many(
         self,
         index,
         count,
         debugger: Debugger = Debugger(enabled=False),
 ):
     return (self.__mul__(count, debugger=debugger))\
         .get_index_at_position_after_shuffle(index)
示例#23
0
    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 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 find_min_target_distance(
         self,
         debugger: Debugger = Debugger(enabled=False),
 ) -> int:
     self.measure_distances(debugger=debugger)
     if self.target not in self.distances:
         raise Exception(
             f"Could not find target distance, but found distances for "
             f"{len(self.distances)}, max point was {max(self.distances)}")
     return self.distances[self.target]
    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 get_card_at_position_after_shuffle_many(
         self,
         deck,
         count,
         index,
         debugger: Debugger = Debugger(enabled=False),
 ):
     index = self.get_index_for_position_after_many_shuffles(
         index, count, len(deck), debugger=debugger)
     return deck[index]
 def get_length_after_steps(
         self,
         sequence: str,
         step_count: int = 40,
         debugger: Debugger = Debugger(enabled=False),
 ) -> int:
     """
     >>> LookAndSay().get_length_after_steps("1", 5)
     6
     """
     return len(self.step_many(sequence, step_count, debugger=debugger))
示例#29
0
 def default_solve(self, _input=None, debugger=None):
     """Convenient method to call `solve` with the input from the disk"""
     if _input is None:
         _input = self.input
     if debugger is None:
         from aox.challenge import Debugger
         debugger = Debugger(enabled=False)
     if has_method_arguments(self.solve, "debugger"):
         return self.solve(_input, debugger=debugger)
     else:
         # noinspection PyArgumentList
         return self.solve(_input, debug=debugger)
示例#30
0
 def test_adding_extra_report_format_multiple(self):
     with self.assert_output("Message: [<(Hello)>], extra: extra\n"):
         debugger = Debugger()
         with debugger.adding_extra_report_format(
                 self.wrapping_debugger_format):
             with debugger.adding_extra_report_format(
                     self.wrapping_debugger_format_2):
                 with debugger.adding_extra_report_format(
                         self.wrapping_debugger_format_3):
                     debugger.default_report("Hello")