generated from janezicmatej/aoc-template
Compare commits
15 Commits
0000024016
...
day20
Author | SHA1 | Date | |
---|---|---|---|
000003902f
|
|||
00000380c1
|
|||
0000037022
|
|||
00000360db
|
|||
00000350d1
|
|||
0000034077
|
|||
0000033086
|
|||
0000032032
|
|||
00000310ba
|
|||
0000030011
|
|||
0000029007
|
|||
00000280ae
|
|||
000002707d
|
|||
0000026098
|
|||
000002508c
|
15
data/examples/13.txt
Normal file
15
data/examples/13.txt
Normal file
@@ -0,0 +1,15 @@
|
||||
#.##..##.
|
||||
..#.##.#.
|
||||
##......#
|
||||
##......#
|
||||
..#.##.#.
|
||||
..##..##.
|
||||
#.#.##.#.
|
||||
|
||||
#...##..#
|
||||
#....#..#
|
||||
..##..###
|
||||
#####.##.
|
||||
#####.##.
|
||||
..##..###
|
||||
#....#..#
|
10
data/examples/14.txt
Normal file
10
data/examples/14.txt
Normal file
@@ -0,0 +1,10 @@
|
||||
O....#....
|
||||
O.OO#....#
|
||||
.....##...
|
||||
OO.#O....O
|
||||
.O.....O#.
|
||||
O.#..O.#.#
|
||||
..O..#O..O
|
||||
.......O..
|
||||
#....###..
|
||||
#OO..#....
|
1
data/examples/15.txt
Normal file
1
data/examples/15.txt
Normal file
@@ -0,0 +1 @@
|
||||
rn=1,cm-,qp=3,cm=2,qp-,pc=4,ot=9,ab=5,pc-,pc=6,ot=7
|
10
data/examples/16.txt
Normal file
10
data/examples/16.txt
Normal file
@@ -0,0 +1,10 @@
|
||||
.|...\....
|
||||
|.-.\.....
|
||||
.....|-...
|
||||
........|.
|
||||
..........
|
||||
.........\
|
||||
..../.\\..
|
||||
.-.-/..|..
|
||||
.|....-|.\
|
||||
..//.|....
|
13
data/examples/17.txt
Normal file
13
data/examples/17.txt
Normal file
@@ -0,0 +1,13 @@
|
||||
2413432311323
|
||||
3215453535623
|
||||
3255245654254
|
||||
3446585845452
|
||||
4546657867536
|
||||
1438598798454
|
||||
4457876987766
|
||||
3637877979653
|
||||
4654967986887
|
||||
4564679986453
|
||||
1224686865563
|
||||
2546548887735
|
||||
4322674655533
|
14
data/examples/18.txt
Normal file
14
data/examples/18.txt
Normal file
@@ -0,0 +1,14 @@
|
||||
R 6 (#70c710)
|
||||
D 5 (#0dc571)
|
||||
L 2 (#5713f0)
|
||||
D 2 (#d2c081)
|
||||
R 2 (#59c680)
|
||||
D 2 (#411b91)
|
||||
L 5 (#8ceee2)
|
||||
U 2 (#caa173)
|
||||
L 1 (#1b58a2)
|
||||
U 2 (#caa171)
|
||||
R 2 (#7807d2)
|
||||
U 3 (#a77fa3)
|
||||
L 2 (#015232)
|
||||
U 2 (#7a21e3)
|
17
data/examples/19.txt
Normal file
17
data/examples/19.txt
Normal file
@@ -0,0 +1,17 @@
|
||||
px{a<2006:qkq,m>2090:A,rfg}
|
||||
pv{a>1716:R,A}
|
||||
lnx{m>1548:A,A}
|
||||
rfg{s<537:gd,x>2440:R,A}
|
||||
qs{s>3448:A,lnx}
|
||||
qkq{x<1416:A,crn}
|
||||
crn{x>2662:A,R}
|
||||
in{s<1351:px,qqz}
|
||||
qqz{s>2770:qs,m<1801:hdj,R}
|
||||
gd{a>3333:R,R}
|
||||
hdj{m>838:A,pv}
|
||||
|
||||
{x=787,m=2655,a=1222,s=2876}
|
||||
{x=1679,m=44,a=2067,s=496}
|
||||
{x=2036,m=264,a=79,s=2244}
|
||||
{x=2461,m=1339,a=466,s=291}
|
||||
{x=2127,m=1623,a=2188,s=1013}
|
5
data/examples/20.txt
Normal file
5
data/examples/20.txt
Normal file
@@ -0,0 +1,5 @@
|
||||
broadcaster -> a, b, c
|
||||
%a -> b
|
||||
%b -> c
|
||||
%c -> inv
|
||||
&inv -> a
|
@@ -78,10 +78,13 @@ mod tests {
|
||||
use super::*;
|
||||
#[test]
|
||||
fn test_part_one() {
|
||||
assert_eq!(part_one(&aoc::template::read_file("examples", 9)), None);
|
||||
assert_eq!(
|
||||
part_one(&aoc::template::read_file("examples", 9)),
|
||||
Some(114)
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn test_part_two() {
|
||||
assert_eq!(part_two(&aoc::template::read_file("examples", 9)), None);
|
||||
assert_eq!(part_two(&aoc::template::read_file("examples", 9)), Some(2));
|
||||
}
|
||||
}
|
||||
|
@@ -2,20 +2,16 @@ use std::collections::HashSet;
|
||||
|
||||
fn parse_input(input: &str) -> (Vec<(usize, usize)>, HashSet<usize>, HashSet<usize>) {
|
||||
let mut galaxies = Vec::new();
|
||||
let mut rows = HashSet::new();
|
||||
let mut columns = HashSet::from_iter(1..(input.lines().next().unwrap().len()));
|
||||
let mut rows = HashSet::from_iter(0..input.lines().count());
|
||||
let mut columns = HashSet::from_iter(0..input.lines().next().unwrap().len());
|
||||
|
||||
for (y, line) in input.lines().enumerate() {
|
||||
let mut found = false;
|
||||
for (x, c) in line.chars().enumerate() {
|
||||
if c == '#' {
|
||||
galaxies.push((y, x));
|
||||
found = true;
|
||||
}
|
||||
rows.remove(&y);
|
||||
columns.remove(&x);
|
||||
}
|
||||
if !found {
|
||||
rows.insert(y);
|
||||
}
|
||||
}
|
||||
|
||||
|
64
src/bin/13.rs
Normal file
64
src/bin/13.rs
Normal file
@@ -0,0 +1,64 @@
|
||||
fn mirror_h(shape: &[Vec<char>], smudges: usize) -> Option<usize> {
|
||||
(1..shape.len()).find(|&i| {
|
||||
shape
|
||||
.iter()
|
||||
.skip(i)
|
||||
.zip(shape.iter().take(i).rev())
|
||||
.map(|(x, y)| {
|
||||
x.iter()
|
||||
.zip(y.iter())
|
||||
.map(|(xx, yy)| (xx != yy) as usize)
|
||||
.sum::<usize>()
|
||||
})
|
||||
.sum::<usize>()
|
||||
== smudges
|
||||
})
|
||||
}
|
||||
|
||||
fn mirror_v(shape: &[Vec<char>], smudges: usize) -> Option<usize> {
|
||||
let shape: Vec<Vec<char>> = (0..shape[0].len())
|
||||
.map(|col| (0..shape.len()).map(|row| shape[row][col]).collect())
|
||||
.collect();
|
||||
|
||||
mirror_h(&shape, smudges)
|
||||
}
|
||||
|
||||
fn solve(input: &str, smudges: usize) -> usize {
|
||||
input
|
||||
.split("\n\n")
|
||||
.map(|x| x.lines().map(|line| line.chars().collect()).collect())
|
||||
.map(|shape: Vec<Vec<char>>| {
|
||||
mirror_v(&shape, smudges).unwrap_or_default()
|
||||
+ mirror_h(&shape, smudges).unwrap_or_default() * 100
|
||||
})
|
||||
.sum()
|
||||
}
|
||||
|
||||
pub fn part_one(input: &str) -> Option<usize> {
|
||||
Some(solve(input, 0))
|
||||
}
|
||||
|
||||
pub fn part_two(input: &str) -> Option<usize> {
|
||||
Some(solve(input, 1))
|
||||
}
|
||||
|
||||
aoc::solution!(13);
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
#[test]
|
||||
fn test_part_one() {
|
||||
assert_eq!(
|
||||
part_one(&aoc::template::read_file("examples", 13)),
|
||||
Some(405)
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn test_part_two() {
|
||||
assert_eq!(
|
||||
part_two(&aoc::template::read_file("examples", 13)),
|
||||
Some(400)
|
||||
);
|
||||
}
|
||||
}
|
115
src/bin/14.rs
Normal file
115
src/bin/14.rs
Normal file
@@ -0,0 +1,115 @@
|
||||
use std::{collections::HashMap, iter::once};
|
||||
|
||||
enum Tilt {
|
||||
North,
|
||||
West,
|
||||
South,
|
||||
East,
|
||||
}
|
||||
|
||||
fn get_load(floor: &[Vec<char>]) -> usize {
|
||||
floor
|
||||
.iter()
|
||||
.enumerate()
|
||||
.flat_map(|(i, x)| x.iter().map(move |y| (i, y)))
|
||||
.filter(|x| *x.1 == 'O')
|
||||
.map(|x| floor.len() - x.0)
|
||||
.sum()
|
||||
}
|
||||
|
||||
fn swap<T: Copy>(floor: &mut [Vec<T>], from: (usize, usize), to: (usize, usize)) {
|
||||
let a = floor[from.0][from.1];
|
||||
let b = floor[to.0][to.1];
|
||||
floor[from.0][from.1] = b;
|
||||
floor[to.0][to.1] = a;
|
||||
}
|
||||
|
||||
fn tilt(floor: &mut [Vec<char>], tilt: Tilt) {
|
||||
let (inner, outer) = match tilt {
|
||||
Tilt::North | Tilt::South => (floor[0].len(), floor.len()),
|
||||
Tilt::West | Tilt::East => (floor.len(), floor[0].len()),
|
||||
};
|
||||
let inx_n = |(i, j)| (j, i);
|
||||
let inx_s = |(i, j)| (inner - 1 - j, i);
|
||||
let inx_w = |(i, j)| (i, j);
|
||||
let inx_e = |(i, j)| (i, inner - 1 - j);
|
||||
for i in 0..outer {
|
||||
let mut ptr = 0;
|
||||
for j in 0..inner {
|
||||
let ((ii, jj), (pi, pj)) = match tilt {
|
||||
Tilt::North => (inx_n((i, j)), inx_n((i, ptr))),
|
||||
Tilt::South => (inx_s((i, j)), inx_s((i, ptr))),
|
||||
Tilt::East => (inx_e((i, j)), inx_e((i, ptr))),
|
||||
Tilt::West => (inx_w((i, j)), inx_w((i, ptr))),
|
||||
};
|
||||
match floor[ii][jj] {
|
||||
'O' => {
|
||||
swap(floor, (ii, jj), (pi, pj));
|
||||
ptr += 1;
|
||||
}
|
||||
'#' => ptr = j + 1,
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn tilt_cycle(floor: &mut [Vec<char>]) {
|
||||
use Tilt::*;
|
||||
tilt(floor, North);
|
||||
tilt(floor, West);
|
||||
tilt(floor, South);
|
||||
tilt(floor, East);
|
||||
}
|
||||
|
||||
pub fn part_one(input: &str) -> Option<usize> {
|
||||
let mut f: Vec<Vec<_>> = input.lines().map(|x| x.chars().collect()).collect();
|
||||
tilt(&mut f, Tilt::North);
|
||||
Some(get_load(&f))
|
||||
}
|
||||
|
||||
pub fn part_two(input: &str) -> Option<usize> {
|
||||
let mut f: Vec<Vec<_>> = input.lines().map(|x| x.chars().collect()).collect();
|
||||
let mut memo: HashMap<String, usize> = HashMap::new();
|
||||
|
||||
for i in 1.. {
|
||||
tilt_cycle(&mut f);
|
||||
let repr = f
|
||||
.iter()
|
||||
.flat_map(|x| x.iter().chain(once(&'\n')))
|
||||
.collect::<String>();
|
||||
|
||||
if let Some(ii) = memo.insert(repr, i) {
|
||||
let m = i - ii;
|
||||
let shift = (1_000_000_000 - ii) % m;
|
||||
|
||||
for _ in 0..shift {
|
||||
tilt_cycle(&mut f);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Some(get_load(&f))
|
||||
}
|
||||
|
||||
aoc::solution!(14);
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
#[test]
|
||||
fn test_part_one() {
|
||||
assert_eq!(
|
||||
part_one(&aoc::template::read_file("examples", 14)),
|
||||
Some(136)
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn test_part_two() {
|
||||
assert_eq!(
|
||||
part_two(&aoc::template::read_file("examples", 14)),
|
||||
Some(64)
|
||||
);
|
||||
}
|
||||
}
|
68
src/bin/15.rs
Normal file
68
src/bin/15.rs
Normal file
@@ -0,0 +1,68 @@
|
||||
fn hash(s: &str) -> usize {
|
||||
let mut total = 0;
|
||||
for c in s.chars().map(|x| x as usize) {
|
||||
total += c;
|
||||
total *= 17;
|
||||
total %= 256;
|
||||
}
|
||||
total
|
||||
}
|
||||
|
||||
pub fn part_one(input: &str) -> Option<usize> {
|
||||
Some(input.lines().flat_map(|x| x.split(',')).map(hash).sum())
|
||||
}
|
||||
|
||||
pub fn part_two(input: &str) -> Option<usize> {
|
||||
let mut map = vec![Vec::<(&str, usize)>::new(); 256];
|
||||
|
||||
for instruction in input.lines().flat_map(|x| x.split(',')) {
|
||||
let (label, n) = instruction.split_once(|x| x == '-' || x == '=').unwrap();
|
||||
let hash = hash(label);
|
||||
let indexed_map = map.get_mut(hash).unwrap();
|
||||
|
||||
if instruction.contains('-') {
|
||||
if let Some((i, _)) = indexed_map.iter().enumerate().find(|(_, &x)| x.0 == label) {
|
||||
indexed_map.remove(i);
|
||||
}
|
||||
} else {
|
||||
let nbr = n.parse().unwrap();
|
||||
if let Some((i, _)) = indexed_map.iter().enumerate().find(|(_, &x)| x.0 == label) {
|
||||
indexed_map[i] = (label, nbr);
|
||||
} else {
|
||||
indexed_map.push((label, nbr))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Some(
|
||||
map.into_iter()
|
||||
.enumerate()
|
||||
.flat_map(|(i, x)| {
|
||||
x.into_iter()
|
||||
.enumerate()
|
||||
.map(move |(j, y)| (i + 1) * (j + 1) * y.1)
|
||||
})
|
||||
.sum(),
|
||||
)
|
||||
}
|
||||
|
||||
aoc::solution!(15);
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
#[test]
|
||||
fn test_part_one() {
|
||||
assert_eq!(
|
||||
part_one(&aoc::template::read_file("examples", 15)),
|
||||
Some(1320)
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn test_part_two() {
|
||||
assert_eq!(
|
||||
part_two(&aoc::template::read_file("examples", 15)),
|
||||
Some(145)
|
||||
);
|
||||
}
|
||||
}
|
173
src/bin/16.rs
Normal file
173
src/bin/16.rs
Normal file
@@ -0,0 +1,173 @@
|
||||
use std::collections::HashSet;
|
||||
|
||||
#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq)]
|
||||
enum Direction {
|
||||
Up,
|
||||
Down,
|
||||
Left,
|
||||
Right,
|
||||
}
|
||||
|
||||
impl Direction {
|
||||
fn next(&self, (y, x): (usize, usize)) -> (usize, usize) {
|
||||
use Direction::*;
|
||||
match self {
|
||||
Up => (y.checked_sub(1).unwrap_or(usize::MAX), x),
|
||||
Down => (y + 1, x),
|
||||
Left => (y, x.checked_sub(1).unwrap_or(usize::MAX)),
|
||||
Right => (y, x + 1),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
enum Mirror {
|
||||
Vertical,
|
||||
Horizontal,
|
||||
EvenSymmetric,
|
||||
OddSymmetric,
|
||||
}
|
||||
|
||||
struct ParseMirrorError;
|
||||
|
||||
impl TryFrom<u8> for Mirror {
|
||||
type Error = ParseMirrorError;
|
||||
fn try_from(value: u8) -> Result<Self, Self::Error> {
|
||||
Ok(match value {
|
||||
b'|' => Self::Vertical,
|
||||
b'-' => Self::Horizontal,
|
||||
b'\\' => Self::EvenSymmetric,
|
||||
b'/' => Self::OddSymmetric,
|
||||
_ => return Err(ParseMirrorError),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Mirror {
|
||||
fn bounce(&self, d: &Direction) -> (Direction, Option<Direction>) {
|
||||
use Direction::*;
|
||||
use Mirror::*;
|
||||
|
||||
match self {
|
||||
Vertical => match d {
|
||||
Up | Down => (*d, None),
|
||||
Left | Right => (Up, Some(Down)),
|
||||
},
|
||||
Horizontal => match d {
|
||||
Up | Down => (Left, Some(Right)),
|
||||
Left | Right => (*d, None),
|
||||
},
|
||||
EvenSymmetric => (
|
||||
match d {
|
||||
Up => Left,
|
||||
Down => Right,
|
||||
Left => Up,
|
||||
Right => Down,
|
||||
},
|
||||
None,
|
||||
),
|
||||
OddSymmetric => (
|
||||
match d {
|
||||
Up => Right,
|
||||
Down => Left,
|
||||
Left => Down,
|
||||
Right => Up,
|
||||
},
|
||||
None,
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn solve_with_start(layout: &[Vec<Option<Mirror>>], start: (usize, usize, Direction)) -> usize {
|
||||
let mut queue = vec![start];
|
||||
let mut visited = HashSet::new();
|
||||
|
||||
while let Some((y, x, d)) = queue.pop() {
|
||||
let point = layout.get(y).and_then(|row| row.get(x));
|
||||
|
||||
if point.is_none() {
|
||||
continue;
|
||||
}
|
||||
|
||||
if !visited.insert((y, x, d)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if let Some(Some(m)) = point {
|
||||
let (new_d, opt_new_d) = m.bounce(&d);
|
||||
let (ny, nx) = new_d.next((y, x));
|
||||
queue.push((ny, nx, new_d));
|
||||
|
||||
if let Some(od) = opt_new_d {
|
||||
let (ony, onx) = od.next((y, x));
|
||||
queue.push((ony, onx, od));
|
||||
}
|
||||
} else {
|
||||
let (ny, nx) = d.next((y, x));
|
||||
queue.push((ny, nx, d));
|
||||
}
|
||||
}
|
||||
|
||||
HashSet::<(usize, usize)>::from_iter(visited.into_iter().map(|(y, x, _)| (y, x))).len()
|
||||
}
|
||||
|
||||
pub fn part_one(input: &str) -> Option<usize> {
|
||||
let layout: Vec<Vec<_>> = input
|
||||
.lines()
|
||||
.map(|x| x.as_bytes().iter().map(|&x| x.try_into().ok()).collect())
|
||||
.collect();
|
||||
|
||||
Some(solve_with_start(&layout, (0, 0, Direction::Right)))
|
||||
}
|
||||
|
||||
pub fn part_two(input: &str) -> Option<usize> {
|
||||
let layout: Vec<Vec<_>> = input
|
||||
.lines()
|
||||
.map(|x| x.as_bytes().iter().map(|&x| x.try_into().ok()).collect())
|
||||
.collect();
|
||||
|
||||
let h = layout.len();
|
||||
let w = layout[0].len();
|
||||
|
||||
let mut scores = Vec::with_capacity(2 * (h + w) + 1);
|
||||
|
||||
for hh in 0..h {
|
||||
let left = solve_with_start(&layout, (hh, 0, Direction::Right));
|
||||
let right = solve_with_start(&layout, (hh, w - 1, Direction::Left));
|
||||
|
||||
scores.push(left);
|
||||
scores.push(right);
|
||||
}
|
||||
|
||||
for ww in 0..w {
|
||||
let downward = solve_with_start(&layout, (0, ww, Direction::Down));
|
||||
let upward = solve_with_start(&layout, (h - 1, ww, Direction::Up));
|
||||
|
||||
scores.push(downward);
|
||||
scores.push(upward);
|
||||
}
|
||||
|
||||
scores.into_iter().max()
|
||||
}
|
||||
|
||||
aoc::solution!(16);
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
#[test]
|
||||
fn test_part_one() {
|
||||
assert_eq!(
|
||||
part_one(&aoc::template::read_file("examples", 16)),
|
||||
Some(46)
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn test_part_two() {
|
||||
assert_eq!(
|
||||
part_two(&aoc::template::read_file("examples", 16)),
|
||||
Some(51)
|
||||
);
|
||||
}
|
||||
}
|
192
src/bin/17.rs
Normal file
192
src/bin/17.rs
Normal file
@@ -0,0 +1,192 @@
|
||||
use std::{
|
||||
collections::{BinaryHeap, HashMap},
|
||||
ops::Neg,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
enum Direction {
|
||||
Up,
|
||||
Down,
|
||||
Left,
|
||||
Right,
|
||||
}
|
||||
|
||||
impl Direction {
|
||||
const ALL: [Direction; 4] = [
|
||||
Direction::Up,
|
||||
Direction::Down,
|
||||
Direction::Left,
|
||||
Direction::Right,
|
||||
];
|
||||
}
|
||||
|
||||
impl Neg for Direction {
|
||||
type Output = Self;
|
||||
fn neg(self) -> Self::Output {
|
||||
use Direction::*;
|
||||
match self {
|
||||
Up => Down,
|
||||
Down => Up,
|
||||
Left => Right,
|
||||
Right => Left,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
struct State {
|
||||
heat: usize,
|
||||
position: (usize, usize),
|
||||
direction: Direction,
|
||||
steps: usize,
|
||||
}
|
||||
|
||||
impl PartialOrd for State {
|
||||
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
|
||||
Some(self.cmp(other))
|
||||
}
|
||||
}
|
||||
|
||||
impl Ord for State {
|
||||
fn cmp(&self, other: &Self) -> std::cmp::Ordering {
|
||||
other
|
||||
.heat
|
||||
.cmp(&self.heat)
|
||||
.then_with(|| self.position.cmp(&other.position))
|
||||
}
|
||||
}
|
||||
|
||||
fn find_path(
|
||||
grid: &[Vec<usize>],
|
||||
start: (usize, usize),
|
||||
target: (usize, usize),
|
||||
min_steps: usize,
|
||||
max_steps: usize,
|
||||
) -> Option<usize> {
|
||||
let valid_indexing = |y: usize, x: usize| grid.get(y).and_then(|row| row.get(x)).is_some();
|
||||
|
||||
let mut heap = BinaryHeap::new();
|
||||
heap.push(State {
|
||||
heat: 0,
|
||||
position: (0, 0),
|
||||
direction: Direction::Right,
|
||||
steps: 0,
|
||||
});
|
||||
|
||||
let mut heatmap = HashMap::new();
|
||||
heatmap.insert((start, Direction::Down, 0), 0);
|
||||
heatmap.insert((start, Direction::Up, 0), 0);
|
||||
|
||||
while let Some(State {
|
||||
heat,
|
||||
position,
|
||||
direction,
|
||||
steps,
|
||||
}) = heap.pop()
|
||||
{
|
||||
if position == target {
|
||||
return Some(heat);
|
||||
}
|
||||
|
||||
if *heatmap
|
||||
.get(&(position, direction, steps))
|
||||
.unwrap_or(&usize::MAX)
|
||||
< heat
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
let (y, x) = position;
|
||||
for d in Direction::ALL.iter().filter(|&x| *x != -direction) {
|
||||
if steps < min_steps && *d != direction {
|
||||
continue;
|
||||
}
|
||||
|
||||
let (ny, nx) = match d {
|
||||
Direction::Up => (y.wrapping_sub(1), x),
|
||||
Direction::Down => (y + 1, x),
|
||||
Direction::Left => (y, x.wrapping_sub(1)),
|
||||
Direction::Right => (y, x + 1),
|
||||
};
|
||||
|
||||
if !valid_indexing(ny, nx) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let new_steps = if *d == direction { steps + 1 } else { 1 };
|
||||
|
||||
if new_steps > max_steps {
|
||||
continue;
|
||||
}
|
||||
|
||||
let state = State {
|
||||
heat: heat + grid[ny][nx],
|
||||
position: (ny, nx),
|
||||
direction: *d,
|
||||
steps: new_steps,
|
||||
};
|
||||
|
||||
if *heatmap
|
||||
.get(&((ny, nx), *d, new_steps))
|
||||
.unwrap_or(&usize::MAX)
|
||||
> state.heat
|
||||
{
|
||||
heatmap.insert(((ny, nx), *d, new_steps), state.heat);
|
||||
heap.push(state);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
pub fn part_one(input: &str) -> Option<usize> {
|
||||
let grid: Vec<Vec<_>> = input
|
||||
.lines()
|
||||
.map(|x| {
|
||||
x.chars()
|
||||
.filter_map(|x| x.to_digit(10).map(|x| x as usize))
|
||||
.collect()
|
||||
})
|
||||
.collect();
|
||||
|
||||
let target = (grid.len() - 1, grid[0].len() - 1);
|
||||
|
||||
find_path(&grid, (0, 0), target, 0, 3)
|
||||
}
|
||||
|
||||
pub fn part_two(input: &str) -> Option<usize> {
|
||||
let grid: Vec<Vec<_>> = input
|
||||
.lines()
|
||||
.map(|x| {
|
||||
x.chars()
|
||||
.filter_map(|x| x.to_digit(10).map(|x| x as usize))
|
||||
.collect()
|
||||
})
|
||||
.collect();
|
||||
|
||||
let target = (grid.len() - 1, grid[0].len() - 1);
|
||||
|
||||
find_path(&grid, (0, 0), target, 4, 10)
|
||||
}
|
||||
|
||||
aoc::solution!(17);
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
#[test]
|
||||
fn test_part_one() {
|
||||
assert_eq!(
|
||||
part_one(&aoc::template::read_file("examples", 17)),
|
||||
Some(102)
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn test_part_two() {
|
||||
assert_eq!(
|
||||
part_two(&aoc::template::read_file("examples", 17)),
|
||||
Some(94)
|
||||
);
|
||||
}
|
||||
}
|
126
src/bin/18.rs
Normal file
126
src/bin/18.rs
Normal file
@@ -0,0 +1,126 @@
|
||||
use std::str::FromStr;
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
enum Direction {
|
||||
Up,
|
||||
Down,
|
||||
Left,
|
||||
Right,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct ParseDirectionError;
|
||||
|
||||
impl FromStr for Direction {
|
||||
type Err = ParseDirectionError;
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
use Direction::*;
|
||||
|
||||
Ok(match s {
|
||||
"U" | "3" => Up,
|
||||
"D" | "1" => Down,
|
||||
"L" | "2" => Left,
|
||||
"R" | "0" => Right,
|
||||
_ => return Err(ParseDirectionError),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Direction> for (isize, isize) {
|
||||
fn from(value: Direction) -> Self {
|
||||
use Direction::*;
|
||||
match value {
|
||||
Up => (-1, 0),
|
||||
Down => (1, 0),
|
||||
Left => (0, -1),
|
||||
Right => (0, 1),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn get_area(border: &[(isize, isize)], border_length: isize) -> isize {
|
||||
// get area with shoelace formula (trapezoid variant)
|
||||
// https://en.wikipedia.org/wiki/Shoelace_formula
|
||||
let mut shoelace: isize = 0;
|
||||
for n in 0..border.len() {
|
||||
let (y1, x1) = border[n];
|
||||
let (y2, x2) = border[(n + 1) % border.len()];
|
||||
shoelace += (y1 + y2) * (x1 - x2);
|
||||
}
|
||||
let area = shoelace / 2;
|
||||
|
||||
// get interior by inverting pick's theorem formula
|
||||
// https://en.wikipedia.org/wiki/Pick%27s_theorem
|
||||
let interior = area + 1 - border_length / 2;
|
||||
interior + border_length
|
||||
}
|
||||
|
||||
fn get_border(instructions: &[(Direction, isize)]) -> (Vec<(isize, isize)>, isize) {
|
||||
let mut border = Vec::new();
|
||||
let mut border_length = 0;
|
||||
let (mut sy, mut sx) = (0, 0);
|
||||
border.push((sy, sx));
|
||||
|
||||
for (d, l) in instructions.iter().copied() {
|
||||
let (dy, dx) = d.into();
|
||||
(sy, sx) = (sy + l * dy, sx + l * dx);
|
||||
border.push((sy, sx));
|
||||
border_length += l;
|
||||
}
|
||||
|
||||
border.pop();
|
||||
|
||||
(border, border_length)
|
||||
}
|
||||
|
||||
pub fn part_one(input: &str) -> Option<isize> {
|
||||
let instructions: Vec<(Direction, isize)> = input
|
||||
.lines()
|
||||
.map(|line| {
|
||||
let [d, l, _] = line.splitn(3, ' ').collect::<Vec<_>>().try_into().unwrap();
|
||||
(d.parse().unwrap(), l.parse::<isize>().unwrap())
|
||||
})
|
||||
.collect();
|
||||
|
||||
let (border, border_length) = get_border(&instructions);
|
||||
Some(get_area(&border, border_length))
|
||||
}
|
||||
|
||||
fn join_option_tuple<T, U>((a, b): (Option<T>, Option<U>)) -> Option<(T, U)> {
|
||||
Some((a?, b?))
|
||||
}
|
||||
|
||||
pub fn part_two(input: &str) -> Option<isize> {
|
||||
let instructions: Vec<(Direction, isize)> = input
|
||||
.lines()
|
||||
.filter_map(|line| line.split_once(" (#"))
|
||||
.filter_map(|(_, h)| h.strip_suffix(')'))
|
||||
.map(|h| h.split_at(h.len() - 1))
|
||||
.map(|(hex, dir)| (dir.parse().ok(), isize::from_str_radix(hex, 16).ok()))
|
||||
.filter_map(join_option_tuple)
|
||||
.collect();
|
||||
|
||||
let (border, border_length) = get_border(&instructions);
|
||||
Some(get_area(&border, border_length))
|
||||
}
|
||||
|
||||
aoc::solution!(18);
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
#[test]
|
||||
fn test_part_one() {
|
||||
assert_eq!(
|
||||
part_one(&aoc::template::read_file("examples", 18)),
|
||||
Some(62)
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn test_part_two() {
|
||||
assert_eq!(
|
||||
part_two(&aoc::template::read_file("examples", 18)),
|
||||
Some(952408144115)
|
||||
);
|
||||
}
|
||||
}
|
387
src/bin/19.rs
Normal file
387
src/bin/19.rs
Normal file
@@ -0,0 +1,387 @@
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
ops::{Index, IndexMut},
|
||||
str::FromStr,
|
||||
};
|
||||
|
||||
#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)]
|
||||
enum Gear {
|
||||
X,
|
||||
M,
|
||||
A,
|
||||
S,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct GearParseError;
|
||||
|
||||
impl TryFrom<&str> for Gear {
|
||||
type Error = GearParseError;
|
||||
fn try_from(value: &str) -> Result<Self, Self::Error> {
|
||||
use Gear::*;
|
||||
Ok(match value {
|
||||
"x" => X,
|
||||
"m" => M,
|
||||
"a" => A,
|
||||
"s" => S,
|
||||
_ => return Err(GearParseError),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Hash, PartialEq, Eq)]
|
||||
enum Comparator {
|
||||
Lt,
|
||||
Gt,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct ComparatorParseError;
|
||||
|
||||
impl TryFrom<char> for Comparator {
|
||||
type Error = ComparatorParseError;
|
||||
fn try_from(value: char) -> Result<Self, Self::Error> {
|
||||
use Comparator::*;
|
||||
Ok(match value {
|
||||
'<' => Lt,
|
||||
'>' => Gt,
|
||||
_ => return Err(ComparatorParseError),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Hash, PartialEq, Eq, Clone)]
|
||||
enum Resolver {
|
||||
Accepted,
|
||||
Rejected,
|
||||
Delegated(String),
|
||||
}
|
||||
|
||||
impl From<&str> for Resolver {
|
||||
fn from(value: &str) -> Self {
|
||||
use Resolver::*;
|
||||
match value {
|
||||
"A" => Accepted,
|
||||
"R" => Rejected,
|
||||
x => Delegated(x.to_string()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Hash, PartialEq, Eq)]
|
||||
struct Workflow {
|
||||
workflows: Vec<WorkflowInner>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct ParseWorkflowError;
|
||||
|
||||
impl TryFrom<&str> for Workflow {
|
||||
type Error = ParseWorkflowError;
|
||||
fn try_from(value: &str) -> Result<Self, Self::Error> {
|
||||
Ok(Self {
|
||||
workflows: value.split(',').filter_map(|x| x.try_into().ok()).collect(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Hash, PartialEq, Eq)]
|
||||
enum WorkflowInner {
|
||||
Resolver(Resolver),
|
||||
Rule((Gear, Comparator, usize, Resolver)),
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct ParseWorkflowInnerError;
|
||||
|
||||
impl TryFrom<&str> for WorkflowInner {
|
||||
type Error = ParseWorkflowInnerError;
|
||||
fn try_from(value: &str) -> Result<Self, Self::Error> {
|
||||
if !value.contains(':') {
|
||||
return Ok(WorkflowInner::Resolver(value.into()));
|
||||
}
|
||||
|
||||
let (rest, resolver) = value.split_once(':').unwrap();
|
||||
let resolver = resolver.into();
|
||||
|
||||
let (gear, number) = rest.split_once(|x| x == '<' || x == '>').unwrap();
|
||||
let gear = gear.try_into().map_err(|_| ParseWorkflowInnerError)?;
|
||||
let number = number.parse().map_err(|_| ParseWorkflowInnerError)?;
|
||||
|
||||
let comparator = if value.contains('<') { '<' } else { '>' };
|
||||
let comparator = comparator.try_into().map_err(|_| ParseWorkflowInnerError)?;
|
||||
|
||||
Ok(WorkflowInner::Rule((gear, comparator, number, resolver)))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Xmas {
|
||||
x: usize,
|
||||
m: usize,
|
||||
a: usize,
|
||||
s: usize,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct ParseXmasError;
|
||||
|
||||
impl FromStr for Xmas {
|
||||
type Err = ParseXmasError;
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
let s = s.strip_prefix('{').ok_or(ParseXmasError)?;
|
||||
let s = s.strip_suffix('}').ok_or(ParseXmasError)?;
|
||||
let xmas: Vec<_> = s
|
||||
.split(',')
|
||||
.filter_map(|x| x.split_once('='))
|
||||
.map(|x| x.1)
|
||||
.filter_map(|x| x.parse().ok())
|
||||
.collect();
|
||||
|
||||
Ok(Self {
|
||||
x: xmas[0],
|
||||
m: xmas[1],
|
||||
a: xmas[2],
|
||||
s: xmas[3],
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Index<Gear> for Xmas {
|
||||
type Output = usize;
|
||||
fn index(&self, index: Gear) -> &Self::Output {
|
||||
use Gear::*;
|
||||
match index {
|
||||
X => &self.x,
|
||||
M => &self.m,
|
||||
A => &self.a,
|
||||
S => &self.s,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl IndexMut<Gear> for Xmas {
|
||||
fn index_mut(&mut self, index: Gear) -> &mut Self::Output {
|
||||
use Gear::*;
|
||||
match index {
|
||||
X => &mut self.x,
|
||||
M => &mut self.m,
|
||||
A => &mut self.a,
|
||||
S => &mut self.s,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Xmas {
|
||||
fn sum(&self) -> usize {
|
||||
self.x + self.m + self.a + self.s
|
||||
}
|
||||
|
||||
fn apply(&self, workflow: &Workflow) -> Resolver {
|
||||
for w in workflow.workflows.iter() {
|
||||
match w {
|
||||
WorkflowInner::Resolver(x) => {
|
||||
return x.clone();
|
||||
}
|
||||
WorkflowInner::Rule((g, c, n, r)) => {
|
||||
let is_match = match c {
|
||||
Comparator::Gt => self[*g] > *n,
|
||||
Comparator::Lt => self[*g] < *n,
|
||||
};
|
||||
if is_match {
|
||||
return r.clone();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy)]
|
||||
struct XmasRange {
|
||||
x: (usize, usize),
|
||||
m: (usize, usize),
|
||||
a: (usize, usize),
|
||||
s: (usize, usize),
|
||||
}
|
||||
|
||||
impl Index<Gear> for XmasRange {
|
||||
type Output = (usize, usize);
|
||||
fn index(&self, index: Gear) -> &Self::Output {
|
||||
use Gear::*;
|
||||
match index {
|
||||
X => &self.x,
|
||||
M => &self.m,
|
||||
A => &self.a,
|
||||
S => &self.s,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl IndexMut<Gear> for XmasRange {
|
||||
fn index_mut(&mut self, index: Gear) -> &mut Self::Output {
|
||||
use Gear::*;
|
||||
match index {
|
||||
X => &mut self.x,
|
||||
M => &mut self.m,
|
||||
A => &mut self.a,
|
||||
S => &mut self.s,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl XmasRange {
|
||||
fn size(&self) -> usize {
|
||||
(self.x.1 - self.x.0 + 1)
|
||||
* (self.m.1 - self.m.0 + 1)
|
||||
* (self.a.1 - self.a.0 + 1)
|
||||
* (self.s.1 - self.s.0 + 1)
|
||||
}
|
||||
|
||||
fn divide(self, workflow: &Workflow) -> Vec<(Self, Resolver)> {
|
||||
let mut processed = Vec::new();
|
||||
let mut rest = vec![self];
|
||||
|
||||
for w in workflow.workflows.iter() {
|
||||
let mut new_rest = Vec::new();
|
||||
|
||||
while let Some(xmas_range) = rest.pop() {
|
||||
match w {
|
||||
WorkflowInner::Resolver(r) => processed.push((xmas_range, r.clone())),
|
||||
WorkflowInner::Rule((g, c, n, r)) => {
|
||||
let compare = |x: usize, y: usize| match c {
|
||||
Comparator::Gt => x > y,
|
||||
Comparator::Lt => x < y,
|
||||
};
|
||||
|
||||
let mut min_ok = usize::MAX;
|
||||
let mut max_ok = usize::MIN;
|
||||
let mut min_e = usize::MAX;
|
||||
let mut max_e = usize::MIN;
|
||||
|
||||
for i in xmas_range[*g].0..=xmas_range[*g].1 {
|
||||
if compare(i, *n) {
|
||||
max_ok = if max_ok < i { i } else { max_ok };
|
||||
min_ok = if min_ok > i { i } else { min_ok };
|
||||
} else {
|
||||
max_e = if max_e < i { i } else { max_e };
|
||||
min_e = if min_e > i { i } else { min_e };
|
||||
}
|
||||
}
|
||||
|
||||
if min_e <= max_e {
|
||||
let mut r = xmas_range;
|
||||
r[*g] = (min_e, max_e);
|
||||
new_rest.push(r);
|
||||
}
|
||||
if min_ok <= max_ok {
|
||||
let mut p = xmas_range;
|
||||
p[*g] = (min_ok, max_ok);
|
||||
processed.push((p, r.clone()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rest = new_rest;
|
||||
}
|
||||
|
||||
processed
|
||||
}
|
||||
}
|
||||
|
||||
fn build_map(input: &str) -> HashMap<&str, Workflow> {
|
||||
let mut map = HashMap::new();
|
||||
for (s, w) in input
|
||||
.split("\n\n")
|
||||
.next()
|
||||
.unwrap()
|
||||
.lines()
|
||||
.filter_map(|x| x.strip_suffix('}'))
|
||||
.filter_map(|x| x.split_once('{'))
|
||||
.filter_map(|(s, w)| w.try_into().ok().map(|x| (s, x)))
|
||||
{
|
||||
map.insert(s, w);
|
||||
}
|
||||
|
||||
map
|
||||
}
|
||||
|
||||
pub fn part_one(input: &str) -> Option<usize> {
|
||||
let map = build_map(input);
|
||||
let xmas_vec: Vec<Xmas> = input
|
||||
.split("\n\n")
|
||||
.nth(1)?
|
||||
.lines()
|
||||
.filter_map(|x| x.parse().ok())
|
||||
.collect();
|
||||
|
||||
let mut total = 0;
|
||||
|
||||
'outer: for xmas in xmas_vec.into_iter() {
|
||||
let mut workflow = &map["in"];
|
||||
'apply: loop {
|
||||
match xmas.apply(workflow) {
|
||||
Resolver::Accepted => break 'apply,
|
||||
Resolver::Rejected => continue 'outer,
|
||||
Resolver::Delegated(x) => workflow = &map[x.as_str()],
|
||||
}
|
||||
}
|
||||
|
||||
total += xmas.sum();
|
||||
}
|
||||
|
||||
Some(total)
|
||||
}
|
||||
|
||||
pub fn part_two(input: &str) -> Option<usize> {
|
||||
let map = build_map(input);
|
||||
|
||||
let mut finished = Vec::new();
|
||||
let mut stack = vec![(
|
||||
XmasRange {
|
||||
x: (1, 4000),
|
||||
m: (1, 4000),
|
||||
a: (1, 4000),
|
||||
s: (1, 4000),
|
||||
},
|
||||
Resolver::Delegated("in".to_string()),
|
||||
)];
|
||||
|
||||
while let Some((xmas_range, resolver)) = stack.pop() {
|
||||
match resolver {
|
||||
Resolver::Accepted => finished.push(xmas_range),
|
||||
Resolver::Rejected => {
|
||||
continue;
|
||||
}
|
||||
Resolver::Delegated(x) => {
|
||||
let workflow = &map[x.as_str()];
|
||||
stack.append(&mut xmas_range.divide(workflow))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Some(finished.iter().map(XmasRange::size).sum())
|
||||
}
|
||||
aoc::solution!(19);
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
#[test]
|
||||
fn test_part_one() {
|
||||
assert_eq!(
|
||||
part_one(&aoc::template::read_file("examples", 19)),
|
||||
Some(19114)
|
||||
);
|
||||
}
|
||||
#[test]
|
||||
fn test_part_two() {
|
||||
assert_eq!(
|
||||
part_two(&aoc::template::read_file("examples", 19)),
|
||||
Some(167409079868000)
|
||||
);
|
||||
}
|
||||
}
|
187
src/bin/20.rs
Normal file
187
src/bin/20.rs
Normal file
@@ -0,0 +1,187 @@
|
||||
use std::{
|
||||
collections::{HashMap, VecDeque},
|
||||
mem::swap,
|
||||
str::FromStr,
|
||||
};
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
enum Module {
|
||||
Broadcaster,
|
||||
FlipFlop(bool),
|
||||
Conjuction,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct ParseModuleError;
|
||||
|
||||
impl FromStr for Module {
|
||||
type Err = ParseModuleError;
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
Ok(match s {
|
||||
"%" => Module::FlipFlop(false),
|
||||
"&" => Module::Conjuction,
|
||||
_ => Module::Broadcaster,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Node {
|
||||
index: usize,
|
||||
module: Module,
|
||||
inputs: Vec<usize>,
|
||||
outputs: Vec<usize>,
|
||||
}
|
||||
|
||||
fn parse_input(input: &str) -> Vec<Node> {
|
||||
let mut nodes = Vec::new();
|
||||
let mut mapper: HashMap<String, usize> = HashMap::new();
|
||||
|
||||
for line in input.lines() {
|
||||
let (from, _) = line.split_once(" -> ").unwrap();
|
||||
let (module, mut name) = from.split_at(1);
|
||||
let module: Module = module.parse().unwrap();
|
||||
if module == Module::Broadcaster {
|
||||
name = from;
|
||||
}
|
||||
|
||||
*mapper.entry(name.to_string()).or_default() = nodes.len();
|
||||
|
||||
let l = Node {
|
||||
module,
|
||||
index: nodes.len(),
|
||||
inputs: Vec::new(),
|
||||
outputs: Vec::new(),
|
||||
};
|
||||
|
||||
nodes.push(l);
|
||||
}
|
||||
|
||||
for line in input.lines() {
|
||||
let (from, to) = line.split_once(" -> ").unwrap();
|
||||
let (module, mut name) = from.split_at(1);
|
||||
let module: Module = module.parse().unwrap();
|
||||
if module == Module::Broadcaster {
|
||||
name = from;
|
||||
}
|
||||
|
||||
let index = mapper[name];
|
||||
for destination in to.split(", ") {
|
||||
let to_index = mapper[destination];
|
||||
nodes[index].outputs.push(to_index);
|
||||
nodes[to_index].inputs.push(index);
|
||||
}
|
||||
}
|
||||
|
||||
nodes
|
||||
}
|
||||
|
||||
pub fn part_one(input: &str) -> Option<usize> {
|
||||
let mut nodes = parse_input(input);
|
||||
|
||||
let mut graph = vec![vec![None; nodes.len()]; nodes.len()];
|
||||
|
||||
let mut highs = 0;
|
||||
let mut lows = 0;
|
||||
|
||||
let broadcaster = nodes
|
||||
.iter()
|
||||
.find(|x| x.module == Module::Broadcaster)?
|
||||
.index;
|
||||
|
||||
for _ in 0..1000 {
|
||||
let mut stack = VecDeque::from([broadcaster]);
|
||||
while !stack.is_empty() {
|
||||
let mut new_stack = VecDeque::new();
|
||||
let mut new_graph = graph.clone();
|
||||
|
||||
while let Some(index) = stack.pop_front() {
|
||||
let node = &nodes[index];
|
||||
let mut new_module = None;
|
||||
|
||||
match node.module {
|
||||
Module::Broadcaster => {
|
||||
for dest_index in node.outputs.iter() {
|
||||
new_graph[index][*dest_index] = Some(false);
|
||||
new_stack.push_back(*dest_index);
|
||||
lows += 1;
|
||||
}
|
||||
}
|
||||
Module::FlipFlop(high) => {
|
||||
let mut swapper = None;
|
||||
for in_index in nodes[index].inputs.iter() {
|
||||
let value = &mut graph[*in_index][index];
|
||||
if value.is_some() {
|
||||
debug_assert!(swapper.is_none());
|
||||
swap(&mut swapper, value);
|
||||
}
|
||||
}
|
||||
|
||||
if !swapper.unwrap() {
|
||||
let signal = !high;
|
||||
|
||||
for dest_index in nodes[index].outputs.iter() {
|
||||
new_graph[index][*dest_index] = Some(signal);
|
||||
new_stack.push_back(*dest_index);
|
||||
if signal {
|
||||
highs += 1;
|
||||
} else {
|
||||
lows += 1;
|
||||
}
|
||||
}
|
||||
|
||||
new_module = Some(Module::FlipFlop(signal));
|
||||
}
|
||||
}
|
||||
Module::Conjuction => {
|
||||
let mut all = true;
|
||||
|
||||
for in_index in nodes[index].inputs.iter() {
|
||||
let value = graph[*in_index][index];
|
||||
all &= value.unwrap_or(false);
|
||||
}
|
||||
|
||||
for dest_index in nodes[index].outputs.iter() {
|
||||
new_graph[index][*dest_index] = Some(!all);
|
||||
new_stack.push_back(*dest_index);
|
||||
if !all {
|
||||
highs += 1;
|
||||
} else {
|
||||
lows += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let node = &mut nodes[index];
|
||||
if let Some(module) = new_module {
|
||||
node.module = module;
|
||||
}
|
||||
}
|
||||
|
||||
stack = new_stack;
|
||||
graph = new_graph;
|
||||
}
|
||||
}
|
||||
|
||||
Some(highs * lows)
|
||||
}
|
||||
|
||||
pub fn part_two(input: &str) -> Option<u32> {
|
||||
None
|
||||
}
|
||||
|
||||
aoc::solution!(20);
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
#[test]
|
||||
fn test_part_one() {
|
||||
assert_eq!(part_one(&aoc::template::read_file("examples", 20)), None);
|
||||
}
|
||||
#[test]
|
||||
fn test_part_two() {
|
||||
assert_eq!(part_two(&aoc::template::read_file("examples", 20)), None);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user