- day 8
This commit is contained in:
parent
ce9038a90e
commit
e42e14f215
4 changed files with 178 additions and 0 deletions
7
2024/day8/Cargo.lock
generated
Normal file
7
2024/day8/Cargo.lock
generated
Normal file
|
@ -0,0 +1,7 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "day8"
|
||||
version = "0.1.0"
|
6
2024/day8/Cargo.toml
Normal file
6
2024/day8/Cargo.toml
Normal file
|
@ -0,0 +1,6 @@
|
|||
[package]
|
||||
name = "day8"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
51
2024/day8/README.md
Normal file
51
2024/day8/README.md
Normal file
|
@ -0,0 +1,51 @@
|
|||
# Solution Day 8
|
||||
|
||||
A bit of geometry for a change. The task was quite ambiguous, I felt. The text says:
|
||||
|
||||
> In particular, an antinode occurs at any point that is perfectly in line with two antennas of the same frequency - but only when one of the antennas is twice as far away as the other
|
||||
|
||||
and then goes on to give exambles, where the antinodes lie outside the two antennas, like this:
|
||||
|
||||
```
|
||||
..#....a....a....#....
|
||||
```
|
||||
|
||||
Either I'm not getting the exact meaning of "antinode" (English is not my native language) or there's another set of antinodes _between_ the two antennas:
|
||||
|
||||
```
|
||||
..#....a.##.a....#....
|
||||
```
|
||||
|
||||
Those should also fulfill the requirement of one antenna being twice as far away as the other.
|
||||
Anyway, going with the examples, this is basically just calculating an equation of the form
|
||||
`p = mn + b`, with `p`, and `b` being points in 2D space, `m = a - b` (the vector between
|
||||
antennas `a` and `b`), and `n` being in the natural numbers **N**.
|
||||
|
||||
Implementing this as infrastructure (along with loading and parsing the data), makes the actual tasks rather simple.
|
||||
|
||||
## Task 1
|
||||
|
||||
The main point in this task is to only include points of order 1 formed by 2 different antennas, hence
|
||||
why the inner nested loop in `get_antinodes` has to always start one index _above_ the current outer loop,
|
||||
to never pair an antenna with itself (I got this wrong in the first try). Those nested loops will form all possible pairs
|
||||
of the antennas:
|
||||
|
||||
```
|
||||
[1,2,3] => [[1,2], [1,3], [2,3]]
|
||||
```
|
||||
|
||||
## Task 2
|
||||
|
||||
The change to the code are actually miniscule: antennas can now interfere with themselves (presumably if there's
|
||||
another antenna of the same frequency somewhere on the map. No idea how this makes physical sense, but there you are...) and
|
||||
we need to continue our antinodes beyond order 1 until we're outside the map.
|
||||
|
||||
Since we already had a "outside the map"-filter in place from task 1, the lazy solution was to calculate the antinodes
|
||||
up to an order, that is _definitely_ outside the map in order to capture all possible nodes (hence why calculating the the 50th
|
||||
node, even if that could never be on the map).
|
||||
|
||||
This also pairs antennas with themselves, so the inner loop in `get_antinodes` now starts at the index of the outer node.
|
||||
|
||||
```
|
||||
[1,2,3] => [[1,1], [1,2], [1,3], [2,2], [2,3], [3,3]]
|
||||
```
|
114
2024/day8/src/main.rs
Normal file
114
2024/day8/src/main.rs
Normal file
|
@ -0,0 +1,114 @@
|
|||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
struct Antenna {
|
||||
x: usize,
|
||||
y: usize,
|
||||
marker: char,
|
||||
}
|
||||
|
||||
struct AntennaMap {
|
||||
width: usize,
|
||||
height: usize,
|
||||
antennas: Vec<Antenna>,
|
||||
}
|
||||
|
||||
#[derive(Hash, PartialEq, Eq)]
|
||||
struct Antinode {
|
||||
x: usize,
|
||||
y: usize,
|
||||
}
|
||||
|
||||
fn load_input() -> AntennaMap {
|
||||
let input = std::fs::read_to_string("./input.txt").expect("Should be able to read input file");
|
||||
let lines: Vec<&str> = input.lines().collect();
|
||||
let antennas: Vec<Antenna> = lines
|
||||
.iter()
|
||||
.enumerate()
|
||||
.flat_map(|(y, line)| {
|
||||
line.chars().enumerate().filter_map(move |(x, ch)| {
|
||||
if ch != '.' {
|
||||
Some(Antenna { x, y, marker: ch })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
})
|
||||
.collect();
|
||||
AntennaMap {
|
||||
width: lines.iter().next().unwrap().len(),
|
||||
height: lines.len(),
|
||||
antennas,
|
||||
}
|
||||
}
|
||||
|
||||
fn group_antennas(antennas: &Vec<Antenna>) -> HashMap<char, Vec<&Antenna>> {
|
||||
let mut map = HashMap::new();
|
||||
for antenna in antennas.into_iter() {
|
||||
if !map.contains_key(&antenna.marker) {
|
||||
map.insert(antenna.marker, vec![]);
|
||||
}
|
||||
map.get_mut(&antenna.marker).unwrap().push(antenna);
|
||||
}
|
||||
map
|
||||
}
|
||||
|
||||
fn get_nth_antinode(antenna_a: &Antenna, antenna_b: &Antenna, n: usize) -> Option<Antinode> {
|
||||
if (n + 1) * antenna_a.x >= n * antenna_b.x && (n + 1) * antenna_a.y >= n * antenna_b.y {
|
||||
Some(Antinode {
|
||||
x: (n + 1) * antenna_a.x - n * antenna_b.x,
|
||||
y: (n + 1) * antenna_a.y - n * antenna_b.y,
|
||||
})
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
fn get_antinodes(
|
||||
antennas: &Vec<&Antenna>,
|
||||
width: usize,
|
||||
height: usize,
|
||||
max_n: usize,
|
||||
include_self: bool,
|
||||
) -> Vec<Antinode> {
|
||||
let mut antinodes = vec![];
|
||||
for (idx, antenna_a) in antennas.iter().enumerate() {
|
||||
for antenna_b in antennas[idx + (if include_self { 0 } else { 1 })..].iter() {
|
||||
for n in 1..=max_n {
|
||||
antinodes.push(get_nth_antinode(antenna_a, antenna_b, n));
|
||||
antinodes.push(get_nth_antinode(antenna_b, antenna_a, n));
|
||||
}
|
||||
}
|
||||
}
|
||||
antinodes
|
||||
.into_iter()
|
||||
.filter_map(|node_opt| node_opt)
|
||||
.filter(|node| node.x < width && node.y < height)
|
||||
.collect()
|
||||
}
|
||||
|
||||
fn task1() {
|
||||
let antenna_map = load_input();
|
||||
let by_frequency = group_antennas(&antenna_map.antennas);
|
||||
let all_antinodes = by_frequency
|
||||
.iter()
|
||||
.flat_map(|entry| get_antinodes(entry.1, antenna_map.width, antenna_map.height, 1, false));
|
||||
let unique_antinodes: HashSet<Antinode> = all_antinodes.collect();
|
||||
|
||||
println!("Task 1: Unique nodes: {}", unique_antinodes.len());
|
||||
}
|
||||
|
||||
fn task2() {
|
||||
let antenna_map = load_input();
|
||||
let by_frequency = group_antennas(&antenna_map.antennas);
|
||||
let all_antinodes = by_frequency
|
||||
.iter()
|
||||
.flat_map(|entry| get_antinodes(entry.1, antenna_map.width, antenna_map.height, 50, true)); // arbitrarily high n to make sure we get everything inside the map
|
||||
let unique_antinodes: HashSet<Antinode> = all_antinodes.collect();
|
||||
|
||||
println!("Task 2: Unique nodes: {}", unique_antinodes.len());
|
||||
}
|
||||
|
||||
fn main() {
|
||||
task1();
|
||||
task2();
|
||||
}
|
Loading…
Add table
Reference in a new issue