adventofcode23/src/day13.rs
2023-12-13 15:02:13 +01:00

193 lines
4.3 KiB
Rust

//! Day 13
use grid::Grid;
use itertools::Itertools;
pub const DAY: u8 = 13;
pub const TITLE: &str = "Point of Incidence";
#[derive(Debug, Clone, Copy)]
enum Reflection {
Horizontal(usize),
Vertical(usize),
}
impl Reflection {
fn n(self) -> usize {
match self {
Reflection::Horizontal(n) => n * 100,
Reflection::Vertical(n) => n,
}
}
}
fn parse(input: Vec<String>) -> Vec<Grid<bool>> {
input
.split(|l| l.is_empty())
.map(|gl| {
let mut grid = Grid::new(gl.len(), gl[0].len());
for (y, line) in gl.iter().enumerate() {
for (x, c) in line.chars().enumerate() {
if c == '#' {
grid[(y, x)] = true;
}
}
}
grid
})
.collect()
}
fn find_reflection(grid: &Grid<bool>, smudge: bool) -> Option<Reflection> {
let cols = grid
.iter_cols()
.map(|col| {
col.fold(0_u64, |mut acc, x| {
if *x {
acc |= 1;
}
acc <<= 1;
acc
})
})
.collect_vec();
if let Some(x) = find_ref(&cols, smudge) {
return Some(Reflection::Vertical(x));
}
let rows = grid
.iter_rows()
.map(|col| {
col.fold(0_u64, |mut acc, x| {
if *x {
acc |= 1;
}
acc <<= 1;
acc
})
})
.collect_vec();
if let Some(y) = find_ref(&rows, smudge) {
return Some(Reflection::Horizontal(y));
}
None
}
fn find_ref(v: &[u64], smudge: bool) -> Option<usize> {
if smudge {
find_ref_smudge(v)
} else {
find_ref_normal(v)
}
}
fn find_ref_normal(v: &[u64]) -> Option<usize> {
let mut last = 0;
for (i, n) in v.iter().copied().enumerate() {
if n == last {
// Could be mirror position
if v[..i - 1]
.iter()
.rev()
.zip(v[i + 1..].iter())
.all(|(a, b)| a == b)
{
return Some(i);
}
}
last = n;
}
None
}
fn find_ref_smudge(v: &[u64]) -> Option<usize> {
let mut last = 0;
for (i, n) in v.iter().copied().enumerate() {
if i == 0 {
} else if n == last {
// Could be mirror position with 1 smudge
let mut got_smudge = false;
let mut ok = true;
for (a, b) in v[..i - 1].iter().rev().zip(v[i + 1..].iter()) {
if !got_smudge && smudge_eq(*a, *b) {
got_smudge = true;
} else if a != b {
ok = false;
break;
}
}
if ok && got_smudge {
return Some(i);
}
} else if smudge_eq(n, last) {
// Could be mirror position
if v[..i - 1]
.iter()
.rev()
.zip(v[i + 1..].iter())
.all(|(a, b)| a == b)
{
return Some(i);
}
}
last = n;
}
None
}
fn smudge_eq(a: u64, b: u64) -> bool {
let d = a ^ b;
d > 0 && (d & (d - 1)) == 0
}
pub fn part1(input: Vec<String>) -> String {
let grids = parse(input);
grids
.iter()
.map(|g| find_reflection(g, false).unwrap().n())
.sum::<usize>()
.to_string()
}
pub fn part2(input: Vec<String>) -> String {
let grids = parse(input);
grids
.iter()
.map(|g| find_reflection(g, true).unwrap().n())
.sum::<usize>()
.to_string()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn t_smudge_eq() {
assert!(smudge_eq(1, 0));
assert!(smudge_eq(0b10001100, 0b10000100));
assert!(!smudge_eq(0b10001100, 0b10001100));
}
#[test]
fn t_example1() {
assert_eq!(part1(crate::read_example(DAY, 1)), "405");
}
#[test]
fn t_part1() {
assert_eq!(part1(crate::read_input(DAY)), "35210");
}
#[test]
fn t_example2() {
assert_eq!(part2(crate::read_example(DAY, 2)), "400");
}
#[test]
fn t_part2() {
assert_eq!(part2(crate::read_input(DAY)), "31974");
}
}