Solved day 6
This commit is contained in:
121
src/day6.rs
121
src/day6.rs
@@ -1,11 +1,13 @@
|
||||
use std::collections::HashSet;
|
||||
use itertools::Itertools;
|
||||
|
||||
use crate::day_solver::DaySolver;
|
||||
use crate::util::{Coord, Grid};
|
||||
#[cfg(test)]
|
||||
use crate::util::read_file;
|
||||
|
||||
pub struct Day6 {
|
||||
maze: Grid<char>
|
||||
maze: Grid<char>,
|
||||
initial_path: Option<Vec<Coord<usize>>>
|
||||
}
|
||||
|
||||
const DIRECTIONS: [Coord<isize>; 4] = [
|
||||
@@ -15,6 +17,12 @@ const DIRECTIONS: [Coord<isize>; 4] = [
|
||||
Coord { x: -1, y: 0 }
|
||||
];
|
||||
|
||||
enum GuardResult {
|
||||
Loop,
|
||||
Walkout(Vec<Coord<usize>>),
|
||||
Illegal
|
||||
}
|
||||
|
||||
impl Day6 {
|
||||
|
||||
pub fn create(input: String) -> Self {
|
||||
@@ -22,46 +30,101 @@ impl Day6 {
|
||||
|
||||
// Put the input into the day struct
|
||||
Day6 {
|
||||
maze: Grid::parse(input, None)
|
||||
maze: Grid::parse(input, None),
|
||||
initial_path: None
|
||||
}
|
||||
}
|
||||
|
||||
fn encode_dirs(prev_dirs: &u8, to_add: usize) -> u8 {
|
||||
prev_dirs | 1 << to_add
|
||||
}
|
||||
|
||||
fn has_dir(dirs: &u8, dir_to_check: usize) -> bool {
|
||||
(dirs & 1 << dir_to_check) != 0
|
||||
}
|
||||
|
||||
fn simulate_guard(maze: &Grid<char>) -> GuardResult {
|
||||
let mut cur_pos = match maze.find(&'^') {
|
||||
Some(pos) => pos,
|
||||
None => return GuardResult::Illegal
|
||||
};
|
||||
let mut cur_direction_index = 0;
|
||||
let mut cur_direction = DIRECTIONS[cur_direction_index];
|
||||
let mut next_pos = cur_pos.add_signed(cur_direction);
|
||||
|
||||
let mut visited_positions = Vec::new();
|
||||
let mut previous_directions: Grid<u8> = Grid {
|
||||
data: vec![0; maze.width * maze.height()],
|
||||
width: maze.width,
|
||||
default: Some(0)
|
||||
};
|
||||
|
||||
while maze.in_bounds(next_pos.x, next_pos.y) {
|
||||
|
||||
// println!("pos: {:?}", cur_pos);
|
||||
let c = maze.get(next_pos.x, next_pos.y);
|
||||
if c == &'#' {
|
||||
cur_direction_index = (cur_direction_index + 1) % DIRECTIONS.len();
|
||||
cur_direction = DIRECTIONS[cur_direction_index];
|
||||
next_pos = cur_pos.add_signed(cur_direction);
|
||||
} else {
|
||||
let existing_dirs = previous_directions.get(cur_pos.x, cur_pos.y);
|
||||
if Self::has_dir(existing_dirs, cur_direction_index) {
|
||||
// println!("Loop detected at {:?}", cur_pos);
|
||||
return GuardResult::Loop
|
||||
}
|
||||
previous_directions.set(cur_pos.x, cur_pos.y, Self::encode_dirs(existing_dirs, cur_direction_index));
|
||||
cur_pos = next_pos;
|
||||
visited_positions.push(cur_pos);
|
||||
next_pos = cur_pos.add_signed(cur_direction);
|
||||
}
|
||||
}
|
||||
|
||||
GuardResult::Walkout(visited_positions)
|
||||
}
|
||||
}
|
||||
|
||||
impl DaySolver for Day6 {
|
||||
|
||||
|
||||
fn solve_part1(&mut self) -> String {
|
||||
let mut cur_pos = self.maze.find(&'^').unwrap();
|
||||
let mut cur_direction_index = 0;
|
||||
let mut cur_direction = DIRECTIONS[cur_direction_index];
|
||||
let mut next_pos = cur_pos.add_signed(cur_direction);
|
||||
|
||||
let mut visited_positions = HashSet::new();
|
||||
|
||||
while self.maze.in_bounds(next_pos.x, next_pos.y) {
|
||||
|
||||
// println!("pos: {:?}", cur_pos);
|
||||
let c = self.maze.get(next_pos.x, next_pos.y);
|
||||
if c == &'#' {
|
||||
cur_direction_index = (cur_direction_index + 1) % DIRECTIONS.len();
|
||||
cur_direction = DIRECTIONS[cur_direction_index];
|
||||
next_pos = cur_pos.add_signed(cur_direction);
|
||||
} else {
|
||||
cur_pos = next_pos;
|
||||
visited_positions.insert(cur_pos);
|
||||
next_pos = cur_pos.add_signed(cur_direction);
|
||||
}
|
||||
if let GuardResult::Walkout(path) = Self::simulate_guard(&self.maze) {
|
||||
self.initial_path = Some(path.to_owned());
|
||||
path.iter().unique().count().to_string()
|
||||
} else {
|
||||
panic!("The guard got stuck in the maze")
|
||||
}
|
||||
|
||||
visited_positions.len().to_string()
|
||||
}
|
||||
|
||||
fn solve_part2(&mut self) -> String {
|
||||
// Idea so far:
|
||||
// When walking the path as in part 1, for each position, check if the "next position" has been visited before, but while walking 1 rotation to the left
|
||||
// If that is the case, then if we block the next position, the guard would rotate to go back into his original path
|
||||
|
||||
return 0.to_string();
|
||||
let initial_path = match &self.initial_path {
|
||||
Some(path) => path.to_owned(),
|
||||
None => match Self::simulate_guard(&self.maze) {
|
||||
GuardResult::Walkout(path) => {
|
||||
self.initial_path = Some(path.to_owned());
|
||||
path.to_owned()
|
||||
},
|
||||
GuardResult::Loop => panic!("The initial path does not leave the maze!"),
|
||||
GuardResult::Illegal => panic!("You are BANDIT!")
|
||||
}
|
||||
};
|
||||
|
||||
let mut loop_count = 0;
|
||||
|
||||
// For each position that the guard visits, we check what will happen if it is blocked by a wall
|
||||
for pos in initial_path.iter().unique() {
|
||||
|
||||
let mut new_maze= self.maze.clone();
|
||||
new_maze.set(pos.x, pos.y, '#');
|
||||
|
||||
if let GuardResult::Loop = Self::simulate_guard(&new_maze) {
|
||||
loop_count += 1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
loop_count.to_string()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user