Compare commits
38
Commits
f96ab9d93b
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0ff3b02db9 | ||
|
|
e3affe980a | ||
|
|
d08eabf619 | ||
|
|
ff3fe411ae | ||
|
|
22fc3511b7 | ||
|
|
3740d93f81 | ||
|
|
91d9d7b1dc | ||
|
|
f5788b2825 | ||
|
|
2a340c2e04 | ||
|
|
561f762bc9 | ||
|
|
6585356f0e | ||
|
|
ac4c0f3838 | ||
|
|
b49467a59e | ||
|
|
e215cd7472 | ||
|
|
962e7749f6 | ||
|
|
3db9cd1e69 | ||
|
|
78040a4c80 | ||
|
|
fc65d53120 | ||
|
|
cf5d423f50 | ||
|
|
40b79950bd | ||
|
|
6a39bf7002 | ||
|
|
cfd13f68bf | ||
|
|
285cefa670 | ||
|
|
ed541639bb | ||
|
|
2b379bba1d | ||
|
|
e570358fa7 | ||
|
|
8e9dc7c388 | ||
|
|
e857602932 | ||
|
|
0ecea97752 | ||
|
|
dc5f488212 | ||
|
|
4ce7184b1e | ||
|
|
fdbbd934a4 | ||
|
|
f87e5ea0af | ||
|
|
f9c32f0022 | ||
|
|
2cf47fd7d6 | ||
|
|
647e2fb356 | ||
|
|
8cd421a8ef | ||
|
|
a576f4712f |
No files matched your search
+2
-1
@@ -1,3 +1,4 @@
|
||||
*/target/
|
||||
*/input/
|
||||
*.out
|
||||
*.out
|
||||
.direnv
|
||||
Generated
+7
@@ -55,6 +55,7 @@ dependencies = [
|
||||
"hashbrown",
|
||||
"nom",
|
||||
"num",
|
||||
"num-modular",
|
||||
"regex",
|
||||
"topological-sort",
|
||||
]
|
||||
@@ -169,6 +170,12 @@ dependencies = [
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-modular"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "17bb261bf36fa7d83f4c294f834e91256769097b3cb505d44831e0a179ac647f"
|
||||
|
||||
[[package]]
|
||||
name = "num-rational"
|
||||
version = "0.4.2"
|
||||
|
||||
@@ -9,5 +9,6 @@ aoc-runner-derive = "0.3.0"
|
||||
hashbrown = "0.15.2"
|
||||
nom = "7.1.3"
|
||||
num = "0.4.3"
|
||||
num-modular = "0.6.1"
|
||||
regex = "1.11.1"
|
||||
topological-sort = "0.2.2"
|
||||
@@ -0,0 +1,133 @@
|
||||
use std::collections::VecDeque;
|
||||
|
||||
use aoc_runner_derive::{aoc, aoc_generator};
|
||||
use hashbrown::HashSet;
|
||||
|
||||
type Input = (usize, usize, Vec<Vec<u8>>);
|
||||
|
||||
#[aoc_generator(day10)]
|
||||
fn parse(input: &str) -> Input {
|
||||
let map = input
|
||||
.lines()
|
||||
.map(|line| line.chars().map(|c| c as u8 - 48).collect())
|
||||
.collect::<Vec<Vec<u8>>>();
|
||||
let w = map[0].len();
|
||||
let h = map.len();
|
||||
(w, h, map)
|
||||
}
|
||||
|
||||
#[aoc(day10, part1)]
|
||||
fn part1(input: &Input) -> usize {
|
||||
let mut queue = VecDeque::new();
|
||||
let mut paths = HashSet::new();
|
||||
let (w, h, map) = input;
|
||||
let h = *h;
|
||||
let w = *w;
|
||||
(0..h).for_each(|y| {
|
||||
(0..w).for_each(|x| {
|
||||
if map[y][x] == 0 {
|
||||
queue.push_back(((x, y), (x, y)));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
while !queue.is_empty() {
|
||||
let (begin, (x, y)) = queue.pop_front().unwrap();
|
||||
let val = map[y][x];
|
||||
if val == 9 {
|
||||
paths.insert((begin, (x, y)));
|
||||
continue;
|
||||
}
|
||||
|
||||
if x > 0 && map[y][x - 1] == val + 1 {
|
||||
queue.push_back((begin, (x - 1, y)));
|
||||
}
|
||||
if x < h - 1 && map[y][x + 1] == val + 1 {
|
||||
queue.push_back((begin, (x + 1, y)));
|
||||
}
|
||||
if y > 0 && map[y - 1][x] == val + 1 {
|
||||
queue.push_back((begin, (x, y - 1)));
|
||||
}
|
||||
if y < h - 1 && map[y + 1][x] == val + 1 {
|
||||
queue.push_back((begin, (x, y + 1)));
|
||||
}
|
||||
}
|
||||
|
||||
paths.len()
|
||||
}
|
||||
|
||||
#[aoc(day10, part2)]
|
||||
fn part2(input: &Input) -> u64 {
|
||||
let mut queue = VecDeque::new();
|
||||
let mut starts = Vec::new();
|
||||
let (w, h, map) = input;
|
||||
let h = *h;
|
||||
let w = *w;
|
||||
(0..h).for_each(|y| {
|
||||
(0..w).for_each(|x| {
|
||||
if map[y][x] == 0 {
|
||||
starts.push((x, y));
|
||||
} else if map[y][x] == 9 {
|
||||
queue.push_back((x, y));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
let mut sum = 0;
|
||||
|
||||
while !queue.is_empty() {
|
||||
let (x, y) = queue.pop_front().unwrap();
|
||||
let val = map[y][x];
|
||||
if val == 0 {
|
||||
sum += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
if x > 0 && map[y][x - 1] == val - 1 {
|
||||
queue.push_back((x - 1, y));
|
||||
}
|
||||
if x < h - 1 && map[y][x + 1] == val - 1 {
|
||||
queue.push_back((x + 1, y));
|
||||
}
|
||||
if y > 0 && map[y - 1][x] == val - 1 {
|
||||
queue.push_back((x, y - 1));
|
||||
}
|
||||
if y < h - 1 && map[y + 1][x] == val - 1 {
|
||||
queue.push_back((x, y + 1));
|
||||
}
|
||||
}
|
||||
|
||||
sum
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn part1_example() {
|
||||
assert_eq!(part1(&parse("0123\n1234\n8765\n9876")), 1);
|
||||
assert_eq!(
|
||||
part1(&parse(
|
||||
"89010123\n78121874\n87430965\n96549874\n45678903\n32019012\n01329801\n10456732"
|
||||
)),
|
||||
36
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn part2_example() {
|
||||
assert_eq!(
|
||||
part2(&parse(
|
||||
";;;;;0;\n;;4321;\n;;5;;2;\n;;6543;\n;;7;;4;\n;;8765;\n;;9;;;;"
|
||||
)),
|
||||
3
|
||||
);
|
||||
assert_eq!(
|
||||
part2(&parse(
|
||||
"89010123\n78121874\n87430965\n96549874\n45678903\n32019012\n01329801\n10456732"
|
||||
)),
|
||||
81
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
use aoc_runner_derive::{aoc, aoc_generator};
|
||||
use hashbrown::HashMap;
|
||||
use nom::{
|
||||
character::complete::{space1, u64 as parse_usize},
|
||||
error::Error,
|
||||
multi::separated_list1,
|
||||
};
|
||||
|
||||
type Input = Vec<usize>;
|
||||
|
||||
#[aoc_generator(day11)]
|
||||
fn parse(input: &str) -> Input {
|
||||
separated_list1(space1::<&str, Error<&str>>, parse_usize)(input)
|
||||
.unwrap()
|
||||
.1
|
||||
.into_iter()
|
||||
.map(|v| v as usize)
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[aoc(day11, part1)]
|
||||
fn part1(input: &Input) -> usize {
|
||||
blink(25, input.clone())
|
||||
}
|
||||
|
||||
#[aoc(day11, part2)]
|
||||
fn part2(input: &Input) -> usize {
|
||||
blink(75, input.clone())
|
||||
}
|
||||
|
||||
fn blink(blinks: usize, input: Vec<usize>) -> usize {
|
||||
let mut stones = HashMap::new();
|
||||
for stone in input {
|
||||
*stones.entry(stone).or_insert(0) += 1;
|
||||
}
|
||||
for _ in 0..blinks {
|
||||
stones = single_blink(stones);
|
||||
}
|
||||
stones.values().sum::<usize>()
|
||||
}
|
||||
|
||||
fn single_blink(stones: HashMap<usize, usize>) -> HashMap<usize, usize> {
|
||||
let mut new_stones = HashMap::with_capacity(2 << 12);
|
||||
|
||||
for (stone, count) in stones {
|
||||
if stone == 0 {
|
||||
*new_stones.entry(1).or_insert(0) += count;
|
||||
} else {
|
||||
let digits = stone.ilog10() + 1;
|
||||
if digits % 2 == 0 {
|
||||
let left = stone / 10usize.pow(digits / 2);
|
||||
let right = stone % 10usize.pow(digits / 2);
|
||||
*new_stones.entry(left).or_insert(0) += count;
|
||||
*new_stones.entry(right).or_insert(0) += count;
|
||||
} else {
|
||||
*new_stones.entry(stone * 2024).or_insert(0) += count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
new_stones
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn part1_example() {
|
||||
assert_eq!(
|
||||
part1(&parse(include_str!("../input/2024/day11.txt").trim_end())),
|
||||
203609
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn part2_example() {
|
||||
assert_eq!(
|
||||
part2(&parse(include_str!("../input/2024/day11.txt").trim_end())),
|
||||
240954878211138
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,174 @@
|
||||
use aoc_runner_derive::{aoc, aoc_generator};
|
||||
|
||||
type Input = Vec<Vec<char>>;
|
||||
|
||||
#[aoc_generator(day12)]
|
||||
fn parse(input: &str) -> Input {
|
||||
input.lines().map(|line| line.chars().collect()).collect()
|
||||
}
|
||||
|
||||
#[aoc(day12, part1)]
|
||||
fn part1(input: &Input) -> usize {
|
||||
let w = input[0].len();
|
||||
let h = input.len();
|
||||
|
||||
let mut region_idx = -1;
|
||||
let mut region_border_size = vec![];
|
||||
let mut region_size = vec![];
|
||||
let mut region = vec![vec![-1; w]; h];
|
||||
|
||||
for y in 0..h {
|
||||
for x in 0..w {
|
||||
let val = input[y][x];
|
||||
if region[y][x] == -1 {
|
||||
region_idx += 1;
|
||||
let mut queue = vec![(x, y)];
|
||||
let mut size = 0;
|
||||
let mut border_size = 0;
|
||||
|
||||
while let Some((x, y)) = queue.pop() {
|
||||
if region[y][x] != -1 {
|
||||
continue;
|
||||
}
|
||||
|
||||
region[y][x] = region_idx;
|
||||
size += 1;
|
||||
|
||||
let mut sides = 4;
|
||||
|
||||
if x > 0 && input[y][x - 1] == val {
|
||||
queue.push((x - 1, y));
|
||||
sides -= 1;
|
||||
}
|
||||
if x < w - 1 && input[y][x + 1] == val {
|
||||
queue.push((x + 1, y));
|
||||
sides -= 1;
|
||||
}
|
||||
if y > 0 && input[y - 1][x] == val {
|
||||
queue.push((x, y - 1));
|
||||
sides -= 1;
|
||||
}
|
||||
if y < h - 1 && input[y + 1][x] == val {
|
||||
queue.push((x, y + 1));
|
||||
sides -= 1;
|
||||
}
|
||||
|
||||
border_size += sides;
|
||||
}
|
||||
|
||||
region_size.push(size);
|
||||
region_border_size.push(border_size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
region_size
|
||||
.iter()
|
||||
.zip(region_border_size.iter())
|
||||
.map(|(a, b)| *a * *b)
|
||||
.sum()
|
||||
}
|
||||
|
||||
#[aoc(day12, part2)]
|
||||
fn part2(input: &Input) -> usize {
|
||||
let w = input[0].len();
|
||||
let h = input.len();
|
||||
|
||||
let mut region_idx = -1;
|
||||
let mut region_border_size = vec![];
|
||||
let mut region_size = vec![];
|
||||
let mut region = vec![vec![-1; w]; h];
|
||||
|
||||
for y in 0..h {
|
||||
for x in 0..w {
|
||||
let val = input[y][x];
|
||||
if region[y][x] == -1 {
|
||||
region_idx += 1;
|
||||
let mut queue = vec![(x, y)];
|
||||
let mut size = 0;
|
||||
let mut border_size = 0;
|
||||
|
||||
while let Some((x, y)) = queue.pop() {
|
||||
if region[y][x] != -1 {
|
||||
continue;
|
||||
}
|
||||
|
||||
region[y][x] = region_idx;
|
||||
size += 1;
|
||||
|
||||
let mut up = false;
|
||||
let mut down = false;
|
||||
let mut left = false;
|
||||
let mut right = false;
|
||||
let mut up_right = false;
|
||||
let mut down_left = false;
|
||||
|
||||
if x > 0 && input[y][x - 1] == val {
|
||||
queue.push((x - 1, y));
|
||||
left = true;
|
||||
}
|
||||
if x < w - 1 && input[y][x + 1] == val {
|
||||
queue.push((x + 1, y));
|
||||
right = true;
|
||||
}
|
||||
if y > 0 && input[y - 1][x] == val {
|
||||
queue.push((x, y - 1));
|
||||
up = true;
|
||||
}
|
||||
if y < h - 1 && input[y + 1][x] == val {
|
||||
queue.push((x, y + 1));
|
||||
down = true;
|
||||
}
|
||||
if y > 0 && x < w - 1 && input[y - 1][x + 1] == val {
|
||||
up_right = true;
|
||||
}
|
||||
if y < h - 1 && x > 0 && input[y + 1][x - 1] == val {
|
||||
down_left = true;
|
||||
}
|
||||
|
||||
if !up && !left {
|
||||
border_size += 2;
|
||||
}
|
||||
if !down && !right {
|
||||
border_size += 2;
|
||||
}
|
||||
if up && right && !up_right {
|
||||
border_size += 2;
|
||||
}
|
||||
if down && left && !down_left {
|
||||
border_size += 2;
|
||||
}
|
||||
}
|
||||
|
||||
region_size.push(size);
|
||||
region_border_size.push(border_size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
region_size
|
||||
.iter()
|
||||
.zip(region_border_size.iter())
|
||||
.map(|(a, b)| *a * *b)
|
||||
.sum()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn part1_example() {
|
||||
assert_eq!(part1(&parse("AAAA\nBBCD\nBBCC\nEEEC")), 140);
|
||||
assert_eq!(part1(&parse("OOOOO\nOXOXO\nOOOOO\nOXOXO\nOOOOO")), 772);
|
||||
assert_eq!(part1(&parse("RRRRIICCFF\nRRRRIICCCF\nVVRRRCCFFF\nVVRCCCJFFF\nVVVVCJJCFE\nVVIVCCJJEE\nVVIIICJJEE\nMIIIIIJJEE\nMIIISIJEEE\nMMMISSJEEE")), 1930);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn part2_example() {
|
||||
assert_eq!(part2(&parse("AAAA\nBBCD\nBBCC\nEEEC")), 80);
|
||||
assert_eq!(part2(&parse("OOO\nOXO\nOOO")), 68);
|
||||
assert_eq!(part2(&parse("OOOOO\nOXXXX\nOOOOO\nOXXXX\nOOOOO")), 236);
|
||||
assert_eq!(part2(&parse("RRRRIICCFF\nRRRRIICCCF\nVVRRRCCFFF\nVVRCCCJFFF\nVVVVCJJCFE\nVVIVCCJJEE\nVVIIICJJEE\nMIIIIIJJEE\nMIIISIJEEE\nMMMISSJEEE")), 1206);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
use aoc_runner_derive::{aoc, aoc_generator};
|
||||
use nom::bytes::complete::tag;
|
||||
use nom::character::complete::i64 as parse_u64;
|
||||
use nom::multi::separated_list0;
|
||||
use nom::IResult;
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
struct Case {
|
||||
button_a: (i64, i64),
|
||||
button_b: (i64, i64),
|
||||
prize: (i64, i64),
|
||||
}
|
||||
|
||||
type Input = Vec<Case>;
|
||||
|
||||
#[aoc_generator(day13)]
|
||||
fn parse(input: &str) -> Input {
|
||||
separated_list0(tag("\n\n"), parse_case)(input).unwrap().1
|
||||
}
|
||||
|
||||
fn parse_case(input: &str) -> IResult<&str, Case> {
|
||||
let (input, _) = tag("Button A: X+")(input)?;
|
||||
let (input, button_a_x) = parse_u64(input)?;
|
||||
let (input, _) = tag(", Y+")(input)?;
|
||||
let (input, button_a_y) = parse_u64(input)?;
|
||||
let (input, _) = tag("\nButton B: X+")(input)?;
|
||||
let (input, button_b_x) = parse_u64(input)?;
|
||||
let (input, _) = tag(", Y+")(input)?;
|
||||
let (input, button_b_y) = parse_u64(input)?;
|
||||
let (input, _) = tag("\nPrize: X=")(input)?;
|
||||
let (input, prize_x) = parse_u64(input)?;
|
||||
let (input, _) = tag(", Y=")(input)?;
|
||||
let (input, prize_y) = parse_u64(input)?;
|
||||
|
||||
Ok((
|
||||
input,
|
||||
Case {
|
||||
button_a: (button_a_x, button_a_y),
|
||||
button_b: (button_b_x, button_b_y),
|
||||
prize: (prize_x, prize_y),
|
||||
},
|
||||
))
|
||||
}
|
||||
|
||||
#[aoc(day13, part1)]
|
||||
fn part1(input: &Input) -> i64 {
|
||||
input
|
||||
.iter()
|
||||
.copied()
|
||||
.map(|case| {
|
||||
let vec_a = case.button_a;
|
||||
let vec_b = case.button_b;
|
||||
let vec_p = case.prize;
|
||||
|
||||
if (vec_p.0 * vec_b.1 - vec_b.0 * vec_p.1) % (vec_a.0 * vec_b.1 - vec_b.0 * vec_a.1)
|
||||
== 0
|
||||
&& (vec_a.0 * vec_p.1 - vec_p.0 * vec_a.1) % (vec_a.0 * vec_b.1 - vec_b.0 * vec_a.1)
|
||||
== 0
|
||||
{
|
||||
let a = (vec_p.0 * vec_b.1 - vec_b.0 * vec_p.1)
|
||||
/ (vec_a.0 * vec_b.1 - vec_b.0 * vec_a.1);
|
||||
let b = (vec_a.0 * vec_p.1 - vec_p.0 * vec_a.1)
|
||||
/ (vec_a.0 * vec_b.1 - vec_b.0 * vec_a.1);
|
||||
a * 3 + b
|
||||
} else {
|
||||
0
|
||||
}
|
||||
})
|
||||
.sum()
|
||||
}
|
||||
|
||||
#[aoc(day13, part2)]
|
||||
fn part2(input: &Input) -> i64 {
|
||||
input
|
||||
.iter()
|
||||
.copied()
|
||||
.map(|case| {
|
||||
let vec_a = case.button_a;
|
||||
let vec_b = case.button_b;
|
||||
let vec_p = case.prize;
|
||||
let vec_p = (vec_p.0 + 10_000_000_000_000, vec_p.1 + 10_000_000_000_000);
|
||||
|
||||
if (vec_p.0 * vec_b.1 - vec_b.0 * vec_p.1) % (vec_a.0 * vec_b.1 - vec_b.0 * vec_a.1)
|
||||
== 0
|
||||
&& (vec_a.0 * vec_p.1 - vec_p.0 * vec_a.1) % (vec_a.0 * vec_b.1 - vec_b.0 * vec_a.1)
|
||||
== 0
|
||||
{
|
||||
let a = (vec_p.0 * vec_b.1 - vec_b.0 * vec_p.1)
|
||||
/ (vec_a.0 * vec_b.1 - vec_b.0 * vec_a.1);
|
||||
let b = (vec_a.0 * vec_p.1 - vec_p.0 * vec_a.1)
|
||||
/ (vec_a.0 * vec_b.1 - vec_b.0 * vec_a.1);
|
||||
a * 3 + b
|
||||
} else {
|
||||
0
|
||||
}
|
||||
})
|
||||
.sum()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn part1_example() {
|
||||
assert_eq!(
|
||||
part1(&parse(
|
||||
"Button A: X+94, Y+34
|
||||
Button B: X+22, Y+67
|
||||
Prize: X=8400, Y=5400"
|
||||
)),
|
||||
280
|
||||
);
|
||||
assert_eq!(
|
||||
part1(&parse(
|
||||
"Button A: X+94, Y+34
|
||||
Button B: X+22, Y+67
|
||||
Prize: X=8400, Y=5400
|
||||
|
||||
Button A: X+26, Y+66
|
||||
Button B: X+67, Y+21
|
||||
Prize: X=12748, Y=12176
|
||||
|
||||
Button A: X+17, Y+86
|
||||
Button B: X+84, Y+37
|
||||
Prize: X=7870, Y=6450
|
||||
|
||||
Button A: X+69, Y+23
|
||||
Button B: X+27, Y+71
|
||||
Prize: X=18641, Y=10279"
|
||||
)),
|
||||
480
|
||||
);
|
||||
assert_eq!(
|
||||
part1(&parse(include_str!("../input/2024/day13.txt").trim_end())),
|
||||
28753
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn part2_example() {
|
||||
assert_eq!(
|
||||
part2(&parse(include_str!("../input/2024/day13.txt").trim_end())),
|
||||
102718967795500
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,127 @@
|
||||
use aoc_runner_derive::{aoc, aoc_generator};
|
||||
|
||||
use nom::{
|
||||
bytes::complete::tag,
|
||||
character::complete::{i64 as parse_i64, newline, space1},
|
||||
error::Error,
|
||||
multi::separated_list0,
|
||||
sequence::{preceded, separated_pair},
|
||||
};
|
||||
|
||||
use num_modular::ModularPow;
|
||||
|
||||
type Input = Vec<((i64, i64), (i64, i64))>;
|
||||
|
||||
#[aoc_generator(day14)]
|
||||
fn parse(input: &str) -> Input {
|
||||
separated_list0(
|
||||
newline::<&str, Error<&str>>,
|
||||
separated_pair(
|
||||
preceded(tag("p="), separated_pair(parse_i64, tag(","), parse_i64)),
|
||||
space1,
|
||||
preceded(tag("v="), separated_pair(parse_i64, tag(","), parse_i64)),
|
||||
),
|
||||
)(input)
|
||||
.unwrap()
|
||||
.1
|
||||
}
|
||||
|
||||
#[aoc(day14, part1)]
|
||||
fn part1(input: &Input) -> i64 {
|
||||
#[cfg(test)]
|
||||
let (w, h) = (7, 11);
|
||||
#[cfg(not(test))]
|
||||
let (w, h) = (101, 103);
|
||||
|
||||
let mut quadrants = [0, 0, 0, 0];
|
||||
|
||||
input.iter().copied().for_each(|(p, v)| {
|
||||
let (px, py) = (p.0, p.1);
|
||||
let (vx, vy) = (v.0 + w, v.1 + h);
|
||||
let (ex, ey) = ((px + vx * 100) % w, (py + vy * 100) % h);
|
||||
if ex < w / 2 && ey < h / 2 {
|
||||
quadrants[0] += 1;
|
||||
} else if ex > (w - 1) / 2 && ey < h / 2 {
|
||||
quadrants[1] += 1;
|
||||
} else if ex < w / 2 && ey > (h - 1) / 2 {
|
||||
quadrants[2] += 1;
|
||||
} else if ex > (w - 1) / 2 && ey > (h - 1) / 2 {
|
||||
quadrants[3] += 1;
|
||||
}
|
||||
});
|
||||
|
||||
quadrants.iter().product()
|
||||
}
|
||||
|
||||
#[aoc(day14, part2)]
|
||||
fn part2(input: &Input) -> i64 {
|
||||
let (w, h) = (101, 103);
|
||||
|
||||
let mut offset_x = 0;
|
||||
let mut min_var = f64::INFINITY;
|
||||
for t in 0..w {
|
||||
let values = input.iter().map(|((p, _), (v, _))| (p + t * (v + w)) % w);
|
||||
let mean = values.clone().sum::<i64>() as f64 / input.len() as f64;
|
||||
let variance = values
|
||||
.map(|v| (v as f64 - mean) * (v as f64 - mean))
|
||||
.sum::<f64>()
|
||||
/ input.len() as f64;
|
||||
if variance < min_var {
|
||||
min_var = variance;
|
||||
offset_x = t;
|
||||
}
|
||||
}
|
||||
|
||||
let mut offset_y = 0;
|
||||
let mut min_var = f64::INFINITY;
|
||||
for t in 0..h {
|
||||
let values = input.iter().map(|((_, p), (_, v))| (p + t * (v + h)) % h);
|
||||
let mean = values.clone().sum::<i64>() as f64 / input.len() as f64;
|
||||
let variance = values
|
||||
.map(|v| (v as f64 - mean) * (v as f64 - mean))
|
||||
.sum::<f64>()
|
||||
/ input.len() as f64;
|
||||
if variance < min_var {
|
||||
min_var = variance;
|
||||
offset_y = t;
|
||||
}
|
||||
}
|
||||
|
||||
let invmod_x = (w as u64).powm((h - 2) as u64, &(h as u64)) as i64;
|
||||
let invmod_y = (h as u64).powm((w - 2) as u64, &(w as u64)) as i64;
|
||||
(offset_x * h * invmod_x + offset_y * w * invmod_y) % (w * h)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn part1_example() {
|
||||
assert_eq!(
|
||||
part1(&parse(
|
||||
"p=0,4 v=3,-3
|
||||
p=6,3 v=-1,-3
|
||||
p=10,3 v=-1,2
|
||||
p=2,0 v=2,-1
|
||||
p=0,0 v=1,3
|
||||
p=3,0 v=-2,-2
|
||||
p=7,6 v=-1,-3
|
||||
p=3,0 v=-1,-2
|
||||
p=9,3 v=2,3
|
||||
p=7,3 v=-1,2
|
||||
p=2,4 v=2,-3
|
||||
p=9,5 v=-3,-3"
|
||||
)),
|
||||
12
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn part2_example() {
|
||||
assert_eq!(
|
||||
part2(&parse(include_str!("../input/2024/day14.txt").trim_end())),
|
||||
6398
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,417 @@
|
||||
use aoc_runner_derive::{aoc, aoc_generator};
|
||||
|
||||
use hashbrown::HashSet;
|
||||
use nom::branch::alt;
|
||||
use nom::character::complete::{multispace0, one_of};
|
||||
use nom::combinator::map;
|
||||
use nom::error::Error;
|
||||
use nom::multi::many1;
|
||||
use nom::sequence::preceded;
|
||||
use nom::{character::complete::newline, multi::separated_list0, sequence::separated_pair};
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
enum Cell {
|
||||
Wall,
|
||||
Empty,
|
||||
Box,
|
||||
Sub,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
enum Cell2 {
|
||||
Wall,
|
||||
Empty,
|
||||
BoxLeft,
|
||||
BoxRight,
|
||||
Sub,
|
||||
}
|
||||
|
||||
type Input = (Vec<Vec<Cell>>, Vec<(i32, i32)>);
|
||||
|
||||
#[aoc_generator(day15)]
|
||||
fn parse(input: &str) -> Input {
|
||||
separated_pair(
|
||||
separated_list0(
|
||||
newline::<&str, Error<&str>>,
|
||||
many1(map(one_of("#.O@"), |c| match c {
|
||||
'#' => Cell::Wall,
|
||||
'.' => Cell::Empty,
|
||||
'O' => Cell::Box,
|
||||
'@' => Cell::Sub,
|
||||
_ => panic!("Invalid cell"),
|
||||
})),
|
||||
),
|
||||
multispace0,
|
||||
many1(map(
|
||||
alt((preceded(newline, one_of("^v<>")), one_of("^v<>"))),
|
||||
|c| match c {
|
||||
'^' => (0, -1),
|
||||
'v' => (0, 1),
|
||||
'<' => (-1, 0),
|
||||
'>' => (1, 0),
|
||||
_ => panic!("Invalid direction"),
|
||||
},
|
||||
)),
|
||||
)(input)
|
||||
.unwrap()
|
||||
.1
|
||||
}
|
||||
|
||||
#[aoc(day15, part1)]
|
||||
fn part1(input: &Input) -> usize {
|
||||
let mut map = input.0.clone();
|
||||
let steps = input.1.clone();
|
||||
let (mut x, mut y): (usize, usize) = map
|
||||
.iter()
|
||||
.enumerate()
|
||||
.find_map(|(y, row)| {
|
||||
row.iter().enumerate().find_map(|(x, cell)| match cell {
|
||||
Cell::Sub => Some((x, y)),
|
||||
_ => None,
|
||||
})
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
for step in steps {
|
||||
let (dx, dy) = step;
|
||||
let (nx, ny) = ((x as i32 + dx) as usize, (y as i32 + dy) as usize);
|
||||
match map[ny][nx] {
|
||||
Cell::Wall => continue,
|
||||
Cell::Box => {
|
||||
let (mut bx, mut by) = ((nx as i32 + dx) as usize, (ny as i32 + dy) as usize);
|
||||
while map[by][bx] == Cell::Box {
|
||||
(bx, by) = ((bx as i32 + dx) as usize, (by as i32 + dy) as usize);
|
||||
}
|
||||
match map[by][bx] {
|
||||
Cell::Box => unreachable!(),
|
||||
Cell::Sub => unreachable!(),
|
||||
Cell::Empty => {
|
||||
map[ny][nx] = Cell::Sub;
|
||||
map[by][bx] = Cell::Box;
|
||||
map[y][x] = Cell::Empty;
|
||||
y = ny;
|
||||
x = nx;
|
||||
}
|
||||
Cell::Wall => continue,
|
||||
}
|
||||
}
|
||||
Cell::Empty => {
|
||||
map[y][x] = Cell::Empty;
|
||||
map[ny][nx] = Cell::Sub;
|
||||
x = nx;
|
||||
y = ny;
|
||||
}
|
||||
Cell::Sub => unreachable!(),
|
||||
};
|
||||
}
|
||||
|
||||
map.iter()
|
||||
.enumerate()
|
||||
.map(|(y, line)| {
|
||||
line.iter()
|
||||
.enumerate()
|
||||
.map(|(x, cell)| match cell {
|
||||
Cell::Box => x + 100 * y,
|
||||
_ => 0,
|
||||
})
|
||||
.sum::<usize>()
|
||||
})
|
||||
.sum()
|
||||
}
|
||||
|
||||
#[aoc(day15, part2)]
|
||||
fn part2(input: &Input) -> usize {
|
||||
let mut map = input
|
||||
.0
|
||||
.clone()
|
||||
.iter()
|
||||
.map(|line| {
|
||||
line.iter()
|
||||
.flat_map(|cell| match cell {
|
||||
Cell::Wall => vec![Cell2::Wall, Cell2::Wall],
|
||||
Cell::Empty => vec![Cell2::Empty, Cell2::Empty],
|
||||
Cell::Box => vec![Cell2::BoxLeft, Cell2::BoxRight],
|
||||
Cell::Sub => vec![Cell2::Sub, Cell2::Empty],
|
||||
})
|
||||
.collect()
|
||||
})
|
||||
.collect::<Vec<Vec<Cell2>>>();
|
||||
let steps = input.1.clone();
|
||||
let (mut x, mut y): (usize, usize) = map
|
||||
.iter()
|
||||
.enumerate()
|
||||
.find_map(|(y, row)| {
|
||||
row.iter().enumerate().find_map(|(x, cell)| match cell {
|
||||
Cell2::Sub => Some((x, y)),
|
||||
_ => None,
|
||||
})
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
// for line in &map {
|
||||
// for cell in line {
|
||||
// print!(
|
||||
// "{}",
|
||||
// match cell {
|
||||
// Cell2::Wall => '#',
|
||||
// Cell2::Empty => ' ',
|
||||
// Cell2::BoxLeft => '[',
|
||||
// Cell2::BoxRight => ']',
|
||||
// Cell2::Sub => '@',
|
||||
// }
|
||||
// );
|
||||
// }
|
||||
// println!();
|
||||
// }
|
||||
|
||||
for step in steps {
|
||||
let (dx, dy) = step;
|
||||
let (nx, ny) = ((x as i32 + dx) as usize, (y as i32 + dy) as usize);
|
||||
match map[ny][nx] {
|
||||
Cell2::Wall => continue,
|
||||
Cell2::Empty => {
|
||||
map[y][x] = Cell2::Empty;
|
||||
map[ny][nx] = Cell2::Sub;
|
||||
x = nx;
|
||||
y = ny;
|
||||
}
|
||||
Cell2::Sub => unreachable!(),
|
||||
Cell2::BoxLeft => {
|
||||
if dy == 0 {
|
||||
let (mut bx, mut by) = ((x as i32 + dx) as usize, (y as i32 + dy) as usize);
|
||||
while map[by][bx] == Cell2::BoxLeft {
|
||||
(bx, by) = ((bx as i32 + 2 * dx) as usize, (by as i32 + 2 * dy) as usize);
|
||||
}
|
||||
match map[by][bx] {
|
||||
Cell2::BoxLeft | Cell2::BoxRight => unreachable!(),
|
||||
Cell2::Sub => unreachable!(),
|
||||
Cell2::Empty => {
|
||||
for i in nx..bx {
|
||||
map[y][bx + nx - i] = map[y][bx + nx - i - 1];
|
||||
}
|
||||
map[ny][nx] = Cell2::Sub;
|
||||
map[y][x] = Cell2::Empty;
|
||||
y = ny;
|
||||
x = nx;
|
||||
}
|
||||
Cell2::Wall => continue,
|
||||
}
|
||||
} else {
|
||||
let mut pushing = vec![(x, y)];
|
||||
let mut pushed = HashSet::new();
|
||||
let mut pushable = true;
|
||||
while let Some(test) = pushing.pop() {
|
||||
match map[(test.1 as i32 + dy) as usize][test.0] {
|
||||
Cell2::Sub => unreachable!(),
|
||||
Cell2::Wall => {
|
||||
pushable = false;
|
||||
break;
|
||||
}
|
||||
Cell2::Empty => {
|
||||
pushed.insert(test);
|
||||
}
|
||||
Cell2::BoxLeft => {
|
||||
pushed.insert(test);
|
||||
pushing.push((test.0, (test.1 as i32 + dy) as usize));
|
||||
pushing.push((test.0 + 1, (test.1 as i32 + dy) as usize));
|
||||
}
|
||||
Cell2::BoxRight => {
|
||||
pushed.insert(test);
|
||||
pushing.push((test.0, (test.1 as i32 + dy) as usize));
|
||||
pushing.push((test.0 - 1, (test.1 as i32 + dy) as usize));
|
||||
}
|
||||
}
|
||||
}
|
||||
if !pushable {
|
||||
continue;
|
||||
} else {
|
||||
let moved = pushed.clone();
|
||||
let mut pushed = pushed.into_iter().collect::<Vec<(usize, usize)>>();
|
||||
pushed.sort_by(|a, b| (b.1 as i32 * dy).cmp(&(a.1 as i32 * dy)));
|
||||
pushed.into_iter().for_each(|(x, y)| {
|
||||
map[(y as i32 + dy) as usize][x] = map[y][x];
|
||||
if !moved.contains(&(x, (y as i32 - dy) as usize)) {
|
||||
map[y][x] = Cell2::Empty;
|
||||
}
|
||||
});
|
||||
y = ny;
|
||||
x = nx;
|
||||
}
|
||||
}
|
||||
}
|
||||
Cell2::BoxRight => {
|
||||
if dy == 0 {
|
||||
let (mut bx, mut by) = ((x as i32 + dx) as usize, (y as i32 + dy) as usize);
|
||||
while map[by][bx] == Cell2::BoxRight {
|
||||
(bx, by) = ((bx as i32 + 2 * dx) as usize, (by as i32 + 2 * dy) as usize);
|
||||
}
|
||||
match map[by][bx] {
|
||||
Cell2::BoxLeft | Cell2::BoxRight => unreachable!(),
|
||||
Cell2::Sub => unreachable!(),
|
||||
Cell2::Empty => {
|
||||
for i in bx..nx {
|
||||
map[y][i] = map[y][i + 1];
|
||||
}
|
||||
map[ny][nx] = Cell2::Sub;
|
||||
map[y][x] = Cell2::Empty;
|
||||
y = ny;
|
||||
x = nx;
|
||||
}
|
||||
Cell2::Wall => continue,
|
||||
}
|
||||
} else {
|
||||
let mut pushing = vec![(x, y)];
|
||||
let mut pushed = HashSet::new();
|
||||
let mut pushable = true;
|
||||
while let Some(test) = pushing.pop() {
|
||||
match map[(test.1 as i32 + dy) as usize][test.0] {
|
||||
Cell2::Sub => unreachable!(),
|
||||
Cell2::Wall => {
|
||||
pushable = false;
|
||||
break;
|
||||
}
|
||||
Cell2::Empty => {
|
||||
pushed.insert(test);
|
||||
}
|
||||
Cell2::BoxLeft => {
|
||||
pushed.insert(test);
|
||||
pushing.push((test.0, (test.1 as i32 + dy) as usize));
|
||||
pushing.push((test.0 + 1, (test.1 as i32 + dy) as usize));
|
||||
}
|
||||
Cell2::BoxRight => {
|
||||
pushed.insert(test);
|
||||
pushing.push((test.0, (test.1 as i32 + dy) as usize));
|
||||
pushing.push((test.0 - 1, (test.1 as i32 + dy) as usize));
|
||||
}
|
||||
}
|
||||
}
|
||||
if !pushable {
|
||||
continue;
|
||||
} else {
|
||||
let moved = pushed.clone();
|
||||
let mut pushed = pushed.into_iter().collect::<Vec<(usize, usize)>>();
|
||||
pushed.sort_by(|a, b| (b.1 as i32 * dy).cmp(&(a.1 as i32 * dy)));
|
||||
pushed.into_iter().for_each(|(x, y)| {
|
||||
map[(y as i32 + dy) as usize][x] = map[y][x];
|
||||
if !moved.contains(&(x, (y as i32 - dy) as usize)) {
|
||||
map[y][x] = Cell2::Empty;
|
||||
}
|
||||
});
|
||||
y = ny;
|
||||
x = nx;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// println!("\nStep: {:?}", step);
|
||||
// for line in &map {
|
||||
// for cell in line {
|
||||
// print!(
|
||||
// "{}",
|
||||
// match cell {
|
||||
// Cell2::Wall => '#',
|
||||
// Cell2::Empty => ' ',
|
||||
// Cell2::BoxLeft => '[',
|
||||
// Cell2::BoxRight => ']',
|
||||
// Cell2::Sub => '@',
|
||||
// }
|
||||
// );
|
||||
// }
|
||||
// println!();
|
||||
// }
|
||||
}
|
||||
|
||||
map.iter()
|
||||
.enumerate()
|
||||
.map(|(y, line)| {
|
||||
line.iter()
|
||||
.enumerate()
|
||||
.map(|(x, cell)| match cell {
|
||||
Cell2::BoxLeft => x + 100 * y,
|
||||
_ => 0,
|
||||
})
|
||||
.sum::<usize>()
|
||||
})
|
||||
.sum()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn part1_example() {
|
||||
assert_eq!(
|
||||
part1(&parse(
|
||||
"########
|
||||
#..O.O.#
|
||||
##@.O..#
|
||||
#...O..#
|
||||
#.#.O..#
|
||||
#...O..#
|
||||
#......#
|
||||
########
|
||||
|
||||
<^^>>>vv<v>>v<<"
|
||||
)),
|
||||
2028
|
||||
);
|
||||
assert_eq!(
|
||||
part1(&parse(
|
||||
"##########
|
||||
#..O..O.O#
|
||||
#......O.#
|
||||
#.OO..O.O#
|
||||
#..O@..O.#
|
||||
#O#..O...#
|
||||
#O..O..O.#
|
||||
#.OO.O.OO#
|
||||
#....O...#
|
||||
##########
|
||||
|
||||
<vv>^<v^>v>^vv^v>v<>v^v<v<^vv<<<^><<><>>v<vvv<>^v^>^<<<><<v<<<v^vv^v>^
|
||||
vvv<<^>^v^^><<>>><>^<<><^vv^^<>vvv<>><^^v>^>vv<>v<<<<v<^v>^<^^>>>^<v<v
|
||||
><>vv>v^v^<>><>>>><^^>vv>v<^^^>>v^v^<^^>v^^>v^<^v>v<>>v^v^<v>v^^<^^vv<
|
||||
<<v<^>>^^^^>>>v^<>vvv^><v<<<>^^^vv^<vvv>^>v<^^^^v<>^>vvvv><>>v^<<^^^^^
|
||||
^><^><>>><>^^<<^^v>>><^<v>^<vv>>v>>>^v><>^v><<<<v>>v<v<v>vvv>^<><<>^><
|
||||
^>><>^v<><^vvv<^^<><v<<<<<><^v<<<><<<^^<v<^^^><^>>^<v^><<<^>>^v<v^v<v^
|
||||
>^>>^v>vv>^<<^v<>><<><<v<<v><>v<^vv<<<>^^v^>^^>>><<^v>>v^v><^^>>^<>vv^
|
||||
<><^^>^^^<><vvvvv^v<v<<>^v<v>v<<^><<><<><<<^^<<<^<<>><<><^^^>^^<>^>v<>
|
||||
^^>vv<^v^v<vv>^<><v<^v>^^^>>>^^vvv^>vvv<>>>^<^>>>>>^<<^v>^vvv<>^<><<v>
|
||||
v^^>>><<^^<>>^v^<v^vv<>v^<<>^<^v^v><^<<<><<^<v><v<>vv>>v><v^<vv<>v^<<^"
|
||||
)),
|
||||
10092
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn part2_example() {
|
||||
assert_eq!(
|
||||
part2(&parse(
|
||||
"##########
|
||||
#..O..O.O#
|
||||
#......O.#
|
||||
#.OO..O.O#
|
||||
#..O@..O.#
|
||||
#O#..O...#
|
||||
#O..O..O.#
|
||||
#.OO.O.OO#
|
||||
#....O...#
|
||||
##########
|
||||
|
||||
<vv>^<v^>v>^vv^v>v<>v^v<v<^vv<<<^><<><>>v<vvv<>^v^>^<<<><<v<<<v^vv^v>^
|
||||
vvv<<^>^v^^><<>>><>^<<><^vv^^<>vvv<>><^^v>^>vv<>v<<<<v<^v>^<^^>>>^<v<v
|
||||
><>vv>v^v^<>><>>>><^^>vv>v<^^^>>v^v^<^^>v^^>v^<^v>v<>>v^v^<v>v^^<^^vv<
|
||||
<<v<^>>^^^^>>>v^<>vvv^><v<<<>^^^vv^<vvv>^>v<^^^^v<>^>vvvv><>>v^<<^^^^^
|
||||
^><^><>>><>^^<<^^v>>><^<v>^<vv>>v>>>^v><>^v><<<<v>>v<v<v>vvv>^<><<>^><
|
||||
^>><>^v<><^vvv<^^<><v<<<<<><^v<<<><<<^^<v<^^^><^>>^<v^><<<^>>^v<v^v<v^
|
||||
>^>>^v>vv>^<<^v<>><<><<v<<v><>v<^vv<<<>^^v^>^^>>><<^v>>v^v><^^>>^<>vv^
|
||||
<><^^>^^^<><vvvvv^v<v<<>^v<v>v<<^><<><<><<<^^<<<^<<>><<><^^^>^^<>^>v<>
|
||||
^^>vv<^v^v<vv>^<><v<^v>^^^>>>^^vvv^>vvv<>>>^<^>>>>>^<<^v>^vvv<>^<><<v>
|
||||
v^^>>><<^^<>>^v^<v^vv<>v^<<>^<^v^v><^<<<><<^<v><v<>vv>>v><v^<vv<>v^<<^"
|
||||
)),
|
||||
9021
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,287 @@
|
||||
use std::{
|
||||
cmp::{Ordering, Reverse},
|
||||
collections::BinaryHeap,
|
||||
};
|
||||
|
||||
use aoc_runner_derive::{aoc, aoc_generator};
|
||||
use hashbrown::HashSet;
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
enum Cell {
|
||||
Empty,
|
||||
Wall,
|
||||
}
|
||||
|
||||
type Input = ((usize, usize), (usize, usize), Vec<Vec<Cell>>);
|
||||
|
||||
#[aoc_generator(day16)]
|
||||
fn parse(input: &str) -> Input {
|
||||
let mut pos = (0, 0);
|
||||
let mut end = (0, 0);
|
||||
let map = input
|
||||
.lines()
|
||||
.enumerate()
|
||||
.map(|(y, line)| {
|
||||
line.chars()
|
||||
.enumerate()
|
||||
.map(|(x, c)| match c {
|
||||
'#' => Cell::Wall,
|
||||
'.' => Cell::Empty,
|
||||
'E' => {
|
||||
end = (x, y);
|
||||
Cell::Empty
|
||||
}
|
||||
'S' => {
|
||||
pos = (x, y);
|
||||
Cell::Empty
|
||||
}
|
||||
_ => unreachable!(),
|
||||
})
|
||||
.collect()
|
||||
})
|
||||
.collect();
|
||||
(pos, end, map)
|
||||
}
|
||||
|
||||
#[aoc(day16, part1)]
|
||||
fn part1(input: &Input) -> i64 {
|
||||
let start = (input.0 .0 as i64, input.0 .1 as i64);
|
||||
let end = (input.1 .0 as i64, input.1 .1 as i64);
|
||||
let map = input.2.clone();
|
||||
let w = map[0].len();
|
||||
let h = map.len();
|
||||
let mut d = vec![vec![[i64::MAX; 4]; w]; h];
|
||||
|
||||
#[allow(clippy::type_complexity)]
|
||||
let mut queue: BinaryHeap<Reverse<(i64, i64, i64, i64, i64)>> = BinaryHeap::new();
|
||||
d[start.1 as usize][start.0 as usize][1] = 0;
|
||||
queue.push(Reverse((0, start.0, start.1, 1, 0)));
|
||||
|
||||
while let Some(Reverse((dist, pos_x, pos_y, dir_x, dir_y))) = queue.pop() {
|
||||
let dir_num = match (dir_x, dir_y) {
|
||||
(0, -1) => 0,
|
||||
(1, 0) => 1,
|
||||
(0, 1) => 2,
|
||||
(-1, 0) => 3,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
if d[pos_y as usize][pos_x as usize][dir_num] < dist {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (pos_x, pos_y) == end {
|
||||
break;
|
||||
}
|
||||
|
||||
if map[(pos_y + dir_y) as usize][(pos_x + dir_x) as usize] == Cell::Empty {
|
||||
let dist_next = &mut d[(pos_y + dir_y) as usize][(pos_x + dir_x) as usize][dir_num];
|
||||
if (dist + 1).cmp(dist_next) == Ordering::Less {
|
||||
*dist_next = dist + 1;
|
||||
queue.push(Reverse((
|
||||
*dist_next,
|
||||
pos_x + dir_x,
|
||||
pos_y + dir_y,
|
||||
dir_x,
|
||||
dir_y,
|
||||
)));
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
let dist_next = &mut d[pos_y as usize][pos_x as usize][(dir_num + 3) % 4];
|
||||
if (dist + 1000).cmp(dist_next) == Ordering::Less {
|
||||
*dist_next = dist + 1000;
|
||||
queue.push(Reverse((*dist_next, pos_x, pos_y, dir_y, -dir_x)));
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
let dist_next = &mut d[pos_y as usize][pos_x as usize][(dir_num + 1) % 4];
|
||||
if (dist + 1000).cmp(dist_next) == Ordering::Less {
|
||||
*dist_next = dist + 1000;
|
||||
queue.push(Reverse((*dist_next, pos_x, pos_y, -dir_y, dir_x)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
*d[end.1 as usize][end.0 as usize].iter().min().unwrap()
|
||||
}
|
||||
|
||||
#[aoc(day16, part2)]
|
||||
fn part2(input: &Input) -> i64 {
|
||||
let start = (input.0 .0 as i64, input.0 .1 as i64);
|
||||
let end = (input.1 .0 as i64, input.1 .1 as i64);
|
||||
let map = input.2.clone();
|
||||
let w = map[0].len();
|
||||
let h = map.len();
|
||||
let mut d = vec![vec![[i64::MAX; 4]; w]; h];
|
||||
let mut b = vec![vec![[vec![], vec![], vec![], vec![]]; w]; h];
|
||||
|
||||
#[allow(clippy::type_complexity)]
|
||||
let mut queue: BinaryHeap<Reverse<(i64, i64, i64, i64, i64)>> = BinaryHeap::new();
|
||||
d[start.1 as usize][start.0 as usize][1] = 0;
|
||||
queue.push(Reverse((0, start.0, start.1, 1, 0)));
|
||||
|
||||
while let Some(Reverse((dist, pos_x, pos_y, dir_x, dir_y))) = queue.pop() {
|
||||
let dir_num = match (dir_x, dir_y) {
|
||||
(0, -1) => 0,
|
||||
(1, 0) => 1,
|
||||
(0, 1) => 2,
|
||||
(-1, 0) => 3,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
if d[pos_y as usize][pos_x as usize][dir_num] < dist {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (pos_x, pos_y) == end {
|
||||
break;
|
||||
}
|
||||
|
||||
if map[(pos_y + dir_y) as usize][(pos_x + dir_x) as usize] == Cell::Empty {
|
||||
let dist_next = &mut d[(pos_y + dir_y) as usize][(pos_x + dir_x) as usize][dir_num];
|
||||
let back_next = &mut b[(pos_y + dir_y) as usize][(pos_x + dir_x) as usize][dir_num];
|
||||
if (dist + 1).cmp(dist_next) == Ordering::Less {
|
||||
*dist_next = dist + 1;
|
||||
back_next.clear();
|
||||
back_next.push((pos_x, pos_y, dir_num));
|
||||
queue.push(Reverse((
|
||||
*dist_next,
|
||||
pos_x + dir_x,
|
||||
pos_y + dir_y,
|
||||
dir_x,
|
||||
dir_y,
|
||||
)));
|
||||
} else if (dist + 1).cmp(dist_next) == Ordering::Equal {
|
||||
back_next.push((pos_x, pos_y, dir_num));
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
let dist_next = &mut d[pos_y as usize][pos_x as usize][(dir_num + 3) % 4];
|
||||
let back_next = &mut b[pos_y as usize][pos_x as usize][(dir_num + 3) % 4];
|
||||
if (dist + 1000).cmp(dist_next) == Ordering::Less {
|
||||
*dist_next = dist + 1000;
|
||||
back_next.clear();
|
||||
back_next.push((pos_x, pos_y, dir_num));
|
||||
queue.push(Reverse((*dist_next, pos_x, pos_y, dir_y, -dir_x)));
|
||||
} else if (dist + 1000).cmp(dist_next) == Ordering::Equal {
|
||||
back_next.push((pos_x, pos_y, dir_num));
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
let dist_next = &mut d[pos_y as usize][pos_x as usize][(dir_num + 1) % 4];
|
||||
let back_next = &mut b[pos_y as usize][pos_x as usize][(dir_num + 1) % 4];
|
||||
if (dist + 1000).cmp(dist_next) == Ordering::Less {
|
||||
*dist_next = dist + 1000;
|
||||
back_next.clear();
|
||||
back_next.push((pos_x, pos_y, dir_num));
|
||||
queue.push(Reverse((*dist_next, pos_x, pos_y, -dir_y, dir_x)));
|
||||
} else if (dist + 1000).cmp(dist_next) == Ordering::Equal {
|
||||
back_next.push((pos_x, pos_y, dir_num));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let mut visited = HashSet::new();
|
||||
let dist_min = *d[end.1 as usize][end.0 as usize].iter().min().unwrap();
|
||||
let mut stack = vec![];
|
||||
d[end.1 as usize][end.0 as usize]
|
||||
.iter()
|
||||
.enumerate()
|
||||
.for_each(|(i, d)| {
|
||||
if *d == dist_min {
|
||||
stack.push((end.0, end.1, i));
|
||||
}
|
||||
});
|
||||
|
||||
while let Some((pos_x, pos_y, dir_num)) = stack.pop() {
|
||||
visited.insert((pos_x, pos_y));
|
||||
stack.append(&mut b[pos_y as usize][pos_x as usize][dir_num]);
|
||||
}
|
||||
|
||||
visited.len() as i64
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn part1_example() {
|
||||
assert_eq!(
|
||||
part1(&parse(
|
||||
"###############
|
||||
#.......#....E#
|
||||
#.#.###.#.###.#
|
||||
#.....#.#...#.#
|
||||
#.###.#####.#.#
|
||||
#.#.#.......#.#
|
||||
#.#.#####.###.#
|
||||
#...........#.#
|
||||
###.#.#####.#.#
|
||||
#...#.....#.#.#
|
||||
#.#.#.###.#.#.#
|
||||
#.....#...#.#.#
|
||||
#.###.#.#.#.#.#
|
||||
#S..#.....#...#
|
||||
###############"
|
||||
)),
|
||||
7036
|
||||
);
|
||||
// assert_eq!(
|
||||
// part1(&parse(include_str!("../input/2024/day16.txt"))),
|
||||
// 85420
|
||||
// );
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn part2_example() {
|
||||
assert_eq!(
|
||||
part2(&parse(
|
||||
"###############
|
||||
#.......#....E#
|
||||
#.#.###.#.###.#
|
||||
#.....#.#...#.#
|
||||
#.###.#####.#.#
|
||||
#.#.#.......#.#
|
||||
#.#.#####.###.#
|
||||
#...........#.#
|
||||
###.#.#####.#.#
|
||||
#...#.....#.#.#
|
||||
#.#.#.###.#.#.#
|
||||
#.....#...#.#.#
|
||||
#.###.#.#.#.#.#
|
||||
#S..#.....#...#
|
||||
###############"
|
||||
)),
|
||||
45
|
||||
);
|
||||
assert_eq!(
|
||||
part2(&parse(
|
||||
"#################
|
||||
#...#...#...#..E#
|
||||
#.#.#.#.#.#.#.#.#
|
||||
#.#.#.#...#...#.#
|
||||
#.#.#.#.###.#.#.#
|
||||
#...#.#.#.....#.#
|
||||
#.#.#.#.#.#####.#
|
||||
#.#...#.#.#.....#
|
||||
#.#.#####.#.###.#
|
||||
#.#.#.......#...#
|
||||
#.#.###.#####.###
|
||||
#.#.#...#.....#.#
|
||||
#.#.#.#####.###.#
|
||||
#.#.#.........#.#
|
||||
#.#.#.#########.#
|
||||
#S#.............#
|
||||
#################"
|
||||
)),
|
||||
64
|
||||
);
|
||||
// assert_eq!(part2(&parse(include_str!("../input/2024/day16.txt"))), 492);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
use aoc_runner_derive::{aoc, aoc_generator};
|
||||
use nom::bytes::complete::tag;
|
||||
use nom::character::complete::i64 as parse_i64;
|
||||
use nom::error::Error;
|
||||
use nom::multi::separated_list1;
|
||||
use nom::sequence::{preceded, separated_pair, tuple};
|
||||
|
||||
type Input = ((i64, i64, i64), Vec<i64>);
|
||||
|
||||
#[aoc_generator(day17)]
|
||||
fn parse(input: &str) -> Input {
|
||||
separated_pair(
|
||||
tuple((
|
||||
preceded(tag("Register A: "), parse_i64::<&str, Error<&str>>),
|
||||
preceded(tag("\nRegister B: "), parse_i64),
|
||||
preceded(tag("\nRegister C: "), parse_i64),
|
||||
)),
|
||||
tag("\n\n"),
|
||||
preceded(tag("Program: "), separated_list1(tag(","), parse_i64)),
|
||||
)(input)
|
||||
.unwrap()
|
||||
.1
|
||||
}
|
||||
|
||||
#[aoc(day17, part1)]
|
||||
fn part1(input: &Input) -> String {
|
||||
let mut registers = input.0;
|
||||
let program = input.1.clone();
|
||||
|
||||
let mut pc = 0;
|
||||
|
||||
let mut output = Vec::new();
|
||||
|
||||
loop {
|
||||
if pc >= program.len() {
|
||||
break;
|
||||
}
|
||||
|
||||
let instruction = program[pc];
|
||||
let literal_value = program[pc + 1];
|
||||
let combo_value = match program[pc + 1] {
|
||||
0 => 0,
|
||||
1 => 1,
|
||||
2 => 2,
|
||||
3 => 3,
|
||||
4 => registers.0,
|
||||
5 => registers.1,
|
||||
6 => registers.2,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
match instruction {
|
||||
0 => registers.0 >>= combo_value,
|
||||
1 => registers.1 ^= literal_value,
|
||||
2 => registers.1 = combo_value % 8,
|
||||
3 => {
|
||||
if registers.0 != 0 {
|
||||
pc = literal_value as usize;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
4 => registers.1 ^= registers.2,
|
||||
5 => output.push(combo_value % 8),
|
||||
6 => registers.1 = registers.0 >> combo_value,
|
||||
7 => registers.2 = registers.0 >> combo_value,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
|
||||
pc += 2;
|
||||
}
|
||||
|
||||
output
|
||||
.iter()
|
||||
.map(|x| x.to_string())
|
||||
.collect::<Vec<String>>()
|
||||
.join(",")
|
||||
}
|
||||
|
||||
#[aoc(day17, part2)]
|
||||
fn part2(input: &Input) -> i64 {
|
||||
let program = input.1.clone();
|
||||
let mut candidates = vec![0];
|
||||
|
||||
for target in program.iter().rev() {
|
||||
let mut new_candidates = vec![];
|
||||
for candidate in candidates {
|
||||
let a = candidate;
|
||||
new_candidates.extend(
|
||||
(0..8)
|
||||
.filter(|b| {
|
||||
let a = (a << 3) + b;
|
||||
let b = a % 8;
|
||||
let b = b ^ 5;
|
||||
let c = a >> b;
|
||||
let b = b ^ c;
|
||||
let b = b ^ 6;
|
||||
|
||||
b % 8 == *target
|
||||
})
|
||||
.map(|b| (a << 3) + b),
|
||||
);
|
||||
}
|
||||
candidates = new_candidates;
|
||||
}
|
||||
|
||||
candidates[0]
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn part1_example() {
|
||||
assert_eq!(
|
||||
part1(&parse(
|
||||
"Register A: 729
|
||||
Register B: 0
|
||||
Register C: 0
|
||||
|
||||
Program: 0,1,5,4,3,0"
|
||||
)),
|
||||
"4,6,3,5,6,3,5,2,1,0"
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,205 @@
|
||||
use std::collections::VecDeque;
|
||||
|
||||
use aoc_runner_derive::{aoc, aoc_generator};
|
||||
use nom::{
|
||||
bytes::complete::tag,
|
||||
character::complete::{i64 as parse_i64, newline},
|
||||
error::Error,
|
||||
multi::separated_list0,
|
||||
sequence::separated_pair,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
enum Cell {
|
||||
Empty,
|
||||
Byte,
|
||||
Visited,
|
||||
}
|
||||
|
||||
type Input = Vec<(i64, i64)>;
|
||||
|
||||
#[aoc_generator(day18)]
|
||||
fn parse(input: &str) -> Input {
|
||||
separated_list0(
|
||||
newline::<&str, Error<&str>>,
|
||||
separated_pair(parse_i64, tag(","), parse_i64),
|
||||
)(input)
|
||||
.unwrap()
|
||||
.1
|
||||
}
|
||||
|
||||
#[aoc(day18, part1)]
|
||||
fn part1(input: &Input) -> usize {
|
||||
#[cfg(not(test))]
|
||||
let (w, h, bytes) = (71, 71, &input[..1024]);
|
||||
#[cfg(test)]
|
||||
let (w, h, bytes) = (7, 7, &input[..12]);
|
||||
|
||||
let mut map = vec![vec![Cell::Empty; w]; h];
|
||||
|
||||
bytes
|
||||
.iter()
|
||||
.for_each(|(x, y)| map[*y as usize][*x as usize] = Cell::Byte);
|
||||
|
||||
let mut deque = VecDeque::new();
|
||||
deque.push_back((0, 0, 0));
|
||||
|
||||
while let Some((x, y, depth)) = deque.pop_front() {
|
||||
if x == w - 1 && y == h - 1 {
|
||||
return depth;
|
||||
}
|
||||
|
||||
if map[y][x] == Cell::Visited {
|
||||
continue;
|
||||
}
|
||||
|
||||
map[y][x] = Cell::Visited;
|
||||
|
||||
if x >= 1 && map[y][x - 1] == Cell::Empty {
|
||||
deque.push_back((x - 1, y, depth + 1));
|
||||
}
|
||||
if x < w - 1 && map[y][x + 1] == Cell::Empty {
|
||||
deque.push_back((x + 1, y, depth + 1));
|
||||
}
|
||||
if y >= 1 && map[y - 1][x] == Cell::Empty {
|
||||
deque.push_back((x, y - 1, depth + 1));
|
||||
}
|
||||
if y < h - 1 && map[y + 1][x] == Cell::Empty {
|
||||
deque.push_back((x, y + 1, depth + 1));
|
||||
}
|
||||
}
|
||||
|
||||
unreachable!()
|
||||
}
|
||||
|
||||
#[aoc(day18, part2)]
|
||||
fn part2(input: &Input) -> String {
|
||||
#[cfg(not(test))]
|
||||
let (w, h, min_idx) = (71, 71, 1024);
|
||||
#[cfg(test)]
|
||||
let (w, h, min_idx) = (7, 7, 12);
|
||||
|
||||
let (mut l, mut r) = (min_idx - 1, input.len() - 1);
|
||||
|
||||
while r - l > 1 {
|
||||
let mut possible = false;
|
||||
|
||||
let mut map = vec![vec![Cell::Empty; w]; h];
|
||||
|
||||
let bytes = (l + r) >> 1;
|
||||
|
||||
input[0..bytes]
|
||||
.iter()
|
||||
.for_each(|(x, y)| map[*y as usize][*x as usize] = Cell::Byte);
|
||||
|
||||
let mut deque = VecDeque::new();
|
||||
deque.push_back((0, 0, 0));
|
||||
|
||||
while let Some((x, y, depth)) = deque.pop_front() {
|
||||
if x == w - 1 && y == h - 1 {
|
||||
possible = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if map[y][x] == Cell::Visited {
|
||||
continue;
|
||||
}
|
||||
|
||||
map[y][x] = Cell::Visited;
|
||||
|
||||
if x >= 1 && map[y][x - 1] == Cell::Empty {
|
||||
deque.push_back((x - 1, y, depth + 1));
|
||||
}
|
||||
if x < w - 1 && map[y][x + 1] == Cell::Empty {
|
||||
deque.push_back((x + 1, y, depth + 1));
|
||||
}
|
||||
if y >= 1 && map[y - 1][x] == Cell::Empty {
|
||||
deque.push_back((x, y - 1, depth + 1));
|
||||
}
|
||||
if y < h - 1 && map[y + 1][x] == Cell::Empty {
|
||||
deque.push_back((x, y + 1, depth + 1));
|
||||
}
|
||||
}
|
||||
|
||||
if possible {
|
||||
l = bytes;
|
||||
} else {
|
||||
r = bytes;
|
||||
}
|
||||
}
|
||||
|
||||
format!("{},{}", input[l].0, input[l].1).to_string()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn part1_example() {
|
||||
assert_eq!(
|
||||
part1(&parse(
|
||||
"5,4
|
||||
4,2
|
||||
4,5
|
||||
3,0
|
||||
2,1
|
||||
6,3
|
||||
2,4
|
||||
1,5
|
||||
0,6
|
||||
3,3
|
||||
2,6
|
||||
5,1
|
||||
1,2
|
||||
5,5
|
||||
2,5
|
||||
6,5
|
||||
1,4
|
||||
0,4
|
||||
6,4
|
||||
1,1
|
||||
6,1
|
||||
1,0
|
||||
0,5
|
||||
1,6
|
||||
2,0"
|
||||
)),
|
||||
22
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn part2_example() {
|
||||
assert_eq!(
|
||||
part2(&parse(
|
||||
"5,4
|
||||
4,2
|
||||
4,5
|
||||
3,0
|
||||
2,1
|
||||
6,3
|
||||
2,4
|
||||
1,5
|
||||
0,6
|
||||
3,3
|
||||
2,6
|
||||
5,1
|
||||
1,2
|
||||
5,5
|
||||
2,5
|
||||
6,5
|
||||
1,4
|
||||
0,4
|
||||
6,4
|
||||
1,1
|
||||
6,1
|
||||
1,0
|
||||
0,5
|
||||
1,6
|
||||
2,0"
|
||||
)),
|
||||
"6,1"
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
use aoc_runner_derive::{aoc, aoc_generator};
|
||||
use nom::{
|
||||
bytes::complete::tag,
|
||||
character::complete::{newline, one_of},
|
||||
combinator::map,
|
||||
multi::{many1, separated_list1},
|
||||
sequence::separated_pair,
|
||||
IResult,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
enum Color {
|
||||
White,
|
||||
Blue,
|
||||
Black,
|
||||
Red,
|
||||
Green,
|
||||
}
|
||||
|
||||
type Input = (Vec<Vec<Color>>, Vec<Vec<Color>>);
|
||||
|
||||
#[aoc_generator(day19)]
|
||||
fn parse(input: &str) -> Input {
|
||||
separated_pair(
|
||||
separated_list1(tag(", "), many1(parse_color)),
|
||||
tag("\n\n"),
|
||||
separated_list1(newline, many1(parse_color)),
|
||||
)(input)
|
||||
.unwrap()
|
||||
.1
|
||||
}
|
||||
|
||||
fn parse_color(input: &str) -> IResult<&str, Color> {
|
||||
map(one_of("wubrg"), |c| match c {
|
||||
'w' => Color::White,
|
||||
'u' => Color::Blue,
|
||||
'b' => Color::Black,
|
||||
'r' => Color::Red,
|
||||
'g' => Color::Green,
|
||||
_ => unreachable!(),
|
||||
})(input)
|
||||
}
|
||||
|
||||
#[aoc(day19, part1)]
|
||||
fn part1(input: &Input) -> usize {
|
||||
let towels = &input.0;
|
||||
let patterns = &input.1;
|
||||
|
||||
patterns
|
||||
.iter()
|
||||
.map(|pattern| {
|
||||
let mut m = vec![0u64; pattern.len() + 1];
|
||||
m[0] = 1;
|
||||
|
||||
for i in 1..=pattern.len() {
|
||||
for towel in towels {
|
||||
if towel.len() <= i && &pattern[i - towel.len()..i] == towel {
|
||||
m[i] += m[i - towel.len()];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m[pattern.len()]
|
||||
})
|
||||
.filter(|n| *n > 0)
|
||||
.count()
|
||||
}
|
||||
|
||||
#[aoc(day19, part2)]
|
||||
fn part2(input: &Input) -> u64 {
|
||||
let towels = &input.0;
|
||||
let patterns = &input.1;
|
||||
|
||||
patterns
|
||||
.iter()
|
||||
.map(|pattern| {
|
||||
let mut m = vec![0u64; pattern.len() + 1];
|
||||
m[0] = 1;
|
||||
|
||||
for i in 1..=pattern.len() {
|
||||
for towel in towels {
|
||||
if towel.len() <= i && &pattern[i - towel.len()..i] == towel {
|
||||
m[i] += m[i - towel.len()];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m[pattern.len()]
|
||||
})
|
||||
.sum()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn part1_example() {
|
||||
assert_eq!(
|
||||
part1(&parse(
|
||||
"r, wr, b, g, bwu, rb, gb, br
|
||||
|
||||
brwrr
|
||||
bggr
|
||||
gbbr
|
||||
rrbgbr
|
||||
ubwu
|
||||
bwurrg
|
||||
brgr
|
||||
bbrgwb"
|
||||
)),
|
||||
6
|
||||
);
|
||||
}
|
||||
|
||||
// #[test]
|
||||
// fn part2_example() {
|
||||
// assert_eq!(part2(&parse("<EXAMPLE>")), 0);
|
||||
// }
|
||||
}
|
||||
@@ -0,0 +1,252 @@
|
||||
use aoc_runner_derive::{aoc, aoc_generator};
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
enum Cell {
|
||||
Empty,
|
||||
Wall,
|
||||
}
|
||||
|
||||
type Input = ((usize, usize), (usize, usize), Vec<Vec<Cell>>);
|
||||
|
||||
#[aoc_generator(day20)]
|
||||
fn parse(input: &str) -> Input {
|
||||
let mut start = (0, 0);
|
||||
let mut end = (0, 0);
|
||||
let map = input
|
||||
.lines()
|
||||
.enumerate()
|
||||
.map(|(y, line)| {
|
||||
line.chars()
|
||||
.enumerate()
|
||||
.map(|(x, c)| match c {
|
||||
'.' => Cell::Empty,
|
||||
'#' => Cell::Wall,
|
||||
'S' => {
|
||||
start = (x, y);
|
||||
Cell::Empty
|
||||
}
|
||||
'E' => {
|
||||
end = (x, y);
|
||||
Cell::Empty
|
||||
}
|
||||
_ => panic!("unexpected character: {}", c),
|
||||
})
|
||||
.collect()
|
||||
})
|
||||
.collect();
|
||||
(start, end, map)
|
||||
}
|
||||
|
||||
#[aoc(day20, part1)]
|
||||
fn part1(input: &Input) -> usize {
|
||||
let start = input.0;
|
||||
let map = &input.2;
|
||||
|
||||
#[cfg(not(test))]
|
||||
let min_saving = 100;
|
||||
#[cfg(test)]
|
||||
let min_saving = 2;
|
||||
|
||||
let w = map[0].len();
|
||||
let h = map.len();
|
||||
|
||||
let mut dist = vec![vec![usize::MAX; w]; h];
|
||||
dist[start.1][start.0] = 0;
|
||||
|
||||
let mut deque = std::collections::VecDeque::new();
|
||||
deque.push_back((0, start));
|
||||
|
||||
while let Some((d, pos)) = deque.pop_front() {
|
||||
if dist[pos.1][pos.0] < d {
|
||||
continue;
|
||||
}
|
||||
|
||||
dist[pos.1][pos.0] = d;
|
||||
|
||||
[(1, 0), (-1, 0), (0, -1), (0, 1)]
|
||||
.iter()
|
||||
.for_each(|(dx, dy)| {
|
||||
let x = (pos.0 as isize + dx) as usize;
|
||||
let y = (pos.1 as isize + dy) as usize;
|
||||
|
||||
if x < w && y < h && map[y][x] == Cell::Empty {
|
||||
let new_d = d + 1;
|
||||
if new_d < dist[y][x] {
|
||||
deque.push_back((new_d, (x, y)));
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
map.iter()
|
||||
.enumerate()
|
||||
.map(|(y, line)| {
|
||||
line.iter()
|
||||
.enumerate()
|
||||
.map(|(x, cell)| {
|
||||
let y = y as isize;
|
||||
if *cell == Cell::Empty {
|
||||
[
|
||||
(0, 2),
|
||||
(0, -2),
|
||||
(2, 0),
|
||||
(-2, 0),
|
||||
(1, 1),
|
||||
(1, -1),
|
||||
(-1, 1),
|
||||
(-1, -1),
|
||||
]
|
||||
.iter()
|
||||
.filter(|(dx, dy)| {
|
||||
let nx = x as isize + dx;
|
||||
let ny = y + dy;
|
||||
nx >= 0
|
||||
&& nx < w as isize
|
||||
&& ny >= 0
|
||||
&& ny < h as isize
|
||||
&& map[ny as usize][nx as usize] == Cell::Empty
|
||||
&& dist[ny as usize][nx as usize]
|
||||
>= dist[y as usize][x] + min_saving + 2
|
||||
})
|
||||
.count()
|
||||
} else {
|
||||
0
|
||||
}
|
||||
})
|
||||
.sum::<usize>()
|
||||
})
|
||||
.sum()
|
||||
}
|
||||
|
||||
#[aoc(day20, part2)]
|
||||
fn part2(input: &Input) -> usize {
|
||||
let start = input.0;
|
||||
let map = &input.2;
|
||||
|
||||
#[cfg(not(test))]
|
||||
let min_saving = 100;
|
||||
#[cfg(test)]
|
||||
let min_saving = 60;
|
||||
|
||||
let max_skip = 20;
|
||||
|
||||
let w = map[0].len();
|
||||
let h = map.len();
|
||||
|
||||
let mut dist = vec![vec![usize::MAX; w]; h];
|
||||
dist[start.1][start.0] = 0;
|
||||
|
||||
let mut deque = std::collections::VecDeque::new();
|
||||
deque.push_back((0, start));
|
||||
|
||||
while let Some((d, pos)) = deque.pop_front() {
|
||||
if dist[pos.1][pos.0] < d {
|
||||
continue;
|
||||
}
|
||||
|
||||
dist[pos.1][pos.0] = d;
|
||||
|
||||
[(1, 0), (-1, 0), (0, -1), (0, 1)]
|
||||
.iter()
|
||||
.for_each(|(dx, dy)| {
|
||||
let x = (pos.0 as isize + dx) as usize;
|
||||
let y = (pos.1 as isize + dy) as usize;
|
||||
|
||||
if x < w && y < h && map[y][x] == Cell::Empty {
|
||||
let new_d = d + 1;
|
||||
if new_d < dist[y][x] {
|
||||
deque.push_back((new_d, (x, y)));
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
map.iter()
|
||||
.enumerate()
|
||||
.map(|(y, line)| {
|
||||
line.iter()
|
||||
.enumerate()
|
||||
.map(|(x, cell)| {
|
||||
let y = y as isize;
|
||||
if *cell == Cell::Empty {
|
||||
(-max_skip..=max_skip)
|
||||
.flat_map(|dx: isize| {
|
||||
let skip = max_skip - dx.abs();
|
||||
(-skip..=skip).map(move |dy| (dx, dy))
|
||||
})
|
||||
.filter(|(dx, dy)| {
|
||||
let nx = x as isize + dx;
|
||||
let ny = y + dy;
|
||||
nx >= 0
|
||||
&& nx < w as isize
|
||||
&& ny >= 0
|
||||
&& ny < h as isize
|
||||
&& map[ny as usize][nx as usize] == Cell::Empty
|
||||
&& dist[ny as usize][nx as usize]
|
||||
>= dist[y as usize][x]
|
||||
+ min_saving
|
||||
+ dx.unsigned_abs()
|
||||
+ dy.unsigned_abs()
|
||||
})
|
||||
.count()
|
||||
} else {
|
||||
0
|
||||
}
|
||||
})
|
||||
.sum::<usize>()
|
||||
})
|
||||
.sum()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn part1_example() {
|
||||
assert_eq!(
|
||||
part1(&parse(
|
||||
"###############
|
||||
#...#...#.....#
|
||||
#.#.#.#.#.###.#
|
||||
#S#...#.#.#...#
|
||||
#######.#.#.###
|
||||
#######.#.#...#
|
||||
#######.#.###.#
|
||||
###..E#...#...#
|
||||
###.#######.###
|
||||
#...###...#...#
|
||||
#.#####.#.###.#
|
||||
#.#...#.#.#...#
|
||||
#.#.#.#.#.#.###
|
||||
#...#...#...###
|
||||
###############"
|
||||
)),
|
||||
44
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn part2_example() {
|
||||
assert_eq!(
|
||||
part2(&parse(
|
||||
"###############
|
||||
#...#...#.....#
|
||||
#.#.#.#.#.###.#
|
||||
#S#...#.#.#...#
|
||||
#######.#.#.###
|
||||
#######.#.#...#
|
||||
#######.#.###.#
|
||||
###..E#...#...#
|
||||
###.#######.###
|
||||
#...###...#...#
|
||||
#.#####.#.###.#
|
||||
#.#...#.#.#...#
|
||||
#.#.#.#.#.#.###
|
||||
#...#...#...###
|
||||
###############"
|
||||
)),
|
||||
129
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,392 @@
|
||||
use aoc_runner_derive::{aoc, aoc_generator};
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
enum NumButton {
|
||||
One,
|
||||
Two,
|
||||
Three,
|
||||
Four,
|
||||
Five,
|
||||
Six,
|
||||
Seven,
|
||||
Eight,
|
||||
Nine,
|
||||
Zero,
|
||||
A,
|
||||
}
|
||||
|
||||
impl NumButton {
|
||||
const fn pos(&self) -> (usize, usize) {
|
||||
match self {
|
||||
NumButton::One => (0, 2),
|
||||
NumButton::Two => (1, 2),
|
||||
NumButton::Three => (2, 2),
|
||||
NumButton::Four => (0, 1),
|
||||
NumButton::Five => (1, 1),
|
||||
NumButton::Six => (2, 1),
|
||||
NumButton::Seven => (0, 0),
|
||||
NumButton::Eight => (1, 0),
|
||||
NumButton::Nine => (2, 0),
|
||||
NumButton::Zero => (1, 3),
|
||||
NumButton::A => (2, 3),
|
||||
}
|
||||
}
|
||||
|
||||
const fn index(&self) -> usize {
|
||||
self.pos().1 * 3 + self.pos().0
|
||||
}
|
||||
|
||||
const fn value(&self) -> usize {
|
||||
match self {
|
||||
NumButton::One => 1,
|
||||
NumButton::Two => 2,
|
||||
NumButton::Three => 3,
|
||||
NumButton::Four => 4,
|
||||
NumButton::Five => 5,
|
||||
NumButton::Six => 6,
|
||||
NumButton::Seven => 7,
|
||||
NumButton::Eight => 8,
|
||||
NumButton::Nine => 9,
|
||||
NumButton::Zero => 0,
|
||||
NumButton::A => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
||||
const fn from_index(idx: usize) -> Self {
|
||||
match idx {
|
||||
0 => NumButton::Seven,
|
||||
1 => NumButton::Eight,
|
||||
2 => NumButton::Nine,
|
||||
3 => NumButton::Four,
|
||||
4 => NumButton::Five,
|
||||
5 => NumButton::Six,
|
||||
6 => NumButton::One,
|
||||
7 => NumButton::Two,
|
||||
8 => NumButton::Three,
|
||||
10 => NumButton::Zero,
|
||||
11 => NumButton::A,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
const fn paths_to(&self, other: &NumButton) -> [Path; 2] {
|
||||
let start = self.pos();
|
||||
let end = other.pos();
|
||||
|
||||
let bx = if start.0 < end.0 { RIGHT } else { LEFT };
|
||||
let dx = start.0.abs_diff(end.0);
|
||||
|
||||
let by = if start.1 < end.1 { DOWN } else { UP };
|
||||
let dy = start.1.abs_diff(end.1);
|
||||
|
||||
if (start.0 == 0 && end.1 == 3) || start.1 == end.1 {
|
||||
let mut x = [DistanceSize::MAX; 6];
|
||||
let y = [DistanceSize::MAX; 6];
|
||||
let mut i = dx;
|
||||
while i > 0 {
|
||||
x[i - 1] = bx;
|
||||
i -= 1;
|
||||
}
|
||||
let mut i = dy;
|
||||
while i > 0 {
|
||||
x[i - 1 + dx] = by;
|
||||
i -= 1;
|
||||
}
|
||||
x[dx + dy] = A;
|
||||
[x, y]
|
||||
} else if (end.0 == 0 && start.1 == 3) || start.0 == end.0 {
|
||||
let x = [DistanceSize::MAX; 6];
|
||||
let mut y = [DistanceSize::MAX; 6];
|
||||
let mut i = dx;
|
||||
while i > 0 {
|
||||
y[i - 1 + dy] = bx;
|
||||
i -= 1;
|
||||
}
|
||||
let mut i = dy;
|
||||
while i > 0 {
|
||||
y[i - 1] = by;
|
||||
i -= 1;
|
||||
}
|
||||
y[dx + dy] = A;
|
||||
[y, x]
|
||||
} else {
|
||||
let mut x = [DistanceSize::MAX; 6];
|
||||
let mut y = [DistanceSize::MAX; 6];
|
||||
let mut i = dx;
|
||||
while i > 0 {
|
||||
x[i - 1] = bx;
|
||||
y[i - 1 + dy] = bx;
|
||||
i -= 1;
|
||||
}
|
||||
let mut i = dy;
|
||||
while i > 0 {
|
||||
x[i - 1 + dx] = by;
|
||||
y[i - 1] = by;
|
||||
i -= 1;
|
||||
}
|
||||
x[dx + dy] = A;
|
||||
y[dx + dy] = A;
|
||||
[x, y]
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[aoc_generator(day21)]
|
||||
fn parse(input: &str) -> &'static str {
|
||||
unsafe { std::mem::transmute(input) }
|
||||
}
|
||||
|
||||
type Path = [DistanceSize; 6];
|
||||
|
||||
type DistanceSize = usize;
|
||||
type DistanceMatrix = [[DistanceSize; 5]; 5];
|
||||
|
||||
const fn shortest_dir_path(path: &Path, steps: &DistanceMatrix) -> DistanceSize {
|
||||
let mut sum = 0;
|
||||
let mut pos = A;
|
||||
let mut i = 0;
|
||||
|
||||
while i < 6 && path[i] != DistanceSize::MAX {
|
||||
sum += steps[pos][path[i]];
|
||||
pos = path[i];
|
||||
i += 1;
|
||||
}
|
||||
|
||||
sum
|
||||
}
|
||||
|
||||
const fn shortest_path(
|
||||
path: &[usize],
|
||||
paths: &[[[Path; 2]; 12]; 12],
|
||||
steps: &DistanceMatrix,
|
||||
) -> DistanceSize {
|
||||
let mut sum = 0;
|
||||
let mut pos = NumButton::A.index();
|
||||
let mut i = 0;
|
||||
while i < path.len() {
|
||||
let step_paths = paths[pos][path[i]];
|
||||
sum += if step_paths[1][0] != DistanceSize::MAX {
|
||||
min(
|
||||
shortest_dir_path(&step_paths[0], steps),
|
||||
shortest_dir_path(&step_paths[1], steps),
|
||||
)
|
||||
} else {
|
||||
shortest_dir_path(&step_paths[0], steps)
|
||||
};
|
||||
pos = path[i];
|
||||
i += 1;
|
||||
}
|
||||
sum
|
||||
}
|
||||
|
||||
const UP: usize = 0;
|
||||
const DOWN: usize = 1;
|
||||
const LEFT: usize = 2;
|
||||
const RIGHT: usize = 3;
|
||||
const A: usize = 4;
|
||||
|
||||
const fn min(a: usize, b: usize) -> usize {
|
||||
if a < b {
|
||||
a
|
||||
} else {
|
||||
b
|
||||
}
|
||||
}
|
||||
|
||||
const fn precompute_steps_new<const N: usize>(matrix: DistanceMatrix) -> DistanceMatrix {
|
||||
let mut steps = 0;
|
||||
let mut matrix = matrix;
|
||||
|
||||
while steps < N {
|
||||
let up = matrix[A][UP] + matrix[UP][A];
|
||||
let down = matrix[A][DOWN] + matrix[DOWN][A];
|
||||
let left = matrix[A][LEFT] + matrix[LEFT][A];
|
||||
let right = matrix[A][RIGHT] + matrix[RIGHT][A];
|
||||
let left_left = matrix[A][LEFT] + matrix[LEFT][LEFT] + matrix[LEFT][A];
|
||||
let right_right = matrix[A][RIGHT] + matrix[RIGHT][RIGHT] + matrix[RIGHT][A];
|
||||
let left_down = matrix[A][LEFT] + matrix[LEFT][DOWN] + matrix[DOWN][A];
|
||||
let down_left = matrix[A][DOWN] + matrix[DOWN][LEFT] + matrix[LEFT][A];
|
||||
let left_up = matrix[A][LEFT] + matrix[LEFT][UP] + matrix[UP][A];
|
||||
let up_left = matrix[A][UP] + matrix[UP][LEFT] + matrix[LEFT][A];
|
||||
let right_down = matrix[A][RIGHT] + matrix[RIGHT][DOWN] + matrix[DOWN][A];
|
||||
let down_right = matrix[A][DOWN] + matrix[DOWN][RIGHT] + matrix[RIGHT][A];
|
||||
let right_up = matrix[A][RIGHT] + matrix[RIGHT][UP] + matrix[UP][A];
|
||||
let up_right = matrix[A][UP] + matrix[UP][RIGHT] + matrix[RIGHT][A];
|
||||
let down_left_left =
|
||||
matrix[A][DOWN] + matrix[DOWN][LEFT] + matrix[LEFT][LEFT] + matrix[LEFT][A];
|
||||
let right_right_up =
|
||||
matrix[A][RIGHT] + matrix[RIGHT][RIGHT] + matrix[RIGHT][UP] + matrix[UP][A];
|
||||
|
||||
matrix[UP][UP] = 1;
|
||||
matrix[UP][DOWN] = down;
|
||||
matrix[UP][LEFT] = down_left;
|
||||
matrix[UP][RIGHT] = min(down_right, right_down);
|
||||
matrix[UP][A] = right;
|
||||
|
||||
matrix[DOWN][UP] = up;
|
||||
matrix[DOWN][DOWN] = 1;
|
||||
matrix[DOWN][LEFT] = left;
|
||||
matrix[DOWN][RIGHT] = right;
|
||||
matrix[DOWN][A] = min(up_right, right_up);
|
||||
|
||||
matrix[LEFT][UP] = right_up;
|
||||
matrix[LEFT][DOWN] = right;
|
||||
matrix[LEFT][LEFT] = 1;
|
||||
matrix[LEFT][RIGHT] = right_right;
|
||||
matrix[LEFT][A] = right_right_up;
|
||||
|
||||
matrix[RIGHT][UP] = min(up_left, left_up);
|
||||
matrix[RIGHT][DOWN] = left;
|
||||
matrix[RIGHT][LEFT] = left_left;
|
||||
matrix[RIGHT][RIGHT] = 1;
|
||||
matrix[RIGHT][A] = up;
|
||||
|
||||
matrix[A][UP] = left;
|
||||
matrix[A][DOWN] = min(left_down, down_left);
|
||||
matrix[A][LEFT] = down_left_left;
|
||||
matrix[A][RIGHT] = down;
|
||||
matrix[A][A] = 1;
|
||||
|
||||
steps += 1;
|
||||
}
|
||||
|
||||
matrix
|
||||
}
|
||||
|
||||
const fn precompute_paths() -> [[[Path; 2]; 12]; 12] {
|
||||
let mut paths = [[[[DistanceSize::MAX; 6], [DistanceSize::MAX; 6]]; 12]; 12];
|
||||
|
||||
let mut start = 0usize;
|
||||
while start < 12 {
|
||||
let mut end = 0usize;
|
||||
if start == 9 {
|
||||
start += 1;
|
||||
continue;
|
||||
}
|
||||
while end < 12 {
|
||||
if end == 9 {
|
||||
end += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
paths[start][end] = NumButton::from_index(start).paths_to(&NumButton::from_index(end));
|
||||
|
||||
end += 1;
|
||||
}
|
||||
start += 1;
|
||||
}
|
||||
|
||||
paths
|
||||
}
|
||||
|
||||
const fn precompute_num_paths(
|
||||
steps: &[[usize; 5]; 5],
|
||||
paths: &[[[[usize; 6]; 2]; 12]; 12],
|
||||
) -> [[[usize; 12]; 12]; 12] {
|
||||
let mut matrix = [[[0; 12]; 12]; 12];
|
||||
|
||||
let mut one = 0usize;
|
||||
while one < 12 {
|
||||
if one == 9 || one == 11 {
|
||||
one += 1;
|
||||
continue;
|
||||
}
|
||||
let mut two = 0usize;
|
||||
while two < 12 {
|
||||
if two == 9 || two == 11 {
|
||||
two += 1;
|
||||
continue;
|
||||
}
|
||||
let mut three = 0usize;
|
||||
while three < 12 {
|
||||
if three == 9 || three == 11 {
|
||||
three += 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
let num = NumButton::from_index(one).value() * 100
|
||||
+ NumButton::from_index(two).value() * 10
|
||||
+ NumButton::from_index(three).value();
|
||||
matrix[one][two][three] =
|
||||
num * shortest_path(&[one, two, three, NumButton::A.index()], paths, steps);
|
||||
|
||||
three += 1;
|
||||
}
|
||||
two += 1;
|
||||
}
|
||||
one += 1;
|
||||
}
|
||||
|
||||
matrix
|
||||
}
|
||||
|
||||
const CHAR_IDX: [usize; 10] = [
|
||||
NumButton::Zero.index(),
|
||||
NumButton::One.index(),
|
||||
NumButton::Two.index(),
|
||||
NumButton::Three.index(),
|
||||
NumButton::Four.index(),
|
||||
NumButton::Five.index(),
|
||||
NumButton::Six.index(),
|
||||
NumButton::Seven.index(),
|
||||
NumButton::Eight.index(),
|
||||
NumButton::Nine.index(),
|
||||
];
|
||||
|
||||
#[aoc(day21, part1)]
|
||||
fn part1(input: &str) -> DistanceSize {
|
||||
let num_paths = const {
|
||||
let steps = precompute_steps_new::<2>([[1; 5]; 5]);
|
||||
let paths = precompute_paths();
|
||||
precompute_num_paths(&steps, &paths)
|
||||
};
|
||||
|
||||
input
|
||||
.as_bytes()
|
||||
.chunks(5)
|
||||
.map(|chunk| {
|
||||
let num = [
|
||||
CHAR_IDX[chunk[0] as usize - 48],
|
||||
CHAR_IDX[chunk[1] as usize - 48],
|
||||
CHAR_IDX[chunk[2] as usize - 48],
|
||||
];
|
||||
num_paths[num[0]][num[1]][num[2]]
|
||||
})
|
||||
.sum()
|
||||
}
|
||||
|
||||
#[aoc(day21, part2)]
|
||||
fn part2(input: &str) -> DistanceSize {
|
||||
let num_paths = const {
|
||||
let steps = precompute_steps_new::<25>([[1; 5]; 5]);
|
||||
let paths = precompute_paths();
|
||||
precompute_num_paths(&steps, &paths)
|
||||
};
|
||||
|
||||
input
|
||||
.as_bytes()
|
||||
.chunks(5)
|
||||
.map(|chunk| {
|
||||
let num = [
|
||||
CHAR_IDX[chunk[0] as usize - 48],
|
||||
CHAR_IDX[chunk[1] as usize - 48],
|
||||
CHAR_IDX[chunk[2] as usize - 48],
|
||||
];
|
||||
num_paths[num[0]][num[1]][num[2]]
|
||||
})
|
||||
.sum()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn part1_example() {
|
||||
assert_eq!(part1(parse("379A")), 64 * 379);
|
||||
assert_eq!(part1(parse("179A")), 68 * 179);
|
||||
assert_eq!(part1(parse("456A")), 64 * 456);
|
||||
assert_eq!(part1(parse("980A")), 60 * 980);
|
||||
assert_eq!(part1(parse("029A")), 68 * 29);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
use aoc_runner_derive::{aoc, aoc_generator};
|
||||
|
||||
type Input = Vec<usize>;
|
||||
|
||||
#[aoc_generator(day22)]
|
||||
fn parse(input: &str) -> Input {
|
||||
input.lines().map(|n| n.parse().unwrap()).collect()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn next_number(number: usize) -> usize {
|
||||
let step1 = (number ^ (number << 6)) % (1 << 24);
|
||||
let step2 = (step1 ^ (step1 >> 5)) % (1 << 24);
|
||||
(step2 ^ step2 << 11) % (1 << 24)
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn step_2000(number: usize) -> usize {
|
||||
let mut number = number;
|
||||
for _ in 0..2000 {
|
||||
number = next_number(number);
|
||||
}
|
||||
number
|
||||
}
|
||||
|
||||
#[aoc(day22, part1)]
|
||||
fn part1(input: &Input) -> usize {
|
||||
input.iter().map(|n| step_2000(*n)).sum()
|
||||
}
|
||||
|
||||
#[aoc(day22, part2)]
|
||||
fn part2(input: &Input) -> usize {
|
||||
let mut total = Box::new([[[[0; 19]; 19]; 19]; 19]);
|
||||
let mut max_total = 0;
|
||||
let mut last_monkey = Box::new([[[[0; 19]; 19]; 19]; 19]);
|
||||
for (monkey, secret) in input.iter().enumerate() {
|
||||
let mut one = *secret;
|
||||
let mut two = next_number(one);
|
||||
let mut three = next_number(two);
|
||||
let mut four = next_number(three);
|
||||
for _ in 0..2000 - 3 {
|
||||
let next = next_number(four);
|
||||
|
||||
let idx_1 = (two % 10) - (one % 10) + 9;
|
||||
let idx_2 = (three % 10) - (two % 10) + 9;
|
||||
let idx_3 = (four % 10) - (three % 10) + 9;
|
||||
let idx_4 = (next % 10) - (four % 10) + 9;
|
||||
|
||||
if last_monkey[idx_1][idx_2][idx_3][idx_4] <= monkey {
|
||||
last_monkey[idx_1][idx_2][idx_3][idx_4] = monkey + 1;
|
||||
total[idx_1][idx_2][idx_3][idx_4] += next % 10;
|
||||
max_total = max_total.max(total[idx_1][idx_2][idx_3][idx_4]);
|
||||
}
|
||||
|
||||
one = two;
|
||||
two = three;
|
||||
three = four;
|
||||
four = next;
|
||||
}
|
||||
}
|
||||
max_total
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn part1_example() {
|
||||
assert_eq!(part1(&parse("1")), 8685429);
|
||||
assert_eq!(part1(&parse("10")), 4700978);
|
||||
assert_eq!(part1(&parse("100")), 15273692);
|
||||
assert_eq!(part1(&parse("2024")), 8667524);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,233 @@
|
||||
use aoc_runner_derive::{aoc, aoc_generator};
|
||||
use hashbrown::{HashMap, HashSet};
|
||||
|
||||
type Input = (usize, [Vec<usize>; 26 * 26], HashMap<usize, String>);
|
||||
|
||||
#[aoc_generator(day23)]
|
||||
fn parse(input: &str) -> Input {
|
||||
let mut i = 0;
|
||||
let mut names = HashMap::new();
|
||||
let mut ids = HashMap::new();
|
||||
let mut adj_list = [const { Vec::new() }; 26 * 26];
|
||||
input.lines().for_each(|line| {
|
||||
let mut splits = line.split('-');
|
||||
let left = splits.next().unwrap().to_string();
|
||||
let right = splits.next().unwrap().to_string();
|
||||
|
||||
let left_id = {
|
||||
*ids.entry(left.clone()).or_insert_with(|| {
|
||||
let id = i;
|
||||
i += 1;
|
||||
names.insert(id, left);
|
||||
id
|
||||
})
|
||||
};
|
||||
let right_id = {
|
||||
*ids.entry(right.clone()).or_insert_with(|| {
|
||||
let id = i;
|
||||
i += 1;
|
||||
names.insert(id, right);
|
||||
id
|
||||
})
|
||||
};
|
||||
|
||||
adj_list[left_id].push(right_id);
|
||||
adj_list[right_id].push(left_id);
|
||||
});
|
||||
|
||||
(i, adj_list, names)
|
||||
}
|
||||
|
||||
#[aoc(day23, part1)]
|
||||
fn part1(input: &Input) -> usize {
|
||||
let num = input.0;
|
||||
let adj_list = &input.1;
|
||||
let names = &input.2;
|
||||
|
||||
(0..num)
|
||||
.filter(|id| names[id].starts_with("t"))
|
||||
.flat_map(|id| {
|
||||
let mut three_groups = HashSet::new();
|
||||
let adj = &adj_list[id];
|
||||
|
||||
for left in 0..adj.len() {
|
||||
for right in left + 1..adj.len() {
|
||||
if adj_list[adj[left]].contains(&adj[right]) {
|
||||
let mut group = [id, adj[left], adj[right]];
|
||||
group.sort();
|
||||
three_groups.insert((group[0], group[1], group[2]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
three_groups
|
||||
})
|
||||
.collect::<HashSet<(usize, usize, usize)>>()
|
||||
.into_iter()
|
||||
.count()
|
||||
}
|
||||
|
||||
#[aoc(day23, part2)]
|
||||
fn part2(input: &Input) -> String {
|
||||
let num = input.0;
|
||||
let adj_list = &input.1;
|
||||
let names = &input.2;
|
||||
|
||||
let max_clique = (0..num)
|
||||
.filter(|id| names[id].starts_with("t"))
|
||||
.flat_map(|id| {
|
||||
let mut three_groups = HashSet::new();
|
||||
let adj = &adj_list[id];
|
||||
|
||||
for left in 0..adj.len() {
|
||||
for right in left + 1..adj.len() {
|
||||
if adj_list[adj[left]].contains(&adj[right]) {
|
||||
let mut group = [id, adj[left], adj[right]];
|
||||
group.sort();
|
||||
three_groups.insert((group[0], group[1], group[2]));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
three_groups
|
||||
})
|
||||
.collect::<HashSet<(usize, usize, usize)>>()
|
||||
.into_iter()
|
||||
.flat_map(|(a, b, c)| {
|
||||
let clique = vec![a, b, c];
|
||||
expand_cliques(clique, num, adj_list)
|
||||
})
|
||||
.max_by_key(|clique| clique.len())
|
||||
.unwrap();
|
||||
|
||||
let mut names = max_clique
|
||||
.iter()
|
||||
.map(|id| names[id].clone())
|
||||
.collect::<Vec<String>>();
|
||||
names.sort();
|
||||
|
||||
names.join(",")
|
||||
}
|
||||
|
||||
fn expand_cliques(current: Vec<usize>, num: usize, adj: &[Vec<usize>]) -> Vec<Vec<usize>> {
|
||||
let mut expansions: Vec<Vec<usize>> = vec![current.clone()];
|
||||
|
||||
for node in 0..num {
|
||||
if current.contains(&node) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if !current
|
||||
.iter()
|
||||
.all(|clique_node| adj[*clique_node].contains(&node))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
let mut pushed = false;
|
||||
for clique in &mut expansions {
|
||||
if clique
|
||||
.iter()
|
||||
.all(|clique_node| adj[*clique_node].contains(&node))
|
||||
{
|
||||
clique.push(node);
|
||||
pushed = true;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if !pushed {
|
||||
let mut new_clique = current.clone();
|
||||
new_clique.push(node);
|
||||
expansions.push(new_clique);
|
||||
}
|
||||
}
|
||||
|
||||
expansions
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn part1_example() {
|
||||
assert_eq!(
|
||||
part1(&parse(
|
||||
"kh-tc
|
||||
qp-kh
|
||||
de-cg
|
||||
ka-co
|
||||
yn-aq
|
||||
qp-ub
|
||||
cg-tb
|
||||
vc-aq
|
||||
tb-ka
|
||||
wh-tc
|
||||
yn-cg
|
||||
kh-ub
|
||||
ta-co
|
||||
de-co
|
||||
tc-td
|
||||
tb-wq
|
||||
wh-td
|
||||
ta-ka
|
||||
td-qp
|
||||
aq-cg
|
||||
wq-ub
|
||||
ub-vc
|
||||
de-ta
|
||||
wq-aq
|
||||
wq-vc
|
||||
wh-yn
|
||||
ka-de
|
||||
kh-ta
|
||||
co-tc
|
||||
wh-qp
|
||||
tb-vc
|
||||
td-yn"
|
||||
)),
|
||||
7
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn part2_example() {
|
||||
assert_eq!(
|
||||
part2(&parse(
|
||||
"kh-tc
|
||||
qp-kh
|
||||
de-cg
|
||||
ka-co
|
||||
yn-aq
|
||||
qp-ub
|
||||
cg-tb
|
||||
vc-aq
|
||||
tb-ka
|
||||
wh-tc
|
||||
yn-cg
|
||||
kh-ub
|
||||
ta-co
|
||||
de-co
|
||||
tc-td
|
||||
tb-wq
|
||||
wh-td
|
||||
ta-ka
|
||||
td-qp
|
||||
aq-cg
|
||||
wq-ub
|
||||
ub-vc
|
||||
de-ta
|
||||
wq-aq
|
||||
wq-vc
|
||||
wh-yn
|
||||
ka-de
|
||||
kh-ta
|
||||
co-tc
|
||||
wh-qp
|
||||
tb-vc
|
||||
td-yn"
|
||||
)),
|
||||
"co,de,ka,ta"
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,303 @@
|
||||
use std::collections::VecDeque;
|
||||
|
||||
use aoc_runner_derive::{aoc, aoc_generator};
|
||||
use hashbrown::HashMap;
|
||||
use nom::{
|
||||
bytes::complete::tag,
|
||||
character::complete::{anychar, newline, one_of, space1, u64},
|
||||
multi::{fill, many1, separated_list0},
|
||||
sequence::{delimited, separated_pair},
|
||||
IResult,
|
||||
};
|
||||
|
||||
type Line = [char; 3];
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
enum Operation {
|
||||
And,
|
||||
Or,
|
||||
Xor,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
struct Gate {
|
||||
operation: Operation,
|
||||
in_0: Line,
|
||||
in_1: Line,
|
||||
out: Line,
|
||||
}
|
||||
|
||||
type Input = (Vec<(Line, u64)>, Vec<Gate>);
|
||||
|
||||
#[aoc_generator(day24)]
|
||||
fn parse(input: &str) -> Input {
|
||||
separated_pair(
|
||||
separated_list0(newline, separated_pair(parse_line, tag(": "), u64)),
|
||||
tag("\n\n"),
|
||||
separated_list0(newline, parse_instruction),
|
||||
)(input)
|
||||
.unwrap()
|
||||
.1
|
||||
}
|
||||
|
||||
fn parse_line(input: &str) -> IResult<&str, [char; 3]> {
|
||||
let mut buffer = ['0'; 3];
|
||||
let (input, _) = fill(anychar, &mut buffer)(input)?;
|
||||
Ok((input, buffer))
|
||||
}
|
||||
|
||||
fn parse_instruction(input: &str) -> IResult<&str, Gate> {
|
||||
let (input, in_0) = parse_line(input)?;
|
||||
let (input, operation) = delimited(space1, many1(one_of("ANDXOR")), space1)(input)?;
|
||||
let (input, in_1) = parse_line(input)?;
|
||||
let (input, _) = tag(" -> ")(input)?;
|
||||
let (input, out) = parse_line(input)?;
|
||||
match operation[0] {
|
||||
'A' => Ok((
|
||||
input,
|
||||
Gate {
|
||||
in_0,
|
||||
in_1,
|
||||
out,
|
||||
operation: Operation::And,
|
||||
},
|
||||
)),
|
||||
'O' => Ok((
|
||||
input,
|
||||
Gate {
|
||||
in_0,
|
||||
in_1,
|
||||
out,
|
||||
operation: Operation::Or,
|
||||
},
|
||||
)),
|
||||
'X' => Ok((
|
||||
input,
|
||||
Gate {
|
||||
in_0,
|
||||
in_1,
|
||||
out,
|
||||
operation: Operation::Xor,
|
||||
},
|
||||
)),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
#[aoc(day24, part1)]
|
||||
fn part1(input: &Input) -> usize {
|
||||
let fixed = &input.0;
|
||||
|
||||
let mut values = HashMap::new();
|
||||
|
||||
for (line, value) in fixed {
|
||||
values.insert(line, *value);
|
||||
}
|
||||
|
||||
let mut gates = VecDeque::new();
|
||||
|
||||
for gate in &input.1 {
|
||||
gates.push_back(gate);
|
||||
}
|
||||
|
||||
while let Some(gate) = gates.pop_front() {
|
||||
if values.contains_key(&gate.in_0) && values.contains_key(&gate.in_1) {
|
||||
let in_0 = values[&gate.in_0];
|
||||
let in_1 = values[&gate.in_1];
|
||||
let out = match gate.operation {
|
||||
Operation::And => in_0 & in_1,
|
||||
Operation::Or => in_0 | in_1,
|
||||
Operation::Xor => in_0 ^ in_1,
|
||||
};
|
||||
values.insert(&gate.out, out);
|
||||
} else {
|
||||
gates.push_back(gate);
|
||||
}
|
||||
}
|
||||
|
||||
values
|
||||
.into_iter()
|
||||
.filter(|(line, value)| line[0] == 'z' && *value == 1)
|
||||
.map(|(line, _)| {
|
||||
1 << line[1..3]
|
||||
.iter()
|
||||
.collect::<String>()
|
||||
.parse::<u64>()
|
||||
.unwrap()
|
||||
})
|
||||
.sum()
|
||||
}
|
||||
|
||||
#[aoc(day24, part2)]
|
||||
fn part2(input: &Input) -> String {
|
||||
let swaps: &[(Line, Line)] = &[
|
||||
(['h', 'm', 't'], ['z', '1', '8']),
|
||||
(['b', 'f', 'q'], ['z', '2', '7']),
|
||||
(['h', 'k', 'h'], ['z', '3', '1']),
|
||||
(['f', 'j', 'p'], ['b', 'n', 'g']),
|
||||
];
|
||||
// let swaps: &[(Line, Line)] = &[];
|
||||
let mut gates = input.1.clone();
|
||||
for gate in &mut gates {
|
||||
for (from, to) in swaps {
|
||||
if gate.out == *from {
|
||||
gate.out = *to;
|
||||
} else if gate.out == *to {
|
||||
gate.out = *from;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for gate in &gates {
|
||||
if gate.out[0] == 'z' && gate.operation != Operation::Xor {
|
||||
println!("gate {:?} outputs to a z but is not xor", gate);
|
||||
}
|
||||
}
|
||||
|
||||
let mut c = ['r', 'j', 'r'];
|
||||
|
||||
for bit in 1..=44 {
|
||||
let x = [
|
||||
'x',
|
||||
(bit as u8 / 10 + 48) as char,
|
||||
(bit as u8 % 10 + 48) as char,
|
||||
];
|
||||
let y = [
|
||||
'y',
|
||||
(bit as u8 / 10 + 48) as char,
|
||||
(bit as u8 % 10 + 48) as char,
|
||||
];
|
||||
|
||||
let xy_gates = find_gate(&gates, &x, &y);
|
||||
|
||||
assert_eq!(
|
||||
xy_gates.len(),
|
||||
2,
|
||||
"there is not exactly two xy gates {:?}\nrelated gates: {:?}",
|
||||
xy_gates,
|
||||
find_debug_gate(&gates, &x, &y)
|
||||
);
|
||||
|
||||
let line_xy_xor = xy_gates
|
||||
.iter()
|
||||
.find(|g| g.operation == Operation::Xor)
|
||||
.unwrap()
|
||||
.out;
|
||||
|
||||
let line_xy_and = xy_gates
|
||||
.iter()
|
||||
.find(|g| g.operation == Operation::And)
|
||||
.unwrap()
|
||||
.out;
|
||||
|
||||
let xyc_gates = find_gate(&gates, &line_xy_xor, &c);
|
||||
|
||||
assert_eq!(xyc_gates.len(), 2,
|
||||
"there is not exactly two output gates {:?}\nrelated gates: {:?}\nxy_and: {}\nxy_xor: {}",
|
||||
xyc_gates,
|
||||
find_debug_gate(&gates, &line_xy_xor, &c),
|
||||
display_line(&line_xy_and),
|
||||
display_line(&line_xy_xor),
|
||||
);
|
||||
|
||||
let line_xyc_xor = xyc_gates
|
||||
.iter()
|
||||
.find(|g| g.operation == Operation::Xor)
|
||||
.unwrap()
|
||||
.out;
|
||||
|
||||
let line_xyc_and = xyc_gates
|
||||
.iter()
|
||||
.find(|g| g.operation == Operation::And)
|
||||
.unwrap()
|
||||
.out;
|
||||
|
||||
let xyco_gates = find_gate(&gates, &line_xyc_and, &line_xy_and);
|
||||
|
||||
assert_eq!(
|
||||
xyco_gates.len(),
|
||||
1,
|
||||
"there is no or more than one output gate {:?}\nrelated gates: {:?}\nxy_and: {}\nxy_xor: {}\nxyc_and: {}\nxyc_xor: {}",
|
||||
xyco_gates,
|
||||
find_debug_gate(&gates, &line_xyc_and, &line_xy_and),
|
||||
display_line(&line_xy_and),
|
||||
display_line(&line_xy_xor),
|
||||
display_line(&line_xyc_and),
|
||||
display_line(&line_xyc_xor),
|
||||
);
|
||||
|
||||
c = xyco_gates[0].out;
|
||||
|
||||
println!(
|
||||
"for bit {} z-output is {} and carry out is {}",
|
||||
bit,
|
||||
display_line(&line_xyc_xor),
|
||||
display_line(&c)
|
||||
);
|
||||
}
|
||||
|
||||
let mut wires = swaps
|
||||
.iter()
|
||||
.flat_map(|(a, b)| [display_line(a), display_line(b)])
|
||||
.collect::<Vec<String>>();
|
||||
|
||||
wires.sort();
|
||||
|
||||
wires.join(",")
|
||||
}
|
||||
|
||||
fn display_line(line: &Line) -> String {
|
||||
line.iter().collect()
|
||||
}
|
||||
|
||||
fn find_gate(gates: &[Gate], in_0: &Line, in_1: &Line) -> Vec<Gate> {
|
||||
gates
|
||||
.iter()
|
||||
.filter(|gate| {
|
||||
(gate.in_0 == *in_0 && gate.in_1 == *in_1) || (gate.in_0 == *in_1 && gate.in_1 == *in_0)
|
||||
})
|
||||
.copied()
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn find_debug_gate(gates: &[Gate], in_0: &Line, in_1: &Line) -> Vec<Gate> {
|
||||
gates
|
||||
.iter()
|
||||
.filter(|gate| {
|
||||
gate.in_0 == *in_0 || gate.in_1 == *in_1 || gate.in_0 == *in_1 || gate.in_1 == *in_0
|
||||
})
|
||||
.copied()
|
||||
.collect()
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn part1_example() {
|
||||
assert_eq!(
|
||||
part1(&parse(
|
||||
"x00: 1
|
||||
x01: 1
|
||||
x02: 1
|
||||
y00: 0
|
||||
y01: 1
|
||||
y02: 0
|
||||
|
||||
x00 AND y00 -> z00
|
||||
x01 XOR y01 -> z01
|
||||
x02 OR y02 -> z02"
|
||||
)),
|
||||
4
|
||||
);
|
||||
}
|
||||
|
||||
// #[test]
|
||||
// fn part2_example() {
|
||||
// assert_eq!(
|
||||
// part2(&parse(include_str!("../input/2024/day24.txt").trim_end())),
|
||||
// "z00,z01,z02,z05"
|
||||
// );
|
||||
// }
|
||||
}
|
||||
@@ -0,0 +1,152 @@
|
||||
use aoc_runner_derive::{aoc, aoc_generator};
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
enum Cell {
|
||||
Empty,
|
||||
Filled,
|
||||
}
|
||||
|
||||
type Code = [usize; 5];
|
||||
|
||||
type Input = (Vec<Code>, Vec<Code>);
|
||||
|
||||
#[aoc_generator(day25)]
|
||||
fn parse(input: &str) -> Input {
|
||||
let (keys, locks): (Vec<Option<Vec<Vec<Cell>>>>, Vec<Option<Vec<Vec<Cell>>>>) = input
|
||||
.split("\n\n")
|
||||
.map(|block| {
|
||||
block
|
||||
.lines()
|
||||
.map(|line| {
|
||||
line.chars()
|
||||
.map(|c| match c {
|
||||
'.' => Cell::Empty,
|
||||
'#' => Cell::Filled,
|
||||
_ => panic!("Invalid cell"),
|
||||
})
|
||||
.collect()
|
||||
})
|
||||
.collect()
|
||||
})
|
||||
.map(|block: Vec<Vec<Cell>>| {
|
||||
if block[0][0] == Cell::Filled {
|
||||
(None, Some(block))
|
||||
} else {
|
||||
(Some(block), None)
|
||||
}
|
||||
})
|
||||
.unzip();
|
||||
|
||||
let keys = keys
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.map(|block| {
|
||||
let mut buffer = [0; 5];
|
||||
for (y, row) in block.iter().enumerate() {
|
||||
for x in 0..5 {
|
||||
if row[x] == Cell::Empty {
|
||||
buffer[x] = y;
|
||||
}
|
||||
}
|
||||
}
|
||||
buffer
|
||||
})
|
||||
.collect();
|
||||
let locks = locks
|
||||
.into_iter()
|
||||
.flatten()
|
||||
.map(|block| {
|
||||
let mut buffer = [0; 5];
|
||||
for (y, row) in block.iter().enumerate() {
|
||||
for x in 0..5 {
|
||||
if row[x] == Cell::Filled {
|
||||
buffer[x] = y;
|
||||
}
|
||||
}
|
||||
}
|
||||
buffer
|
||||
})
|
||||
.collect();
|
||||
|
||||
(keys, locks)
|
||||
}
|
||||
|
||||
#[aoc(day25, part1)]
|
||||
fn part1(input: &Input) -> usize {
|
||||
let (keys, locks) = input;
|
||||
|
||||
let mut valid_combinations = 0;
|
||||
|
||||
for key in keys {
|
||||
for lock in locks {
|
||||
if (0..5).all(|i| key[i] >= lock[i]) {
|
||||
valid_combinations += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
valid_combinations
|
||||
}
|
||||
|
||||
// #[aoc(day25, part2)]
|
||||
// fn part2(input: &Input) -> usize {
|
||||
// todo!()
|
||||
// }
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn part1_example() {
|
||||
assert_eq!(
|
||||
part1(&parse(
|
||||
"#####
|
||||
.####
|
||||
.####
|
||||
.####
|
||||
.#.#.
|
||||
.#...
|
||||
.....
|
||||
|
||||
#####
|
||||
##.##
|
||||
.#.##
|
||||
...##
|
||||
...#.
|
||||
...#.
|
||||
.....
|
||||
|
||||
.....
|
||||
#....
|
||||
#....
|
||||
#...#
|
||||
#.#.#
|
||||
#.###
|
||||
#####
|
||||
|
||||
.....
|
||||
.....
|
||||
#.#..
|
||||
###..
|
||||
###.#
|
||||
###.#
|
||||
#####
|
||||
|
||||
.....
|
||||
.....
|
||||
.....
|
||||
#....
|
||||
#.#..
|
||||
#.#.#
|
||||
#####"
|
||||
)),
|
||||
3
|
||||
);
|
||||
}
|
||||
|
||||
// #[test]
|
||||
// fn part2_example() {
|
||||
// assert_eq!(part2(&parse("<EXAMPLE>")), 0);
|
||||
// }
|
||||
}
|
||||
@@ -1,5 +1,21 @@
|
||||
mod day1;
|
||||
mod day10;
|
||||
mod day11;
|
||||
mod day12;
|
||||
mod day13;
|
||||
mod day14;
|
||||
mod day15;
|
||||
mod day16;
|
||||
mod day17;
|
||||
mod day18;
|
||||
mod day19;
|
||||
mod day2;
|
||||
mod day20;
|
||||
mod day21;
|
||||
mod day22;
|
||||
mod day23;
|
||||
mod day24;
|
||||
mod day25;
|
||||
mod day3;
|
||||
mod day4;
|
||||
mod day5;
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
use aoc_runner_derive::{aoc, aoc_generator};
|
||||
|
||||
type Input = ();
|
||||
|
||||
#[aoc_generator(day)]
|
||||
fn parse(input: &str) -> Input {
|
||||
todo!()
|
||||
}
|
||||
|
||||
#[aoc(day, part1)]
|
||||
fn part1(input: &Input) -> usize {
|
||||
todo!()
|
||||
}
|
||||
|
||||
// #[aoc(day, part2)]
|
||||
// fn part2(input: &Input) -> usize {
|
||||
// todo!()
|
||||
// }
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn part1_example() {
|
||||
assert_eq!(part1(&parse("<EXAMPLE>")), 0);
|
||||
}
|
||||
|
||||
// #[test]
|
||||
// fn part2_example() {
|
||||
// assert_eq!(part2(&parse("<EXAMPLE>")), 0);
|
||||
// }
|
||||
}
|
||||
Generated
+149
@@ -0,0 +1,149 @@
|
||||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 4
|
||||
|
||||
[[package]]
|
||||
name = "allocator-api2"
|
||||
version = "0.2.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
|
||||
|
||||
[[package]]
|
||||
name = "aoc_2025"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"hashbrown",
|
||||
"num",
|
||||
"num-modular",
|
||||
"topological-sort",
|
||||
"winnow",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8"
|
||||
|
||||
[[package]]
|
||||
name = "equivalent"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
|
||||
|
||||
[[package]]
|
||||
name = "foldhash"
|
||||
version = "0.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb"
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.16.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
|
||||
dependencies = [
|
||||
"allocator-api2",
|
||||
"equivalent",
|
||||
"foldhash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.7.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273"
|
||||
|
||||
[[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-modular"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "17bb261bf36fa7d83f4c294f834e91256769097b3cb505d44831e0a179ac647f"
|
||||
|
||||
[[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",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "topological-sort"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ea68304e134ecd095ac6c3574494fc62b909f416c4fca77e440530221e549d3d"
|
||||
|
||||
[[package]]
|
||||
name = "winnow"
|
||||
version = "0.7.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
]
|
||||
@@ -0,0 +1,11 @@
|
||||
[package]
|
||||
name = "aoc_2025"
|
||||
version = "0.1.0"
|
||||
edition = "2024"
|
||||
|
||||
[dependencies]
|
||||
hashbrown = "0.16.1"
|
||||
num = "0.4.3"
|
||||
num-modular = "0.6.1"
|
||||
topological-sort = "0.2.2"
|
||||
winnow = "0.7.14"
|
||||
@@ -0,0 +1,3 @@
|
||||
```bash
|
||||
bacon test -- dayN
|
||||
```
|
||||
@@ -0,0 +1,124 @@
|
||||
use crate::{AoC, WinnowDay};
|
||||
use winnow::{
|
||||
Parser, Result,
|
||||
ascii::{digit1, multispace0},
|
||||
combinator::{alt, preceded, repeat},
|
||||
};
|
||||
|
||||
type Parsed = Vec<Side>;
|
||||
enum Side {
|
||||
Left(i64),
|
||||
Right(i64),
|
||||
}
|
||||
|
||||
fn day() -> impl WinnowDay {
|
||||
AoC::new(parse as _, part1 as _, part2 as _)
|
||||
}
|
||||
|
||||
fn parse(input: &mut &str) -> Result<Parsed> {
|
||||
repeat(1.., parse_line).parse_next(input)
|
||||
}
|
||||
|
||||
fn parse_line(input: &mut &str) -> Result<Side> {
|
||||
preceded(multispace0, alt((parse_left, parse_right))).parse_next(input)
|
||||
}
|
||||
|
||||
fn parse_left(input: &mut &str) -> Result<Side> {
|
||||
preceded("L", digit1.parse_to())
|
||||
.map(Side::Left)
|
||||
.parse_next(input)
|
||||
}
|
||||
|
||||
fn parse_right(input: &mut &str) -> Result<Side> {
|
||||
preceded("R", digit1.parse_to())
|
||||
.map(Side::Right)
|
||||
.parse_next(input)
|
||||
}
|
||||
|
||||
fn part1(input: Parsed) -> String {
|
||||
let mut res = 0;
|
||||
let mut pos = 50i64;
|
||||
for change in input {
|
||||
match change {
|
||||
Side::Left(dx) => pos = (pos + 100 - dx) % 100,
|
||||
Side::Right(dx) => pos = (pos + dx) % 100,
|
||||
}
|
||||
if pos == 0 {
|
||||
res += 1
|
||||
}
|
||||
}
|
||||
format!("{res}")
|
||||
}
|
||||
|
||||
fn part2(input: Parsed) -> String {
|
||||
let input = input
|
||||
.iter()
|
||||
.flat_map(|side| match side {
|
||||
Side::Left(dx) => (0..*dx).map(|_| Side::Left(1)).collect::<Vec<Side>>(),
|
||||
Side::Right(dx) => (0..*dx).map(|_| Side::Right(1)).collect::<Vec<Side>>(),
|
||||
})
|
||||
.collect::<Vec<Side>>();
|
||||
let mut res = 0;
|
||||
let mut pos = 50i64;
|
||||
for change in input {
|
||||
match change {
|
||||
Side::Left(dx) => pos = (pos + 100 - dx) % 100,
|
||||
Side::Right(dx) => pos = (pos + dx) % 100,
|
||||
}
|
||||
if pos == 0 {
|
||||
res += 1
|
||||
}
|
||||
}
|
||||
format!("{res}")
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::Day;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn part1_test() {
|
||||
day().assert1(
|
||||
"L68
|
||||
L30
|
||||
R48
|
||||
L5
|
||||
R60
|
||||
L55
|
||||
L1
|
||||
L99
|
||||
R14
|
||||
L82",
|
||||
"3",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn part1_solve() {
|
||||
day().assert1(include_str!("../input/day1.txt"), "982");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn part2_test() {
|
||||
day().assert2(
|
||||
"L68
|
||||
L30
|
||||
R48
|
||||
L5
|
||||
R60
|
||||
L55
|
||||
L1
|
||||
L99
|
||||
R14
|
||||
L82",
|
||||
"6",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn part2_solve() {
|
||||
day().assert2(include_str!("../input/day1.txt"), "6106");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
use crate::{AoC, WinnowDay};
|
||||
use winnow::{
|
||||
Parser, Result,
|
||||
ascii::digit1,
|
||||
combinator::{separated, separated_pair},
|
||||
};
|
||||
|
||||
type Parsed = Vec<(u64, u64)>;
|
||||
|
||||
fn day() -> impl WinnowDay {
|
||||
AoC::new(parse as _, part1 as _, part2 as _)
|
||||
}
|
||||
|
||||
fn parse(input: &mut &str) -> Result<Parsed> {
|
||||
separated(1.., parse_range, ",").parse_next(input)
|
||||
}
|
||||
|
||||
fn parse_range(input: &mut &str) -> Result<(u64, u64)> {
|
||||
separated_pair(digit1.parse_to(), "-", digit1.parse_to()).parse_next(input)
|
||||
}
|
||||
|
||||
fn part1(input: Parsed) -> String {
|
||||
let res = input
|
||||
.iter()
|
||||
.map(|(from, to)| {
|
||||
(*from..=*to)
|
||||
.filter(|i| {
|
||||
let n = (i.ilog10() + 1) >> 1;
|
||||
let p = 10u64.pow(n);
|
||||
i / p == i % p
|
||||
})
|
||||
.sum::<u64>()
|
||||
})
|
||||
.sum::<u64>();
|
||||
format!("{res}")
|
||||
}
|
||||
|
||||
fn part2(input: Parsed) -> String {
|
||||
let res = input
|
||||
.iter()
|
||||
.map(|(from, to)| {
|
||||
(*from..=*to)
|
||||
.filter(|i| {
|
||||
let n = i.ilog10() + 1;
|
||||
(1..=(n >> 1)).filter(|p| n % p == 0).any(|p| {
|
||||
(1..(n / p))
|
||||
.all(|s| i % 10u64.pow(p) == (i / 10u64.pow(p * s)) % 10u64.pow(p))
|
||||
})
|
||||
})
|
||||
.sum::<u64>()
|
||||
})
|
||||
.sum::<u64>();
|
||||
format!("{res}")
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::Day;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn part1_test() {
|
||||
day().assert1(
|
||||
"11-22,95-115,998-1012,1188511880-1188511890,222220-222224,1698522-1698528,446443-446449,38593856-38593862,565653-565659,824824821-824824827,2121212118-2121212124",
|
||||
"1227775554",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn part1_solve() {
|
||||
day().assert1(include_str!("../input/day2.txt"), "12599655151");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn part2_test() {
|
||||
day().assert2(
|
||||
"11-22,95-115,998-1012,1188511880-1188511890,222220-222224,1698522-1698528,446443-446449,38593856-38593862,565653-565659,824824821-824824827,2121212118-2121212124",
|
||||
"4174379265",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn part2_solve() {
|
||||
day().assert2(include_str!("../input/day2.txt"), "20942028255");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
use std::iter::Sum;
|
||||
|
||||
use crate::{AoC, WinnowDay};
|
||||
use winnow::{
|
||||
Parser, Result,
|
||||
ascii::newline,
|
||||
combinator::{repeat, terminated},
|
||||
error::ContextError,
|
||||
token::one_of,
|
||||
};
|
||||
|
||||
fn day() -> impl WinnowDay {
|
||||
AoC::new(parse as _, part1 as _, part2 as _)
|
||||
}
|
||||
|
||||
fn parse(input: &mut &str) -> Result<Vec<Vec<usize>>> {
|
||||
repeat(1.., parse_line).parse_next(input)
|
||||
}
|
||||
|
||||
fn parse_line(input: &mut &str) -> Result<Vec<usize>, ContextError> {
|
||||
terminated(
|
||||
repeat(
|
||||
1..,
|
||||
one_of(('0'..='9',)).map(|c: char| c.to_string().parse::<usize>().unwrap()),
|
||||
),
|
||||
newline,
|
||||
)
|
||||
.parse_next(input)
|
||||
}
|
||||
|
||||
fn part1(input: Vec<Vec<usize>>) -> usize {
|
||||
input.iter().map(joltage(2)).sum()
|
||||
}
|
||||
|
||||
fn part2(input: Vec<Vec<usize>>) -> usize {
|
||||
input.iter().map(joltage(12)).sum()
|
||||
}
|
||||
|
||||
fn joltage<
|
||||
'a,
|
||||
B: IntoIterator<IntoIter = I> + Copy,
|
||||
I: ExactSizeIterator<Item = &'a N>,
|
||||
N: Sum<&'a N> + Into<usize> + From<usize> + Copy + Default + 'a,
|
||||
>(
|
||||
size: usize,
|
||||
) -> impl Fn(B) -> N {
|
||||
move |line| {
|
||||
(line.into_iter().len() - size..line.into_iter().len())
|
||||
.fold((0, 0), |(start, val), end| {
|
||||
line.into_iter()
|
||||
.enumerate()
|
||||
.skip(start)
|
||||
.take(end - start + 1)
|
||||
.map(|(i, v)| (i + 1, (*v).into() + val * 10))
|
||||
.reduce(|a, b| if a.1 >= b.1 { a } else { b })
|
||||
.unwrap()
|
||||
})
|
||||
.1
|
||||
.into()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::Day;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn part1_test() {
|
||||
day().assert1(
|
||||
"987654321111111
|
||||
811111111111119
|
||||
234234234234278
|
||||
818181911112111
|
||||
",
|
||||
"357",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn part1_solve() {
|
||||
day().assert1(include_str!("../input/day3.txt"), "17343");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn part2_test() {
|
||||
day().assert2(
|
||||
"987654321111111
|
||||
811111111111119
|
||||
234234234234278
|
||||
818181911112111
|
||||
",
|
||||
"3121910778619",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn part2_solve() {
|
||||
day().assert2(include_str!("../input/day3.txt"), "172664333119298");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
use winnow::{
|
||||
Parser, Result,
|
||||
ascii::{multispace0, newline},
|
||||
combinator::{alt, preceded, repeat, terminated},
|
||||
};
|
||||
|
||||
use crate::{AoC, SimpleDay, WinnowDay};
|
||||
|
||||
fn day() -> impl WinnowDay {
|
||||
AoC::new(parse as _, part1 as _, part2 as _)
|
||||
}
|
||||
|
||||
type Parsed = Vec<Vec<Cell>>;
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
enum Cell {
|
||||
Empty,
|
||||
Paper,
|
||||
}
|
||||
|
||||
impl Cell {
|
||||
fn is_empty(&self) -> bool {
|
||||
self == &Cell::Empty
|
||||
}
|
||||
|
||||
fn is_paper(&self) -> bool {
|
||||
self == &Cell::Paper
|
||||
}
|
||||
}
|
||||
|
||||
fn parse(input: &mut &str) -> Result<Parsed> {
|
||||
repeat(1.., preceded(multispace0, parse_line)).parse_next(input)
|
||||
}
|
||||
|
||||
fn parse_line(input: &mut &str) -> Result<Vec<Cell>> {
|
||||
repeat(1.., parse_cell).parse_next(input)
|
||||
}
|
||||
|
||||
fn parse_cell(input: &mut &str) -> Result<Cell> {
|
||||
alt((".".map(|_| Cell::Empty), "@".map(|_| Cell::Paper))).parse_next(input)
|
||||
}
|
||||
|
||||
fn part1(input: Parsed) -> usize {
|
||||
let mut input = input;
|
||||
remove_paper(&mut input)
|
||||
}
|
||||
|
||||
fn remove_paper(input: &mut [Vec<Cell>]) -> usize {
|
||||
let mut score = 0;
|
||||
let mut access = Vec::new();
|
||||
let h = input.len();
|
||||
let w = input[0].len();
|
||||
let directions = [
|
||||
(-1, -1),
|
||||
(0, -1),
|
||||
(1, -1),
|
||||
(1, 0),
|
||||
(1, 1),
|
||||
(0, 1),
|
||||
(-1, 1),
|
||||
(-1, 0),
|
||||
];
|
||||
for y in 0..h {
|
||||
for x in 0..w {
|
||||
match input[y][x] {
|
||||
Cell::Empty => {}
|
||||
Cell::Paper => {
|
||||
let count = directions
|
||||
.iter()
|
||||
.filter(|dir| {
|
||||
let nx = x as isize + dir.0;
|
||||
let ny = y as isize + dir.1;
|
||||
nx >= 0
|
||||
&& nx < w as isize
|
||||
&& ny >= 0
|
||||
&& ny < h as isize
|
||||
&& input[ny as usize][nx as usize].is_paper()
|
||||
})
|
||||
.count();
|
||||
if count < 4 {
|
||||
score += 1;
|
||||
access.push((x, y));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
access.iter().for_each(|(x, y)| {
|
||||
input[*y][*x] = Cell::Empty;
|
||||
});
|
||||
score
|
||||
}
|
||||
|
||||
fn part2(input: Parsed) -> usize {
|
||||
let mut input = input;
|
||||
let mut score = 0;
|
||||
loop {
|
||||
let removed = remove_paper(&mut input);
|
||||
score += removed;
|
||||
if removed == 0 {
|
||||
return score;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::Day;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn part1_test() {
|
||||
day().assert1(
|
||||
"..@@.@@@@.
|
||||
@@@.@.@.@@
|
||||
@@@@@.@.@@
|
||||
@.@@@@..@.
|
||||
@@.@@@@.@@
|
||||
.@@@@@@@.@
|
||||
.@.@.@.@@@
|
||||
@.@@@.@@@@
|
||||
.@@@@@@@@.
|
||||
@.@.@@@.@.",
|
||||
"13",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn part1_solve() {
|
||||
day().assert1(include_str!("../input/day4.txt"), "1356");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn part2_test() {
|
||||
day().assert2(
|
||||
"..@@.@@@@.
|
||||
@@@.@.@.@@
|
||||
@@@@@.@.@@
|
||||
@.@@@@..@.
|
||||
@@.@@@@.@@
|
||||
.@@@@@@@.@
|
||||
.@.@.@.@@@
|
||||
@.@@@.@@@@
|
||||
.@@@@@@@@.
|
||||
@.@.@@@.@.",
|
||||
"43",
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn part2_solve() {
|
||||
day().assert2(include_str!("../input/day4.txt"), "8713");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
#![allow(dead_code)]
|
||||
#![allow(unused)]
|
||||
|
||||
use std::fmt::{Debug, Display};
|
||||
|
||||
use winnow::Parser;
|
||||
|
||||
mod day1;
|
||||
mod day2;
|
||||
mod day3;
|
||||
mod day4;
|
||||
|
||||
trait Day {
|
||||
fn assert1(&self, input: &str, output: &str);
|
||||
fn assert2(&self, input: &str, output: &str);
|
||||
}
|
||||
|
||||
struct AoC<P, O1, O2> {
|
||||
parse: P,
|
||||
part1: O1,
|
||||
part2: O2,
|
||||
}
|
||||
|
||||
impl<P, O1, O2> AoC<P, O1, O2> {
|
||||
fn new(parse: P, part1: O1, part2: O2) -> Self {
|
||||
Self {
|
||||
parse,
|
||||
part1,
|
||||
part2,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
trait SimpleDay: Day {}
|
||||
|
||||
impl<P, O1, O2> SimpleDay for AoC<fn(String) -> P, fn(P) -> O1, fn(P) -> O2>
|
||||
where
|
||||
O1: Display,
|
||||
O2: Display,
|
||||
{
|
||||
}
|
||||
|
||||
impl<P, O1, O2> Day for AoC<fn(String) -> P, fn(P) -> O1, fn(P) -> O2>
|
||||
where
|
||||
O1: Display,
|
||||
O2: Display,
|
||||
{
|
||||
fn assert1(&self, input: &str, output: &str) {
|
||||
let parsed = (self.parse)(input.to_string());
|
||||
assert_eq!(format!("{}", (self.part1)(parsed)), output);
|
||||
}
|
||||
|
||||
fn assert2(&self, input: &str, output: &str) {
|
||||
let parsed = (self.parse)(input.to_string());
|
||||
assert_eq!(format!("{}", (self.part2)(parsed)), output);
|
||||
}
|
||||
}
|
||||
|
||||
trait WinnowDay: Day {}
|
||||
|
||||
impl<O, E, O1, O2> WinnowDay
|
||||
for AoC<for<'a, 'b> fn(&'a mut &'b str) -> Result<O, E>, fn(O) -> O1, fn(O) -> O2>
|
||||
where
|
||||
E: Debug,
|
||||
O1: Display,
|
||||
O2: Display,
|
||||
{
|
||||
}
|
||||
|
||||
fn as_parser<O, E>(
|
||||
func: for<'a, 'b> fn(&'a mut &'b str) -> Result<O, E>,
|
||||
) -> impl for<'a> Parser<&'a str, O, E>
|
||||
where
|
||||
E: Debug,
|
||||
{
|
||||
func
|
||||
}
|
||||
|
||||
impl<O, E, O1, O2> Day
|
||||
for AoC<for<'a, 'b> fn(&'a mut &'b str) -> Result<O, E>, fn(O) -> O1, fn(O) -> O2>
|
||||
where
|
||||
E: Debug,
|
||||
O1: Display,
|
||||
O2: Display,
|
||||
{
|
||||
fn assert1(&self, input: &str, output: &str) {
|
||||
let mut input = input;
|
||||
let mut parser = as_parser(self.parse);
|
||||
let parsed = parser.parse_next(&mut input).unwrap();
|
||||
assert_eq!(format!("{}", (self.part1)(parsed)), output);
|
||||
}
|
||||
|
||||
fn assert2(&self, input: &str, output: &str) {
|
||||
let mut input = input;
|
||||
let mut parser = as_parser(self.parse);
|
||||
let parsed = parser.parse_next(&mut input).unwrap();
|
||||
assert_eq!(format!("{}", (self.part2)(parsed)), output);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
use winnow::{Parser, Result};
|
||||
|
||||
use crate::{AoC, SimpleDay, WinnowDay};
|
||||
|
||||
fn day() -> impl WinnowDay {
|
||||
AoC::new(parse as _, part1 as _, part2 as _)
|
||||
}
|
||||
|
||||
type Parsed = ();
|
||||
|
||||
fn parse(input: &mut &str) -> Result<Parsed> {
|
||||
"".map(|_| ()).parse_next(input)
|
||||
}
|
||||
|
||||
fn part1(input: Parsed) -> usize {
|
||||
0
|
||||
}
|
||||
|
||||
fn part2(input: Parsed) -> usize {
|
||||
0
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use crate::Day;
|
||||
|
||||
use super::*;
|
||||
|
||||
#[test]
|
||||
fn part1_test() {
|
||||
day().assert1("", "");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn part1_solve() {
|
||||
day().assert1(include_str!("../input/day.txt"), "");
|
||||
}
|
||||
|
||||
// #[test]
|
||||
// fn part2_test() {
|
||||
// day().assert2("", "");
|
||||
// }
|
||||
|
||||
// #[test]
|
||||
// fn part2_solve() {
|
||||
// day().assert2(include_str!("../input/day.txt"), "");
|
||||
// }
|
||||
}
|
||||
Generated
+61
@@ -0,0 +1,61 @@
|
||||
{
|
||||
"nodes": {
|
||||
"flake-utils": {
|
||||
"inputs": {
|
||||
"systems": "systems"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1731533236,
|
||||
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1764242076,
|
||||
"narHash": "sha256-sKoIWfnijJ0+9e4wRvIgm/HgE27bzwQxcEmo2J/gNpI=",
|
||||
"owner": "nixos",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "2fad6eac6077f03fe109c4d4eb171cf96791faa4",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nixos",
|
||||
"ref": "nixos-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"flake-utils": "flake-utils",
|
||||
"nixpkgs": "nixpkgs"
|
||||
}
|
||||
},
|
||||
"systems": {
|
||||
"locked": {
|
||||
"lastModified": 1681028828,
|
||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"type": "github"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
{
|
||||
inputs = {
|
||||
nixpkgs.url = "github:nixos/nixpkgs/nixos-unstable";
|
||||
flake-utils.url = "github:numtide/flake-utils";
|
||||
};
|
||||
|
||||
outputs =
|
||||
{
|
||||
nixpkgs,
|
||||
flake-utils,
|
||||
...
|
||||
}:
|
||||
flake-utils.lib.eachDefaultSystem (
|
||||
system:
|
||||
let
|
||||
pkgs = import nixpkgs {
|
||||
inherit system;
|
||||
};
|
||||
in
|
||||
{
|
||||
devShells.default = pkgs.mkShell {
|
||||
buildInputs = with pkgs; [
|
||||
cargo
|
||||
rustc
|
||||
rustfmt
|
||||
clippy
|
||||
bacon
|
||||
];
|
||||
};
|
||||
}
|
||||
);
|
||||
}
|
||||
Reference in new issue
Block a user