Day 11. Sigh.

This commit is contained in:
2020-12-11 10:13:12 -08:00
parent 0e7746bdde
commit 806f86214b
5 changed files with 374 additions and 7 deletions

115
src/bin/ferry.rs Normal file
View File

@@ -0,0 +1,115 @@
use advent2020::errors::{MapOperationError, MapParseError, TopLevelError};
use advent2020::map::Map;
use std::convert::TryFrom;
use std::env;
use std::fs;
#[derive(Clone, Debug, Eq, PartialEq)]
enum FerryLocation {
Floor,
EmptySeat,
TakenSeat,
}
impl FerryLocation {
fn is_seat(&self) -> bool {
self != &FerryLocation::Floor
}
}
impl TryFrom<char> for FerryLocation {
type Error = MapParseError;
fn try_from(c: char) -> Result<Self,Self::Error> {
match c {
'.' => Ok(FerryLocation::Floor),
'L' => Ok(FerryLocation::EmptySeat),
'#' => Ok(FerryLocation::TakenSeat),
_ => Err(MapParseError::UnexpectedCharacter(c)),
}
}
}
impl Into<char> for FerryLocation {
fn into(self) -> char {
match self {
FerryLocation::Floor => '.',
FerryLocation::EmptySeat => 'L',
FerryLocation::TakenSeat => '#',
}
}
}
struct EvolvingMap {
next_map: Option<Map<FerryLocation>>,
occupation_tolerance: usize,
view: fn(&Map<FerryLocation>, usize, usize) -> Result<Vec<FerryLocation>, MapOperationError>,
}
impl From<Map<FerryLocation>> for EvolvingMap {
fn from(start_map: Map<FerryLocation>) -> EvolvingMap {
EvolvingMap {
next_map: Some(start_map),
occupation_tolerance: 4,
view: |m, x, y| { m.adjacents(x, y) },
}
}
}
impl Iterator for EvolvingMap {
type Item = Map<FerryLocation>;
fn next(&mut self) -> Option<Self::Item> {
let current_map = self.next_map.clone()?;
let mut next_map = current_map.clone();
for (x, y, value) in current_map.locations() {
let occupied_neighbors = (self.view)(&current_map, x, y).ok()?.iter().filter(|x| **x == FerryLocation::TakenSeat).count();
match value {
FerryLocation::EmptySeat if occupied_neighbors == 0 => {
next_map.set(x, y, FerryLocation::TakenSeat).ok()?;
}
FerryLocation::TakenSeat if occupied_neighbors >= self.occupation_tolerance => {
next_map.set(x, y, FerryLocation::EmptySeat).ok()?;
}
_ => {}
}
}
if next_map == current_map {
self.next_map = None;
} else {
self.next_map = Some(next_map);
}
Some(current_map)
}
}
fn main() -> Result<(),TopLevelError> {
let filename = env::args().nth(1).expect("No file argument given.");
let contents = fs::read_to_string(filename)?;
let map = Map::<FerryLocation>::try_from(contents.as_str())?;
// part 1
let base_evolving_map = EvolvingMap::from(map.clone());
for (idx, step) in base_evolving_map.enumerate() {
println!("Base map, step #{}", idx);
step.print();
println!("# of occupied seats: {}\n", step.count(FerryLocation::TakenSeat));
}
// part 2
let view_evolving_map = EvolvingMap {
next_map: Some(map),
occupation_tolerance: 5,
view: |m,x,y| m.adjacents_until(x, y, FerryLocation::is_seat),
};
for (idx, step) in view_evolving_map.enumerate() {
println!("Extended map, step #{}", idx);
step.print();
println!("# of occupied seats: {}\n", step.count(FerryLocation::TakenSeat));
}
Ok(())
}

View File

