[Day 8: Resonant Collinearity](https://adventofcode.com/2024/day/8)
グリッド上での座標計算。
ループが多くなったり記述が面倒になりがちだが、難しくはない
## 例
```
............
........0...
.....0......
.......0....
....0.......
......A.....
............
............
........A...
.........A..
............
............
```
同じ文字のもの同士で、2点間の距離と同じだけ両側に離れた場所にatinodesが作られる。この例では以下の`#`で示される場所になり、 **`14`** 個となる。
```
......#....#
...#....0...
....#0....#.
..#....0....
....0....#..
.#....A.....
...#........
#......#....
........A...
.........A..
..........#.
..........#.
```
part2では、両側2つだけでなくその直線上で等間隔で幾つでも(antenna自身も含む)、というルールで数える。以下のようになり **`34`** 個となる。
```
##....#....#
.#.#....0...
..#.#0....#.
..##...0....
....0....#..
.#...#A....#
...#..#.....
#....#.#....
..#.....A...
....#....A..
.#........#.
...#......##
```
## 方針
同じ文字のもの同士で座標を集め、それぞれについて2点の組み合わせを全列挙。
各組み合わせから導き出された座標を最終的に `Set`的なもので集計するだけ。
`(px0, py0)`, `(px1, py1)` があったら 差分 `dx = px1 - px0`, `dy = py1 - py0` を計算し、その差分を両点に足したり引いたりしていけば等間隔の座標を得られる。
範囲外に出ていくまで配列で取得して、part1ではそのうち近いところにある1個だけを使う、といった分岐をしてやると処理を共通化できる。
## 実装
### Rust
イテレータを使って計算。 `iter::successors`でマップ範囲外に出るまで座標移動したものを作れる。
part1のときはその結果に対し `.skip(1).take(1)` で対象のものだけに絞れる。あとは両方向で同様に出してまとめるだけ。
```rust
struct Solution {
map_size: (usize, usize),
antennas: HashMap<u8, Vec<(usize, usize)>>,
}
impl Solution {
fn count_antinodes(&self, update: bool) -> usize {
self.antennas
.values()
.flat_map(|v| {
v.iter().combinations(2).flat_map(|c| {
let (di, dj) = (c[1].0.wrapping_sub(c[0].0), c[1].1.wrapping_sub(c[0].1));
let it0 = iter::successors(Some(*c[0]), move |(i, j)| {
Some((i.wrapping_sub(di), j.wrapping_sub(dj))).filter(|(i, j)| {
(0..self.map_size.0).contains(i) && (0..self.map_size.1).contains(j)
})
});
let it1 = iter::successors(Some(*c[1]), move |(i, j)| {
Some((i.wrapping_add(di), j.wrapping_add(dj))).filter(|(i, j)| {
(0..self.map_size.0).contains(i) && (0..self.map_size.1).contains(j)
})
});
if update {
it0.chain(it1).collect_vec()
} else {
it0.skip(1).take(1).chain(it1.skip(1).take(1)).collect_vec()
}
})
})
.unique()
.count()
}
fn new<R>(r: R) -> Result<Self, Error>
where
R: Read,
{
let map = BufReader::new(r)
.lines()
.map(|line| line.map(|line| line.bytes().collect()))
.collect::<Result<Vec<Vec<_>>, _>>()?;
let mut antennas = HashMap::new();
for (i, row) in map.iter().enumerate() {
for (j, col) in row.iter().enumerate() {
if col != &b'.' {
antennas.entry(*col).or_insert_with(Vec::new).push((i, j));
}
}
}
Ok(Self {
map_size: (map.len(), map[0].len()),
antennas,
})
}
fn part1(&self) -> usize {
self.count_antinodes(false)
}
fn part2(&self) -> usize {
self.count_antinodes(true)
}
}
```