Now it gets ugly
This commit is contained in:
parent
3d78aaee63
commit
e5669fac3b
193
2024/rust/src/day6.rs
Normal file
193
2024/rust/src/day6.rs
Normal 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
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,6 +3,8 @@ pub use std::{
|
||||||
cmp::Ordering,
|
cmp::Ordering,
|
||||||
collections::HashMap,
|
collections::HashMap,
|
||||||
collections::HashSet,
|
collections::HashSet,
|
||||||
|
convert::Infallible,
|
||||||
|
fmt::Display,
|
||||||
fs::File,
|
fs::File,
|
||||||
io::Read,
|
io::Read,
|
||||||
iter::zip,
|
iter::zip,
|
||||||
|
|
Loading…
Reference in a new issue