diff --git a/2016/13/Cargo.lock b/2016/13/Cargo.lock new file mode 100644 index 0000000..7aac128 --- /dev/null +++ b/2016/13/Cargo.lock @@ -0,0 +1,4 @@ +[root] +name = "13" +version = "0.1.0" + diff --git a/2016/13/Cargo.toml b/2016/13/Cargo.toml new file mode 100644 index 0000000..ae75f73 --- /dev/null +++ b/2016/13/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "13" +version = "0.1.0" +authors = ["logaritmisk "] + +[dependencies] diff --git a/2016/13/src/main.rs b/2016/13/src/main.rs new file mode 100644 index 0000000..9157b17 --- /dev/null +++ b/2016/13/src/main.rs @@ -0,0 +1,291 @@ +use std::collections::{BTreeSet, BTreeMap}; +use std::usize; +use std::fmt; + + +type Cell = (usize, usize); + + +#[derive(Debug, PartialEq, Copy, Clone)] +pub enum Space { + Unknown, + Open, + Wall +} + +pub struct Office { + seed: usize, + floor: Vec> +} + +impl Office { + pub fn new(seed: usize) -> Self { + Office { + seed: seed, + floor: Vec::new() + } + } + + pub fn cell_type(&mut self, cell: Cell) -> Space { + while cell.1 >= self.floor.len() { + self.floor.push(Vec::new()); + } + + while cell.0 >= self.floor[cell.1].len() { + self.floor[cell.1].push(Space::Unknown); + } + + match self.floor[cell.1][cell.0] { + Space::Unknown => { + let integer = (cell.0 * cell.0) + (3 * cell.0) + (2 * cell.0 * cell.1) + cell.1 + (cell.1 * cell.1) + self.seed; + + let binary = format!("{:b}", integer); + + let ones = binary.chars() + .filter(|x| *x == '1') + .count(); + + if ones % 2 == 0 { + self.floor[cell.1][cell.0] = Space::Open; + } else { + self.floor[cell.1][cell.0] = Space::Wall; + } + + self.floor[cell.1][cell.0] + } + s @ _ => { + s + } + } + } + + fn cell_neighbors(&mut self, cell: Cell) -> Vec { + let mut neighbors = Vec::new(); + + // Over. + if cell.1 > 0 { + if let Space::Open = self.cell_type((cell.0, cell.1 - 1)) { + neighbors.push((cell.0, cell.1 - 1)); + } + } + + // Below. + if let Space::Open = self.cell_type((cell.0, cell.1 + 1)) { + neighbors.push((cell.0, cell.1 + 1)); + } + + // Left. + if cell.0 > 0 { + if let Space::Open = self.cell_type((cell.0 - 1, cell.1)) { + neighbors.push((cell.0 - 1, cell.1)); + } + } + + // Right. + if let Space::Open = self.cell_type((cell.0 + 1, cell.1)) { + neighbors.push((cell.0 + 1, cell.1)); + } + + neighbors + } + + pub fn path_between(&mut self, start: Cell, goal: Cell) -> Result, ()> { + let mut closed_set = BTreeSet::new(); + let mut open_set = BTreeSet::new(); + + open_set.insert(start); + + let mut came_from = BTreeMap::new();; + + let mut g_score = BTreeMap::new(); + + g_score.insert(start, 0); + + let mut f_score = BTreeMap::new(); + + f_score.insert(start, heuristic_cost_estimate(start, goal)); + + while !open_set.is_empty() { + let (current, _) = f_score.iter() + .filter(|&(&(x, y), _)| open_set.contains(&(x, y))) + .fold(((0, 0), usize::MAX), |((min_x, min_y), min_score), (&(x, y), &score)| { + if score < min_score { + ((x, y), score) + } else { + ((min_x, min_y), min_score) + } + }); + + if current == goal { + return Ok(reconstruct_path(&came_from, current)); + } + + open_set.remove(¤t); + closed_set.insert(current); + + for neighbor in self.cell_neighbors(current) { + if closed_set.contains(&neighbor) { + continue; + } + + let tentative_g_score = *g_score.entry(current).or_insert(usize::MAX) + dist_between(current, neighbor); + + if !open_set.contains(&neighbor) { + open_set.insert(neighbor); + } else if tentative_g_score >= *g_score.entry(neighbor).or_insert(usize::MAX) { + continue + } + + came_from.insert(neighbor, current); + + g_score.insert(neighbor, tentative_g_score); + f_score.insert(neighbor, tentative_g_score + heuristic_cost_estimate(neighbor, goal)); + } + } + + Err(()) + } + +} + +impl fmt::Display for Office { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + for row in self.floor.iter() { + for cell in row { + match *cell { + Space::Unknown => { write!(f, " ")? }, + Space::Open => { write!(f, ".")? }, + Space::Wall => { write!(f, "#")? }, + } + } + + writeln!(f, "")? + } + + Ok(()) + } +} + + +fn reconstruct_path(came_from: &BTreeMap, current: Cell) -> Vec { + let mut current = current; + let mut path = Vec::new(); + + path.push(current); + + while came_from.contains_key(¤t) { + if let Some(next) = came_from.get(¤t) { + current = *next; + + path.push(*next); + } + } + + path +} + +fn dist_between(start: Cell, goal: Cell) -> usize { + let delta_x = goal.0 as i64 - start.0 as i64; + let delta_y = goal.1 as i64 - start.1 as i64; + + (delta_x.abs() + delta_y.abs()) as usize +} + +fn heuristic_cost_estimate(start: Cell, goal: Cell) -> usize { + dist_between(start, goal) +} + + +fn main() { + let mut office = Office::new(1352); + + let path = office.path_between((1, 1), (31, 39)).unwrap(); + + for (y, row) in office.floor.iter().enumerate() { + for (x, cell) in row.iter().enumerate() { + if path.contains(&(x, y)) { + print!("\x1b[32m"); + } + + match *cell { + Space::Unknown => { print!(" "); }, + Space::Open => { print!("."); }, + Space::Wall => { print!("#"); }, + } + + if path.contains(&(x, y)) { + print!("\x1b[0m"); + } + } + + println!(""); + } + + println!("length={}", path.len() - 1); + + + let mut office = Office::new(1352); + let mut reachable = BTreeSet::new(); + + for x in 0..50 + 2 { + for y in 0..50 + 2 { + if let Ok(path) = office.path_between((1, 1), (x, y)) { + for cell in path.iter().rev().take(51) { + reachable.insert(cell.clone()); + } + } + } + } + + for (y, row) in office.floor.iter().enumerate() { + for (x, cell) in row.iter().enumerate() { + if reachable.contains(&(x, y)) { + print!("\x1b[32m"); + } + + match *cell { + Space::Unknown => { print!(" "); }, + Space::Open => { print!("."); }, + Space::Wall => { print!("#"); }, + } + + if reachable.contains(&(x, y)) { + print!("\x1b[0m"); + } + } + + println!(""); + } + + println!("length={}", reachable.len()); +} + + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_cell_space() { + let mut office = Office::new(10); + + assert_eq!(Space::Open, office.cell_type((0, 0))); + assert_eq!(Space::Wall, office.cell_type((1, 0))); + assert_eq!(Space::Open, office.cell_type((2, 0))); + + assert_eq!(Space::Open, office.cell_type((0, 1))); + assert_eq!(Space::Open, office.cell_type((1, 1))); + assert_eq!(Space::Wall, office.cell_type((2, 1))); + + assert_eq!(Space::Wall, office.cell_type((0, 2))); + assert_eq!(Space::Open, office.cell_type((1, 2))); + assert_eq!(Space::Open, office.cell_type((2, 2))); + } + + #[test] + fn test_path_between() { + let mut office = Office::new(10); + + assert_eq!(Ok(vec![(7, 4), (6, 4), (6, 5), (5, 5), (4, 5), (4, 4), (3, 4), (3, 3), (3, 2), (2, 2), (1, 2), (1, 1)]), office.path_between((1, 1), (7, 4))); + } +}