This commit is contained in:
2016-12-25 22:06:23 +01:00
parent 8c5ec3fe9d
commit 83e9f642e6
5 changed files with 307 additions and 0 deletions

249
2016/24/src/main.rs Normal file
View File

@@ -0,0 +1,249 @@
use std::io::{self, BufRead};
use std::collections::{BTreeSet, BTreeMap, HashMap};
use std::usize;
pub type Cell = (usize, usize);
pub trait PathBuilder {
fn neighbors(&mut self, cell: Cell) -> Vec<Cell>;
fn distance_between(&mut self, start: Cell, goal: Cell) -> usize;
fn heuristic_cost_estimate(&mut self, start: Cell, goal: Cell) -> usize {
self.distance_between(start, goal)
}
fn reconstruct_path(&self, came_from: &BTreeMap<Cell, Cell>, current: Cell) -> Vec<Cell> {
let mut current = current;
let mut path = Vec::new();
path.push(current);
while came_from.contains_key(&current) {
if let Some(next) = came_from.get(&current) {
current = *next;
path.push(*next);
}
}
path
}
}
pub fn path_between(builder: &mut PathBuilder, start: Cell, goal: Cell) -> Result<Vec<Cell>, ()> {
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, builder.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(builder.reconstruct_path(&came_from, current));
}
open_set.remove(&current);
closed_set.insert(current);
for neighbor in builder.neighbors(current) {
if closed_set.contains(&neighbor) {
continue;
}
let tentative_g_score = *g_score
.entry(current)
.or_insert(usize::MAX) + builder.distance_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 + builder.heuristic_cost_estimate(neighbor, goal));
}
}
Err(())
}
pub struct Grid {
grid: Vec<Vec<char>>
}
impl PathBuilder for Grid {
fn neighbors(&mut self, cell: Cell) -> Vec<Cell> {
let mut neighbors = Vec::new();
// Check cell above.
if cell.1 > 0 && self.grid[cell.1 - 1][cell.0] != '#' {
neighbors.push((cell.0, cell.1 - 1));
}
// Check cell below.
if cell.1 < self.grid.len() - 1 && self.grid[cell.1 + 1][cell.0] != '#' {
neighbors.push((cell.0, cell.1 + 1));
}
// Check left cell.
if cell.0 > 0 && self.grid[cell.1][cell.0 - 1] != '#' {
neighbors.push((cell.0 - 1, cell.1));
}
// Check right cell.
if cell.0 < self.grid[0].len() - 1 && self.grid[cell.1][cell.0 + 1] != '#' {
neighbors.push((cell.0 + 1, cell.1));
}
neighbors
}
fn distance_between(&mut self, 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 main() {
let stdin = io::stdin();
let grid = stdin.lock().lines()
.filter_map(|line| line.ok())
.map(|line| line.chars().collect::<Vec<_>>())
.collect::<Vec<_>>();
let mut grid = Grid { grid: grid };
let targets: HashMap<char, (usize, usize)> = grid.grid.iter()
.enumerate()
.flat_map(|(y, row)| {
row.iter()
.cloned()
.enumerate()
.filter(|&(_, c)| c != '#' && c != '.')
.map(move |(x, c)| (c, (x, y)))
})
.collect();
let mut table = HashMap::new();
for left in targets.iter() {
for right in targets.iter().filter(|right| *right != left) {
if let Ok(path) = path_between(&mut grid, *left.1, *right.1) {
table.entry(left.0.clone())
.or_insert(HashMap::new())
.insert(right.0.clone(), path.len());
}
}
}
// Part 1.
let mut queue = Vec::new();
queue.push((0, vec!['0']));
'main: loop {
let mut variants = Vec::new();
for &(ref total, ref items) in queue.iter() {
if items.len() == targets.len() {
break 'main;
}
let last = items.last().unwrap();
for (next, distance) in table.get(last).unwrap().iter() {
if items.contains(&next) {
continue;
}
let mut variant = items.clone();
variant.push(next.clone());
variants.push((total + distance - 1, variant));
}
}
queue = variants;
}
queue.sort_by_key(|&(distance, _)| distance);
println!("{:#?}", queue[0]);
// Part 2.
let mut queue = Vec::new();
queue.push((0, vec!['0']));
'main2: loop {
let mut variants = Vec::new();
for &(ref total, ref items) in queue.iter() {
let last = items.last().unwrap();
if items.len() >= targets.len() {
if *last != '0' {
if let Some(distance) = table.get(last).unwrap().get(&'0') {
let mut variant = items.clone();
variant.push('0');
variants.push((total + distance - 1, variant));
}
} else {
break 'main2;
}
}
for (next, distance) in table.get(last).unwrap().iter() {
if items.contains(&next) {
continue;
}
let mut variant = items.clone();
variant.push(next.clone());
variants.push((total + distance - 1, variant));
}
}
queue = variants;
}
queue.sort_by_key(|&(distance, _)| distance);
println!("{:#?}", queue[0]);
}