@@ -42,6 +42,10 @@ pub enum TopLevelError {
},
#[error("Numeric conversion error: {0}")]
NumConversionError(#[from] ParseIntError),
#[error("Error parsing map: {0}")]
MapParseError(#[from] MapParseError),
#[error("Error accessing map: {0}")]
MapOperationError(#[from] MapOperationError),
}
#[derive(Error, Debug)]
@@ -130,3 +134,9 @@ pub enum ExecutionError {
#[error("Tried to execute non-existent instruction at {0}")]
NonExistentLocation(isize),
}
#[derive(Error, Debug)]
pub enum MapOperationError {
#[error("Out of bounds indexing map with ({0},{1})")]
OutOfBounds(usize, usize),
}

View File

@@ -1,9 +1,10 @@
use crate::errors::MapParseError;
use std::convert::TryFrom;
use crate::errors::{MapOperationError, MapParseError};
use std::convert::{Into, TryFrom};
#[derive(Clone)]
pub struct Map<A: Clone> {
width: usize,
_height: usize,
height: usize,
data: Vec<Vec<A>>,
}
@@ -16,7 +17,7 @@ where
fn try_from(s: &str) -> Result<Map<X>, E> {
let mut width = 0;
let mut _height = 0;
let mut height = 0;
let mut data = Vec::new();
for line in s.lines() {
@@ -27,18 +28,18 @@ where
current_line.push(item);
}
_height += 1;
height += 1;
if width == 0 {
width = current_line.len();
} else if width != current_line.len() {
return Err(E::from(MapParseError::UnevenLines(_height)));
return Err(E::from(MapParseError::UnevenLines(height)));
}
data.push(current_line);
}
Ok(Map {
width,
_height,
height,
data,
})
}
@@ -50,4 +51,138 @@ impl<X: Clone> Map<X> {
let wrapped_x = x % self.width;
Some(row[wrapped_x].clone())
}
pub fn at_unwrapped(&self, x: usize, y: usize) -> Option<X> {
let row = self.data.get(y)?;
row.get(x).map(|x| x.clone())
}
fn view(&self, f: fn(&X) -> bool, x: usize, y: usize, rise: isize, run: isize) -> Option<X> {
let mut sx = (x as isize) + run;
let mut sy = (y as isize) + rise;
loop {
if sx < 0 { return None; }
if sy < 0 { return None; }
let entry = self.at_unwrapped(sx as usize, sy as usize)?;
if f(&entry) {
return Some(entry)
}
sx += run;
sy += rise;
}
}
pub fn set(&mut self, x: usize, y: usize, value: X) -> Result<(), MapOperationError> {
let row = self.data.get_mut(y).ok_or(MapOperationError::OutOfBounds(x, y))?;
if x >= self.width {
return Err(MapOperationError::OutOfBounds(x, y));
}
row[x] = value;
Ok(())
}
pub fn adjacents(&self, x: usize, y: usize) -> Result<Vec<X>, MapOperationError> {
self.adjacents_until(x, y, |_| true)
}
pub fn adjacents_until(&self, x: usize, y: usize, f: fn(&X) -> bool) -> Result<Vec<X>, MapOperationError>
{
if y >= self.height {
return Err(MapOperationError::OutOfBounds(x, y));
}
if x >= self.width {
return Err(MapOperationError::OutOfBounds(x, y));
}
let mut results = Vec::new();
push_some(&mut results, self.view(f, x, y, -1, -1));
push_some(&mut results, self.view(f, x, y, -1, -0));
push_some(&mut results, self.view(f, x, y, -1, 1));
push_some(&mut results, self.view(f, x, y, -0, -1));
push_some(&mut results, self.view(f, x, y, -0, 1));
push_some(&mut results, self.view(f, x, y, 1, -1));
push_some(&mut results, self.view(f, x, y, 1, 0));
push_some(&mut results, self.view(f, x, y, 1, 1));
Ok(results)
}
pub fn locations<'a>(&'a self) -> MapLocations<'a, X> {
MapLocations {
underlying: self,
x: 0,
y: 0,
}
}
}
impl<X: Clone + PartialEq> Map<X> {
pub fn count(&self, x: X) -> usize {
self.locations().filter(|v| v.2 == x).count()
}
}
pub struct MapLocations<'a,X: Clone> {
underlying: &'a Map<X>,
x: usize,
y: usize,
}
impl<'a, X: Clone> Iterator for MapLocations<'a,X> {
type Item = (usize, usize, X);
fn next(&mut self) -> Option<Self::Item> {
loop {
if (self.x == self.underlying.width) && (self.y == self.underlying.height) {
return None;
}
if self.x == self.underlying.width {
self.x = 0;
self.y += 1;
continue;
}
let value = self.underlying.at_unwrapped(self.x, self.y)?;
let result = (self.x, self.y, value);
self.x += 1;
return Some(result);
}
}
}
impl<X: Clone + Into<char>> Map<X> {
pub fn print(&self) {
for y in 0..self.height {
for x in 0..self.width {
print!("{}", self.at(x, y).unwrap().into());
}
println!("");
}
}
}
impl<X: Clone + PartialEq> PartialEq<Map<X>> for Map<X> {
fn eq(&self, other: &Self) -> bool {
self.height == other.height &&
self.width == other.width &&
self.data == other.data
}
}
impl<X: Clone + PartialEq + Eq> Eq for Map<X> { }
fn push_some<X>(vector: &mut Vec<X>, value: Option<X>) {
if let Some(val) = value {
vector.push(val);
}
}