1
0
Fork 0

day 13 solution

main
Andrew Coleman 2023-12-14 15:12:33 -05:00
parent a7c5708357
commit d4c02b30de
2 changed files with 337 additions and 0 deletions

336
2023/src/day13.rs Normal file
View File

@ -0,0 +1,336 @@
use aoc_runner_derive::{aoc, aoc_generator};
type Board = Vec<Vec<char>>;
type Pair = (usize, usize);
#[derive(Debug)]
struct Map {
rows: Board,
cols: Board,
width: usize,
height: usize,
}
impl Map {
fn find_mirror(&self, board: &Board) -> Option<Pair> {
let mut matches: Vec<Pair> = vec![];
let mut start_index = 0;
while let Some(m) = self.find_row(board, start_index, 0) {
start_index = m.1;
matches.push(m);
}
let mut res: Vec<Option<Pair>> = vec![];
for &(r1, r2) in matches.iter() {
let mut rows_matched = 0;
let mut found = true;
loop {
let start = r1 - rows_matched;
let end = r2 + rows_matched;
if board[start] == board[end] {
rows_matched += 1;
} else {
found = false;
break;
}
if start == 0 || end == board.len() - 1 {
break;
}
}
if found {
res.push(Some((r1, r2)));
}
}
if res.len() > 1 {
panic!("got too many matches {:?}", res);
} else if res.is_empty() {
None
} else {
res[0]
}
}
fn find_row(&self, board: &Board, index: usize, max_diffs: usize) -> Option<Pair> {
let row_len = board[0].len();
for t in index..(board.len() - 1) {
let mut diff_chars = 0;
for k in 0..row_len {
if board[t][k] == board[t + 1][k] {
continue;
}
diff_chars += 1;
if diff_chars > max_diffs {
break;
}
}
if diff_chars <= max_diffs {
return Some((t, t + 1));
}
}
None
}
fn get_horiz_score(&self) -> Option<usize> {
if let Some((_r1, r2)) = self.find_mirror(&self.rows) {
return Some(100 * r2);
}
None
}
fn get_vert_score(&self) -> Option<usize> {
if let Some((_c1, c2)) = self.find_mirror(&self.cols) {
return Some(c2);
}
None
}
fn part1(&self) -> usize {
if self.width < self.height {
if let Some(n) = self.get_horiz_score() {
return n;
}
if let Some(n) = self.get_vert_score() {
return n;
}
} else {
if let Some(n) = self.get_vert_score() {
return n;
}
if let Some(n) = self.get_horiz_score() {
return n;
}
}
0
}
fn find_smudge(&self, board: &Board) -> Option<Pair> {
let mut matches: Vec<Pair> = vec![];
let mut start_index = 0;
while let Some(m) = self.find_row(board, start_index, 1) {
start_index = m.1;
matches.push(m);
}
println!("matches {:?}", matches);
let mut res: Vec<Option<Pair>> = vec![];
for &(r1, r2) in matches.iter() {
let mut rows_matched = 0;
let mut diff_chars = 0;
loop {
let start = r1 - rows_matched;
let end = r2 + rows_matched;
println!(
"comparing {} to {} with {} and {}",
start,
end,
board[start].iter().collect::<String>(),
board[end].iter().collect::<String>(),
);
if board[start] == board[end] {
rows_matched += 1;
} else {
for index in 0..board[start].len() {
if board[start][index] != board[end][index] {
diff_chars += 1;
}
}
if diff_chars > 1 {
break;
}
rows_matched += 1;
}
if start == 0 || end == board.len() - 1 {
break;
}
}
println!(
"{},{} rows matched {} diff_chars {}",
r1, r2, rows_matched, diff_chars
);
if diff_chars == 1 {
res.push(Some((r1, r2)));
}
}
if res.len() > 1 {
panic!("got too many matches {:?}", res);
} else if res.is_empty() {
None
} else {
res[0]
}
}
fn get_horiz_smudge(&self) -> Option<usize> {
println!("get horiz smudge");
if let Some((r1, r2)) = self.find_smudge(&self.rows) {
println!("got horiz smudge rows of {},{}", r1, r2);
return Some(100 * r2);
}
None
}
fn get_vert_smudge(&self) -> Option<usize> {
println!("get vert smudge");
if let Some((c1, c2)) = self.find_smudge(&self.cols) {
println!("got vert smudge cols of {},{}", c1, c2);
return Some(c2);
}
None
}
fn part2(&self) -> usize {
println!();
for line in self.rows.iter() {
println!("{}", line.iter().collect::<String>());
}
println!("width {} height {}", self.width, self.height);
let hscore = self.get_horiz_smudge();
let vscore = self.get_vert_smudge();
if hscore.is_some() && vscore.is_some() {
panic!("got both scores!");
}
if hscore.is_some() {
hscore.unwrap()
} else if vscore.is_some() {
vscore.unwrap()
} else {
0
}
}
}
fn gen_map(cur: &Board) -> Map {
let width = cur[0].len();
let height = cur.len();
let mut cols: Board = vec![];
for row in 0..width {
cols.push(vec![]);
for col in 0..height {
cols[row].push(cur[col][row]);
}
}
Map {
rows: cur.clone(),
cols,
width,
height,
}
}
// don't forget to add an extra empty newline
#[aoc_generator(day13)]
fn parse(input: &str) -> Vec<Map> {
let mut result: Vec<Map> = vec![];
let mut cur: Board = vec![];
for line in input.lines() {
if line.is_empty() {
if !cur.is_empty() {
result.push(gen_map(&cur));
cur.clear();
}
continue;
}
cur.push(
line.chars()
.filter_map(|t| (!t.is_ascii_whitespace()).then_some(t))
.collect(),
);
}
if !cur.is_empty() {
result.push(gen_map(&cur));
}
result
}
#[aoc(day13, part1)]
fn part1(input: &[Map]) -> usize {
input.iter().map(|t| t.part1()).sum()
}
#[aoc(day13, part2)]
fn part2(input: &[Map]) -> usize {
input.iter().map(|t| t.part2()).sum()
}
#[cfg(test)]
mod tests {
use super::*;
const SAMPLE_DATA: &'static str = r#"#.##..##.
..#.##.#.
##......#
##......#
..#.##.#.
..##..##.
#.#.##.#.
#...##..#
#....#..#
..##..###
#####.##.
#####.##.
..##..###
#....#..#
"#;
#[test]
fn sample_data() {
let input = parse(&SAMPLE_DATA);
assert_eq!(part1(&input), 405);
assert_eq!(part2(&input), 400);
}
const SAMPLE_DATA2: &'static str = r#"#.##..##.
..#.##.#.
##......#
##......#
..#.##.#.
..##..##.
#.#.##.#.
#...##..#
#....#..#
..##..###
#####.##.
#####.##.
..##..###
#....#..#
.#.##.#.#
.##..##..
.#.##.#..
#......##
#......##
.#.##.#..
.##..##.#
#..#....#
###..##..
.##.#####
.##.#####
###..##..
#..#....#
#..##...#
#.##..##.
..#.##.#.
##..#...#
##...#..#
..#.##.#.
..##..##.
#.#.##.#.
"#;
#[test]
fn sample_data2() {
let input = parse(&SAMPLE_DATA2);
assert_eq!(part1(&input), 709);
// assert_eq!(part2(&input), 1400);
}
}

View File

@ -12,5 +12,6 @@ mod day09;
mod day10;
mod day11;
mod day12;
mod day13;
aoc_lib! { year = 2023 }