This commit is contained in:
2016-12-23 16:57:55 +01:00
parent 831acafd8c
commit e653edb47b
5 changed files with 1449 additions and 0 deletions

316
2016/22/src/main.rs Normal file
View File

@@ -0,0 +1,316 @@
#[macro_use] extern crate log;
extern crate env_logger;
#[macro_use] extern crate lazy_static;
extern crate regex;
use std::io::{self, BufRead};
use std::str::FromStr;
use std::collections::{BTreeSet, BTreeMap};
use std::usize;
use regex::Regex;
#[derive(Debug, PartialEq)]
pub struct Disk {
x: usize,
y: usize,
size: u16,
used: u16,
}
impl Disk {
pub fn new(x: usize, y: usize, size: u16, used: u16) -> Self {
Disk {
x: x,
y: y,
size: size,
used: used
}
}
}
impl FromStr for Disk {
type Err = ();
fn from_str(s: &str) -> Result<Disk, Self::Err> {
lazy_static! {
static ref RE: Regex = Regex::new(r"-x(\d+)-y(\d+)\s+(\d+)T\s+(\d+)T").unwrap();
}
if let Some(caps) = RE.captures(s) {
let x = caps.at(1).unwrap().parse::<usize>().unwrap();
let y = caps.at(2).unwrap().parse::<usize>().unwrap();
let size = caps.at(3).unwrap().parse::<u16>().unwrap();
let used = caps.at(4).unwrap().parse::<u16>().unwrap();
Ok(Disk::new(x, y, size, used))
} else {
Err(())
}
}
}
type Cell = (usize, usize);
#[derive(Debug, PartialEq)]
pub struct Grid {
grid: Vec<Vec<Disk>>,
lock: Option<Cell>
}
impl Grid {
pub fn new() -> Self {
Grid {
grid: Vec::new(),
lock: None
}
}
fn lock(&mut self, cell: Cell) {
self.lock = Some(cell);
}
fn cell_neighbors(&mut self, cell: Cell) -> Vec<Cell> {
let mut neighbors = Vec::new();
let ref disk = self.grid[cell.1][cell.0];
debug!("neighbor[@]={:?}", disk);
// Check disk above.
if cell.1 > 0 && self.grid[cell.1 - 1][cell.0].used <= disk.size {
debug!("neighbor[^]={:?}", self.grid[cell.1 - 1][cell.0]);
neighbors.push((cell.0, cell.1 - 1));
}
// Check disk below.
if cell.1 < self.grid.len() - 1 && self.grid[cell.1 + 1][cell.0].used <= disk.size {
debug!("neighbor[v]={:?}", self.grid[cell.1 + 1][cell.0]);
neighbors.push((cell.0, cell.1 + 1));
}
// Check left disk.
if cell.0 > 0 && self.grid[cell.1][cell.0 - 1].used <= disk.size {
debug!("neighbor[<]={:?}", self.grid[cell.1][cell.0 - 1]);
neighbors.push((cell.0 - 1, cell.1));
}
// Check right disk.
if cell.0 < self.grid[0].len() - 1 && self.grid[cell.1][cell.0 + 1].used <= disk.size {
debug!("neighbor[>]={:?}", self.grid[cell.1][cell.0 + 1]);
neighbors.push((cell.0 + 1, cell.1));
}
if let Some(lock) = self.lock {
neighbors = neighbors.iter()
.filter(|cell| **cell != lock)
.map(|cell| *cell)
.collect::<Vec<_>>();
}
neighbors
}
pub fn path_between(&mut self, 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, 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(&current);
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(())
}
}
fn reconstruct_path(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
}
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() {
env_logger::init().unwrap();
let stdin = io::stdin();
let mut disks = stdin.lock().lines()
.filter_map(|line| line.ok())
.filter_map(|line| line.parse::<Disk>().ok())
.collect::<Vec<_>>();
// Part 1.
let pairs: usize = disks.iter()
.filter(|&a| a.used > 0)
.map(|a| {
disks.iter()
.filter(|&b| *a != *b)
.filter(|&b| a.used <= (b.size - b.used))
.count()
})
.sum();
println!("number_of_pairs={}", pairs);
// Part 2.
let empty = {
if let Some(disk) = disks.iter().find(|&disk| disk.used == 0) {
(disk.x, disk.y)
} else {
(0, 0)
}
};
println!("empty={:?}", empty);
let mut grid = Grid::new();
disks.sort_by_key(|disk| (disk.y, disk.x));
for disk in disks {
while grid.grid.len() < disk.y + 1 {
grid.grid.push(Vec::new());
}
grid.grid[disk.y].push(disk);
}
let mut steps = 0;
let goal = {
(grid.grid[0].len() - 1, 0)
};
let path = grid.path_between(empty, goal).unwrap();
steps += path.len();
render_grid(&grid, &path);
debug!("path={:?}", path);
grid.lock(path[1]);
let mut start = path[0];
let mut end = (path[0].0 - 2, 0);
loop {
let path = grid.path_between(start, end).unwrap();
render_grid(&grid, &path);
steps += path.len();
if end == (0, 0) {
break;
}
grid.lock(path[0]);
start.0 -= 1;
end.0 -= 1;
}
println!("steps={}", steps - 1);
}
fn render_grid(grid: &Grid, path: &Vec<Cell>) {
for (y, row) in grid.grid.iter().enumerate() {
for (x, disk) in row.iter().enumerate() {
let symbol = if disk.used == 0 {
'_'
} else if disk.size > 97 {
'#'
} else {
'.'
};
if path.contains(&(x, y)) {
print!("\x1b[32m");
}
print!("{}", symbol);
if path.contains(&(x, y)) {
print!("\x1b[0m");
}
}
println!("");
}
println!("");
}