Gyrfalcon's recent activity

  1. Comment on Announcing Tildes' Make Something Month (Timasomo) for 2023! in ~creative.timasomo

    Gyrfalcon
    Link
    I was not terribly successful on this last year so I have two ideas, both aiming to scale back a bit: Complete my project from last year, which was a round shield inspired by Viking round shields...

    I was not terribly successful on this last year so I have two ideas, both aiming to scale back a bit:

    1. Complete my project from last year, which was a round shield inspired by Viking round shields
    2. Hammer out a good dairy free caramel recipe. I already have a dairy free fudge recipe which I make at Christmas every year. There's also caramels, but my SO has a dairy allergy so she cannot partake, so I would like to work out a good caramel recipe. I have made previous attempts at this, but they all fell victim to various mishaps, ranging from becoming overcooked into burnt hard candy to having a fly nosedive into the cooking caramel

    This will be made a little more complicated by having a week of business travel as well as a family visit at the end of the month, but I am hoping to accomplish something fun without setting too high of expectations for myself.

    2 votes
  2. Comment on Monitor recommendations? in ~tech

    Gyrfalcon
    Link Parent
    That's super weird, I have a 1440p monitor with an M1 Mac for work and a Linux desktop for personal and I've never noticed this. My screen is 27" and I use it over HDMI for the Mac, is that...

    That's super weird, I have a 1440p monitor with an M1 Mac for work and a Linux desktop for personal and I've never noticed this. My screen is 27" and I use it over HDMI for the Mac, is that similar to your setup? Either way, seems like this could be a deal breaker or a complete non issue for OP depending on them as an individual.

    1 vote
  3. Comment on Does anyone actually like canned beans? in ~food

    Gyrfalcon
    Link
    I honestly don't think I've ever cooked a dried bean? My family always did canned for convenience, and with my SO having food allergies, dried beans are much more likely to be cross contaminated...

    I honestly don't think I've ever cooked a dried bean? My family always did canned for convenience, and with my SO having food allergies, dried beans are much more likely to be cross contaminated and are almost never labeled as gluten free, so it's just not worth the additional effort and stress to find ones that work for her.

    In conclusion, maybe dried beans would be a flavor revolution for me, but I love beans even from cans so I kinda doubt it.

    1 vote
  4. Comment on Day 10: Cathode-Ray Tube in ~comp

    Gyrfalcon
    Link
    This one I think I got a good handle on. I had some trouble with part 2 because I initially thought I would need to do something special to handle the order of events, but it turned out that...

    This one I think I got a good handle on. I had some trouble with part 2 because I initially thought I would need to do something special to handle the order of events, but it turned out that didn't matter, but what did matter was me forgetting that upper bounds are non inclusive and also that formatting multiline strings can be hard.

    Part 1 and 2, Python
    current_dir = os.path.realpath(os.path.dirname(__file__))
    INPUT_FILE = "/".join([current_dir, "input.txt"])
    TEST_FILE_1 = "/".join([current_dir, "test1.txt"])
    TEST_RESULTS_1 = (
        13140,
        (   
            "##..##..##..##..##..##..##..##..##..##..\n"
            "###...###...###...###...###...###...###.\n"
            "####....####....####....####....####....\n"
            "#####.....#####.....#####.....#####.....\n"
            "######......######......######......####\n"
            "#######.......#######.......#######....."
        ),
    )
    
    
    def process_command(command: List[str], signal_strength: int) -> List[int]:
    
        if command[0] == "noop":
            return [signal_strength]
    
        return [signal_strength, signal_strength + int(command[1])]
    
    
    def run_computer(commands: List[List[str]]) -> List[int]:
        
        signal_history = [1]
        signal_strength = 1
    
        for command in commands:
            signal_history.extend(process_command(command, signal_strength))
            signal_strength = signal_history[-1]
    
        return signal_history
    
    
    def analyze_signal(signal_history: List[int]) -> int:
    
        return sum((signal_history[idx] * (idx + 1) for idx in range(19, 220, 40)))
    
    
    def draw_screen(signal_history: List[int]) -> str:
        
        pixels: List[str] = []
        for idx in range(240):
            if abs((len(pixels) % 40) - signal_history[idx]) < 2:
                pixels.append("#")
            else:
                pixels.append(".")
    
        grid = ["".join(pixels[0 + idx : 40 + idx]) for idx in range(0, 201, 40)]
    
        return "\n".join(grid)
    
    
    def main(filepath: str) -> Tuple[int, str]:
    
        commands = [line.split() for line in load_file.load_cleaned_lines(filepath)]
        signal_history = run_computer(commands)
    
        return (analyze_signal(signal_history), draw_screen(signal_history))
    
    1 vote
  5. Comment on Day 8: Treetop Tree House in ~comp

    Gyrfalcon
    Link Parent
    Okay, I think I see the main difference in how we approached the problem. You looked at even the first part from the tree perspective, whereas I looked at that from a row and column wise...

    Okay, I think I see the main difference in how we approached the problem. You looked at even the first part from the tree perspective, whereas I looked at that from a row and column wise perspective. Even with that I still think I would have made a bunch of loops rather than mapping things into lambdas, but I think that's more an issue of not actually spending that much time in Python and not really having a good handle on functional approaches than anything else.

    1 vote
  6. Comment on Day 6: Tuning Trouble in ~comp

    Gyrfalcon
    Link Parent
    Well I definitely appreciate you posting, even though I can't say I understand what is going on all that much better after reading you describe what it is.

    Well I definitely appreciate you posting, even though I can't say I understand what is going on all that much better after reading you describe what it is.

    1 vote
  7. Comment on Day 9: Rope Bridge in ~comp

    Gyrfalcon
    Link
    Late, long, and clunky, but correct solution! Ended up refactoring for part 2 to make things configurable which helped marginally, but not as much as one might hope, since that wasn't really the...

    Late, long, and clunky, but correct solution! Ended up refactoring for part 2 to make things configurable which helped marginally, but not as much as one might hope, since that wasn't really the part I'm not proud of anyway lol.

    Part 1 and 2, Python
    current_dir = os.path.realpath(os.path.dirname(__file__))
    INPUT_FILE = "/".join([current_dir, "input.txt"])
    TEST_FILE_1 = "/".join([current_dir, "test1.txt"])
    TEST_RESULTS_1 = (13, 1)
    TEST_FILE_2 = "/".join([current_dir, "test2.txt"])
    TEST_RESULTS_2 = (88, 36)
    
    
    def tail_move(head_pos, tail_pos: Tuple[int, int]) -> Tuple[int, int]:
        if head_pos[0] == tail_pos[0]:
            if head_pos[1] == tail_pos[1] + 2:
                return (tail_pos[0], tail_pos[1] + 1)
            if head_pos[1] == tail_pos[1] - 2:
                return (tail_pos[0], tail_pos[1] - 1)
            return tail_pos
        
        if head_pos[1] == tail_pos[1]:
            if head_pos[0] == tail_pos[0] + 2:
                return (tail_pos[0] + 1, tail_pos[1])
            if head_pos[0] == tail_pos[0] - 2:
                return (tail_pos[0] - 1, tail_pos[1])
            return tail_pos
        
        if (
            (head_pos[0] == tail_pos[0] + 2 and head_pos[1] == tail_pos[1] + 1) 
            or (head_pos[0] == tail_pos[0] + 1 and head_pos[1] == tail_pos[1] + 2)
            or (head_pos[0] == tail_pos[0] + 2 and head_pos[1] == tail_pos[1] + 2)
        ):
            return (tail_pos[0] + 1, tail_pos[1] + 1)
        
        if (
            (head_pos[0] == tail_pos[0] - 2 and head_pos[1] == tail_pos[1] + 1) 
            or (head_pos[0] == tail_pos[0] - 1 and head_pos[1] == tail_pos[1] + 2)
            or (head_pos[0] == tail_pos[0] - 2 and head_pos[1] == tail_pos[1] + 2)
        ):
            return (tail_pos[0] - 1, tail_pos[1] + 1)
        
        if (
            (head_pos[0] == tail_pos[0] + 2 and head_pos[1] == tail_pos[1] - 1) 
            or (head_pos[0] == tail_pos[0] + 1 and head_pos[1] == tail_pos[1] - 2)
            or (head_pos[0] == tail_pos[0] + 2 and head_pos[1] == tail_pos[1] - 2)
        ):
            return (tail_pos[0] + 1, tail_pos[1] - 1)
        
        if (
            (head_pos[0] == tail_pos[0] - 2 and head_pos[1] == tail_pos[1] - 1) 
            or (head_pos[0] == tail_pos[0] - 1 and head_pos[1] == tail_pos[1] - 2)
            or (head_pos[0] == tail_pos[0] - 2 and head_pos[1] == tail_pos[1] - 2)
        ):
            return (tail_pos[0] - 1, tail_pos[1] - 1)
    
        return tail_pos
    
    
    def run_commands(commands: List[List[str]], rope_length: int) -> Set[Tuple[int, int]]:
    
        rope_pos = [(0, 0) for _ in range(rope_length)]
        visited = {rope_pos[-1]}
    
        moves = {
            "R": lambda pos: (pos[0] + 1, pos[1]),
            "L": lambda pos: (pos[0] - 1, pos[1]),
            "U": lambda pos: (pos[0], pos[1] + 1),
            "D": lambda pos: (pos[0], pos[1] - 1),
        }
    
        for command in commands:
            for _ in range(int(command[1])):
                rope_pos[0] = moves[command[0]](rope_pos[0])
                for idx in range(1, rope_length):
                    rope_pos[idx] = tail_move(rope_pos[idx - 1], rope_pos[idx])
                visited.add(rope_pos[-1])
    
        return visited
    
    
    def main(filepath: str) -> Tuple[int, int]:
    
        commands = [line.split() for line in load_file.load_cleaned_lines(filepath)]
    
        return (len(run_commands(commands, 2)), len(run_commands(commands, 10)))
    
    
    if __name__ == "__main__":
        part1, part2 = main(INPUT_FILE)
    
        print(part1)
        print(part2)
    
    1 vote
  8. Comment on Day 8: Treetop Tree House in ~comp

    Gyrfalcon
    Link Parent
    Could you share more about how you arrived at this solution? You allege that it is not optimal, which I can't say one way or the other, but I do find it elegant and concise - and something I never...

    Could you share more about how you arrived at this solution? You allege that it is not optimal, which I can't say one way or the other, but I do find it elegant and concise - and something I never would have come up with even if I had days to work on it.

    1 vote
  9. Comment on Day 8: Treetop Tree House in ~comp

    Gyrfalcon
    Link
    Well I certainly repeated myself in this code, and Python's "don't modify while iterating" concept is not one I am fond of, but it works. Part 1 and 2, Python Tree = namedtuple("Tree", ["height",...

    Well I certainly repeated myself in this code, and Python's "don't modify while iterating" concept is not one I am fond of, but it works.

    Part 1 and 2, Python
    Tree = namedtuple("Tree", ["height", "visible"])
    
    
    def process_grid(lines: List[str]) -> List[List[Tree]]:
    
        return [[Tree(int(height), False) for height in line] for line in lines]
    
    
    def determine_visible(grid: List[List[Tree]]):
    
        # Left to right
        for row_idx in range(len(grid)):
            max_height: int = -1
            for col_idx in range(len(grid[row_idx])):
                if grid[row_idx][col_idx].height > max_height:
                    max_height = grid[row_idx][col_idx].height
                    grid[row_idx][col_idx] = grid[row_idx][col_idx]._replace(visible=True)
    
        # Right to left
        for row_idx in range(len(grid)):
            max_height = -1
            for col_idx in range(len(grid[row_idx]) - 1, -1, -1):
                if grid[row_idx][col_idx].height > max_height:
                    max_height = grid[row_idx][col_idx].height
                    grid[row_idx][col_idx] = grid[row_idx][col_idx]._replace(visible=True)
    
        # top to bottom
        for row_idx in range(len(grid)):
            max_height = -1
            for col_idx in range(len(grid[row_idx])):
                if grid[col_idx][row_idx].height > max_height:
                    max_height = grid[col_idx][row_idx].height
                    grid[col_idx][row_idx] = grid[col_idx][row_idx]._replace(visible=True)
    
        # bottom to top
        for row_idx in range(len(grid)):
            max_height = -1
            for col_idx in range(len(grid[row_idx]) - 1, -1, -1):
                if grid[col_idx][row_idx].height > max_height:
                    max_height = grid[col_idx][row_idx].height
                    grid[col_idx][row_idx] = grid[col_idx][row_idx]._replace(visible=True)
    
    
    def viewing_score(grid: List[List[Tree]], row: int, col: int) -> int:
    
        curr_tree = grid[row][col]
    
        up = 0
        for row_idx in range(row - 1, -1, -1):
            if grid[row_idx][col].height >= curr_tree.height:
                up += 1
                break
            up += 1
    
        down = 0
        for row_idx in range(row + 1, len(grid)):
            if grid[row_idx][col].height >= curr_tree.height:
                down += 1
                break
            down += 1
    
        left = 0
        for col_idx in range(col - 1, -1, -1):
            if grid[row][col_idx].height >= curr_tree.height:
                left += 1
                break
            left += 1
    
        right = 0
        for col_idx in range(col + 1, len(grid[row])):
            if grid[row][col_idx].height >= curr_tree.height:
                right += 1
                break
            right += 1
    
        return up * down * left * right
    
    
    def main(filepath: str) -> Tuple[int, int]:
    
        unprocessed_grid = load_file.load_cleaned_lines(filepath)
        grid = process_grid(unprocessed_grid)
        determine_visible(grid)
    
        return (
            sum((tree.visible for row in grid for tree in row)),
            max(
                (
                    viewing_score(grid, row_idx, col_idx)
                    for row_idx, row in enumerate(grid)
                    for col_idx, tree in enumerate(row)
                )
            ),
        )
    
    2 votes
  10. Comment on Day 7: No Space Left On Device in ~comp

    Gyrfalcon
    Link
    Well, today was definitely not a good day to try an object oriented approach while attempting to continue forcing static typing into Python, and you can see that in how long and clunky my code is....

    Well, today was definitely not a good day to try an object oriented approach while attempting to continue forcing static typing into Python, and you can see that in how long and clunky my code is. But, it works, and I'm going to keep telling myself that that is the only thing that matters.

    Parts 1 and 2, Python
    class FilesystemObject:
        def __init__(self, parent, name: str):
            self.name = name
            self.parent = parent  # Want to hint this, but get not defined error?
    
        @property
        def size(self) -> int:
            raise NotImplementedError
    
    
    class Directory(FilesystemObject):
        def __init__(
            self,
            parent,  # Want to hint this, but get not defined error?
            name: str,
            children: Optional[Dict[str, FilesystemObject]],
        ):  
            if children is None:
                self.children: Dict[str, FilesystemObject] = {}
            else:
                self.children = children
            super().__init__(parent, name)
    
        def add_child(self, child: FilesystemObject):
            self.children[child.name] = child
    
        @property
        def size(self) -> int:
            return sum((item.size for item in self.children.values()))
    
    
    class File(FilesystemObject):
        def __init__(self, parent: Optional[Directory], name: str, size: int):
            self._size = size
            super().__init__(parent, name)
    
        @property
        def size(self) -> int:
            return self._size
    
    
    def parse_commands(commands: List[str]) -> Directory:
        root = Directory(None, "/", None)
        cwd = root
        ls_mode = False
    
        for command in commands[1:]:
            breakdown = command.split()
            if breakdown[0] == "$":
                ls_mode = False
                if breakdown[1] == "cd":
                    if breakdown[2] == "..":
                        cwd = cast(Directory, cwd.parent)
                        continue
    
                    if breakdown[2] == "/" and root is not None:
                        cwd = root
                        continue
    
                    if breakdown[2] == "/" and root is None:
                        root = Directory(cwd, "/", None)
                        cwd = root
                        continue
    
                    cwd = cast(Directory, cwd.children[breakdown[2]])
    
                elif breakdown[1] == "ls":
                    ls_mode = (
                        True  # I don't think I need this but if we expand this I might
                    )
    
            elif ls_mode:
    
                if breakdown[0] == "dir":
                    cwd.add_child(Directory(cwd, breakdown[1], None))
                    continue
    
                cwd.add_child(File(cwd, breakdown[1], int(breakdown[0])))
    
            else:
                raise NotImplementedError
    
        return root
    
    
    def find_small(current: FilesystemObject) -> List[int]:
    
        if isinstance(current, File):
            return []
    
        if current.size <= 100000:
            out = [current.size]
            for child in cast(Directory, current).children.values():
                out.extend(find_small(child))
            return out
    
        out = []
        for child in cast(Directory, current).children.values():
            out.extend(find_small(child))
        return out
    
    
    def find_bigger(current: FilesystemObject, minimum: int) -> List[int]:
    
        if isinstance(current, File):
            return []
    
        if current.size >= minimum:
            out = [current.size]
            for child in cast(Directory, current).children.values():
                out.extend(find_bigger(child, minimum))
            return out
    
        return []
    
    
    def main(filepath: str) -> Tuple[int, int]:
    
        inputs = load_file.load_cleaned_lines(filepath)
        root_dir = parse_commands(inputs)
    
        target = 30000000 - (70000000 - root_dir.size)
    
        return (sum(find_small(root_dir)), min(find_bigger(root_dir, target)))
    
    2 votes
  11. Comment on Day 6: Tuning Trouble in ~comp

    Gyrfalcon
    Link Parent
    Do you have a link to where I could learn more about Python-ish? When I try to google it I just get a bunch of people talking about things that are like or similar to Python and not what you are...

    Do you have a link to where I could learn more about Python-ish? When I try to google it I just get a bunch of people talking about things that are like or similar to Python and not what you are using.

    2 votes
  12. Comment on Day 5: Supply Stacks in ~comp

    Gyrfalcon
    Link Parent
    An interesting idea, but I don't think assertions are accounted for when mypy parses the code. Something like this: instructions = [] indices = (1, 3, 5) for instruction in lines[line_num + 1 :]:...

    An interesting idea, but I don't think assertions are accounted for when mypy parses the code. Something like this:

    instructions = []
    indices = (1, 3, 5)
    for instruction in lines[line_num + 1 :]:
        test = tuple([int(instruction.strip().split()[idx]) for idx in indices])
        assert len(test) == 3
        instructions.append(test)
    

    ...yields this complaint:

    aoc2022/day5/solution.py:43: error: Incompatible return value type (got "Tuple[List[deque[str]], List[Tuple[int, ...]]]", expected "Tuple[List[deque[str]], List[Tuple[int, int, int]]]")  [return-value]
    

    I also tried hinting test as Tuple[int, int, int] but that got a type mismatch on assignment complaint. I think mypy will always assume that a tuple constructed from a loop or comprehension is indeterminate in size :/

    3 votes
  13. Comment on Day 6: Tuning Trouble in ~comp

    Gyrfalcon
    Link
    Well after yesterday saying that I had never done regular expressions in Python I saw a text matching challenge and tried it and then figured out that this is not, in fact, the sort of task that...

    Well after yesterday saying that I had never done regular expressions in Python I saw a text matching challenge and tried it and then figured out that this is not, in fact, the sort of task that regular expressions make easy. I ended up doing this a little differently than the other Python solutions I see so far today, not using either queues or sets, but getting to test drive for else syntax, which was neat. I also did it kind of a clunky, single use way for part one, and then wrote part 2 generally so I could replace my part 1 function, though I left it for posterity. I do think my solution has a useful benefit, in that if it finds, say that the last two characters in a long search window are the same, it will skip forward such that the window starts with what was previously the last character, since we know that all windows that include those two characters at the end will not be what we are looking for.

    Parts 1 and 2, Python
    def find_packet_marker(message: str) -> int:
        end = 3 
        while end < len(message):
            if message[end - 3] in message[end - 2 : end + 1]: 
                end += 1
                continue
    
            if message[end - 2] in message[end - 1 : end + 1]: 
                end += 2
                continue
    
            if message[end - 1] == message[end]:
                end += 3
                continue
    
            return end + 1 
    
        return -1
    
    
    def find_marker(message: str, marker_size: int) -> int:
    
        end = marker_size - 1 
        while end < len(message):
            for offset in range(marker_size - 1, 0, -1):
                if message[end - offset] in message[end - (offset - 1) : end + 1]: 
                    end += marker_size - offset
                    break
            else:
                return end + 1 
    
        return -1
    
    
    def main(filepath: str) -> Tuple[int, int]:
    
        message = load_file.load_cleaned_lines(filepath)[0]
        return (find_marker(message, 4), find_marker(message, 14))
    
    1 vote
  14. Comment on Day 5: Supply Stacks in ~comp

    Gyrfalcon
    Link
    Going to be honest, I saw a chance to whip out deque and I just went for it even though nothing in part 1 indicated that it couldn't be handled with a regular Python list. It was probably actually...

    Going to be honest, I saw a chance to whip out deque and I just went for it even though nothing in part 1 indicated that it couldn't be handled with a regular Python list. It was probably actually a hinderance overall, but I still enjoyed getting to play with it. You may also notice that I didn't use regex, which is because I have not really used it in Python and continue to get away with that.

    On the feedback front, if anyone here is familiar with mypy and it's enforcement of static typing, is there any good way to fill or construct a tuple using a loop while enforcing its length? I kept trying to do that with the translated instructions in my input parsing function but eventually gave up and hand wrote the bit since it was only 3 elements anyway.

    Parts 1 and 2
    def parse_input(
        lines: List[str],
    ) -> Tuple[List[deque[str]], List[Tuple[int, int, int]]]:
    
        line_num = 0 
        while lines[line_num] != "\n":
            line_num += 1
    
        num_stacks = int(lines[line_num - 1].split()[-1])
        crates: List[deque[str]] = [deque() for stack in range(num_stacks)]
        for layer in lines[line_num - 2 :: -1]:
            # doing this the lazy way because I think I'll get away with it
            for stack, location in enumerate(range(1, len(layer), 4)):
                if layer[location] == " ":
                    continue
    
                crates[stack].append(layer[location])
    
        instructions = []
        for instruction in lines[line_num + 1 :]: 
            # Is there a better way to do this while still enforcing tuple length?
            breakdown = instruction.strip().split()
            amount: int = int(breakdown[1])
            src: int = int(breakdown[3]) - 1 
            dst: int = int(breakdown[5]) - 1 
            instructions.append((amount, src, dst))
    
        return (crates, instructions)
    
    
    def apply_instruction(crates: List[deque[str]], instruction: Tuple[int, int, int]):
        for _ in range(instruction[0]):
            crates[instruction[2]].append(crates[instruction[1]].pop())
    
    
    def apply_fancy_instruction(
        crates: List[deque[str]], instruction: Tuple[int, int, int]
    ):
    
        hand = []
        for _ in range(instruction[0]):
            hand.append(crates[instruction[1]].pop())
    
        for _ in range(instruction[0]):
            crates[instruction[2]].append(hand.pop())
    
    
    def main(filepath: str) -> Tuple[str, str]:
    
        lines = load_file.load_lines(filepath)
        crates, instructions = parse_input(lines)
        crates_2 = deepcopy(crates)
        for instruction in instructions:
            apply_instruction(crates, instruction)
            apply_fancy_instruction(crates_2, instruction)
    
        return (
            "".join((crate[-1] for crate in crates)),
            "".join((crate[-1] for crate in crates_2)),
        )
    
    2 votes
  15. Comment on Day 4: Camp Cleanup in ~comp

    Gyrfalcon
    Link
    When I first read the problem description, I went to sets, just like I did yesterday and like it seems like many people did here. Then, I got worried that this was a trick, and that I was going to...

    When I first read the problem description, I went to sets, just like I did yesterday and like it seems like many people did here. Then, I got worried that this was a trick, and that I was going to create some kind of monstrosity with huge sets if I went that path, so I just went with keeping the high and low of each assignment and having to think about the conditionals for overlapping, which tripped me up for a little bit on part 2.

    Parts 1 and 2
    def create_assignments(pair: str) -> Tuple[Tuple[int, int], Tuple[int, int]]:
        first, second = pair.split(",")
        first_low = int(first.split("-")[0])
        first_high = int(first.split("-")[1])
        second_low = int(second.split("-")[0])
        second_high = int(second.split("-")[1])
        return ((first_low, first_high), (second_low, second_high))
    
    
    def are_completely_overlapping(
        assignments: Tuple[Tuple[int, int], Tuple[int, int]]
    ) -> bool:
        return (
            assignments[0][0] <= assignments[1][0]
            and assignments[0][1] >= assignments[1][1]
        ) or (
            assignments[1][0] <= assignments[0][0]
            and assignments[1][1] >= assignments[0][1]
        )   
    
    
    def are_overlapping(assignments: Tuple[Tuple[int, int], Tuple[int, int]]) -> bool:
        return (
            assignments[0][1] >= assignments[1][0]
            and assignments[0][0] <= assignments[1][1]
        )   
    
    
    def main(filepath: str) -> Tuple[int, int]:
        pairs = load_file.load_cleaned_lines(filepath)
        assignments = [create_assignments(pair) for pair in pairs]
        return (
            sum((are_completely_overlapping(assignment) for assignment in assignments)),
            sum((are_overlapping(assignment) for assignment in assignments)),
        ) 
    
    1 vote
  16. Comment on Day 3: Rucksack Reorganization in ~comp

    Gyrfalcon
    Link
    Happy with how this turned out, though I think I learned that I need to find a more elegant way to interact with Python's Set, especially given how handy I find it in general. Parts 1 and 2 import...

    Happy with how this turned out, though I think I learned that I need to find a more elegant way to interact with Python's Set, especially given how handy I find it in general.

    Parts 1 and 2
    import os
    from typing import Tuple
    from string import ascii_lowercase, ascii_uppercase
    from aoc2022.common import load_file
    
    current_dir = os.path.realpath(os.path.dirname(__file__))
    INPUT_FILE = "/".join([current_dir, "input.txt"])
    TEST_FILE_1 = "/".join([current_dir, "test1.txt"])
    TEST_RESULTS_1 = (157, 70)
    
    priorities = {
        character: priority + 1 for priority, character in enumerate(ascii_lowercase)
    }
    priorities.update(
        {character: priority + 27 for priority, character in enumerate(ascii_uppercase)}
    )
    
    
    def compartment_commonality(rucksack: str) -> str:
        compartment_1 = set(rucksack[: len(rucksack) // 2])
        compartment_2 = set(rucksack[len(rucksack) // 2 :])
        return compartment_1.intersection(compartment_2).pop()
    
    
    def generate_groups(rucksacks: list[str]) -> list[list[str]]:
        grouped = []
        for idx in range(0, len(rucksacks), 3):
            grouped.append(rucksacks[idx : idx + 3])
    
        return grouped
    
    
    def find_badge(rucksacks: list[str]) -> str:
        sets = [set(rucksack) for rucksack in rucksacks]
        return sets[0].intersection(*sets).pop()
    
    
    def main(filepath: str) -> Tuple[int, int]:
        rucksacks = load_file.load_cleaned_lines(filepath)
        common_items = map(compartment_commonality, rucksacks)
    
        groups = generate_groups(rucksacks)
        badges = map(find_badge, groups)
    
        return (
            sum((priorities[item] for item in common_items)),
            sum((priorities[badge] for badge in badges)),
        )
    
    
    if __name__ == "__main__":
        part1, part2 = main(INPUT_FILE)
    
        print(part1)
        print(part2)
    
    2 votes
  17. Comment on Day 2: Rock Paper Scissors in ~comp

    Gyrfalcon
    Link
    I am actually happier with this than I thought I would be. I did most of the thinking and worked out the values for all the combinations, and just let Python crank on the calculating. I am also...

    I am actually happier with this than I thought I would be. I did most of the thinking and worked out the values for all the combinations, and just let Python crank on the calculating. I am also really happy with how I decided to do my testing, although I think if I tried harder I could probably make something that dynamically builds up the tests as I add days. Either way, it comes in handy. This code is also a little nicer because I actually ran and listened to mypy and black before posting my code! I am also going to leave out most of my fixture code, but if anyone is interested, just ask.

    Parts 1 and 2
    scoring = {
        "A X": 4,
        "A Y": 8,
        "A Z": 3,
        "B X": 1,
        "B Y": 5,
        "B Z": 9,
        "C X": 7,
        "C Y": 2,
        "C Z": 6,
    }
    
    decoded_scoring = {
        "A X": 3,
        "A Y": 4,
        "A Z": 8,
        "B X": 1,
        "B Y": 5,
        "B Z": 9,
        "C X": 2,
        "C Y": 6,
        "C Z": 7,
    }
    
    
    def score_game(game: str, decoded: bool = False) -> int:
        return decoded_scoring[game] if decoded else scoring[game]
    
    
    def main(filepath: str) -> Tuple[int, int]:
    
        games = load_file.load_cleaned_lines(filepath)
        return (
            sum([score_game(game) for game in games]),
            sum([score_game(game, True) for game in games]),
        )
    

    And my new file loader that works in the way it probably should have from the beginning:

    def load_cleaned_lines(filename: str) -> list[str]:
        with open(filename, "r") as fp:
            lines = fp.readlines()
    
        return [line.strip() for line in lines]
    
    1 vote
  18. Comment on Day 1: Calorie Counting in ~comp

    Gyrfalcon
    Link
    I had hoped to try out Rust this year, but I haven't gotten around to actually learning much of it so I settled on Python, since my new work uses it a lot and it would be good to brush up. I am...

    I had hoped to try out Rust this year, but I haven't gotten around to actually learning much of it so I settled on Python, since my new work uses it a lot and it would be good to brush up. I am also going hard on organizing code, adding at least semi real tests for the provided test cases, etc. Anyway, solution:

    Parts 1 and 2
    import os
    from aoc2022.common import load_file
    
    current_dir = os.path.realpath(os.path.dirname(__file__))
    INPUT_FILE = "/".join([current_dir, "input.txt"])
    TEST_FILE_1 = "/".join([current_dir, "test1.txt"])
    TEST_RESULTS_1 = (24000, 45000)
    
    
    def parse_elves(lines: list[str]) -> list[list[int]]:
        elves = []
        elf: list[int] = []
        for line in lines:
            if line == "\n":
                elves.append(elf)
                elf = []
            else:
                elf.append(int(line))
    
        elves.append(elf)
    
        return elves
    
    
    def count_calories(elves: list[list[int]]) -> list[int]:
        return [sum(elf) for elf in elves]
    
    
    def main(filepath: str) -> int:
        lines = load_file.load_lines(filepath)
        elves = parse_elves(lines)
    
        calorie_totals = count_calories(elves)
        calorie_totals.sort(reverse=True)
        return (calorie_totals[0], sum(calorie_totals[:3]))
    
    
    if __name__ == "__main__":
        part1, part2 = main(INPUT_FILE)
    
        print(part1)
        print(part2)
    

    And the common file which will hopefully fill out a bit as time goes on:

    def load_lines(filename: str) -> list[str]:
        with open(filename, "r") as fp:
            return fp.readlines()
    
    1 vote
  19. Comment on Timasomo 2022: Showcase Thread in ~creative

    Gyrfalcon
    Link
    Still Unfinished Shield This is more of an update than a showcase since I intend to finish this project and make a separate post at that time. I forgot in my calculations of whether or not I would...
    • Exemplary

    Still Unfinished Shield

    This is more of an update than a showcase since I intend to finish this project and make a separate post at that time. I forgot in my calculations of whether or not I would finish that my in-laws were coming to town, and that the time would change so that I wouldn't have light to work outside after work, and then I went ahead and bought Victoria 3 to distract myself even more. However, I got the green half the way I want, and learned a lot about how best to use the acrylic on this kind of surface, which you can see here.

    I normally link to Beehaw down here, but there wasn't anything posted on the showcase thread over there, despite some early activity. The community there is a bit small still, but if you like Tildes and are interested in trying out a federated community I would say it is a good one!

    11 votes
  20. Comment on Timasomo 2022: Final Update Thread in ~creative

    Gyrfalcon
    Link
    My apologies for falling off the face of the earth for last week's update. I started a new job that week, and ended up on travel for most of this week for work, so progress has not been what I had...

    My apologies for falling off the face of the earth for last week's update. I started a new job that week, and ended up on travel for most of this week for work, so progress has not been what I had hoped. Realistically, I don't think I am going to be able to finish everything before Monday, but I might be able to get something respectable before the actual showcase thread next Saturday. Anyway, what I did accomplish:

    • Made cuts on the main body of the shield and the handle. The shields outer and inner curves are definitely not perfect, and I somehow managed to cut the handle too long? But I think it won't be too noticeable in the finished product, and a too long piece can always be cut down.
    • I made one attempt at attaching the linen facing, but went about it all wrong. I tried to spread glue over the full surface and lay it down all in one go, which I had some misgivings about but thought would work. When I went to add more glue on top of the linen to complete my composite, it bubbled horribly.
    • I made a second attempt at attaching the linen, this time gluing down one section at a time, pulling the fabric taught as I laid it down on the glue, using an old library card to push out bubbles, and waiting about 60 seconds before moving on. This worked quite well, and I was able to add more glue for the top surface without issue.
    • I trimmed the excess linen. I started with scissors for this, but because of the glue in the fabric, a box cutter ended up being the more appropriate tool.
    • I applied my primer to the whole surface of the shield to make sure my paint sticks nicely.
    • I applied a coat of green to approximately half of the shield. I think this will need at least one more coat to get everything reasonably even.

    Pictures of my progress so far here.

    I am also running a parallel Make Something Month on Beehaw

    5 votes