
9 changed files with 1616 additions and 1616 deletions
@ -1,222 +1,222 @@
|
||||
use std::ops::Range; |
||||
|
||||
const INPUT: &str = include_str!("../../data/day17"); |
||||
|
||||
#[derive(Clone, Copy, Default)] |
||||
struct Dimension { |
||||
offset: u32, |
||||
size: u32, |
||||
} |
||||
|
||||
impl Dimension { |
||||
fn map(&self, pos: i32) -> Option<usize> { |
||||
let p = (self.offset as i32) + pos; |
||||
if p >= 0 && p < (self.size as i32) { |
||||
Some(p as usize) |
||||
} else { |
||||
None |
||||
} |
||||
} |
||||
|
||||
fn include(&self, pos: i32) -> Self { |
||||
let left = std::cmp::min(pos, -(self.offset as i32)); |
||||
let right = std::cmp::max(pos, (self.size - self.offset - 1) as i32); |
||||
Self { |
||||
offset: (-left) as u32, |
||||
size: (right - left + 1) as u32, |
||||
} |
||||
} |
||||
|
||||
fn extend(&self) -> Self { |
||||
Self { |
||||
offset: self.offset + 1, |
||||
size: self.size + 2, |
||||
} |
||||
} |
||||
} |
||||
|
||||
impl IntoIterator for Dimension { |
||||
type IntoIter = Range<i32>; |
||||
type Item = i32; |
||||
|
||||
fn into_iter(self) -> Self::IntoIter { |
||||
-(self.offset as i32)..((self.size - self.offset) as i32) |
||||
} |
||||
} |
||||
|
||||
#[derive(Clone)] |
||||
struct Field { |
||||
x_dim: Dimension, |
||||
y_dim: Dimension, |
||||
z_dim: Dimension, |
||||
w_dim: Dimension, |
||||
cells: Vec<bool>, |
||||
} |
||||
|
||||
impl Default for Field { |
||||
fn default() -> Self { |
||||
Self { |
||||
x_dim: Dimension { offset: 0, size: 1}, |
||||
y_dim: Dimension { offset: 0, size: 1}, |
||||
z_dim: Dimension { offset: 0, size: 1}, |
||||
w_dim: Dimension { offset: 0, size: 1}, |
||||
cells: vec![false], |
||||
} |
||||
} |
||||
} |
||||
|
||||
impl Field { |
||||
fn alloc(x_dim: Dimension, y_dim: Dimension, z_dim: Dimension, w_dim: Dimension) -> Self { |
||||
let mut cells = Vec::new(); |
||||
let size = (x_dim.size * y_dim.size * z_dim.size * w_dim.size) as usize; |
||||
cells.resize(size, false); |
||||
Self { x_dim, y_dim, z_dim, w_dim, cells } |
||||
} |
||||
|
||||
fn _addr(&self, x: i32, y: i32, z: i32, w: i32) -> Option<usize> { |
||||
let x = self.x_dim.map(x)?; |
||||
let y = self.y_dim.map(y)?; |
||||
let z = self.z_dim.map(z)?; |
||||
let w = self.w_dim.map(w)?; |
||||
let addr = ((w * (self.z_dim.size as usize) + z) * (self.y_dim.size as usize) + y) * (self.x_dim.size as usize) + x; |
||||
Some(addr) |
||||
} |
||||
|
||||
fn _get(&self, x: i32, y: i32, z: i32, w: i32) -> Option<bool> { |
||||
let addr = self._addr(x, y, z, w)?; |
||||
Some(self.cells[addr]) |
||||
} |
||||
|
||||
fn get(&self, x: i32, y: i32, z: i32, w: i32) -> bool { |
||||
self._get(x, y, z, w).unwrap_or(false) |
||||
} |
||||
|
||||
fn set(&mut self, x: i32, y: i32, z: i32, w: i32, value: bool) { |
||||
if let Some(addr) = self._addr(x, y, z, w) { |
||||
self.cells[addr] = value; |
||||
} else if value { |
||||
// don't resize to set "false"
|
||||
let x_dim = self.x_dim.include(x); |
||||
let y_dim = self.y_dim.include(y); |
||||
let z_dim = self.z_dim.include(z); |
||||
let w_dim = self.w_dim.include(w); |
||||
let mut new_field = Self::alloc(x_dim, y_dim, z_dim, w_dim); |
||||
for w in self.w_dim { |
||||
for z in self.z_dim { |
||||
for y in self.y_dim { |
||||
for x in self.x_dim { |
||||
let addr = new_field._addr(x, y, z, w).unwrap(); |
||||
new_field.cells[addr] = self.get(x, y, z, w); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
let addr = new_field._addr(x, y, z, w).unwrap(); |
||||
new_field.cells[addr] = value; |
||||
*self = new_field; |
||||
} |
||||
} |
||||
|
||||
fn parse(data: &str) -> Self { |
||||
let mut this = Self::default(); |
||||
for (y, line) in data.lines().enumerate() { |
||||
for (x, chr) in line.chars().enumerate() { |
||||
this.set(x as i32, y as i32, 0, 0, chr == '#'); |
||||
} |
||||
} |
||||
this |
||||
} |
||||
|
||||
fn count_active_neighbors(&self, x: i32, y: i32, z: i32, w: i32) -> u32 { |
||||
let mut count = 0; |
||||
for dw in -1..=1 { |
||||
for dz in -1..=1 { |
||||
for dy in -1..=1 { |
||||
for dx in -1..=1 { |
||||
if dw == 0 && dz == 0 && dy == 0 && dx == 0 { |
||||
continue; |
||||
} |
||||
if self.get(x + dx, y + dy, z + dz, w + dw) { |
||||
count += 1; |
||||
if count > 3 { return count; } // more than 3 is always bad
|
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
count |
||||
} |
||||
|
||||
fn cycle_3dim(&self) -> Self { |
||||
let mut result = self.clone(); |
||||
let w = 0; |
||||
for z in self.z_dim.extend() { |
||||
for y in self.y_dim.extend() { |
||||
for x in self.x_dim.extend() { |
||||
let count = self.count_active_neighbors(x, y, z, w); |
||||
let active = count == 3 || (count == 2 && self.get(x, y, z, w)); |
||||
result.set(x, y, z, w, active); |
||||
} |
||||
} |
||||
} |
||||
result |
||||
} |
||||
|
||||
fn cycle_4dim(&self) -> Self { |
||||
let mut result = self.clone(); |
||||
for w in self.w_dim.extend() { |
||||
for z in self.z_dim.extend() { |
||||
for y in self.y_dim.extend() { |
||||
for x in self.x_dim.extend() { |
||||
let count = self.count_active_neighbors(x, y, z, w); |
||||
let active = count == 3 || (count == 2 && self.get(x, y, z, w)); |
||||
result.set(x, y, z, w, active); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
result |
||||
} |
||||
} |
||||
|
||||
impl std::fmt::Debug for Field { |
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
||||
use std::fmt::Write; |
||||
for z in self.z_dim { |
||||
for w in self.w_dim { |
||||
writeln!(f, "z = {}, w = {}", z, w)?; |
||||
for y in self.y_dim { |
||||
for x in self.x_dim { |
||||
if x == 0 && y == 0 { |
||||
f.write_char( |
||||
if self.get(x, y, z, w) { 'X' } else { '+' } |
||||
)?; |
||||
} else { |
||||
f.write_char( |
||||
if self.get(x, y, z, w) { '#' } else { '.' } |
||||
)?; |
||||
} |
||||
} |
||||
f.write_char('\n')?; |
||||
} |
||||
f.write_char('\n')?; |
||||
} |
||||
} |
||||
Ok(()) |
||||
} |
||||
} |
||||
|
||||
fn main() { |
||||
let input = Field::parse(INPUT); |
||||
let mut field = input.clone(); |
||||
for _ in 0..6 { |
||||
field = field.cycle_3dim(); |
||||
} |
||||
println!("Active after 6 3-dim rounds: {}", field.cells.iter().filter(|&&c| c).count()); |
||||
let mut field = input.clone(); |
||||
for _ in 0..6 { |
||||
field = field.cycle_4dim(); |
||||
} |
||||
println!("Active after 6 4-dim rounds: {}", field.cells.iter().filter(|&&c| c).count()); |
||||
println!("{:?}", input); |
||||
} |
||||
use std::ops::Range; |
||||
|
||||
const INPUT: &str = include_str!("../../data/day17"); |
||||
|
||||
#[derive(Clone, Copy, Default)] |
||||
struct Dimension { |
||||
offset: u32, |
||||
size: u32, |
||||
} |
||||
|
||||
impl Dimension { |
||||
fn map(&self, pos: i32) -> Option<usize> { |
||||
let p = (self.offset as i32) + pos; |
||||
if p >= 0 && p < (self.size as i32) { |
||||
Some(p as usize) |
||||
} else { |
||||
None |
||||
} |
||||
} |
||||
|
||||
fn include(&self, pos: i32) -> Self { |
||||
let left = std::cmp::min(pos, -(self.offset as i32)); |
||||
let right = std::cmp::max(pos, (self.size - self.offset - 1) as i32); |
||||
Self { |
||||
offset: (-left) as u32, |
||||
size: (right - left + 1) as u32, |
||||
} |
||||
} |
||||
|
||||
fn extend(&self) -> Self { |
||||
Self { |
||||
offset: self.offset + 1, |
||||
size: self.size + 2, |
||||
} |
||||
} |
||||
} |
||||
|
||||
impl IntoIterator for Dimension { |
||||
type IntoIter = Range<i32>; |
||||
type Item = i32; |
||||
|
||||
fn into_iter(self) -> Self::IntoIter { |
||||
-(self.offset as i32)..((self.size - self.offset) as i32) |
||||
} |
||||
} |
||||
|
||||
#[derive(Clone)] |
||||
struct Field { |
||||
x_dim: Dimension, |
||||
y_dim: Dimension, |
||||
z_dim: Dimension, |
||||
w_dim: Dimension, |
||||
cells: Vec<bool>, |
||||
} |
||||
|
||||
impl Default for Field { |
||||
fn default() -> Self { |
||||
Self { |
||||
x_dim: Dimension { offset: 0, size: 1}, |
||||
y_dim: Dimension { offset: 0, size: 1}, |
||||
z_dim: Dimension { offset: 0, size: 1}, |
||||
w_dim: Dimension { offset: 0, size: 1}, |
||||
cells: vec![false], |
||||
} |
||||
} |
||||
} |
||||
|
||||
impl Field { |
||||
fn alloc(x_dim: Dimension, y_dim: Dimension, z_dim: Dimension, w_dim: Dimension) -> Self { |
||||
let mut cells = Vec::new(); |
||||
let size = (x_dim.size * y_dim.size * z_dim.size * w_dim.size) as usize; |
||||
cells.resize(size, false); |
||||
Self { x_dim, y_dim, z_dim, w_dim, cells } |
||||
} |
||||
|
||||
fn _addr(&self, x: i32, y: i32, z: i32, w: i32) -> Option<usize> { |
||||
let x = self.x_dim.map(x)?; |
||||
let y = self.y_dim.map(y)?; |
||||
let z = self.z_dim.map(z)?; |
||||
let w = self.w_dim.map(w)?; |
||||
let addr = ((w * (self.z_dim.size as usize) + z) * (self.y_dim.size as usize) + y) * (self.x_dim.size as usize) + x; |
||||
Some(addr) |
||||
} |
||||
|
||||
fn _get(&self, x: i32, y: i32, z: i32, w: i32) -> Option<bool> { |
||||
let addr = self._addr(x, y, z, w)?; |
||||
Some(self.cells[addr]) |
||||
} |
||||
|
||||
fn get(&self, x: i32, y: i32, z: i32, w: i32) -> bool { |
||||
self._get(x, y, z, w).unwrap_or(false) |
||||
} |
||||
|
||||
fn set(&mut self, x: i32, y: i32, z: i32, w: i32, value: bool) { |
||||
if let Some(addr) = self._addr(x, y, z, w) { |
||||
self.cells[addr] = value; |
||||
} else if value { |
||||
// don't resize to set "false"
|
||||
let x_dim = self.x_dim.include(x); |
||||
let y_dim = self.y_dim.include(y); |
||||
let z_dim = self.z_dim.include(z); |
||||
let w_dim = self.w_dim.include(w); |
||||
let mut new_field = Self::alloc(x_dim, y_dim, z_dim, w_dim); |
||||
for w in self.w_dim { |
||||
for z in self.z_dim { |
||||
for y in self.y_dim { |
||||
for x in self.x_dim { |
||||
let addr = new_field._addr(x, y, z, w).unwrap(); |
||||
new_field.cells[addr] = self.get(x, y, z, w); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
let addr = new_field._addr(x, y, z, w).unwrap(); |
||||
new_field.cells[addr] = value; |
||||
*self = new_field; |
||||
} |
||||
} |
||||
|
||||
fn parse(data: &str) -> Self { |
||||
let mut this = Self::default(); |
||||
for (y, line) in data.lines().enumerate() { |
||||
for (x, chr) in line.chars().enumerate() { |
||||
this.set(x as i32, y as i32, 0, 0, chr == '#'); |
||||
} |
||||
} |
||||
this |
||||
} |
||||
|
||||
fn count_active_neighbors(&self, x: i32, y: i32, z: i32, w: i32) -> u32 { |
||||
let mut count = 0; |
||||
for dw in -1..=1 { |
||||
for dz in -1..=1 { |
||||
for dy in -1..=1 { |
||||
for dx in -1..=1 { |
||||
if dw == 0 && dz == 0 && dy == 0 && dx == 0 { |
||||
continue; |
||||
} |
||||
if self.get(x + dx, y + dy, z + dz, w + dw) { |
||||
count += 1; |
||||
if count > 3 { return count; } // more than 3 is always bad
|
||||
} |
||||
} |
||||
} |
||||
} |
||||
} |
||||
count |
||||
} |
||||
|
||||
fn cycle_3dim(&self) -> Self { |
||||
let mut result = self.clone(); |
||||
let w = 0; |
||||
for z in self.z_dim.extend() { |
||||
for y in self.y_dim.extend() { |
||||
for x in self.x_dim.extend() { |
||||
let count = self.count_active_neighbors(x, y, z, w); |
||||
let active = count == 3 || (count == 2 && self.get(x, y, z, w)); |
||||
result.set(x, y, z, w, active); |
||||
} |
||||
} |
||||
} |
||||
result |
||||
} |
||||
|
||||
fn cycle_4dim(&self) -> Self { |
||||
let mut result = self.clone(); |
||||
for w in self.w_dim.extend() { |
||||
for z in self.z_dim.extend() { |
||||
for y in self.y_dim.extend() { |
||||
for x in self.x_dim.extend() { |
||||
let count = self.count_active_neighbors(x, y, z, w); |
||||
let active = count == 3 || (count == 2 && self.get(x, y, z, w)); |
||||
result.set(x, y, z, w, active); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
result |
||||
} |
||||
} |
||||
|
||||
impl std::fmt::Debug for Field { |
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { |
||||
use std::fmt::Write; |
||||
for z in self.z_dim { |
||||
for w in self.w_dim { |
||||
writeln!(f, "z = {}, w = {}", z, w)?; |
||||
for y in self.y_dim { |
||||
for x in self.x_dim { |
||||
if x == 0 && y == 0 { |
||||
f.write_char( |
||||
if self.get(x, y, z, w) { 'X' } else { '+' } |
||||
)?; |
||||
} else { |
||||
f.write_char( |
||||
if self.get(x, y, z, w) { '#' } else { '.' } |
||||
)?; |
||||
} |
||||
} |
||||
f.write_char('\n')?; |
||||
} |
||||
f.write_char('\n')?; |
||||
} |
||||
} |
||||
Ok(()) |
||||
} |
||||
} |
||||
|
||||
fn main() { |
||||
let input = Field::parse(INPUT); |
||||
let mut field = input.clone(); |
||||
for _ in 0..6 { |
||||
field = field.cycle_3dim(); |
||||
} |
||||
println!("Active after 6 3-dim rounds: {}", field.cells.iter().filter(|&&c| c).count()); |
||||
let mut field = input.clone(); |
||||
for _ in 0..6 { |
||||
field = field.cycle_4dim(); |
||||
} |
||||
println!("Active after 6 4-dim rounds: {}", field.cells.iter().filter(|&&c| c).count()); |
||||
println!("{:?}", input); |
||||
} |
||||
|
@ -1,169 +1,169 @@
|
||||
const INPUT: &str = include_str!("../../data/day18"); |
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)] |
||||
enum Op { |
||||
Sum, |
||||
Mul, |
||||
} |
||||
|
||||
impl Op { |
||||
fn apply<T>(self, a: T, b: T) -> T |
||||
where |
||||
T: std::ops::Add<T, Output=T> + std::ops::Mul<T, Output=T>, |
||||
{ |
||||
match self { |
||||
Self::Sum => a + b, |
||||
Self::Mul => a * b, |
||||
} |
||||
} |
||||
} |
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)] |
||||
enum Elem<T> { |
||||
ParenLeft, |
||||
Op(Op), |
||||
Item(T), |
||||
} |
||||
|
||||
impl<T> Elem<T> { |
||||
fn expect_item(self) -> T { |
||||
if let Elem::Item(item) = self { |
||||
item |
||||
} else { |
||||
panic!("Expected item"); |
||||
} |
||||
} |
||||
|
||||
fn expect_op(self) -> Op { |
||||
if let Elem::Op(op) = self { |
||||
op |
||||
} else { |
||||
panic!("Expected op"); |
||||
} |
||||
} |
||||
|
||||
fn expect_paren_left(self) { |
||||
if let Elem::ParenLeft = self { |
||||
() |
||||
} else { |
||||
panic!("Expected '('"); |
||||
} |
||||
} |
||||
} |
||||
|
||||
#[derive(Debug)] |
||||
struct Stack<T> { |
||||
elems: Vec<Elem<T>>, |
||||
same_precedence: bool, |
||||
} |
||||
|
||||
impl<T> Stack<T> |
||||
where |
||||
T: std::ops::Add<T, Output=T> + std::ops::Mul<T, Output=T> + std::fmt::Debug, |
||||
{ |
||||
fn new(same_precedence: bool) -> Self { |
||||
Self { |
||||
elems: Vec::new(), |
||||
same_precedence, |
||||
} |
||||
} |
||||
|
||||
fn push_item(&mut self, item: T) { |
||||
self.elems.push(Elem::Item(item)) |
||||
} |
||||
|
||||
fn push_op(&mut self, op: Op) { |
||||
self.elems.push(Elem::Op(op)) |
||||
} |
||||
|
||||
fn push_paren_left(&mut self) { |
||||
self.elems.push(Elem::ParenLeft) |
||||
} |
||||
|
||||
fn _peek_top(&self) -> &Elem<T> { |
||||
&self.elems[self.elems.len() - 1] |
||||
} |
||||
|
||||
fn _peek_below_top(&self) -> &Elem<T> { |
||||
&self.elems[self.elems.len() - 2] |
||||
} |
||||
|
||||
fn pop_item(&mut self) -> T { |
||||
self.elems.pop().expect("expect non empty stack").expect_item() |
||||
} |
||||
|
||||
fn pop_op(&mut self) -> Op { |
||||
self.elems.pop().expect("expect non empty stack").expect_op() |
||||
} |
||||
|
||||
fn pop_paren_left(&mut self) { |
||||
self.elems.pop().expect("expect non empty stack").expect_paren_left() |
||||
} |
||||
|
||||
fn _do_reduce_op(&self, finish: bool) -> bool { |
||||
match self._peek_below_top() { |
||||
Elem::Op(Op::Sum) => true, |
||||
Elem::Op(Op::Mul) => finish || self.same_precedence, |
||||
_ => false, |
||||
} |
||||
} |
||||
|
||||
fn is_empty(&self) -> bool { |
||||
self.elems.is_empty() |
||||
} |
||||
|
||||
fn eval_top(&mut self, finish: bool) { |
||||
while self.elems.len() > 2 && matches!(self._peek_top(), Elem::Item(_)) && self._do_reduce_op(finish) { |
||||
let value2 = self.pop_item(); |
||||
let op = self.pop_op(); |
||||
let value1 = self.pop_item(); |
||||
self.push_item(op.apply(value1, value2)); |
||||
} |
||||
} |
||||
} |
||||
|
||||
fn parse<T>(mut line: &str, same_precedence: bool) -> T |
||||
where |
||||
T: std::ops::Add<T, Output=T> + std::ops::Mul<T, Output=T> + std::str::FromStr + std::fmt::Debug, |
||||
<T as std::str::FromStr>::Err: std::fmt::Debug, |
||||
{ |
||||
let mut stack = Stack::new(same_precedence); |
||||
|
||||
loop { |
||||
line = line.trim(); |
||||
if line.is_empty() {
|
||||
break; |
||||
} |
||||
if let Some(rem) = line.strip_prefix('+') { |
||||
stack.push_op(Op::Sum); |
||||
line = rem; |
||||
} else if let Some(rem) = line.strip_prefix('*') { |
||||
stack.push_op(Op::Mul); |
||||
line = rem; |
||||
} else if let Some(rem) = line.strip_prefix('(') { |
||||
stack.push_paren_left(); |
||||
line = rem; |
||||
} else if let Some(rem) = line.strip_prefix(')') { |
||||
stack.eval_top(true); |
||||
let item = stack.pop_item(); |
||||
stack.pop_paren_left(); |
||||
stack.push_item(item); |
||||
line = rem; |
||||
} else { |
||||
let num_end = line.find(|c: char| !c.is_ascii_digit()).unwrap_or(line.len()); |
||||
let num = line[..num_end].parse::<T>().unwrap(); |
||||
stack.push_item(num); |
||||
line = &line[num_end..]; |
||||
} |
||||
stack.eval_top(false); |
||||
} |
||||
stack.eval_top(true); |
||||
let item = stack.pop_item(); |
||||
assert!(stack.is_empty()); |
||||
item |
||||
} |
||||
|
||||
fn main() { |
||||
println!("Sum of evaluated lines (simple math): {}", INPUT.lines().map(|l| parse::<u64>(l, true)).sum::<u64>()); |
||||
println!("Sum of evaluated lines (advanced math): {}", INPUT.lines().map(|l| parse::<u64>(l, false)).sum::<u64>()); |
||||
} |
||||
const INPUT: &str = include_str!("../../data/day18"); |
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)] |
||||
enum Op { |
||||
Sum, |
||||
Mul, |
||||
} |
||||
|
||||
impl Op { |
||||
fn apply<T>(self, a: T, b: T) -> T |
||||
where |
||||
T: std::ops::Add<T, Output=T> + std::ops::Mul<T, Output=T>, |
||||
{ |
||||
match self { |
||||
Self::Sum => a + b, |
||||
Self::Mul => a * b, |
||||
} |
||||
} |
||||
} |
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)] |
||||
enum Elem<T> { |
||||
ParenLeft, |
||||
Op(Op), |
||||
Item(T), |
||||
} |
||||
|
||||
impl<T> Elem<T> { |
||||
fn expect_item(self) -> T { |
||||
if let Elem::Item(item) = self { |
||||
item |
||||
} else { |
||||
panic!("Expected item"); |
||||
} |
||||
} |
||||
|
||||
fn expect_op(self) -> Op { |
||||
if let Elem::Op(op) = self { |
||||
op |
||||
} else { |
||||
panic!("Expected op"); |
||||
} |
||||
} |
||||
|
||||
fn expect_paren_left(self) { |
||||
if let Elem::ParenLeft = self { |
||||
() |
||||
} else { |
||||
panic!("Expected '('"); |
||||
} |
||||
} |
||||
} |
||||
|
||||
#[derive(Debug)] |
||||
struct Stack<T> { |
||||
elems: Vec<Elem<T>>, |
||||
same_precedence: bool, |
||||
} |
||||
|
||||
impl<T> Stack<T> |
||||
where |
||||
T: std::ops::Add<T, Output=T> + std::ops::Mul<T, Output=T> + std::fmt::Debug, |
||||
{ |
||||
fn new(same_precedence: bool) -> Self { |
||||
Self { |
||||
elems: Vec::new(), |
||||
same_precedence, |
||||
} |
||||
} |
||||
|
||||
fn push_item(&mut self, item: T) { |
||||
self.elems.push(Elem::Item(item)) |
||||
} |
||||
|
||||
fn push_op(&mut self, op: Op) { |
||||
self.elems.push(Elem::Op(op)) |
||||
} |
||||
|
||||
fn push_paren_left(&mut self) { |
||||
self.elems.push(Elem::ParenLeft) |
||||
} |
||||
|
||||
fn _peek_top(&self) -> &Elem<T> { |
||||
&self.elems[self.elems.len() - 1] |
||||
} |
||||
|
||||
fn _peek_below_top(&self) -> &Elem<T> { |
||||
&self.elems[self.elems.len() - 2] |
||||
} |
||||
|
||||
fn pop_item(&mut self) -> T { |
||||
self.elems.pop().expect("expect non empty stack").expect_item() |
||||
} |
||||
|
||||
fn pop_op(&mut self) -> Op { |
||||
self.elems.pop().expect("expect non empty stack").expect_op() |
||||
} |
||||
|
||||
fn pop_paren_left(&mut self) { |
||||
self.elems.pop().expect("expect non empty stack").expect_paren_left() |
||||
} |
||||
|
||||
fn _do_reduce_op(&self, finish: bool) -> bool { |
||||
match self._peek_below_top() { |
||||
Elem::Op(Op::Sum) => true, |
||||
Elem::Op(Op::Mul) => finish || self.same_precedence, |
||||
_ => false, |
||||
} |
||||
} |
||||
|
||||
fn is_empty(&self) -> bool { |
||||
self.elems.is_empty() |
||||
} |
||||
|
||||
fn eval_top(&mut self, finish: bool) { |
||||
while self.elems.len() > 2 && matches!(self._peek_top(), Elem::Item(_)) && self._do_reduce_op(finish) { |
||||
let value2 = self.pop_item(); |
||||
let op = self.pop_op(); |
||||
let value1 = self.pop_item(); |
||||
self.push_item(op.apply(value1, value2)); |
||||
} |
||||
} |
||||
} |
||||
|
||||
fn parse<T>(mut line: &str, same_precedence: bool) -> T |
||||
where |
||||
T: std::ops::Add<T, Output=T> + std::ops::Mul<T, Output=T> + std::str::FromStr + std::fmt::Debug, |
||||
<T as std::str::FromStr>::Err: std::fmt::Debug, |
||||
{ |
||||
let mut stack = Stack::new(same_precedence); |
||||
|
||||
loop { |
||||
line = line.trim(); |
||||
if line.is_empty() {
|
||||
break; |
||||
} |
||||
if let Some(rem) = line.strip_prefix('+') { |
||||
stack.push_op(Op::Sum); |
||||
line = rem; |
||||
} else if let Some(rem) = line.strip_prefix('*') { |
||||
stack.push_op(Op::Mul); |
||||
line = rem; |
||||
} else if let Some(rem) = line.strip_prefix('(') { |
||||
stack.push_paren_left(); |
||||
line = rem; |
||||
} else if let Some(rem) = line.strip_prefix(')') { |
||||
stack.eval_top(true); |
||||
let item = stack.pop_item(); |
||||
stack.pop_paren_left(); |
||||
stack.push_item(item); |
||||
line = rem; |
||||
} else { |
||||
let num_end = line.find(|c: char| !c.is_ascii_digit()).unwrap_or(line.len()); |
||||
let num = line[..num_end].parse::<T>().unwrap(); |
||||
stack.push_item(num); |
||||
line = &line[num_end..]; |
||||
} |
||||
stack.eval_top(false); |
||||
} |
||||
stack.eval_top(true); |
||||
let item = stack.pop_item(); |
||||
assert!(stack.is_empty()); |
||||
item |
||||
} |
||||
|
||||
fn main() { |
||||
println!("Sum of evaluated lines (simple math): {}", INPUT.lines().map(|l| parse::<u64>(l, true)).sum::<u64>()); |
||||
println!("Sum of evaluated lines (advanced math): {}", INPUT.lines().map(|l| parse::<u64>(l, false)).sum::<u64>()); |
||||
} |
||||
|
@ -1,203 +1,203 @@
|
||||
use std::collections::{HashMap, HashSet}; |
||||
use std::sync::Arc; |
||||
|
||||
const INPUT: &str = include_str!("../../data/day19"); |
||||
|
||||
#[derive(Clone, Debug)] |
||||
enum Rule { |
||||
Char(char), |
||||
Chain(Vec<u32>), |
||||
} |
||||
|
||||
impl Rule { |
||||
fn parse_part(alt: &str) -> Self { |
||||
let alt = alt.trim(); |
||||
if let Some(alt) = alt.strip_prefix('"') { |
||||
let alt = alt.strip_suffix('"').unwrap(); |
||||
assert_eq!(alt.len(), 1); |
||||
Self::Char(alt.chars().next().unwrap()) |
||||
} else { |
||||
Self::Chain(alt.split_whitespace().map(|s| s.parse::<u32>().unwrap()).collect()) |
||||
} |
||||
} |
||||
|
||||
fn parse_line(line: &str) -> (u32, CachedRule) { |
||||
let colon = line.find(": ").unwrap(); |
||||
let id = line[..colon].parse::<u32>().unwrap(); |
||||
let alts = line[colon+2..].split(" | ").map(Self::parse_part).collect(); |
||||
(id, CachedRule::Alternatives(alts)) |
||||
} |
||||
} |
||||
|
||||
fn _make_combs(target: &mut HashSet<String>, current: &mut String, list: &[Arc<HashSet<String>>]) { |
||||
if list.is_empty() { |
||||
target.insert(current.clone()); |
||||
} else { |
||||
let old_len = current.len(); |
||||
for s in &*list[0] { |
||||
current.push_str(s); |
||||
_make_combs(target, current, &list[1..]); |
||||
current.truncate(old_len); |
||||
} |
||||
} |
||||
} |
||||
|
||||
#[derive(Clone, Debug)] |
||||
enum CachedRule { |
||||
Alternatives(Vec<Rule>), |
||||
CachedStrings(Arc<HashSet<String>>, HashSet<usize>), |
||||
} |
||||
|
||||
#[derive(Clone, Debug)] |
||||
struct Grammar { |
||||
expansions: HashMap<u32, CachedRule>, |
||||
} |
||||
|
||||
impl Grammar { |
||||
fn parse(rule_lines: &str) -> Self { |
||||
let expansions = rule_lines.lines().map(Rule::parse_line).collect(); |
||||
Self { expansions } |
||||
} |
||||
|
||||
fn _build_sets(&mut self, recursive_check: &mut HashSet<u32>, ndx: u32) -> Option<Arc<HashSet<String>>> { |
||||
let rules = self.expansions.get(&ndx).unwrap(); |
||||
let alts = match rules { |
||||
CachedRule::Alternatives(alts) => alts, |
||||
CachedRule::CachedStrings(cs, _) => return Some(cs.clone()), |
||||
}; |
||||
|
||||
if recursive_check.contains(&ndx) { |
||||
return None; |
||||
} |
||||
recursive_check.insert(ndx); |
||||
|
||||
let mut result = HashSet::<String>::new(); |
||||
|
||||
let mut found_recursion = false; |
||||
for rule in alts.clone() { |
||||
match rule { |
||||
Rule::Char(c) => { |
||||
result.insert(c.to_string()); |
||||
}, |
||||
Rule::Chain(parts) => { |
||||
let mut comb = Vec::new(); |
||||
for part_ndx in parts { |
||||
// abort if nested lookup loops
|
||||
if let Some(cs) = self._build_sets(recursive_check, part_ndx) { |
||||
comb.push(cs); |
||||
} else { |
||||
found_recursion = true; |
||||
} |
||||
} |
||||
if !found_recursion { |
||||
_make_combs(&mut result, &mut String::new(), &comb); |
||||
} |
||||
}, |
||||
} |
||||
} |
||||
if found_recursion { |
||||
return None; |
||||
} |
||||
let lengths: HashSet<usize> = result.iter().map(|s| s.len()).collect(); |
||||
let result = Arc::new(result); |
||||
recursive_check.remove(&ndx); |
||||
self.expansions.insert(ndx, CachedRule::CachedStrings(result.clone(), lengths)); |
||||
Some(result) |
||||
} |
||||
|
||||
fn optimize(&mut self) { |
||||
let mut recursive_check = HashSet::new(); |
||||
self._build_sets(&mut recursive_check, 0); |
||||
recursive_check.insert(0); |
||||
for id in recursive_check.clone() { |
||||
if let Some(CachedRule::Alternatives(alts)) = self.expansions.get(&id) { |
||||
for alt in alts { |
||||
if let Rule::Chain(chain) = alt { |
||||
recursive_check.extend(chain); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
let rule_ids: HashSet<u32> = self.expansions.keys().cloned().collect(); |
||||
for id in rule_ids.difference(&recursive_check) { |
||||
self.expansions.remove(id); |
||||
} |
||||
} |
||||
|
||||
fn simple_match(&self, data: &str) -> bool { |
||||
match self.expansions.get(&0) { |
||||
Some(CachedRule::CachedStrings(cs, _)) => cs.contains(data), |
||||
_ => panic!("not simple enough"), |
||||
} |
||||
} |
||||
|
||||
fn _complex_match_chain<'data>(&self, chain: &[u32], data: &'data str) -> Vec<&'data str> { |
||||
if chain.is_empty() { |
||||
return vec![data]; |
||||
} |
||||
self._complex_match(chain[0], data).into_iter().map(|rem| { |
||||
self._complex_match_chain(&chain[1..], rem) |
||||
}).flatten().collect() |
||||
} |
||||
|
||||
fn _complex_match<'data>(&self, start: u32, data: &'data str) -> Vec<&'data str> { |
||||
let cr = self.expansions.get(&start).unwrap(); |
||||
match cr { |
||||
CachedRule::Alternatives(alts) => { |
||||
alts.iter().map(|rule| { |
||||
let chain = match rule { |
||||
Rule::Chain(chain) => chain, |
||||
_ => panic!("only chains here"), |
||||
}; |
||||
self._complex_match_chain(&chain, data) |
||||
}).flatten().collect() |
||||
}, |
||||
CachedRule::CachedStrings(cs, lens) => { |
||||
lens.iter().filter_map(|&len| { |
||||
if data.len() >= len && cs.contains(&data[..len]) { |
||||
Some(&data[len..]) |
||||
} else { |
||||
None |
||||
} |
||||
}).collect() |
||||
}, |
||||
} |
||||
} |
||||
|
||||
fn complex_match(&self, data: &str) -> bool { |
||||
self._complex_match(0, data).into_iter().any(str::is_empty) |
||||
} |
||||
} |
||||
|
||||
fn main() { |
||||
let input_grammar; |
||||
let data; |
||||
{ |
||||
let split_pos = INPUT.find("\n\n").unwrap(); |
||||
input_grammar = Grammar::parse(&INPUT[..split_pos]); |
||||
data = INPUT[split_pos+2..].lines().map(str::to_string).collect::<Vec<String>>(); |
||||
} |
||||
{ |
||||
let mut grammar = input_grammar.clone(); |
||||
grammar.optimize(); |
||||
println!("Lines matching simple grammar: {}", data.iter().filter(|line| grammar.simple_match(line)).count()); |
||||
} |
||||
{ |
||||
let mut grammar = input_grammar.clone(); |
||||
grammar.expansions.extend(vec![ |
||||
Rule::parse_line("8: 42 | 42 8"), |
||||
Rule::parse_line("11: 42 31 | 42 11 31"), |
||||
]); |
||||
grammar.optimize(); |
||||
/* |
||||
println!("{:?}", grammar.expansions.keys()); |
||||
for (rule_id, rule) in grammar.expansions { |
||||
match rule { |
||||
CachedRule::Alternatives(alt) => println!("{} -> {:?}", rule_id, alt), |
||||
CachedRule::CachedStrings(_, lens) => println!("{} -> string with lengths: {:?}", rule_id, lens), |
||||
} |
||||
} |
||||
*/ |
||||
println!("Lines matching complex grammar: {}", data.iter().filter(|line| grammar.complex_match(line)).count()); |
||||
} |
||||
} |
||||
use std::collections::{HashMap, HashSet}; |
||||
use std::sync::Arc; |
||||
|
||||
const INPUT: &str = include_str!("../../data/day19"); |
||||
|
||||
#[derive(Clone, Debug)] |
||||
enum Rule { |
||||
Char(char), |
||||
Chain(Vec<u32>), |
||||
} |
||||
|
||||
impl Rule { |
||||
fn parse_part(alt: &str) -> Self { |
||||
let alt = alt.trim(); |
||||
if let Some(alt) = alt.strip_prefix('"') { |
||||
let alt = alt.strip_suffix('"').unwrap(); |
||||
assert_eq!(alt.len(), 1); |
||||
Self::Char(alt.chars().next().unwrap()) |
||||
} else { |
||||
Self::Chain(alt.split_whitespace().map(|s| s.parse::<u32>().unwrap()).collect()) |
||||
} |
||||
} |
||||
|
||||
fn parse_line(line: &str) -> (u32, CachedRule) { |
||||
let colon = line.find(": ").unwrap(); |
||||
let id = line[..colon].parse::<u32>().unwrap(); |
||||
let alts = line[colon+2..].split(" | ").map(Self::parse_part).collect(); |
||||
(id, CachedRule::Alternatives(alts)) |
||||
} |
||||
} |
||||
|
||||
fn _make_combs(target: &mut HashSet<String>, current: &mut String, list: &[Arc<HashSet<String>>]) { |
||||
if list.is_empty() { |
||||
target.insert(current.clone()); |
||||
} else { |
||||
let old_len = current.len(); |
||||
for s in &*list[0] { |
||||
current.push_str(s); |
||||
_make_combs(target, current, &list[1..]); |
||||
current.truncate(old_len); |
||||
} |
||||
} |
||||
} |
||||
|
||||
#[derive(Clone, Debug)] |
||||
enum CachedRule { |
||||
Alternatives(Vec<Rule>), |
||||
CachedStrings(Arc<HashSet<String>>, HashSet<usize>), |
||||
} |
||||
|
||||
#[derive(Clone, Debug)] |
||||
struct Grammar { |
||||
expansions: HashMap<u32, CachedRule>, |
||||
} |
||||
|
||||
impl Grammar { |
||||
fn parse(rule_lines: &str) -> Self { |
||||
let expansions = rule_lines.lines().map(Rule::parse_line).collect(); |
||||
Self { expansions } |
||||
} |
||||
|
||||
fn _build_sets(&mut self, recursive_check: &mut HashSet<u32>, ndx: u32) -> Option<Arc<HashSet<String>>> { |
||||
let rules = self.expansions.get(&ndx).unwrap(); |
||||
let alts = match rules { |
||||
CachedRule::Alternatives(alts) => alts, |
||||
CachedRule::CachedStrings(cs, _) => return Some(cs.clone()), |
||||
}; |
||||
|
||||
if recursive_check.contains(&ndx) { |
||||
return None; |
||||
} |
||||
recursive_check.insert(ndx); |
||||
|
||||
let mut result = HashSet::<String>::new(); |
||||
|
||||
let mut found_recursion = false; |
||||
for rule in alts.clone() { |
||||
match rule { |
||||
Rule::Char(c) => { |
||||
result.insert(c.to_string()); |
||||
}, |
||||
Rule::Chain(parts) => { |
||||
let mut comb = Vec::new(); |
||||
for part_ndx in parts { |
||||
// abort if nested lookup loops
|
||||
if let Some(cs) = self._build_sets(recursive_check, part_ndx) { |
||||
comb.push(cs); |
||||
} else { |
||||
found_recursion = true; |
||||
} |
||||
} |
||||
if !found_recursion { |
||||
_make_combs(&mut result, &mut String::new(), &comb); |
||||
} |
||||
}, |
||||
} |
||||
} |
||||
if found_recursion { |
||||
return None; |
||||
} |
||||
let lengths: HashSet<usize> = result.iter().map(|s| s.len()).collect(); |
||||
let result = Arc::new(result); |
||||
recursive_check.remove(&ndx); |
||||
self.expansions.insert(ndx, CachedRule::CachedStrings(result.clone(), lengths)); |
||||
Some(result) |
||||
} |
||||
|
||||
fn optimize(&mut self) { |
||||
let mut recursive_check = HashSet::new(); |
||||
self._build_sets(&mut recursive_check, 0); |
||||
recursive_check.insert(0); |
||||
for id in recursive_check.clone() { |
||||
if let Some(CachedRule::Alternatives(alts)) = self.expansions.get(&id) { |
||||
for alt in alts { |
||||
if let Rule::Chain(chain) = alt { |
||||
recursive_check.extend(chain); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
let rule_ids: HashSet<u32> = self.expansions.keys().cloned().collect(); |
||||
for id in rule_ids.difference(&recursive_check) { |
||||
self.expansions.remove(id); |
||||
} |
||||
} |
||||
|
||||
fn simple_match(&self, data: &str) -> bool { |
||||
match self.expansions.get(&0) { |
||||
Some(CachedRule::CachedStrings(cs, _)) => cs.contains(data), |
||||
_ => panic!("not simple enough"), |
||||
} |
||||
} |
||||
|
||||
fn _complex_match_chain<'data>(&self, chain: &[u32], data: &'data str) -> Vec<&'data str> { |
||||
if chain.is_empty() { |
||||
return vec![data]; |
||||
} |
||||
self._complex_match(chain[0], data).into_iter().map(|rem| { |
||||
self._complex_match_chain(&chain[1..], rem) |
||||
}).flatten().collect() |
||||
} |
||||
|
||||
fn _complex_match<'data>(&self, start: u32, data: &'data str) -> Vec<&'data str> { |
||||
let cr = self.expansions.get(&start).unwrap(); |
||||
match cr { |
||||
CachedRule::Alternatives(alts) => { |
||||
alts.iter().map(|rule| { |
||||
let chain = match rule { |
||||
Rule::Chain(chain) => chain, |
||||
_ => panic!("only chains here"), |
||||
}; |
||||
self._complex_match_chain(&chain, data) |
||||
}).flatten().collect() |
||||
}, |
||||
CachedRule::CachedStrings(cs, lens) => { |
||||
lens.iter().filter_map(|&len| { |
||||
if data.len() >= len && cs.contains(&data[..len]) { |
||||
Some(&data[len..]) |
||||
} else { |
||||
None |
||||
} |
||||
}).collect() |
||||
}, |
||||
} |
||||
} |
||||
|
||||
fn complex_match(&self, data: &str) -> bool { |
||||
self._complex_match(0, data).into_iter().any(str::is_empty) |
||||
} |
||||
} |
||||
|
||||
fn main() { |
||||
let input_grammar; |
||||
let data; |
||||
{ |
||||
let split_pos = INPUT.find("\n\n").unwrap(); |
||||
input_grammar = Grammar::parse(&INPUT[..split_pos]); |
||||
data = INPUT[split_pos+2..].lines().map(str::to_string).collect::<Vec<String>>(); |
||||
} |
||||
{ |
||||
let mut grammar = input_grammar.clone(); |
||||
grammar.optimize(); |
||||
println!("Lines matching simple grammar: {}", data.iter().filter(|line| grammar.simple_match(line)).count()); |
||||
} |
||||
{ |
||||
let mut grammar = input_grammar.clone(); |
||||
grammar.expansions.extend(vec![ |
||||
Rule::parse_line("8: 42 | 42 8"), |
||||
Rule::parse_line("11: 42 31 | 42 11 31"), |
||||
]); |
||||
grammar.optimize(); |
||||
/* |
||||
println!("{:?}", grammar.expansions.keys()); |
||||
for (rule_id, rule) in grammar.expansions { |
||||
match rule { |
||||
CachedRule::Alternatives(alt) => println!("{} -> {:?}", rule_id, alt), |
||||
CachedRule::CachedStrings(_, lens) => println!("{} -> string with lengths: {:?}", rule_id, lens), |
||||
} |
||||
} |
||||
*/ |
||||
println!("Lines matching complex grammar: {}", data.iter().filter(|line| grammar.complex_match(line)).count()); |
||||
} |
||||
} |
||||
|
@ -1,466 +1,466 @@
|
||||
use std::collections::HashMap; |
||||
|
||||
const INPUT: &str = include_str!("../../data/day20"); |
||||
|
||||
#[derive(Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Hash, Debug)] |
||||
enum Side { |
||||
Top, |
||||
Right, |
||||
Bottom, |
||||
Left, |
||||
} |
||||
|
||||
impl Side { |
||||
fn opposite(self) -> Self { |
||||
match self { |
||||
Side::Top => Side::Bottom, |
||||
Side::Right => Side::Left, |
||||
Side::Bottom => Side::Top, |
||||
Side::Left => Side::Right, |
||||
} |
||||
} |
||||
|
||||
fn transform(self, width: usize, flip: bool, row: usize, col: usize) -> (usize, usize) { |
||||
let (row, col) = if flip { |
||||
(row, width - col - 1) |
||||
} else { |
||||
(row, col) |
||||
}; |
||||
let (row, col) = match self { |
||||
Side::Top => (row, col), |
||||
Side::Right => (col, width - row - 1), |
||||
Side::Bottom => (width - row - 1, width - col - 1), |
||||
Side::Left => (width - col - 1, row), |
||||
}; |
||||
(row, col) |
||||
} |
||||
} |
||||
|
||||
#[derive(Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Hash)] |
||||
struct Edge { |
||||
len: u32, |
||||
mask: u32, |
||||
} |
||||
|
||||
impl Edge { |
||||
fn new<I>(bits: I) -> Self |
||||
where |
||||
I: IntoIterator<Item=bool>, |
||||
{ |
||||
let (len, mask) = bits.into_iter().fold((0u32, 0u32), |(len, mask), bit| { |
||||
(len+1, (mask << 1) + if bit { 1 } else { 0 }) |
||||
}); |
||||
Edge { len, mask } |
||||
} |
||||
|
||||
fn norm_dir(self) -> Self { |
||||
let rev_mask = self.mask.reverse_bits() >> (32 - self.len); |
||||
if self.mask < rev_mask { |
||||