Compare commits
No commits in common. "d4c02b30de9b99c130d9638363f1909390d3487a" and "fc5670b37ecf960ad7b7efe27d0e26181b480491" have entirely different histories.
d4c02b30de
...
fc5670b37e
|
@ -12,6 +12,3 @@ aoc-runner = "0.3.0"
|
||||||
aoc-runner-derive = "0.3.0"
|
aoc-runner-derive = "0.3.0"
|
||||||
aoc-parse = "0.2.17"
|
aoc-parse = "0.2.17"
|
||||||
num = "0.4"
|
num = "0.4"
|
||||||
|
|
||||||
[profile.release]
|
|
||||||
incremental = true
|
|
||||||
|
|
|
@ -1,336 +0,0 @@
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -12,6 +12,5 @@ mod day09;
|
||||||
mod day10;
|
mod day10;
|
||||||
mod day11;
|
mod day11;
|
||||||
mod day12;
|
mod day12;
|
||||||
mod day13;
|
|
||||||
|
|
||||||
aoc_lib! { year = 2023 }
|
aoc_lib! { year = 2023 }
|
||||||
|
|
Loading…
Reference in New Issue