示例#1
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")
示例#2
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 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 __mul__(
            self,
            count: int,
            debugger: Debugger = Debugger(enabled=False),
    ) -> "ModuloShuffle":
        cls = type(self)
        if count < 0:
            raise Exception(f"Cannot calculate negative count shuffle")

        # noinspection PyArgumentList
        total = cls(
            factor=1,
            offset=0,
            size=self.size,
        )
        power_shuffle = self
        power = 1
        remaining_count = count
        debugger.default_report(
            f"Remaining {math.ceil(math.log2(remaining_count))} rounds "
            f"({remaining_count}, {bin(remaining_count)})")
        while debugger.step_if(remaining_count):
            debugger.default_report_if(
                f"Remaining {math.ceil(math.log2(remaining_count))} rounds "
                f"({remaining_count}, {bin(remaining_count)})")
            if remaining_count % 2:
                total = total + power_shuffle
            remaining_count //= 2
            power *= 2
            power_shuffle = power_shuffle + power_shuffle

        return total
示例#5
0
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 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 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
示例#8
0
    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
示例#9
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")
    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
示例#12
0
    def find_min_mana_necessary(
            self,
            debugger: Debugger = Debugger(enabled=False),
    ) -> int:
        def reporting_format(_: Debugger, message: str) -> str:
            if min_mana_spent is None:
                min_mana_spent_str = "no winner yet"
            else:
                min_mana_spent_str = f"best is {min_mana_spent}"
            return f"{message} ({min_mana_spent_str}, {len(stack)} in stack)"

        with debugger.adding_extra_report_format(reporting_format):
            stack = [deepcopy(self)]
            min_mana_spent = None
            min_mana_game = None
            debugger.default_report_if("Searching for a winning game...")
            while debugger.step_if(stack):
                debugger.default_report_if("Searching for a winning game...")
                game = stack.pop(0)
                if min_mana_spent is not None \
                        and game.player.mana_spent >= min_mana_spent:
                    continue
                next_games = game.get_next_games(debugger)
                for next_game in next_games:
                    if (min_mana_spent is not None
                            and next_game.player.mana_spent >= min_mana_spent):
                        continue
                    if next_game.winner == CharacterEnum.Player:
                        min_mana_spent = next_game.player.mana_spent
                        min_mana_game = next_game
                        debugger.report(f"Better game found: {min_mana_spent}")
                    stack.append(next_game)

            debugger.default_report(f"Finished searching")

        if min_mana_spent is None:
            raise Exception(f"Could not find a winning game")

        options_played_str = ', '.join(
            option.name for option in min_mana_game.options_played
            if isinstance(option, SpellEnum))
        debugger.report(f"Min mana game moves: {options_played_str}")

        return min_mana_spent
    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
示例#14
0
    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_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_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)}")
示例#17
0
 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 find_scanners_positions(
        self,
        debugger: Debugger = Debugger(enabled=False),
    ) -> List[Tuple[BeaconT, ScannerT]]:
        """
        >>> # noinspection PyUnresolvedReferences
        >>> [_position for _position, _ in Scanner2DSet.from_scanners_text('''
        ...     --- scanner 0 ---
        ...     0,2
        ...     4,1
        ...     3,3
        ...
        ...     --- scanner 1 ---
        ...     -1,-1
        ...     -5,0
        ...     -2,1
        ... ''').find_scanners_positions()]
        [Beacon2D(x=0, y=0), Beacon2D(x=5, y=2)]
        """
        if not self.scanners:
            return []

        beacon_class = self.get_beacon_class()
        first_scanner = self.scanners[0]
        positions_and_scanners_by_scanner: Dict[int, Tuple[BeaconT, ScannerT]] \
            = {
                id(first_scanner):
                (beacon_class.get_zero_point(), first_scanner)
            }
        found_scanners = [first_scanner]
        remaining_scanners = self.scanners[1:]
        debugger.default_report(
            f"Looking for {len(self.scanners) - 1} positions, found "
            f"{len(found_scanners) - 1}")
        while remaining_scanners:
            for other_index, other in enumerate(remaining_scanners):
                for found_index, found_scanner \
                        in debugger.step_if(enumerate(found_scanners)):
                    debugger.default_report_if(
                        f"Looking for {len(self.scanners) - 1} positions, "
                        f"found {len(found_scanners) - 1}")
                    found_position, found_reoriented = \
                        positions_and_scanners_by_scanner[id(found_scanner)]
                    position_and_scanner = \
                        found_reoriented.find_other_scanner_position(
                            other, self.min_overlap,
                        )
                    if not position_and_scanner:
                        continue

                    position, reoriented = position_and_scanner
                    position = position.offset(found_position)
                    found_scanners.append(other)
                    positions_and_scanners_by_scanner[id(other)] = \
                        position, reoriented
                    remaining_scanners.remove(other)
                    break
                else:
                    continue

                break
            else:
                raise Exception(f"Could not find any remaining scanner "
                                f"({len(remaining_scanners)} remaining out of "
                                f"{len(self.scanners)})")

        return [
            positions_and_scanners_by_scanner[id(scanner)]
            for scanner in self.scanners
        ]