From 1fc68329602b80fa534e2bb60ed6219349e716ec Mon Sep 17 00:00:00 2001 From: Sijmen Schoon Date: Tue, 14 Dec 2021 14:27:07 +0100 Subject: [PATCH] add other days --- day08.scm | 12 ++++-- day08_2.py | 59 +++++++++++++++++++++++++++++ day09.py | 45 ++++++++++++++++++++++ day10.py | 41 ++++++++++++++++++++ day11.py | 69 +++++++++++++++++++++++++++++++++ day12.py | 64 +++++++++++++++++++++++++++++++ day13.py | 62 ++++++++++++++++++++++++++++++ rust/Cargo.lock | 25 ++++++++++++ rust/Cargo.toml | 9 +++++ rust/src/main.rs | 99 ++++++++++++++++++++++++++++++++++++++++++++++++ 10 files changed, 482 insertions(+), 3 deletions(-) create mode 100644 day08_2.py create mode 100644 day09.py create mode 100644 day10.py create mode 100644 day11.py create mode 100644 day12.py create mode 100644 day13.py create mode 100644 rust/Cargo.lock create mode 100644 rust/Cargo.toml create mode 100644 rust/src/main.rs diff --git a/day08.scm b/day08.scm index f335642..c712197 100644 --- a/day08.scm +++ b/day08.scm @@ -1,7 +1,9 @@ (import (chicken io)) (import (chicken string)) (import (chicken format)) +(import srfi-113) (import srfi-1) +(import srfi-128) (define (summap a b) (foldl + 0 (map a b))) @@ -11,6 +13,7 @@ (let ((halves (string-split line "|"))) (map string-split halves))) +;; PART 1 (define (is-1478 pattern) (let ((len (string-length pattern))) (not (or (eq? len 5) (eq? len 6))))) @@ -19,6 +22,9 @@ (let-values (((patterns output) (apply values parsed))) (count is-1478 output))) -(let* ((lines (read-lines)) - (separated (map split-line lines))) - (displayln (summap count-1478 separated))) +(define lines (read-lines)) +(define parsed-lines (map split-line lines)) + +(let ((part1 (summap count-1478 parsed-lines))) + (display "part1: ") + (displayln part1)) diff --git a/day08_2.py b/day08_2.py new file mode 100644 index 0000000..5f3f661 --- /dev/null +++ b/day08_2.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python3 +import fileinput +import time + + +lines = list(fileinput.input()) + +t = time.time_ns() + +part2 = 0 +for line in lines: + poss = {} + + digits, output = (l.split() for l in str(line).split(" | ")) + + one = set(next(digit for digit in digits if len(digit) == 2)) + four = set(next(digit for digit in digits if len(digit) == 4)) + + for digit in digits: + digit = "".join(sorted(digit)) + digit_set = set(digit) + + overlap_one = len(digit_set & one) + overlap_four = len(digit_set & four) + + if len(digit) == 2: # 1 + poss[digit] = 1 + elif len(digit) == 3: # 7 + poss[digit] = 7 + elif len(digit) == 4: # 4 + poss[digit] = 4 + elif len(digit) == 5: # 2, 3, 5, 6 + if overlap_four == 2: + poss[digit] = 2 + elif overlap_one == 1: + poss[digit] = 5 + else: + poss[digit] = 3 + elif len(digit) == 6: # 0, 6, 9 + if overlap_four == 4: + poss[digit] = 9 + elif overlap_one == 2: + poss[digit] = 0 + else: + poss[digit] = 6 + elif len(digit) == 7: + poss[digit] = 8 + + result = 0 + for digit in output: + digit = "".join(sorted(digit)) + result = (result * 10) + poss[digit] + + part2 += result + +t2 = time.time_ns() +print(part2) +print(t2 - t, "ns") +print((t2 - t) // 1000, "µs") diff --git a/day09.py b/day09.py new file mode 100644 index 0000000..30c0162 --- /dev/null +++ b/day09.py @@ -0,0 +1,45 @@ +#!/usr/bin/env python3 +import fileinput +import heapq +from typing import Generator, Tuple + +heightmap = [] +for line in fileinput.input(): + heightmap.append([int(c) for c in line.strip()]) + +def neighborhood(x: int, y: int) -> Generator[Tuple[int, int], None, None]: + global heightmap + if x > 0: + yield (x - 1, y) + if x < len(heightmap[0]) - 1: + yield (x + 1, y) + if y > 0: + yield (x, y - 1) + if y < len(heightmap) - 1: + yield (x, y + 1) + +lowpoints = list() +for y, row in enumerate(heightmap): + for x, height in enumerate(row): + if height < min(heightmap[y_][x_] for x_, y_ in neighborhood(x, y)): + lowpoints.append((x, y)) + +print("part 1:", sum(1 + heightmap[y][x] for x, y in lowpoints)) + +basins = [] +for lowpoint in lowpoints: + stack = [lowpoint] + visited = set() + while stack: + x, y = stack.pop() + if (x, y) in visited or heightmap[y][x] == 9: + continue + visited.add((x, y)) + + for neighbor in neighborhood(x, y): + stack.append(neighbor) + + heapq.heappush(basins, len(visited)) + +a, b, c, *_ = heapq.nlargest(3, basins) +print("part 2:", a * b * c) diff --git a/day10.py b/day10.py new file mode 100644 index 0000000..d2df43f --- /dev/null +++ b/day10.py @@ -0,0 +1,41 @@ +#!/usr/bin/env python3 +import fileinput +from collections import deque +from timeit import timeit +from typing import Tuple + +lines = list(fileinput.input()) + +opposites = {"[": "]", "(": ")", "{": "}", "<": ">"} +part1_points = {")": 3, "]": 57, "}": 1197, ">": 25137} +part2_points = {"(": 1, "[": 2, "{": 3, "<": 4} + +def main() -> Tuple[int, int]: + part1 = 0 + part2_scores = [] + + for line_ in lines: + line = str(line_).strip() + stack = deque() + for c in line: + if c in "[({<": + stack.appendleft(c) + elif opposites[stack[0]] != c: + part1 += part1_points[c] + break + else: + stack.popleft() + else: + score = 0 + for c in stack: + score = score * 5 + part2_points[c] + part2_scores.append(score) + + part2_scores.sort() + part2 = part2_scores[len(part2_scores) // 2] + + return (part1, part2) + +if __name__ == "__main__": + print(main()) + print(f"{timeit(main, number=1000):.3f} ms") diff --git a/day11.py b/day11.py new file mode 100644 index 0000000..8a8be8a --- /dev/null +++ b/day11.py @@ -0,0 +1,69 @@ +#!/usr/bin/env python3 +import fileinput +from pprint import pprint +from typing import List, Tuple, Any, Set, Generator +from timeit import timeit + + +lines = list(fileinput.input()) + + +def neighborhood( + m: List[List[int]], x: int, y: int +) -> Generator[Tuple[int, int], None, None]: + h = ((-1, -1), (-1, 0), (-1, 1), (0, -1), (0, 1), (1, -1), (1, 0), (1, 1)) + yield from ( + (x_ + x, y_ + y) + for x_, y_ in h + if 0 <= x_ + x < len(m[0]) and 0 <= y_ + y < len(m) + ) + + +def step(field): + for y in range(len(field)): + for x in range(len(field[y])): + field[y][x] += 1 + + flash_queue = [ + (x, y) + for y in range(len(field)) + for x in range(len(field[y])) + if field[y][x] > 9 + ] + + flashed = set() + while flash_queue: + x, y = flash_queue.pop() + if (x, y) in flashed: + continue + flashed.add((x, y)) + + for x_, y_ in neighborhood(field, x, y): + field[y_][x_] += 1 + if field[y_][x_] > 9 and (x_, y_) not in flashed: + flash_queue.append((x_, y_)) + + for x, y in flashed: + field[y][x] = 0 + + return len(flashed) + + +def main() -> Tuple[int, int]: + field = [[int(c) for c in line.strip()] for line in lines] + len_ = len(field) * len(field[0]) + + part1 = 0 + part2 = 0 + for part2 in range(100): + part1 += step(field) + + while step(field) != len_ and sum(sum(row) for row in field) != 0: + part2 += 1 + + return part1, part2 + 2 + + +if __name__ == "__main__": + print(main()) + print(f"{timeit(main, number=100) * 10:.3f} ms") diff --git a/day12.py b/day12.py new file mode 100644 index 0000000..cd69efd --- /dev/null +++ b/day12.py @@ -0,0 +1,64 @@ +#!/usr/bin/env python3 +import fileinput +from timeit import timeit +from typing import FrozenSet, List, Optional, Tuple, Union +from collections import defaultdict + +lines = list(fileinput.input()) + + +def main(): + # parse + graph = defaultdict(list) + for line in lines: + fro, to = str(line).strip().split("-") + graph[fro].append(to) + graph[to].append(fro) + + # part 1 + stack: List[Tuple[str, FrozenSet[str]]] = [("start", frozenset())] + part1 = 0 + while stack: + pos, visited = stack.pop() + if pos.islower() and pos in visited: + continue + visited = visited | {pos} + + if pos == "end": + part1 += 1 + continue + + for next_pos in graph[pos]: + stack.append((next_pos, visited)) + + stack2: List[Tuple[str, FrozenSet[str], Optional[str]]] = [ + ("start", frozenset(), None) + ] + part2 = 0 + while stack2: + pos, visited, doubled = stack2.pop() + + if pos.islower() and pos in visited: + if doubled is None: + stack2.append((pos, visited, pos)) + continue + if doubled != pos: + continue + doubled = "" + + visited = visited | {pos} + + if pos == "end": + part2 += 1 + continue + + for next_pos in graph[pos]: + if next_pos != "start": + stack2.append((next_pos, visited, doubled)) + + return part1, part2 + + +if __name__ == "__main__": + print(main()) + print(f"{timeit(main, number=20) * 50:.3f} ms") diff --git a/day13.py b/day13.py new file mode 100644 index 0000000..33b12bd --- /dev/null +++ b/day13.py @@ -0,0 +1,62 @@ +#!/usr/bin/env python3 +import fileinput +from pprint import pprint +from timeit import timeit + +lines = list(fileinput.input()) + + +def main(): + h = 0 + w = 0 + + dots = set() + folds = [] + part1 = 0 + for line in lines: + line = str(line) + if line[0].isdigit(): + x, y = (int(n) for n in line.split(",")) + h = max(x + 1, h) + w = max(y + 1, w) + dots.add((x, y)) + elif line[0] == "f": + c, n = line.split("=") + folds.append((c[-1], int(n))) + + for dir, pos in folds: + folded = set() + if dir == "y": + for x, y in dots: + if y > pos: + y2 = 2 * pos - y + folded.add((x, y2)) + else: + folded.add((x, y)) + else: + for x, y in dots: + if x > pos: + x2 = 2 * pos - x + folded.add((x2, y)) + else: + folded.add((x, y)) + dots = folded + + if part1 == 0: + part1 = len(dots) + + xs, ys = zip(*dots) + sheet = [[" "] * (max(xs) + 1) for _ in range(max(ys) + 1)] + for x, y in dots: + sheet[y][x] = "#" + + return part1, ["".join(row).replace("#", "█") for row in sheet] + + +if __name__ == "__main__": + a, b = main() + print("part 1:", a) + print("part 2:") + print("\n".join(b)) + + print(f"{timeit(main, number=1000):.3f} ms") diff --git a/rust/Cargo.lock b/rust/Cargo.lock new file mode 100644 index 0000000..d0c7ff9 --- /dev/null +++ b/rust/Cargo.lock @@ -0,0 +1,25 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 3 + +[[package]] +name = "aoc_rust" +version = "0.1.0" +dependencies = [ + "fxhash", +] + +[[package]] +name = "byteorder" +version = "1.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" + +[[package]] +name = "fxhash" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c31b6d751ae2c7f11320402d34e41349dd1016f8d5d45e48c4312bc8625af50c" +dependencies = [ + "byteorder", +] diff --git a/rust/Cargo.toml b/rust/Cargo.toml new file mode 100644 index 0000000..4cb5663 --- /dev/null +++ b/rust/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "aoc_rust" +version = "0.1.0" +edition = "2021" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +fxhash = "0.2.1" diff --git a/rust/src/main.rs b/rust/src/main.rs new file mode 100644 index 0000000..29b7a43 --- /dev/null +++ b/rust/src/main.rs @@ -0,0 +1,99 @@ +use std::io::BufRead; +use std::time::Instant; +use std::collections::HashMap; + + +fn main() { + let mut graph = [0usize; 32]; + let stdin = std::io::stdin(); + let lines = stdin.lock().lines(); + + let mut nodes = HashMap::new(); + let mut is_small = 0u32; + + let instant = Instant::now(); + for line in lines { + let line = line.unwrap(); + let split = line.split("-").collect::>(); + + let next_a = nodes.len(); + let a = *nodes.entry(split[0].to_string()).or_insert(next_a); + + let next_b = nodes.len(); + let b = *nodes.entry(split[1].to_string()).or_insert(next_b); + + graph[a] |= 1 << b; + graph[b] |= 1 << a; + + if split[0].find(char::is_uppercase).is_none() { + is_small |= 1 << a; + } + if split[1].find(char::is_uppercase).is_none() { + is_small |= 1 << b; + } + } + + let start = nodes["start"]; + let end = nodes["end"]; + + let mut stack = vec![(start, 0u32)]; + let mut part1 = 0; + while stack.len() != 0 { + let (pos, visited) = stack.pop().unwrap(); + let pos_mask = 1 << pos; + if (is_small & visited) & pos_mask != 0 { + continue; + } + + if pos == end { + part1 += 1; + continue; + } + + let mut next = graph[pos]; + while next != 0 { + let next_pos = next.trailing_zeros(); + stack.push((next_pos as usize, visited | pos_mask)); + next &= !(1 << next_pos); + } + } + + #[derive(Debug, Eq, PartialEq, PartialOrd, Ord, Copy, Clone)] + enum Doubled { + None, + Done, + Some(usize), + } + + let mut stack = vec![(start, 0u32, Doubled::None)]; + let mut part2 = 0; + while stack.len() != 0 { + let (pos, visited, mut doubled) = stack.pop().unwrap(); + let pos_mask = 1 << pos; + if (is_small & visited) & pos_mask != 0 { + if doubled == Doubled::None { + stack.push((pos, visited, Doubled::Some(pos))); + continue; + } else if doubled != Doubled::Some(pos) { + continue; + } else { + doubled = Doubled::Done; + } + } + + if pos == end { + part2 += 1; + continue; + } + + let mut next = graph[pos] & !(1 << start); + while next != 0 { + let next_pos = next.trailing_zeros(); + stack.push((next_pos as usize, visited | pos_mask, doubled)); + next &= !(1 << next_pos); + } + } + + let elapsed = instant.elapsed(); + dbg!(part1, part2, elapsed); +}