Esempio n. 1
0
from typing import Tuple
from helpers import Puzzle

puzzle = Puzzle(day=2)


def parse_line(ln: str) -> Tuple[str, int]:
    # parses an instruction line into a 2-tuple
    # where the first element is the instruction
    # and the second element is the amount
    l = ln.split()
    return l[0], int(l[1])


def main() -> int:
    hor, ver = 0, 0
    instructions = map(parse_line, puzzle.lines())

    for mvmt, amt in instructions:
        if mvmt == "forward":
            hor += amt
        elif mvmt == "down":
            ver += amt
        else:
            ver -= amt

    return hor * ver
Esempio n. 2
0
from helpers import Puzzle

puzzle = Puzzle(day=1)


def main() -> int:
    depths = puzzle.intlines()
    count = 0

    for i in range(len(depths) - 2):
        if i == 0:
            continue

        start, end = i, i + 3
        if end >= len(depths):
            break

        if depths[start] < depths[end]:
            count += 1

    return count


print(main())
Esempio n. 3
0
import numpy as np
from helpers import Puzzle

puzzle = Puzzle(day=9)


def parse_input(raw: str) -> np.ndarray:
    return np.array([list(map(int, line)) for line in raw.splitlines()])


def main(raw: str) -> int:
    lines = parse_input(raw)
    padded = np.pad(lines, 1, "constant", constant_values=9)

    # make another array that's padded at all sides with 9 (9 being the largest number wont affect comparisons)
    # then use this to compare each element with it's neighbours

    lows = ((lines < padded[2:, 1:-1])  # shift upwards
            & (lines < padded[:-2, 1:-1])  # downwards
            & (lines < padded[1:-1, 2:])  # right
            & (lines < padded[1:-1, :-2])  # left
            )

    return (lines[lows] + 1).sum()


if __name__ == "__main__":
    raw = puzzle.raw_content
    print(main(raw))
Esempio n. 4
0
from typing import Dict, List, Tuple, Union
from helpers import Puzzle
from day_08_task_01 import parse_input, ParsedLine

puzzle = Puzzle(day=8)


def solve_line(line: ParsedLine) -> int:
    """Figure out the connections for a single line and return it's output number."""
    num_to_pattern: Dict[Union[int, Tuple[int, int, int]],
                         Union[str, List[str]]] = {
                         }  # keep track of the segments needed for each number

    num_of_segs_to_num = {
        6: (0, 6, 9),
        5: (2, 3, 5),
    }  # get which number it could possibly be, based on the number of segments it has

    for num_pattern in line[0]:
        # first figure out the ones we can definitely know
        # like 1: 2 segs, 4: 4 segs, 7: 3 segs, 8: 7 segs
        length = len(num_pattern)
        if length == 2:
            num_to_pattern[1] = num_pattern
        elif length == 4:
            num_to_pattern[4] = num_pattern
        elif length == 3:
            num_to_pattern[7] = num_pattern
        elif length == 7:
            num_to_pattern[8] = num_pattern
        else:
Esempio n. 5
0
from collections import Counter
from typing import Literal
from helpers import Puzzle


puzzle = Puzzle(day=3)
lines = puzzle.lines()


def get_criteria_at(pos: int, nums: list[str], _type: Literal["o2", "co2"]) -> str:
    """Get the most/least common bit at a specified position"""
    counter = Counter([line[pos] for line in nums])
    ones, zeroes = counter['1'], counter['0']
    most = max(counter.keys(), key=lambda i: counter[i])
    least = min(counter.keys(), key=lambda i: counter[i])

    if ones == zeroes:
        return '1' if _type == "o2" else '0'
    
    return most if _type == "o2" else least

def filter_numbers(bytes: list[str], i: int, type_: Literal["o2", "co2"]) -> str:
    """Filter numbers based on the bit conditions."""
    criteria = get_criteria_at(i, bytes, type_)
    filtered = [byte for byte in bytes if byte[i] == criteria]

    if len(filtered) == 1:
        return filtered[0]
    else:
        return filter_numbers(filtered, i + 1, type_)
Esempio n. 6
0
from collections import Counter, defaultdict
from helpers import Puzzle

puzzle = Puzzle(day=6)
# part 1 solution was naive and will run until the heat death of the universe, you need to do better

starting_fish = [int(i) for i in puzzle.raw_content.split(",")]
counter = Counter(starting_fish)

for day in range(256):
    next_iter = defaultdict(int)

    for days_left, count in counter.items():
        if days_left == 0:
            next_iter[6] += count
            next_iter[8] += count
        else:
            next_iter[days_left - 1] += count

    counter = next_iter

print(sum(counter.values()))
Esempio n. 7
0
import math
from collections import Counter
from typing import List, Set, Tuple

from day_05_task_01 import parse_input, get_points as get_straight_line_points
from helpers import Puzzle

puzzle = Puzzle(day=5)

# a list containing two lists, where each inner list is x, y
LineType = List[List[int]]
PointType = Tuple[int, int]


def get_valid_lines(
        lines: List[LineType]) -> Tuple[List[LineType], List[LineType]]:
    hor_ver = []
    diagonal = []
    for line in lines:
        start, end = line
        x1, y1 = start
        x2, y2 = end

        # check for horizontal/vertical like before
        if x1 == x2 or y1 == y2:
            hor_ver.append(line)
        # check for diagonal line with slope 45
        # tan 45 = 1; 45 in the other side -> -1
        elif (x1 - x2) / (y1 - y2) in {1.0, -1.0}:
            diagonal.append(line)
    return hor_ver, diagonal
Esempio n. 8
0
import numpy as np
from helpers import Puzzle


puzzle = Puzzle(day=7)


def main() -> int:
    positions = np.array(puzzle.raw_content.split(","), dtype=int)
    small = float('inf')

    # now we need to consider positions
    # that AREN'T already occupied by some submarine    
    for i in range(positions.max()):
        diff = np.abs(positions - i)
        fuel_needed = (diff * (diff + 1) / 2).sum()
        if fuel_needed < small:
            small = fuel_needed
    
    return int(small)


print(main())
Esempio n. 9
0
from helpers import Puzzle
from day_04_task_01 import check_if_win, calculate_score, parse_input

puzzle = Puzzle(day=4)


def main() -> int:
    nums, boards, positions = parse_input()
    nums = iter(nums)

    while len(boards) > 1:
        # keep going through all boards
        # pop them from the list when they win
        current_num = next(nums)
        pos = positions[current_num]
        pos.marked = True

        # make a new list of boards, that haven't won yet
        boards = [board for board in boards if not check_if_win(board)]
    else:
        # only one board left
        # keep going till this board reaches a win state
        for num in nums:
            pos = positions[num]
            pos.marked = True

            if check_if_win(boards[0]):
                return calculate_score(boards[0], num)
        else:
            # fail case, only to appease type checker
            return -1