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
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())
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))
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:
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_)
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()))
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
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())
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