Browse Source

convert line endings to unix

master
Stefan Bühler 11 months ago
parent
commit
ca4fb1d3ad
  1. 444
      src/bin/day17.rs
  2. 338
      src/bin/day18.rs
  3. 406
      src/bin/day19.rs
  4. 932
      src/bin/day20.rs
  5. 114
      src/bin/day21.rs
  6. 196
      src/bin/day22.rs
  7. 434
      src/bin/day23.rs
  8. 280
      src/bin/day24.rs
  9. 88
      src/bin/day25.rs

444
src/bin/day17.rs

@ -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);
}

338
src/bin/day18.rs

@ -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>());
}

406
src/bin/day19.rs

@ -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());
}
}

932
src/bin/day20.rs

@ -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 {
Edge { len: self.len, mask: self.mask }
} else {
Edge { len: self.len, mask: rev_mask }
}
}
}
impl std::fmt::Debug for Edge {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
use std::fmt::Write;
f.write_char('"')?;
for ndx in 0..self.len {
let v = 0 != self.mask & (1 << ndx);
f.write_char(if v { '#' } else { '.' })?;
}
f.write_char('"')
}
}
#[derive(Clone, Copy)]
struct Mask {
lines: [[bool; 20]; 3],
}
impl Mask {
const EMPTY: Mask = Mask { lines: [[false; 20]; 3] };
const DEFAULT: Mask = {
let mut lines = Self::EMPTY.lines;
lines[0][18] = true;
lines[1][0] = true;
lines[1][5] = true;
lines[1][6] = true;
lines[1][11] = true;
lines[1][12] = true;
lines[1][17] = true;
lines[1][18] = true;
lines[1][19] = true;
lines[2][1] = true;
lines[2][4] = true;
lines[2][7] = true;
lines[2][10] = true;
lines[2][13] = true;
lines[2][16] = true;
Self { lines }
};
fn _flip_y(self) -> Self {
let [a,b,c] = self.lines;
Self { lines: [c, b, a] }
}
fn _flip_x(self) -> Self {
let [mut a, mut b, mut c] = self.lines;
a.reverse();
b.reverse();
c.reverse();
Self { lines: [a, b, c] }
}
fn all_masks() -> Vec<Mask> {
let def = Self::DEFAULT;
let flip_x = def._flip_x();
vec![
def,
def._flip_y(),
flip_x,
flip_x._flip_y(),
]
}
fn matches_horizontal(&self, tile: &Tile, row: usize, col: usize) -> bool {
if row + 3 > tile.width || col + 20 > tile.width {
return false;
}
for y in 0..3 {
for x in 0..20 {
if self.lines[y][x] && !tile[(row+y, col+x)] {
return false;
}
}
}
true
}
fn mark_horizontal(&self, tile: &mut Tile, row: usize, col: usize) {
for y in 0..3 {
for x in 0..20 {
if self.lines[y][x] {
tile[(row+y, col+x)] = true;
}
}
}
}
fn matches_vertical(&self, tile: &Tile, row: usize, col: usize) -> bool {
if row + 20 > tile.width || col + 3 > tile.width {
return false;
}
for y in 0..3 {
for x in 0..20 {
if self.lines[y][x] && !tile[(row+x, col+y)] {
return false;
}
}
}
true
}
fn mark_vertical(&self, tile: &mut Tile, row: usize, col: usize) {
for y in 0..3 {
for x in 0..20 {
if self.lines[y][x] {
tile[(row+x, col+y)] = true;
}
}
}
}
}
impl std::fmt::Debug for Mask {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
use std::fmt::Write;
f.write_char('\n')?;
for y in 0..3 {
for x in 0..20 {
f.write_char(if self.lines[y][x] { '#' } else { '.' })?;
}
f.write_char('\n')?;
}
Ok(())
}
}
#[derive(Clone, PartialEq, Eq, Hash)]
struct Tile {
width: usize,
data: Vec<bool>,