Now it gets ugly

This commit is contained in:
Daniel Flanagan 2024-12-06 06:13:52 -06:00
parent 3d78aaee63
commit e5669fac3b
2 changed files with 195 additions and 0 deletions

193
2024/rust/src/day6.rs Normal file
View file

@ -0,0 +1,193 @@
mod prelude;
pub use crate::prelude::*;
fn main() {
let input = day_input(6);
let mut lab: Lab = input.parse().unwrap();
let mut lab2: Lab = input.parse().unwrap();
show_answers(
lab.num_steps_until_guard_leaves_lab(),
lab2.num_positions_of_new_obstructions_that_cause_guard_loop(),
);
}
#[derive(Default, Debug, PartialEq, Eq, Hash, Clone)]
struct Vec2 {
x: i64,
y: i64,
}
impl Vec2 {
fn new(x: i64, y: i64) -> Self {
Self { x, y }
}
fn add(&self, v: &Vec2) -> Self {
Self::new(self.x + v.x, self.y + v.y)
}
}
impl Display for Vec2 {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "Vec2({}, {})", self.x, self.y)
}
}
#[derive(Debug, Clone)]
struct Obstruction {
pos: Vec2,
}
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
struct Guard {
pos: Vec2,
vel: Vec2,
}
impl Guard {
fn turn(&mut self) {
// too lazy for trig today I guess
if self.vel.x > 0 && self.vel.y > 0 {
self.vel.x *= -1;
} else if self.vel.x < 0 && self.vel.y > 0 {
self.vel.y *= -1;
} else if self.vel.x < 0 && self.vel.y < 0 {
self.vel.x *= -1;
} else if self.vel.x > 0 && self.vel.y < 0 {
self.vel.y *= -1;
} else if self.vel.y != 0 {
self.vel.x = self.vel.y * -1;
self.vel.y = 0;
} else if self.vel.x != 0 {
self.vel.y = self.vel.x;
self.vel.x = 0;
}
}
}
#[derive(Debug, Clone)]
struct Lab {
obstructions: Vec<Obstruction>,
guard: Guard,
size: Vec2,
}
impl FromStr for Lab {
type Err = Infallible;
fn from_str(s: &str) -> Result<Self, Self::Err> {
let mut size = Vec2::default();
let mut obstructions = vec![];
let mut guard: Option<Guard> = None;
for (y, line) in s.lines().enumerate() {
size.y += 1;
size.x = line.len() as i64; // whatever - still linear ;)
for (x, c) in line.bytes().enumerate() {
match c {
b'#' => obstructions.push(Obstruction {
pos: Vec2::new(x as i64, y as i64),
}),
b'^' => {
guard = Some(Guard {
pos: Vec2::new(x as i64, y as i64),
vel: Vec2::new(0, -1),
})
}
_ => {}
}
}
}
Ok(Lab {
size,
guard: guard.unwrap(),
obstructions,
})
}
}
impl Lab {
fn step(&mut self) {
// move guard
let next_pos = self.guard.pos.add(&self.guard.vel);
self.guard.pos = next_pos;
// after moving, check if we need to turn
// TODO: obstructions should lookup by position instead of this
// broad-phase "check each one each time" slow business
while self.has_obstacle_at(&self.guard.pos.add(&self.guard.vel)) {
self.guard.turn();
}
}
fn contains(&self, pos: &Vec2) -> bool {
(0..self.size.x).contains(&pos.x) && (0..self.size.y).contains(&pos.y)
}
fn num_steps_until_guard_leaves_lab(&mut self) -> usize {
let mut visited = HashSet::new();
visited.insert(self.guard.pos.clone());
while self.contains(&self.guard.pos) {
self.step();
visited.insert(self.guard.pos.clone());
}
visited.len() - 1
}
fn has_obstacle_at(&self, pos: &Vec2) -> bool {
self.obstructions.iter().any(|o| o.pos == *pos)
}
fn num_positions_of_new_obstructions_that_cause_guard_loop(&mut self) -> usize {
let mut positions_with_cycles_detected = 0;
let mut visited = HashSet::new();
visited.insert(self.guard.clone());
// TODO: avoid super brute force
for x in 0..self.size.x {
for y in 0..self.size.y {
println!("Simulating at {x} {y}...");
let mut alternate_visited = visited.clone();
let mut alternate_lab = self.clone();
alternate_lab.obstructions.push(Obstruction {
pos: Vec2::new(x, y),
});
while alternate_lab.contains(&alternate_lab.guard.pos) {
alternate_lab.step();
if alternate_visited.contains(&alternate_lab.guard) {
positions_with_cycles_detected += 1;
break;
}
alternate_visited.insert(alternate_lab.guard.clone());
}
}
}
positions_with_cycles_detected
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test() {
let input = "
....#.....
.........#
..........
..#.......
.......#..
..........
.#..^.....
........#.
#.........
......#..."
.trim();
let mut lab: Lab = input.parse().unwrap();
let mut lab2 = lab.clone();
assert_eq!(lab.num_steps_until_guard_leaves_lab(), 41);
assert_eq!(
lab2.num_positions_of_new_obstructions_that_cause_guard_loop(),
6
);
}
}

View file

@ -3,6 +3,8 @@ pub use std::{
cmp::Ordering,
collections::HashMap,
collections::HashSet,
convert::Infallible,
fmt::Display,
fs::File,
io::Read,
iter::zip,