diff options
Diffstat (limited to 'aoc/2025')
32 files changed, 1052 insertions, 0 deletions
diff --git a/aoc/2025/01/c.c b/aoc/2025/01/c.c new file mode 100644 index 0000000..115ea13 --- /dev/null +++ b/aoc/2025/01/c.c @@ -0,0 +1,19 @@ +#include <stdio.h> +#include <stdint.h> +#include <stdlib.h> + +int main() +{ + char dir; + int64_t n; + int64_t p = 50; + int64_t p1 = 0, p2 = 0; + while (scanf("%c%ld ", &dir, &n) == 2) { + int64_t new = p + (dir == 'R' ? n : -n); + p2 += labs(new) / 100 + (p != 0 && new <= 0); + p = (p = new % 100) >= 0 ? p : p + 100; + p1 += p == 0; + } + printf("silver: %ld\ngold: %ld", p1, p2); + return 0; +} diff --git a/aoc/2025/01/python.py b/aoc/2025/01/python.py new file mode 100755 index 0000000..d5ff1fa --- /dev/null +++ b/aoc/2025/01/python.py @@ -0,0 +1,15 @@ +#!/usr/bin/env python3 + +from fileinput import input +from itertools import accumulate + + +def fn(p: tuple[int, int], rot: str) -> tuple[int, int]: + x = p[0] + int(rot[1:]) * (1 if rot[0] == "R" else -1) + return x % 100, abs(x) // 100 + (p[0] and x <= 0) + + +p1, p2 = zip(*list(accumulate(input(), fn, initial=(50, 0)))) + +print(f"silver: {sum(not p for p in p1)}") +print(f"gold: {sum(p2)}") diff --git a/aoc/2025/01/rust/Cargo.lock b/aoc/2025/01/rust/Cargo.lock new file mode 100644 index 0000000..8c50f75 --- /dev/null +++ b/aoc/2025/01/rust/Cargo.lock @@ -0,0 +1,89 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "puzzle" +version = "0.1.0" +dependencies = [ + "num", +] + +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "num" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23" +dependencies = [ + "num-bigint", + "num-complex", + "num-integer", + "num-iter", + "num-rational", + "num-traits", +] + +[[package]] +name = "num-bigint" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" +dependencies = [ + "num-integer", + "num-traits", +] + +[[package]] +name = "num-complex" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-iter" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" +dependencies = [ + "num-bigint", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] diff --git a/aoc/2025/01/rust/Cargo.toml b/aoc/2025/01/rust/Cargo.toml new file mode 100644 index 0000000..9a6e47e --- /dev/null +++ b/aoc/2025/01/rust/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "puzzle" +version = "0.1.0" +edition = "2024" + +[dependencies] +num = { version = "0.4.3", features = ["num-bigint"] } diff --git a/aoc/2025/01/rust/src/main.rs b/aoc/2025/01/rust/src/main.rs new file mode 100644 index 0000000..72b718b --- /dev/null +++ b/aoc/2025/01/rust/src/main.rs @@ -0,0 +1,25 @@ +use num::BigUint; +use std::io; + +fn main() { + let mut curr = 50; + let mut silver: u64 = 0; + let mut gold: BigUint = BigUint::ZERO; + io::stdin() + .lines() + .flatten() + .map(|line| match line.split_at(1) { + ("L", n) => -n.parse::<i64>().unwrap(), + (_, n) => n.parse::<i64>().unwrap(), + }) + .for_each(|n| { + let prev = curr; + curr += n; + gold += (curr.abs() / 100) as u64 + (prev != 0 && curr <= 0) as u64; + curr = curr.rem_euclid(100); + silver += (curr == 0) as u64; + }); + + println!("silver: {silver}"); + println!("gold: {gold}"); +} diff --git a/aoc/2025/02/python.py b/aoc/2025/02/python.py new file mode 100755 index 0000000..a3b4d1e --- /dev/null +++ b/aoc/2025/02/python.py @@ -0,0 +1,33 @@ +#!/usr/bin/env python3 + + +from itertools import accumulate +from bisect import bisect_left, bisect_right +from functools import cache + +rs = [(int(a), int(b)) for r in input().split(",") for [a, b] in [r.split("-")]] +N = max(len(str(x)) for r in rs for x in r) + + +@cache +def memo(p2: bool): + ids = sorted( + { + int(str(n) * reps) + for n in range(10 ** (N // 2)) + for reps in range(2, N // len(str(n)) + 1 if p2 else 3) + } + ) + sums = list(accumulate(ids)) + return ids[1:], sums + + +def prefix_sum(a: int, b: int, ids: list[int], sums: list[int]): + return sums[bisect_right(ids, b)] - sums[bisect_left(ids, a)] + + +silver = sum(prefix_sum(*r, *memo(False)) for r in rs) +gold = sum(prefix_sum(*r, *memo(True)) for r in rs) + +print("silver:", silver) +print("gold:", gold) diff --git a/aoc/2025/03/python.py b/aoc/2025/03/python.py new file mode 100755 index 0000000..b0d4bfd --- /dev/null +++ b/aoc/2025/03/python.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python3 + +from fileinput import input + +lines = [[int(b) for b in line.strip()] for line in input()] + + +def jolt(bs: list[int], n: int) -> int: + if n == 1: + return max(bs) + n -= 1 + b = max(bs[:-n]) + i = bs.index(b) + return b * (10**n) + jolt(bs[i + 1 :], n) + + +silver = sum(jolt(bs, 2) for bs in lines) +gold = sum(jolt(bs, 12) for bs in lines) + +print("silver:", silver) +print("gold:", gold) diff --git a/aoc/2025/03/rust/Cargo.lock b/aoc/2025/03/rust/Cargo.lock new file mode 100644 index 0000000..ac79b17 --- /dev/null +++ b/aoc/2025/03/rust/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "puzzle" +version = "0.1.0" diff --git a/aoc/2025/03/rust/Cargo.toml b/aoc/2025/03/rust/Cargo.toml new file mode 100644 index 0000000..26e4e77 --- /dev/null +++ b/aoc/2025/03/rust/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "puzzle" +version = "0.1.0" +edition = "2024" + +[dependencies] diff --git a/aoc/2025/03/rust/src/main.rs b/aoc/2025/03/rust/src/main.rs new file mode 100644 index 0000000..be0b3e6 --- /dev/null +++ b/aoc/2025/03/rust/src/main.rs @@ -0,0 +1,42 @@ +use std::io; + +fn max(bs: &Vec<u8>, start: usize, end: usize) -> (u8, usize) { + let mut bests = [None; 10]; + for idx in start..=end { + if bs[idx] == 9 { + return (9, idx); + } + bests[bs[idx] as usize].get_or_insert((bs[idx], idx)); + } + bests.into_iter().flatten().last().unwrap() +} + +fn jolt(bs: &Vec<u8>, n: usize) -> u64 { + let mut j: u64 = 0; + let mut start = 0; + for n in (1..=n).rev() { + j *= 10; + let (b, idx) = max(bs, start, bs.len() - n); + j += b as u64; + start = idx + 1; + } + j +} + +fn main() { + let (silver, gold) = io::stdin() + .lines() + .flatten() + .map(|line| { + line.into_bytes() + .iter() + .map(|&x| x - b'0') + .collect::<Vec<u8>>() + }) + .map(|bs| (jolt(&bs, 2), jolt(&bs, 12))) + .reduce(|(a0, a1), (x0, x1)| (a0 + x0, a1 + x1)) + .unwrap(); + + println!("silver: {silver}"); + println!("gold: {gold}"); +} diff --git a/aoc/2025/04/python.py b/aoc/2025/04/python.py new file mode 100755 index 0000000..844c38e --- /dev/null +++ b/aoc/2025/04/python.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python3 + +from fileinput import input + +g = { + complex(x, y): ch + for y, line in enumerate(input()) + for x, ch in enumerate(line.strip()) +} + + +def accessible(): + return { + p + for p, x in g.items() + if x == "@" + and sum( + 1 + for dx in (-1, 0, 1) + for dy in (-1j, 0j, 1j) + if dx + dy != 0 and g.get(p + dx + dy, ".") == "@" + ) + < 4 + } + + +silver = len(accessible()) +gold = 0 + +while rem := accessible(): + gold += len(rem) + for gone in rem: + g.pop(gone) + +print("silver:", silver) +print("gold:", gold) diff --git a/aoc/2025/04/rust/Cargo.lock b/aoc/2025/04/rust/Cargo.lock new file mode 100644 index 0000000..ac79b17 --- /dev/null +++ b/aoc/2025/04/rust/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "puzzle" +version = "0.1.0" diff --git a/aoc/2025/04/rust/Cargo.toml b/aoc/2025/04/rust/Cargo.toml new file mode 100644 index 0000000..26e4e77 --- /dev/null +++ b/aoc/2025/04/rust/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "puzzle" +version = "0.1.0" +edition = "2024" + +[dependencies] diff --git a/aoc/2025/04/rust/src/main.rs b/aoc/2025/04/rust/src/main.rs new file mode 100644 index 0000000..3b13140 --- /dev/null +++ b/aoc/2025/04/rust/src/main.rs @@ -0,0 +1,66 @@ +use std::{collections::VecDeque, io}; + +fn main() { + let mut grid: Vec<Vec<Option<usize>>> = io::stdin() + .lines() + .flatten() + .map(|line| line.chars().map(|ch| (ch == '@').then_some(0)).collect()) + .collect(); + + // count neighbours + points(&grid) + .collect::<Vec<_>>() + .into_iter() + .for_each(|p @ (x, y)| { + let ns = neighbours(p, &grid).count(); + if let Some(n) = grid[y][x].as_mut() { + *n = ns; + } + }); + + let mut stack = points(&grid) + .filter(|&(x, y)| grid[y][x].is_some_and(|n| n < 4)) + .collect::<VecDeque<_>>(); + let silver = stack.len(); + let mut gold = 0; + + while let Some(p @ (x, y)) = stack.pop_back() { + if grid[y][x].take().is_none() { + continue; + } + gold += 1; + // reduce neighbours' neighbour counts by 1 + neighbours(p, &grid) + .collect::<Vec<_>>() + .into_iter() + .for_each(|(nx, ny)| { + if let Some(n) = grid[ny][nx].as_mut() { + *n -= 1; + if *n < 4 { + stack.push_back((nx, ny)) + } + } + }); + } + + println!("silver: {silver}"); + println!("gold: {gold}"); +} + +fn neighbours<T>( + (x, y): (usize, usize), + grid: &Vec<Vec<Option<T>>>, +) -> impl Iterator<Item = (usize, usize)> { + let xs = x.saturating_sub(1)..=(x + 1).min(grid.first().unwrap().len() - 1); + let ys = y.saturating_sub(1)..=(y + 1).min(grid.len() - 1); + + xs.flat_map(move |nx| ys.clone().map(move |ny| (nx, ny))) + .filter(move |n| *n != (x, y)) + .filter(|&(nx, ny)| grid.get(ny).and_then(|row| row.get(nx)).unwrap().is_some()) +} + +fn points<T>(grid: &Vec<Vec<T>>) -> impl Iterator<Item = (usize, usize)> { + let xs = 0..grid.first().unwrap().len(); + let ys = 0..grid.len(); + xs.flat_map(move |x| ys.clone().map(move |y| (x, y))) +} diff --git a/aoc/2025/05/python.py b/aoc/2025/05/python.py new file mode 100755 index 0000000..9977623 --- /dev/null +++ b/aoc/2025/05/python.py @@ -0,0 +1,39 @@ +#!/usr/bin/env python3 + +from itertools import takewhile +from fileinput import input + + +inp = map(str.strip, input()) +rs = [tuple(map(int, r.split("-"))) for r in takewhile(bool, inp)] +ids = list(map(int, inp)) + + +def add(a: int, b: int, c: int, d: int) -> tuple[int, int] | None: + if c < a: + return add(c, d, a, b) + if b < c: + return None + return (a, max(b, d)) + + +done = False +while not done: + ms = [] + for r in rs: + for idx, m in enumerate(ms): + if new := add(*r, *m): + ms[idx] = new + break + + else: + ms.append(r) + done = len(rs) == len(ms) + rs = ms + + +silver = sum(1 for i in ids if any(lo <= i <= hi for lo, hi in rs)) +gold = sum(b - a + 1 for a, b in rs) + +print("silver:", silver) +print("gold:", gold) diff --git a/aoc/2025/05/rust/Cargo.lock b/aoc/2025/05/rust/Cargo.lock new file mode 100644 index 0000000..ac79b17 --- /dev/null +++ b/aoc/2025/05/rust/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "puzzle" +version = "0.1.0" diff --git a/aoc/2025/05/rust/Cargo.toml b/aoc/2025/05/rust/Cargo.toml new file mode 100644 index 0000000..26e4e77 --- /dev/null +++ b/aoc/2025/05/rust/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "puzzle" +version = "0.1.0" +edition = "2024" + +[dependencies] diff --git a/aoc/2025/05/rust/src/main.rs b/aoc/2025/05/rust/src/main.rs new file mode 100644 index 0000000..cd78338 --- /dev/null +++ b/aoc/2025/05/rust/src/main.rs @@ -0,0 +1,55 @@ +use std::io; + +fn main() { + let mut input = io::stdin().lines().flatten().map(|line| line); + let ranges = input.by_ref().take_while(|s| !s.is_empty()).map(|s| { + let (lo, hi) = s.split_once('-').unwrap(); + ( + lo.to_owned().parse().unwrap(), + hi.to_owned().parse().unwrap(), + ) + }); + + let mut merged: Vec<(u64, u64)> = vec![]; + for (mut lo, mut hi) in ranges { + let ldx = merged.partition_point(|&(_, max)| max < lo); + let mut rdx = merged.partition_point(|&(_, max)| max < hi); + + let left = merged.get(ldx); + let right = merged.get(rdx); + + // ldx = merged.len() -> left is none -> insert at end + let Some(&(llo, lhi)) = left else { + merged.push((lo, hi)); + continue; + }; + + if (llo..=lhi).contains(&lo) { + lo = lo.min(llo); // include range start point + } + + // rdx = merged.len() -> right is none -> merge with all + let Some(&(rlo, rhi)) = right else { + merged.drain(ldx..); + merged.push((lo, hi)); + continue; + }; + + if (rlo..=rhi).contains(&hi) { + hi = hi.max(rhi); + rdx += 1; + } + + merged.drain(ldx..rdx); + merged.insert(ldx, (lo, hi)); + } + + let silver = input + .map(|n| n.parse::<u64>().unwrap()) + .filter(|id| merged.iter().any(|&(lo, hi)| (lo..=hi).contains(id))) + .count(); + let gold: u64 = merged.iter().map(|(lo, hi)| hi - lo + 1).sum(); + + println!("silver: {silver}"); + println!("gold: {gold}"); +} diff --git a/aoc/2025/06/python.py b/aoc/2025/06/python.py new file mode 100755 index 0000000..4c57829 --- /dev/null +++ b/aoc/2025/06/python.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 + +from math import prod +from fileinput import input +from itertools import takewhile, repeat + +*nums, ops = [line.strip("\n") for line in input()] +ops = [prod if op == "*" else sum for op in ops.split()] + +horizontal = [map(int, row.split()) for row in nums] +silver = sum(op(chunk) for op, chunk in zip(ops, zip(*horizontal))) + +vertical = ("".join(ds).strip() for ds in [*zip(*nums)]) +chunks = repeat(lambda: [*map(int, takewhile(bool, vertical))]) +gold = sum(op(chunk()) for op, chunk in zip(ops, chunks)) + +print("silver:", silver) +print("gold:", gold) diff --git a/aoc/2025/07/python.py b/aoc/2025/07/python.py new file mode 100755 index 0000000..639c368 --- /dev/null +++ b/aoc/2025/07/python.py @@ -0,0 +1,31 @@ +#!/usr/bin/env python3 + +from fileinput import input +from functools import cache + +lines = [line.strip() for line in input()] +start = lines[0].index("S") + +silver = 0 +bs = {start} +for line in lines: + for b in bs.copy(): + if line[b] == "^": + silver += 1 + bs -= {b} + bs |= {b - 1, b + 1} + + +@cache +def tls(r: int, c: int) -> int: + if r == len(lines): + return 1 + if lines[r][c] == "^": + return tls(r, c - 1) + tls(r, c + 1) + return tls(r + 1, c) + + +gold = tls(0, start) + +print("silver:", silver) +print("gold:", gold) diff --git a/aoc/2025/08/python.py b/aoc/2025/08/python.py new file mode 100755 index 0000000..98a4725 --- /dev/null +++ b/aoc/2025/08/python.py @@ -0,0 +1,29 @@ +#!/usr/bin/env python3 + +from fileinput import input +from math import dist, prod +from operator import itemgetter + +MAX_CONNS = 1000 # test: 10, input: 1000 + +boxes = [[*map(int, line.split(","))] for line in input()] +N = len(boxes) + +js = {n: {n} for n in range(N)} +conns = sorted( + ((x, y) for x in range(N) for y in range(x)), + key=lambda t: dist(*itemgetter(*t)(boxes)), +) + +for n, (x, y) in enumerate(conns): + if n == MAX_CONNS: + lens = {id(j): len(j) for j in js.values()}.values() + print("silver:", prod(sorted(lens, reverse=True)[:3])) + + j = js[x] | js[y] + for b in j: + js[b] = j + + if len(j) == N: + print("gold:", boxes[x][0] * boxes[y][0]) + break diff --git a/aoc/2025/09/python.py b/aoc/2025/09/python.py new file mode 100755 index 0000000..6c4df6c --- /dev/null +++ b/aoc/2025/09/python.py @@ -0,0 +1,13 @@ +#!/usr/bin/env python3 + +from fileinput import input + +pts = [[*map(int, line.split(","))] for line in input()] + +silver = max( + (abs(x1 - x2) + 1) * (abs(y1 - y2) + 1) for (x1, y1) in pts for (x2, y2) in pts +) +gold = 0 + +print("silver:", silver) +print("gold:", gold) diff --git a/aoc/2025/09/rust/Cargo.lock b/aoc/2025/09/rust/Cargo.lock new file mode 100644 index 0000000..ac79b17 --- /dev/null +++ b/aoc/2025/09/rust/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "puzzle" +version = "0.1.0" diff --git a/aoc/2025/09/rust/Cargo.toml b/aoc/2025/09/rust/Cargo.toml new file mode 100644 index 0000000..26e4e77 --- /dev/null +++ b/aoc/2025/09/rust/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "puzzle" +version = "0.1.0" +edition = "2024" + +[dependencies] diff --git a/aoc/2025/09/rust/src/main.rs b/aoc/2025/09/rust/src/main.rs new file mode 100644 index 0000000..32847e3 --- /dev/null +++ b/aoc/2025/09/rust/src/main.rs @@ -0,0 +1,113 @@ +use std::io; + +#[derive(Debug)] +struct Floor { + x_comp: Vec<usize>, + y_comp: Vec<usize>, +} + +impl Floor { + fn new(pts: Vec<(usize, usize)>) -> (Self, Vec<(usize, usize)>) { + let (mut xs, mut ys): (Vec<_>, Vec<_>) = pts.clone().into_iter().unzip(); + xs.sort_unstable(); + xs.dedup(); + ys.sort_unstable(); + ys.dedup(); + + let pts = pts + .into_iter() + .map(|(x, y)| (xs.binary_search(&x).unwrap(), ys.binary_search(&y).unwrap())) + .collect(); + + ( + Self { + x_comp: xs, + y_comp: ys, + }, + pts, + ) + } + + fn uncomp(&self, (x, y): (usize, usize)) -> (usize, usize) { + (self.x_comp[x], self.y_comp[y]) + } + + fn area(&self, (p1, p2): ((usize, usize), (usize, usize))) -> usize { + let (x1, y1) = self.uncomp(p1); + let (x2, y2) = self.uncomp(p2); + (x1.abs_diff(x2) + 1) * (y1.abs_diff(y2) + 1) + } +} + +fn main() { + let pts = io::stdin() + .lines() + .flatten() + .map(|line| { + let (x, y) = line.split_once(',').unwrap(); + (x.parse().unwrap(), y.parse().unwrap()) + }) + .collect::<Vec<(usize, usize)>>(); + + let (floor, pts) = Floor::new(pts); + + let xmax = *pts.iter().map(|(x, _)| x).max().unwrap(); + let ymax = *pts.iter().map(|(_, y)| y).max().unwrap(); + + // some padding so that flood fill works correctly + let mut grid: Vec<Vec<Option<bool>>> = vec![vec![None; xmax + 3]; ymax + 3]; + + // draw green/red tiles + for pair in [vec![*pts.last().unwrap()], pts.clone()] + .concat() + .windows(2) + { + let (x1, y1) = pair[0]; + let (x2, y2) = pair[1]; + (x1.min(x2)..=x1.max(x2)).for_each(|x| { + (y1.min(y2)..=(y1.max(y2))).for_each(|y| grid[y + 1][x + 1] = Some(true)) + }); + } + + // flood fill from top left + let mut q = vec![(0, 0)]; + while let Some((x, y)) = q.pop() { + grid[y][x] = Some(false); + let ns = vec![ + (x.saturating_sub(1), y), + ((x + 1).min(xmax + 2), y), + (x, y.saturating_sub(1)), + (x, (y + 1).min(ymax + 2)), + ]; + q.extend(ns.into_iter().filter(|&(x, y)| grid[y][x].is_none())); + } + + // replace inside cells with Some(true) and undo the padding + let grid: Vec<Vec<bool>> = grid + .into_iter() + .skip(1) + .map(|r| r.into_iter().skip(1).map(|c| c.unwrap_or(true)).collect()) + .collect(); + + let mut rectangles = pts + .iter() + .enumerate() + .flat_map(|(idx, p1)| pts.iter().take(idx).map(move |p2| (*p1, *p2))) + .collect::<Vec<_>>(); + rectangles.sort_unstable_by_key(|&r| std::cmp::Reverse(floor.area(r))); + + let silver: usize = floor.area(*rectangles.first().unwrap()); + let gold: usize = floor.area( + *rectangles + .iter() + .filter(|&&((x1, y1), (x2, y2))| { + (x1.min(x2)..=x1.max(x2)) + .flat_map(|x| (y1.min(y2)..=y1.max(y2)).map(move |y| (x, y))) + .all(|(x, y)| grid[y][x]) + }) + .next() + .unwrap(), + ); + println!("silver: {silver}"); + println!("gold: {gold}"); +} diff --git a/aoc/2025/10/rust/Cargo.lock b/aoc/2025/10/rust/Cargo.lock new file mode 100644 index 0000000..b6a74c9 --- /dev/null +++ b/aoc/2025/10/rust/Cargo.lock @@ -0,0 +1,155 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "puzzle" +version = "0.1.0" +dependencies = [ + "good_lp", + "itertools", +] + +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "either" +version = "1.15.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" + +[[package]] +name = "fnv" +version = "1.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" + +[[package]] +name = "good_lp" +version = "1.14.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "776aa1ba88ac058e78408c17f4dbff826a51ae08ed6642f71ca0edd7fe9383f3" +dependencies = [ + "fnv", + "microlp", +] + +[[package]] +name = "itertools" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" +dependencies = [ + "either", +] + +[[package]] +name = "log" +version = "0.4.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" + +[[package]] +name = "matrixmultiply" +version = "0.3.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a06de3016e9fae57a36fd14dba131fccf49f74b40b7fbdb472f96e361ec71a08" +dependencies = [ + "autocfg", + "rawpointer", +] + +[[package]] +name = "microlp" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51d1790c73b93164ff65868f63164497cb32339458a9297e17e212d91df62258" +dependencies = [ + "log", + "sprs", +] + +[[package]] +name = "ndarray" +version = "0.16.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "882ed72dce9365842bf196bdeedf5055305f11fc8c03dee7bb0194a6cad34841" +dependencies = [ + "matrixmultiply", + "num-complex", + "num-integer", + "num-traits", + "portable-atomic", + "portable-atomic-util", + "rawpointer", +] + +[[package]] +name = "num-complex" +version = "0.4.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "73f88a1307638156682bada9d7604135552957b7818057dcef22705b4d509495" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-integer" +version = "0.1.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" +dependencies = [ + "num-traits", +] + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + +[[package]] +name = "portable-atomic" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f84267b20a16ea918e43c6a88433c2d54fa145c92a811b5b047ccbe153674483" + +[[package]] +name = "portable-atomic-util" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d8a2f0d8d040d7848a709caf78912debcc3f33ee4b3cac47d73d1e1069e83507" +dependencies = [ + "portable-atomic", +] + +[[package]] +name = "rawpointer" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "60a357793950651c4ed0f3f52338f53b2f809f32d83a07f72909fa13e4c6c1e3" + +[[package]] +name = "smallvec" +version = "1.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" + +[[package]] +name = "sprs" +version = "0.11.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6dca58a33be2188d4edc71534f8bafa826e787cc28ca1c47f31be3423f0d6e55" +dependencies = [ + "ndarray", + "num-complex", + "num-traits", + "smallvec", +] diff --git a/aoc/2025/10/rust/Cargo.toml b/aoc/2025/10/rust/Cargo.toml new file mode 100644 index 0000000..f8128d3 --- /dev/null +++ b/aoc/2025/10/rust/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "puzzle" +version = "0.1.0" +edition = "2024" + +[dependencies] +good_lp = { version = "1.14.2", features = ["microlp"], default-features = false } +itertools = "0.14.0" diff --git a/aoc/2025/10/rust/src/main.rs b/aoc/2025/10/rust/src/main.rs new file mode 100644 index 0000000..b0cd6ed --- /dev/null +++ b/aoc/2025/10/rust/src/main.rs @@ -0,0 +1,68 @@ +use good_lp::*; +use itertools::Itertools; +use std::io; + +fn main() { + let ms = io::stdin() + .lines() + .flatten() + .map(|line| { + let mut iter = line.split_whitespace(); + let p1 = iter + .next() + .unwrap() + .chars() + .filter_map(|ch| match ch { + '.' => Some(0), + '#' => Some(1), + _ => None, + }) + .collect::<Vec<u32>>(); + + let mut iter = iter.map(|s| { + s.split(|ch: char| ch.is_ascii_punctuation()) + .filter_map(|n| n.parse().ok()) + .collect::<Vec<u32>>() + }); + let p2 = iter.next_back().unwrap(); + let bs = iter.collect::<Vec<_>>(); + (p1, p2, bs) + }) + .collect::<Vec<_>>(); + + let silver: usize = ms.iter().map(|(want, _, bs)| solve_p1(&want, &bs)).sum(); + let gold: usize = ms.iter().map(|(_, want, bs)| solve_p2(&want, &bs)).sum(); + println!("silver: {silver}"); + println!("gold: {gold}"); +} + +fn solve_p1(want: &Vec<u32>, bs: &Vec<Vec<u32>>) -> usize { + bs.into_iter() + .powerset() + .map(|sub| (sub.len(), sub.into_iter().flatten().counts())) + .filter(|(_, counts)| { + want.iter() + .enumerate() + .all(|(n, w)| counts.get(&(n as u32)).unwrap_or(&0) % 2 == *w as usize) + }) + .next() + .unwrap() + .0 +} + +fn solve_p2(want: &Vec<u32>, bs: &Vec<Vec<u32>>) -> usize { + variables! {vars: 0 <= xs[bs.len()] (integer);} + let obj: Expression = xs.iter().sum(); + let mut model = vars.minimise(&obj).using(default_solver); + for (n, w) in want.iter().enumerate() { + let xsum: Expression = bs + .iter() + .enumerate() + .filter(|&(_, b)| b.contains(&(n as u32))) + .map(|(x, _)| xs[x]) + .sum(); + model = model.with(constraint!(xsum == *w)); + } + + model.solve().unwrap().eval(&obj).round() as usize +} diff --git a/aoc/2025/11/rust/Cargo.lock b/aoc/2025/11/rust/Cargo.lock new file mode 100644 index 0000000..ac79b17 --- /dev/null +++ b/aoc/2025/11/rust/Cargo.lock @@ -0,0 +1,7 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "puzzle" +version = "0.1.0" diff --git a/aoc/2025/11/rust/Cargo.toml b/aoc/2025/11/rust/Cargo.toml new file mode 100644 index 0000000..26e4e77 --- /dev/null +++ b/aoc/2025/11/rust/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "puzzle" +version = "0.1.0" +edition = "2024" + +[dependencies] diff --git a/aoc/2025/11/rust/src/main.rs b/aoc/2025/11/rust/src/main.rs new file mode 100644 index 0000000..522d7ad --- /dev/null +++ b/aoc/2025/11/rust/src/main.rs @@ -0,0 +1,84 @@ +use std::{cell::RefCell, collections::HashMap, io, rc::Rc}; + +#[derive(PartialEq, Eq, Hash, Debug)] +struct Node { + device: String, + dac: bool, + fft: bool, +} + +fn main() { + let adj = io::stdin() + .lines() + .flatten() + .map(|line| { + let (k, v) = line.split_once(": ").unwrap(); + ( + String::from(k), + String::from(v) + .split_whitespace() + .map(String::from) + .collect(), + ) + }) + .collect::<HashMap<String, Vec<String>>>(); + + let cache = Rc::new(RefCell::new(HashMap::new())); + + let silver = paths_from( + Node { + device: String::from("you"), + dac: true, + fft: true, + }, + &adj, + Rc::clone(&cache), + ); + let gold = paths_from( + Node { + device: String::from("svr"), + dac: false, + fft: false, + }, + &adj, + Rc::clone(&cache), + ); + + println!("silver: {silver}"); + println!("gold: {gold}"); +} + +fn paths_from( + curr: Node, + adj: &HashMap<String, Vec<String>>, + cache: Rc<RefCell<HashMap<Node, usize>>>, +) -> usize { + if curr.device == "out" { + if curr.dac && curr.fft { + return 1; + } + return 0; + } + + let Some(&v) = cache.borrow().get(&curr) else { + let paths = adj + .get(&curr.device) + .unwrap() + .iter() + .map(|n| { + paths_from( + Node { + device: n.clone(), + dac: curr.dac || curr.device == "dac", + fft: curr.fft || curr.device == "fft", + }, + adj, + Rc::clone(&cache), + ) + }) + .sum(); + cache.borrow_mut().insert(curr, paths); + return paths; + }; + return v; +} diff --git a/aoc/2025/12/python.py b/aoc/2025/12/python.py new file mode 100755 index 0000000..c3b5158 --- /dev/null +++ b/aoc/2025/12/python.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python3 + +from fileinput import input +from itertools import takewhile + + +def check(s: str): + return eval(s.replace(": ", ">=9*(").replace("x", "*").replace(" ", "+") + ")") + + +silver = sum( + map( + check, + takewhile( + bool, + [line.strip() for line in input()][::-1], + ), + ) +) + +print("silver:", silver) |
