I'm bad at this

This commit is contained in:
Daniel Flanagan 2024-12-12 15:40:11 -06:00
parent 61a5cd899e
commit 734b80b05c

View file

@ -1,4 +1,5 @@
mod prelude; mod prelude;
pub use crate::prelude::*; pub use crate::prelude::*;
fn main() { fn main() {
@ -6,7 +7,6 @@ fn main() {
show_answers(part1(&input), part2(&input)); show_answers(part1(&input), part2(&input));
} }
#[derive(Debug, Hash, PartialEq, Eq)]
enum Dir { enum Dir {
N, N,
S, S,
@ -17,7 +17,7 @@ enum Dir {
#[derive(Debug)] #[derive(Debug)]
struct Region { struct Region {
locations: HashSet<(usize, usize)>, locations: HashSet<(usize, usize)>,
perimeters: HashSet<(usize, usize, Dir)>, perimeters: usize,
} }
impl Region { impl Region {
@ -26,24 +26,24 @@ impl Region {
let (height, width) = (bytes.len(), bytes[0].len()); let (height, width) = (bytes.len(), bytes[0].len());
let mut candidates = vec![(sx, sy)]; let mut candidates = vec![(sx, sy)];
let mut locations = HashSet::from_iter(candidates.clone()); let mut locations = HashSet::from_iter(candidates.clone());
let mut perimeters = HashSet::new(); let mut perimeters = 0;
while let Some((x, y)) = candidates.pop() { while let Some((x, y)) = candidates.pop() {
print!("AT {x},{y}: "); print!("AT {x},{y}: ");
for (nx, ny, d) in [ for (nx, ny) in [
(x, y.saturating_sub(1), Dir::N), (x, y.saturating_sub(1)),
(x, y + 1, Dir::S), (x, y + 1),
(x + 1, y, Dir::E), (x + 1, y),
(x.saturating_sub(1), y, Dir::W), (x.saturating_sub(1), y),
] { ] {
if (nx, ny) == (x, y) || nx >= width || ny >= height { if (nx, ny) == (x, y) || nx >= width || ny >= height {
print!("EDGE@{nx},{ny}; "); print!("EDGE@{nx},{ny}; ");
perimeters.insert((nx, ny, d)); perimeters += 1;
continue; continue;
} }
if bytes[ny][nx] != target { if bytes[ny][nx] != target {
print!("EDGE@{nx},{ny}; "); print!("EDGE@{nx},{ny}; ");
perimeters.insert((nx, ny, d)); perimeters += 1;
continue; continue;
} }
if !locations.contains(&(nx, ny)) { if !locations.contains(&(nx, ny)) {
@ -60,7 +60,7 @@ impl Region {
} }
} }
fn part1(input: &str) -> usize { fn regions(input: &str) -> Vec<Region> {
let mut regions = vec![]; let mut regions = vec![];
let bytes: Vec<Vec<u8>> = input let bytes: Vec<Vec<u8>> = input
.trim() .trim()
@ -74,25 +74,71 @@ fn part1(input: &str) -> usize {
continue; continue;
} }
let region = Region::measure_at(&bytes, x, y); let region = Region::measure_at(&bytes, x, y);
println!(
"New Region: (Byte: {}, Area: {}, Perimeter: {})",
bytes[y][x],
&region.locations.len(),
&region.perimeters.len(),
);
regionated.extend(&region.locations); regionated.extend(&region.locations);
regions.push(region); regions.push(region);
} }
} }
println!("{regions:?}");
regions regions
}
fn part1(input: &str) -> usize {
regions(input)
.iter() .iter()
.map(|r| r.locations.len() * r.perimeters.len()) .map(|r| r.locations.len() * r.perimeters)
.sum() .sum()
} }
fn part2(input: &str) -> usize { fn part2(input: &str) -> usize {
0 regions(input)
.iter()
.map(|r| {
println!("{r:?}");
// find top edge in region
if r.locations.len() <= 2 {
return r.locations.len() * 4;
}
let (sx, sy) = {
let (x, mut y) = *r.locations.iter().next().unwrap();
y = (1..y)
.rev()
.find(|y| r.locations.contains(&(x, y - 1)))
.unwrap_or(0);
(x, y)
};
// walk along region edge and count turns until we end up where we started
let mut turns = 0;
let ops = [
|(x, y)| (x + 1, y),
|(x, y)| (x, y + 1),
|(x, y): (usize, usize)| (x.saturating_sub(1), y),
|(x, y): (usize, usize)| (x, y.saturating_sub(1)),
];
let mut dir = 0;
let (mut x, mut y) = (sx, sy);
println!("start at {x},{y}");
loop {
let initial_dir = dir;
while !r.locations.contains(&ops[dir]((x, y))) || ops[dir]((x, y)) == (x, y) {
println!("turning @ {x},{y}");
dir = (dir + 1) % ops.len();
if dir == initial_dir {
panic!("turned too many times in one place");
}
turns += 1;
}
(x, y) = ops[dir]((x, y));
println!("move to {x},{y}");
if turns >= 3 && x == sx && y == sy {
break;
}
}
println!("turns {turns}");
// walk each side, counting turns
r.locations.len() * turns
})
.sum()
} }
#[cfg(test)] #[cfg(test)]
@ -118,7 +164,7 @@ MMMISSJEEE"#;
.collect(); .collect();
let region = Region::measure_at(&bytes, 0, 0); let region = Region::measure_at(&bytes, 0, 0);
assert_eq!(region.locations.len(), 12); assert_eq!(region.locations.len(), 12);
assert_eq!(region.perimeters.len(), 18); assert_eq!(region.perimeters, 18);
assert_eq!(part1(input), 1930); assert_eq!(part1(input), 1930);
assert_eq!(part2(input), 1206); assert_eq!(part2(input), 1206);
} }