Skip to content

Commit 683946f

Browse files
committed
[2024] Day 8 initial solution
1 parent a1a2859 commit 683946f

File tree

3 files changed

+153
-0
lines changed

3 files changed

+153
-0
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ Thank you to [Eric Wastl](http://was.tl) for running this incredible yearly even
1212
- [Day 05: Print Queue](aoc_2024/src/day_05.rs)
1313
- [Day 06: Guard Gallivant](aoc_2024/src/day_06.rs)
1414
- [Day 07: Bridge Repair](aoc_2024/src/day_07.rs)
15+
- [Day 08: Resonant Collinearity](aoc_2024/src/day_08.rs)
1516
<!-- MARKER -->
1617

1718
## [2023](https://adventofcode.com/2023) [![aoc_2023](https://github.com/connorslade/advent-of-code/actions/workflows/aoc_2023.yml/badge.svg)](https://github.com/connorslade/advent-of-code/actions/workflows/aoc_2023.yml)

aoc_2024/src/day_08.rs

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
use std::collections::{HashMap, HashSet};
2+
3+
use aoc_lib::matrix::Matrix;
4+
use common::{solution, Answer};
5+
use nd_vec::{vector, Vec2};
6+
7+
use itertools::Itertools;
8+
9+
solution!("Resonant Collinearity", 8);
10+
11+
#[derive(Clone)]
12+
enum Tile {
13+
Emitter(char),
14+
Empty,
15+
}
16+
17+
fn part_a(input: &str) -> Answer {
18+
let world = Matrix::new_chars(input, |x| match x {
19+
'a'..='z' | 'A'..='Z' | '0'..='9' => Tile::Emitter(x),
20+
_ => Tile::Empty,
21+
});
22+
23+
let mut freqs = HashMap::<char, Vec<Vec2<i32>>>::new();
24+
25+
// oh god just make a Matrix.iter function at this point...
26+
for x in 0..world.size.x() {
27+
for y in 0..world.size.y() {
28+
let pos = vector!(x, y);
29+
if let Tile::Emitter(chr) = world.get(pos).unwrap() {
30+
freqs
31+
.entry(*chr)
32+
.or_default()
33+
.push(pos.try_cast::<i32>().unwrap());
34+
}
35+
}
36+
}
37+
38+
// nodes are on the line between two of the same freq emitters when d1 = 2*d2
39+
// to find nodes, start by finding all lines through matching emitters
40+
41+
let in_bounds = |pos: Vec2<i32>| {
42+
pos.x() >= 0
43+
&& pos.y() >= 0
44+
&& pos.x() < world.size.x() as i32
45+
&& pos.y() < world.size.y() as i32
46+
};
47+
48+
let mut out = HashSet::new();
49+
for (_freq, pos) in freqs {
50+
for (a, b) in pos.into_iter().tuple_combinations() {
51+
let diff_a = a + (a - b);
52+
let diff_b = b + (b - a);
53+
54+
if in_bounds(diff_a) {
55+
out.insert(diff_a);
56+
}
57+
58+
if in_bounds(diff_b) {
59+
out.insert(diff_b);
60+
}
61+
}
62+
}
63+
64+
out.len().into()
65+
}
66+
67+
fn part_b(input: &str) -> Answer {
68+
let world = Matrix::new_chars(input, |x| match x {
69+
'a'..='z' | 'A'..='Z' | '0'..='9' => Tile::Emitter(x),
70+
_ => Tile::Empty,
71+
});
72+
73+
let mut freqs = HashMap::<char, Vec<Vec2<i32>>>::new();
74+
75+
// oh god just make a Matrix.iter function at this point...
76+
for x in 0..world.size.x() {
77+
for y in 0..world.size.y() {
78+
let pos = vector!(x, y);
79+
if let Tile::Emitter(chr) = world.get(pos).unwrap() {
80+
freqs
81+
.entry(*chr)
82+
.or_default()
83+
.push(pos.try_cast::<i32>().unwrap());
84+
}
85+
}
86+
}
87+
88+
// nodes are on the line between two of the same freq emitters when d1 = 2*d2
89+
// to find nodes, start by finding all lines through matching emitters
90+
91+
let in_bounds = |pos: Vec2<i32>| {
92+
pos.x() >= 0
93+
&& pos.y() >= 0
94+
&& pos.x() < world.size.x() as i32
95+
&& pos.y() < world.size.y() as i32
96+
};
97+
98+
let mut out = HashSet::new();
99+
for (_freq, pos) in freqs {
100+
for (a, b) in pos.into_iter().tuple_combinations() {
101+
let diff_a = a - b;
102+
let diff_b = b - a;
103+
104+
let mut pos_a = a;
105+
let mut pos_b = b;
106+
107+
while in_bounds(pos_a) {
108+
out.insert(pos_a);
109+
pos_a += diff_a;
110+
}
111+
112+
while in_bounds(pos_b) {
113+
out.insert(pos_b);
114+
pos_b += diff_b;
115+
}
116+
}
117+
}
118+
119+
out.len().into()
120+
}
121+
122+
#[cfg(test)]
123+
mod test {
124+
use indoc::indoc;
125+
126+
const CASE: &str = indoc! {"
127+
............
128+
........0...
129+
.....0......
130+
.......0....
131+
....0.......
132+
......A.....
133+
............
134+
............
135+
........A...
136+
.........A..
137+
............
138+
............
139+
"};
140+
141+
#[test]
142+
fn part_a() {
143+
assert_eq!(super::part_a(CASE), 14.into());
144+
}
145+
146+
#[test]
147+
fn part_b() {
148+
assert_eq!(super::part_b(CASE), 34.into());
149+
}
150+
}

aoc_2024/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ mod day_04;
77
mod day_05;
88
mod day_06;
99
mod day_07;
10+
mod day_08;
1011
// [import_marker]
1112

1213
pub const SOLUTIONS: &[Solution] = &[
@@ -17,5 +18,6 @@ pub const SOLUTIONS: &[Solution] = &[
1718
day_05::SOLUTION,
1819
day_06::SOLUTION,
1920
day_07::SOLUTION,
21+
day_08::SOLUTION,
2022
// [list_marker]
2123
];

0 commit comments

Comments
 (0)