1
0
Fork 0

convert line endings to unix

This commit is contained in:
Stefan Bühler 2020-12-26 10:38:29 +01:00
parent 5215f3bb5f
commit ca4fb1d3ad
9 changed files with 1616 additions and 1616 deletions

View File

@ -1,222 +1,222 @@
use std::ops::Range; use std::ops::Range;
const INPUT: &str = include_str!("../../data/day17"); const INPUT: &str = include_str!("../../data/day17");
#[derive(Clone, Copy, Default)] #[derive(Clone, Copy, Default)]
struct Dimension { struct Dimension {
offset: u32, offset: u32,
size: u32, size: u32,
} }
impl Dimension { impl Dimension {
fn map(&self, pos: i32) -> Option<usize> { fn map(&self, pos: i32) -> Option<usize> {
let p = (self.offset as i32) + pos; let p = (self.offset as i32) + pos;
if p >= 0 && p < (self.size as i32) { if p >= 0 && p < (self.size as i32) {
Some(p as usize) Some(p as usize)
} else { } else {
None None
} }
} }
fn include(&self, pos: i32) -> Self { fn include(&self, pos: i32) -> Self {
let left = std::cmp::min(pos, -(self.offset as i32)); let left = std::cmp::min(pos, -(self.offset as i32));
let right = std::cmp::max(pos, (self.size - self.offset - 1) as i32); let right = std::cmp::max(pos, (self.size - self.offset - 1) as i32);
Self { Self {
offset: (-left) as u32, offset: (-left) as u32,
size: (right - left + 1) as u32, size: (right - left + 1) as u32,
} }
} }
fn extend(&self) -> Self { fn extend(&self) -> Self {
Self { Self {
offset: self.offset + 1, offset: self.offset + 1,
size: self.size + 2, size: self.size + 2,
} }
} }
} }
impl IntoIterator for Dimension { impl IntoIterator for Dimension {
type IntoIter = Range<i32>; type IntoIter = Range<i32>;
type Item = i32; type Item = i32;
fn into_iter(self) -> Self::IntoIter { fn into_iter(self) -> Self::IntoIter {
-(self.offset as i32)..((self.size - self.offset) as i32) -(self.offset as i32)..((self.size - self.offset) as i32)
} }
} }
#[derive(Clone)] #[derive(Clone)]
struct Field { struct Field {
x_dim: Dimension, x_dim: Dimension,
y_dim: Dimension, y_dim: Dimension,
z_dim: Dimension, z_dim: Dimension,
w_dim: Dimension, w_dim: Dimension,
cells: Vec<bool>, cells: Vec<bool>,
} }
impl Default for Field { impl Default for Field {
fn default() -> Self { fn default() -> Self {
Self { Self {
x_dim: Dimension { offset: 0, size: 1}, x_dim: Dimension { offset: 0, size: 1},
y_dim: Dimension { offset: 0, size: 1}, y_dim: Dimension { offset: 0, size: 1},
z_dim: Dimension { offset: 0, size: 1}, z_dim: Dimension { offset: 0, size: 1},
w_dim: Dimension { offset: 0, size: 1}, w_dim: Dimension { offset: 0, size: 1},
cells: vec![false], cells: vec![false],
} }
} }
} }
impl Field { impl Field {
fn alloc(x_dim: Dimension, y_dim: Dimension, z_dim: Dimension, w_dim: Dimension) -> Self { fn alloc(x_dim: Dimension, y_dim: Dimension, z_dim: Dimension, w_dim: Dimension) -> Self {
let mut cells = Vec::new(); let mut cells = Vec::new();
let size = (x_dim.size * y_dim.size * z_dim.size * w_dim.size) as usize; let size = (x_dim.size * y_dim.size * z_dim.size * w_dim.size) as usize;
cells.resize(size, false); cells.resize(size, false);
Self { x_dim, y_dim, z_dim, w_dim, cells } Self { x_dim, y_dim, z_dim, w_dim, cells }
} }
fn _addr(&self, x: i32, y: i32, z: i32, w: i32) -> Option<usize> { fn _addr(&self, x: i32, y: i32, z: i32, w: i32) -> Option<usize> {
let x = self.x_dim.map(x)?; let x = self.x_dim.map(x)?;
let y = self.y_dim.map(y)?; let y = self.y_dim.map(y)?;
let z = self.z_dim.map(z)?; let z = self.z_dim.map(z)?;
let w = self.w_dim.map(w)?; 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; 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) Some(addr)
} }
fn _get(&self, x: i32, y: i32, z: i32, w: i32) -> Option<bool> { fn _get(&self, x: i32, y: i32, z: i32, w: i32) -> Option<bool> {
let addr = self._addr(x, y, z, w)?; let addr = self._addr(x, y, z, w)?;
Some(self.cells[addr]) Some(self.cells[addr])
} }
fn get(&self, x: i32, y: i32, z: i32, w: i32) -> bool { fn get(&self, x: i32, y: i32, z: i32, w: i32) -> bool {
self._get(x, y, z, w).unwrap_or(false) self._get(x, y, z, w).unwrap_or(false)
} }
fn set(&mut self, x: i32, y: i32, z: i32, w: i32, value: bool) { fn set(&mut self, x: i32, y: i32, z: i32, w: i32, value: bool) {
if let Some(addr) = self._addr(x, y, z, w) { if let Some(addr) = self._addr(x, y, z, w) {
self.cells[addr] = value; self.cells[addr] = value;
} else if value { } else if value {
// don't resize to set "false" // don't resize to set "false"
let x_dim = self.x_dim.include(x); let x_dim = self.x_dim.include(x);
let y_dim = self.y_dim.include(y); let y_dim = self.y_dim.include(y);
let z_dim = self.z_dim.include(z); let z_dim = self.z_dim.include(z);
let w_dim = self.w_dim.include(w); let w_dim = self.w_dim.include(w);
let mut new_field = Self::alloc(x_dim, y_dim, z_dim, w_dim); let mut new_field = Self::alloc(x_dim, y_dim, z_dim, w_dim);
for w in self.w_dim { for w in self.w_dim {
for z in self.z_dim { for z in self.z_dim {
for y in self.y_dim { for y in self.y_dim {
for x in self.x_dim { for x in self.x_dim {
let addr = new_field._addr(x, y, z, w).unwrap(); let addr = new_field._addr(x, y, z, w).unwrap();
new_field.cells[addr] = self.get(x, y, z, w); new_field.cells[addr] = self.get(x, y, z, w);
} }
} }
} }
} }
let addr = new_field._addr(x, y, z, w).unwrap(); let addr = new_field._addr(x, y, z, w).unwrap();
new_field.cells[addr] = value; new_field.cells[addr] = value;
*self = new_field; *self = new_field;
} }
} }
fn parse(data: &str) -> Self { fn parse(data: &str) -> Self {
let mut this = Self::default(); let mut this = Self::default();
for (y, line) in data.lines().enumerate() { for (y, line) in data.lines().enumerate() {
for (x, chr) in line.chars().enumerate() { for (x, chr) in line.chars().enumerate() {
this.set(x as i32, y as i32, 0, 0, chr == '#'); this.set(x as i32, y as i32, 0, 0, chr == '#');
} }
} }
this this
} }
fn count_active_neighbors(&self, x: i32, y: i32, z: i32, w: i32) -> u32 { fn count_active_neighbors(&self, x: i32, y: i32, z: i32, w: i32) -> u32 {
let mut count = 0; let mut count = 0;
for dw in -1..=1 { for dw in -1..=1 {
for dz in -1..=1 { for dz in -1..=1 {
for dy in -1..=1 { for dy in -1..=1 {
for dx in -1..=1 { for dx in -1..=1 {
if dw == 0 && dz == 0 && dy == 0 && dx == 0 { if dw == 0 && dz == 0 && dy == 0 && dx == 0 {
continue; continue;
} }
if self.get(x + dx, y + dy, z + dz, w + dw) { if self.get(x + dx, y + dy, z + dz, w + dw) {
count += 1; count += 1;
if count > 3 { return count; } // more than 3 is always bad if count > 3 { return count; } // more than 3 is always bad
} }
} }
} }
} }
} }
count count
} }
fn cycle_3dim(&self) -> Self { fn cycle_3dim(&self) -> Self {
let mut result = self.clone(); let mut result = self.clone();
let w = 0; let w = 0;
for z in self.z_dim.extend() { for z in self.z_dim.extend() {
for y in self.y_dim.extend() { for y in self.y_dim.extend() {
for x in self.x_dim.extend() { for x in self.x_dim.extend() {
let count = self.count_active_neighbors(x, y, z, w); let count = self.count_active_neighbors(x, y, z, w);
let active = count == 3 || (count == 2 && self.get(x, y, z, w)); let active = count == 3 || (count == 2 && self.get(x, y, z, w));
result.set(x, y, z, w, active); result.set(x, y, z, w, active);
} }
} }
} }
result result
} }
fn cycle_4dim(&self) -> Self { fn cycle_4dim(&self) -> Self {
let mut result = self.clone(); let mut result = self.clone();
for w in self.w_dim.extend() { for w in self.w_dim.extend() {
for z in self.z_dim.extend() { for z in self.z_dim.extend() {
for y in self.y_dim.extend() { for y in self.y_dim.extend() {
for x in self.x_dim.extend() { for x in self.x_dim.extend() {
let count = self.count_active_neighbors(x, y, z, w); let count = self.count_active_neighbors(x, y, z, w);
let active = count == 3 || (count == 2 && self.get(x, y, z, w)); let active = count == 3 || (count == 2 && self.get(x, y, z, w));
result.set(x, y, z, w, active); result.set(x, y, z, w, active);
} }
} }
} }
} }
result result
} }
} }
impl std::fmt::Debug for Field { impl std::fmt::Debug for Field {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
use std::fmt::Write; use std::fmt::Write;
for z in self.z_dim { for z in self.z_dim {
for w in self.w_dim { for w in self.w_dim {
writeln!(f, "z = {}, w = {}", z, w)?; writeln!(f, "z = {}, w = {}", z, w)?;
for y in self.y_dim { for y in self.y_dim {
for x in self.x_dim { for x in self.x_dim {
if x == 0 && y == 0 { if x == 0 && y == 0 {
f.write_char( f.write_char(
if self.get(x, y, z, w) { 'X' } else { '+' } if self.get(x, y, z, w) { 'X' } else { '+' }
)?; )?;
} else { } else {
f.write_char( f.write_char(
if self.get(x, y, z, w) { '#' } else { '.' } if self.get(x, y, z, w) { '#' } else { '.' }
)?; )?;
} }
} }
f.write_char('\n')?; f.write_char('\n')?;
} }
f.write_char('\n')?; f.write_char('\n')?;
} }
} }
Ok(()) Ok(())
} }
} }
fn main() { fn main() {
let input = Field::parse(INPUT); let input = Field::parse(INPUT);
let mut field = input.clone(); let mut field = input.clone();
for _ in 0..6 { for _ in 0..6 {
field = field.cycle_3dim(); field = field.cycle_3dim();
} }
println!("Active after 6 3-dim rounds: {}", field.cells.iter().filter(|&&c| c).count()); println!("Active after 6 3-dim rounds: {}", field.cells.iter().filter(|&&c| c).count());
let mut field = input.clone(); let mut field = input.clone();
for _ in 0..6 { for _ in 0..6 {
field = field.cycle_4dim(); field = field.cycle_4dim();
} }
println!("Active after 6 4-dim rounds: {}", field.cells.iter().filter(|&&c| c).count()); println!("Active after 6 4-dim rounds: {}", field.cells.iter().filter(|&&c| c).count());
println!("{:?}", input); println!("{:?}", input);
} }

View File

@ -1,169 +1,169 @@
const INPUT: &str = include_str!("../../data/day18"); const INPUT: &str = include_str!("../../data/day18");
#[derive(Clone, Copy, PartialEq, Eq, Debug)] #[derive(Clone, Copy, PartialEq, Eq, Debug)]
enum Op { enum Op {
Sum, Sum,
Mul, Mul,
} }
impl Op { impl Op {
fn apply<T>(self, a: T, b: T) -> T fn apply<T>(self, a: T, b: T) -> T
where where
T: std::ops::Add<T, Output=T> + std::ops::Mul<T, Output=T>, T: std::ops::Add<T, Output=T> + std::ops::Mul<T, Output=T>,
{ {
match self { match self {
Self::Sum => a + b, Self::Sum => a + b,
Self::Mul => a * b, Self::Mul => a * b,
} }
} }
} }
#[derive(Clone, Copy, PartialEq, Eq, Debug)] #[derive(Clone, Copy, PartialEq, Eq, Debug)]
enum Elem<T> { enum Elem<T> {
ParenLeft, ParenLeft,
Op(Op), Op(Op),
Item(T), Item(T),
} }
impl<T> Elem<T> { impl<T> Elem<T> {
fn expect_item(self) -> T { fn expect_item(self) -> T {
if let Elem::Item(item) = self { if let Elem::Item(item) = self {
item item
} else { } else {
panic!("Expected item"); panic!("Expected item");
} }
} }
fn expect_op(self) -> Op { fn expect_op(self) -> Op {
if let Elem::Op(op) = self { if let Elem::Op(op) = self {
op op
} else { } else {
panic!("Expected op"); panic!("Expected op");
} }
} }
fn expect_paren_left(self) { fn expect_paren_left(self) {
if let Elem::ParenLeft = self { if let Elem::ParenLeft = self {
() ()
} else { } else {
panic!("Expected '('"); panic!("Expected '('");
} }
} }
} }
#[derive(Debug)] #[derive(Debug)]
struct Stack<T> { struct Stack<T> {
elems: Vec<Elem<T>>, elems: Vec<Elem<T>>,
same_precedence: bool, same_precedence: bool,
} }
impl<T> Stack<T> impl<T> Stack<T>
where where
T: std::ops::Add<T, Output=T> + std::ops::Mul<T, Output=T> + std::fmt::Debug, T: std::ops::Add<T, Output=T> + std::ops::Mul<T, Output=T> + std::fmt::Debug,
{ {
fn new(same_precedence: bool) -> Self { fn new(same_precedence: bool) -> Self {
Self { Self {
elems: Vec::new(), elems: Vec::new(),
same_precedence, same_precedence,
} }
} }
fn push_item(&mut self, item: T) { fn push_item(&mut self, item: T) {
self.elems.push(Elem::Item(item)) self.elems.push(Elem::Item(item))
} }
fn push_op(&mut self, op: Op) { fn push_op(&mut self, op: Op) {
self.elems.push(Elem::Op(op)) self.elems.push(Elem::Op(op))
} }
fn push_paren_left(&mut self) { fn push_paren_left(&mut self) {
self.elems.push(Elem::ParenLeft) self.elems.push(Elem::ParenLeft)
} }
fn _peek_top(&self) -> &Elem<T> { fn _peek_top(&self) -> &Elem<T> {
&self.elems[self.elems.len() - 1] &self.elems[self.elems.len() - 1]
} }
fn _peek_below_top(&self) -> &Elem<T> { fn _peek_below_top(&self) -> &Elem<T> {
&self.elems[self.elems.len() - 2] &self.elems[self.elems.len() - 2]
} }
fn pop_item(&mut self) -> T { fn pop_item(&mut self) -> T {
self.elems.pop().expect("expect non empty stack").expect_item() self.elems.pop().expect("expect non empty stack").expect_item()
} }
fn pop_op(&mut self) -> Op { fn pop_op(&mut self) -> Op {
self.elems.pop().expect("expect non empty stack").expect_op() self.elems.pop().expect("expect non empty stack").expect_op()
} }
fn pop_paren_left(&mut self) { fn pop_paren_left(&mut self) {
self.elems.pop().expect("expect non empty stack").expect_paren_left() self.elems.pop().expect("expect non empty stack").expect_paren_left()
} }
fn _do_reduce_op(&self, finish: bool) -> bool { fn _do_reduce_op(&self, finish: bool) -> bool {
match self._peek_below_top() { match self._peek_below_top() {
Elem::Op(Op::Sum) => true, Elem::Op(Op::Sum) => true,
Elem::Op(Op::Mul) => finish || self.same_precedence, Elem::Op(Op::Mul) => finish || self.same_precedence,
_ => false, _ => false,
} }
} }
fn is_empty(&self) -> bool { fn is_empty(&self) -> bool {
self.elems.is_empty() self.elems.is_empty()
} }
fn eval_top(&mut self, finish: bool) { fn eval_top(&mut self, finish: bool) {
while self.elems.len() > 2 && matches!(self._peek_top(), Elem::Item(_)) && self._do_reduce_op(finish) { while self.elems.len() > 2 && matches!(self._peek_top(), Elem::Item(_)) && self._do_reduce_op(finish) {
let value2 = self.pop_item(); let value2 = self.pop_item();
let op = self.pop_op(); let op = self.pop_op();
let value1 = self.pop_item(); let value1 = self.pop_item();
self.push_item(op.apply(value1, value2)); self.push_item(op.apply(value1, value2));
} }
} }
} }
fn parse<T>(mut line: &str, same_precedence: bool) -> T fn parse<T>(mut line: &str, same_precedence: bool) -> T
where where
T: std::ops::Add<T, Output=T> + std::ops::Mul<T, Output=T> + std::str::FromStr + std::fmt::Debug, 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, <T as std::str::FromStr>::Err: std::fmt::Debug,
{ {
let mut stack = Stack::new(same_precedence); let mut stack = Stack::new(same_precedence);
loop { loop {
line = line.trim(); line = line.trim();
if line.is_empty() { if line.is_empty() {
break; break;
} }
if let Some(rem) = line.strip_prefix('+') { if let Some(rem) = line.strip_prefix('+') {
stack.push_op(Op::Sum); stack.push_op(Op::Sum);
line = rem; line = rem;
} else if let Some(rem) = line.strip_prefix('*') { } else if let Some(rem) = line.strip_prefix('*') {
stack.push_op(Op::Mul); stack.push_op(Op::Mul);
line = rem; line = rem;
} else if let Some(rem) = line.strip_prefix('(') { } else if let Some(rem) = line.strip_prefix('(') {
stack.push_paren_left(); stack.push_paren_left();
line = rem; line = rem;
} else if let Some(rem) = line.strip_prefix(')') { } else if let Some(rem) = line.strip_prefix(')') {
stack.eval_top(true); stack.eval_top(true);
let item = stack.pop_item(); let item = stack.pop_item();
stack.pop_paren_left(); stack.pop_paren_left();
stack.push_item(item); stack.push_item(item);
line = rem; line = rem;
} else { } else {
let num_end = line.find(|c: char| !c.is_ascii_digit()).unwrap_or(line.len()); let num_end = line.find(|c: char| !c.is_ascii_digit()).unwrap_or(line.len());
let num = line[..num_end].parse::<T>().unwrap(); let num = line[..num_end].parse::<T>().unwrap();
stack.push_item(num); stack.push_item(num);
line = &line[num_end..]; line = &line[num_end..];
} }
stack.eval_top(false); stack.eval_top(false);
} }
stack.eval_top(true); stack.eval_top(true);
let item = stack.pop_item(); let item = stack.pop_item();
assert!(stack.is_empty()); assert!(stack.is_empty());
item item
} }
fn main() { fn main() {
println!("Sum of evaluated lines (simple math): {}", INPUT.lines().map(|l| parse::<u64>(l, true)).sum::<u64>()); 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>()); println!("Sum of evaluated lines (advanced math): {}", INPUT.lines().map(|l| parse::<u64>(l, false)).sum::<u64>());
} }

View File

@ -1,203 +1,203 @@
use std::collections::{HashMap, HashSet}; use std::collections::{HashMap, HashSet};
use std::sync::Arc; use std::sync::Arc;
const INPUT: &str = include_str!("../../data/day19"); const INPUT: &str = include_str!("../../data/day19");
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
enum Rule { enum Rule {
Char(char), Char(char),
Chain(Vec<u32>), Chain(Vec<u32>),
} }
impl Rule { impl Rule {
fn parse_part(alt: &str) -> Self { fn parse_part(alt: &str) -> Self {
let alt = alt.trim(); let alt = alt.trim();
if let Some(alt) = alt.strip_prefix('"') { if let Some(alt) = alt.strip_prefix('"') {
let alt = alt.strip_suffix('"').unwrap(); let alt = alt.strip_suffix('"').unwrap();
assert_eq!(alt.len(), 1); assert_eq!(alt.len(), 1);
Self::Char(alt.chars().next().unwrap()) Self::Char(alt.chars().next().unwrap())
} else { } else {
Self::Chain(alt.split_whitespace().map(|s| s.parse::<u32>().unwrap()).collect()) Self::Chain(alt.split_whitespace().map(|s| s.parse::<u32>().unwrap()).collect())
} }
} }
fn parse_line(line: &str) -> (u32, CachedRule) { fn parse_line(line: &str) -> (u32, CachedRule) {
let colon = line.find(": ").unwrap(); let colon = line.find(": ").unwrap();
let id = line[..colon].parse::<u32>().unwrap(); let id = line[..colon].parse::<u32>().unwrap();
let alts = line[colon+2..].split(" | ").map(Self::parse_part).collect(); let alts = line[colon+2..].split(" | ").map(Self::parse_part).collect();
(id, CachedRule::Alternatives(alts)) (id, CachedRule::Alternatives(alts))
} }
} }
fn _make_combs(target: &mut HashSet<String>, current: &mut String, list: &[Arc<HashSet<String>>]) { fn _make_combs(target: &mut HashSet<String>, current: &mut String, list: &[Arc<HashSet<String>>]) {
if list.is_empty() { if list.is_empty() {
target.insert(current.clone()); target.insert(current.clone());
} else { } else {
let old_len = current.len(); let old_len = current.len();
for s in &*list[0] { for s in &*list[0] {
current.push_str(s); current.push_str(s);
_make_combs(target, current, &list[1..]); _make_combs(target, current, &list[1..]);
current.truncate(old_len); current.truncate(old_len);
} }
} }
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
enum CachedRule { enum CachedRule {
Alternatives(Vec<Rule>), Alternatives(Vec<Rule>),
CachedStrings(Arc<HashSet<String>>, HashSet<usize>), CachedStrings(Arc<HashSet<String>>, HashSet<usize>),
} }
#[derive(Clone, Debug)] #[derive(Clone, Debug)]
struct Grammar { struct Grammar {
expansions: HashMap<u32, CachedRule>, expansions: HashMap<u32, CachedRule>,
} }
impl Grammar { impl Grammar {
fn parse(rule_lines: &str) -> Self { fn parse(rule_lines: &str) -> Self {
let expansions = rule_lines.lines().map(Rule::parse_line).collect(); let expansions = rule_lines.lines().map(Rule::parse_line).collect();
Self { expansions } Self { expansions }
} }
fn _build_sets(&mut self, recursive_check: &mut HashSet<u32>, ndx: u32) -> Option<Arc<HashSet<String>>> { fn _build_sets(&mut self, recursive_check: &mut HashSet<u32>, ndx: u32) -> Option<Arc<HashSet<String>>> {
let rules = self.expansions.get(&ndx).unwrap(); let rules = self.expansions.get(&ndx).unwrap();
let alts = match rules { let alts = match rules {
CachedRule::Alternatives(alts) => alts, CachedRule::Alternatives(alts) => alts,
CachedRule::CachedStrings(cs, _) => return Some(cs.clone()), CachedRule::CachedStrings(cs, _) => return Some(cs.clone()),
}; };
if recursive_check.contains(&ndx) { if recursive_check.contains(&ndx) {
return None; return None;
} }
recursive_check.insert(ndx); recursive_check.insert(ndx);
let mut result = HashSet::<String>::new(); let mut result = HashSet::<String>::new();
let mut found_recursion = false; let mut found_recursion = false;
for rule in alts.clone() { for rule in alts.clone() {
match rule { match rule {
Rule::Char(c) => { Rule::Char(c) => {
result.insert(c.to_string()); result.insert(c.to_string());
}, },
Rule::Chain(parts) => { Rule::Chain(parts) => {
let mut comb = Vec::new(); let mut comb = Vec::new();
for part_ndx in parts { for part_ndx in parts {
// abort if nested lookup loops // abort if nested lookup loops
if let Some(cs) = self._build_sets(recursive_check, part_ndx) { if let Some(cs) = self._build_sets(recursive_check, part_ndx) {
comb.push(cs); comb.push(cs);
} else { } else {
found_recursion = true; found_recursion = true;
} }
} }
if !found_recursion { if !found_recursion {
_make_combs(&mut result, &mut String::new(), &comb); _make_combs(&mut result, &mut String::new(), &comb);
} }
}, },
} }
} }
if found_recursion { if found_recursion {
return None; return None;
} }
let lengths: HashSet<usize> = result.iter().map(|s| s.len()).collect(); let lengths: HashSet<usize> = result.iter().map(|s| s.len()).collect();
let result = Arc::new(result); let result = Arc::new(result);
recursive_check.remove(&ndx); recursive_check.remove(&ndx);
self.expansions.insert(ndx, CachedRule::CachedStrings(result.clone(), lengths)); self.expansions.insert(ndx, CachedRule::CachedStrings(result.clone(), lengths));
Some(result) Some(result)
} }
fn optimize(&mut self) { fn optimize(&mut self) {
let mut recursive_check = HashSet::new(); let mut recursive_check = HashSet::new();
self._build_sets(&mut recursive_check, 0); self._build_sets(&mut recursive_check, 0);
recursive_check.insert(0); recursive_check.insert(0);
for id in recursive_check.clone() { for id in recursive_check.clone() {
if let Some(CachedRule::Alternatives(alts)) = self.expansions.get(&id) { if let Some(CachedRule::Alternatives(alts)) = self.expansions.get(&id) {
for alt in alts { for alt in alts {
if let Rule::Chain(chain) = alt { if let Rule::Chain(chain) = alt {
recursive_check.extend(chain); recursive_check.extend(chain);
} }
} }
} }
} }
let rule_ids: HashSet<u32> = self.expansions.keys().cloned().collect(); let rule_ids: HashSet<u32> = self.expansions.keys().cloned().collect();
for id in rule_ids.difference(&recursive_check) { for id in rule_ids.difference(&recursive_check) {
self.expansions.remove(id); self.expansions.remove(id);
} }
} }
fn simple_match(&self, data: &str) -> bool { fn simple_match(&self, data: &str) -> bool {
match self.expansions.get(&0) { match self.expansions.get(&0) {
Some(CachedRule::CachedStrings(cs, _)) => cs.contains(data), Some(CachedRule::CachedStrings(cs, _)) => cs.contains(data),
_ => panic!("not simple enough"), _ => panic!("not simple enough"),
} }
} }
fn _complex_match_chain<'data>(&self, chain: &[u32], data: &'data str) -> Vec<&'data str> { fn _complex_match_chain<'data>(&self, chain: &[u32], data: &'data str) -> Vec<&'data str> {
if chain.is_empty() { if chain.is_empty() {
return vec![data]; return vec![data];
} }
self._complex_match(chain[0], data).into_iter().map(|rem| { self._complex_match(chain[0], data).into_iter().map(|rem| {
self._complex_match_chain(&chain[1..], rem) self._complex_match_chain(&chain[1..], rem)
}).flatten().collect() }).flatten().collect()
} }
fn _complex_match<'data>(&self, start: u32, data: &'data str) -> Vec<&'data str> { fn _complex_match<'data>(&self, start: u32, data: &'data str) -> Vec<&'data str> {
let cr = self.expansions.get(&start).unwrap(); let cr = self.expansions.get(&start).unwrap();
match cr { match cr {
CachedRule::Alternatives(alts) => { CachedRule::Alternatives(alts) => {
alts.iter().map(|rule| { alts.iter().map(|rule| {
let chain = match rule { let chain = match rule {
Rule::Chain(chain) => chain, Rule::Chain(chain) => chain,
_ => panic!("only chains here"), _ => panic!("only chains here"),
}; };
self._complex_match_chain(&chain, data) self._complex_match_chain(&chain, data)
}).flatten().collect() }).flatten().collect()
}, },
CachedRule::CachedStrings(cs, lens) => { CachedRule::CachedStrings(cs, lens) => {
lens.iter().filter_map(|&len| { lens.iter().filter_map(|&len| {
if data.len() >= len && cs.contains(&data[..len]) { if data.len() >= len && cs.contains(&data[..len]) {
Some(&data[len..]) Some(&data[len..])
} else { } else {
None None
} }
}).collect() }).collect()
}, },
} }
} }
fn complex_match(&self, data: &str) -> bool { fn complex_match(&self, data: &str) -> bool {
self._complex_match(0, data).into_iter().any(str::is_empty) self._complex_match(0, data).into_iter().any(str::is_empty)
} }
} }
fn main() { fn main() {
let input_grammar; let input_grammar;
let data; let data;
{ {
let split_pos = INPUT.find("\n\n").unwrap(); let split_pos = INPUT.find("\n\n").unwrap();
input_grammar = Grammar::parse(&INPUT[..split_pos]); input_grammar = Grammar::parse(&INPUT[..split_pos]);
data = INPUT[split_pos+2..].lines().map(str::to_string).collect::<Vec<String>>(); data = INPUT[split_pos+2..].lines().map(str::to_string).collect::<Vec<String>>();
} }
{ {
let mut grammar = input_grammar.clone(); let mut grammar = input_grammar.clone();
grammar.optimize(); grammar.optimize();
println!("Lines matching simple grammar: {}", data.iter().filter(|line| grammar.simple_match(line)).count()); println!("Lines matching simple grammar: {}", data.iter().filter(|line| grammar.simple_match(line)).count());
} }
{ {
let mut grammar = input_grammar.clone(); let mut grammar = input_grammar.clone();
grammar.expansions.extend(vec![ grammar.expansions.extend(vec![
Rule::parse_line("8: 42 | 42 8"), Rule::parse_line("8: 42 | 42 8"),
Rule::parse_line("11: 42 31 | 42 11 31"), Rule::parse_line("11: 42 31 | 42 11 31"),
]); ]);
grammar.optimize(); grammar.optimize();
/* /*
println!("{:?}", grammar.expansions.keys()); println!("{:?}", grammar.expansions.keys());
for (rule_id, rule) in grammar.expansions { for (rule_id, rule) in grammar.expansions {
match rule { match rule {
CachedRule::Alternatives(alt) => println!("{} -> {:?}", rule_id, alt), CachedRule::Alternatives(alt) => println!("{} -> {:?}", rule_id, alt),
CachedRule::CachedStrings(_, lens) => println!("{} -> string with lengths: {:?}", rule_id, lens), CachedRule::CachedStrings(_, lens) => println!("{} -> string with lengths: {:?}", rule_id, lens),
} }
} }
*/ */
println!("Lines matching complex grammar: {}", data.iter().filter(|line| grammar.complex_match(line)).count()); println!("Lines matching complex grammar: {}", data.iter().filter(|line| grammar.complex_match(line)).count());
} }
} }

View File

@ -1,466 +1,466 @@
use std::collections::HashMap; use std::collections::HashMap;
const INPUT: &str = include_str!("../../data/day20"); const INPUT: &str = include_str!("../../data/day20");
#[derive(Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Hash, Debug)] #[derive(Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Hash, Debug)]
enum Side { enum Side {
Top, Top,
Right, Right,
Bottom, Bottom,
Left, Left,
} }
impl Side { impl Side {
fn opposite(self) -> Self { fn opposite(self) -> Self {
match self { match self {
Side::Top => Side::Bottom, Side::Top => Side::Bottom,
Side::Right => Side::Left, Side::Right => Side::Left,
Side::Bottom => Side::Top, Side::Bottom => Side::Top,
Side::Left => Side::Right, Side::Left => Side::Right,
} }
} }
fn transform(self, width: usize, flip: bool, row: usize, col: usize) -> (usize, usize) { fn transform(self, width: usize, flip: bool, row: usize, col: usize) -> (usize, usize) {
let (row, col) = if flip { let (row, col) = if flip {
(row, width - col - 1) (row, width - col - 1)
} else { } else {
(row, col) (row, col)
}; };
let (row, col) = match self { let (row, col) = match self {
Side::Top => (row, col), Side::Top => (row, col),
Side::Right => (col, width - row - 1), Side::Right => (col, width - row - 1),
Side::Bottom => (width - row - 1, width - col - 1), Side::Bottom => (width - row - 1, width - col - 1),
Side::Left => (width - col - 1, row), Side::Left => (width - col - 1, row),
}; };
(row, col) (row, col)
} }
} }
#[derive(Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Hash)] #[derive(Clone, Copy, PartialOrd, Ord, PartialEq, Eq, Hash)]
struct Edge { struct Edge {
len: u32, len: u32,
mask: u32, mask: u32,
} }
impl Edge { impl Edge {
fn new<I>(bits: I) -> Self fn new<I>(bits: I) -> Self
where where
I: IntoIterator<Item=bool>, I: IntoIterator<Item=bool>,
{ {
let (len, mask) = bits.into_iter().fold((0u32, 0u32), |(len, mask), bit| { let (len, mask) = bits.into_iter().fold((0u32, 0u32), |(len, mask), bit| {
(len+1, (mask << 1) + if bit { 1 } else { 0 }) (len+1, (mask << 1) + if bit { 1 } else { 0 })
}); });
Edge { len, mask } Edge { len, mask }
} }
fn norm_dir(self) -> Self { fn norm_dir(self) -> Self {
let rev_mask = self.mask.reverse_bits() >> (32 - self.len); let rev_mask = self.mask.reverse_bits() >> (32 - self.len);
if self.mask < rev_mask { if self.mask < rev_mask {
Edge { len: self.len, mask: self.mask } Edge { len: self.len, mask: self.mask }
} else { } else {
Edge { len: self.len, mask: rev_mask } Edge { len: self.len, mask: rev_mask }
} }
} }
} }
impl std::fmt::Debug for Edge { impl std::fmt::Debug for Edge {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
use std::fmt::Write; use std::fmt::Write;
f.write_char('"')?; f.write_char('"')?;
for ndx in 0..self.len { for ndx in 0..self.len {
let v = 0 != self.mask & (1 << ndx); let v = 0 != self.mask & (1 << ndx);
f.write_char(if v { '#' } else { '.' })?; f.write_char(if v { '#' } else { '.' })?;
} }
f.write_char('"') f.write_char('"')
} }
} }
#[derive(Clone, Copy)] #[derive(Clone, Copy)]
struct Mask { struct Mask {
lines: [[bool; 20]; 3], lines: [[bool; 20]; 3],
} }
impl Mask { impl Mask {
const EMPTY: Mask = Mask { lines: [[false; 20]; 3] }; const EMPTY: Mask = Mask { lines: [[false; 20]; 3] };
const DEFAULT: Mask = { const DEFAULT: Mask = {
let mut lines = Self::EMPTY.lines; let mut lines = Self::EMPTY.lines;
lines[0][18] = true; lines[0][18] = true;
lines[1][0] = true; lines[1][0] = true;
lines[1][5] = true; lines[1][5] = true;
lines[1][6] = true; lines[1][6] = true;
lines[1][11] = true; lines[1][11] = true;
lines[1][12] = true; lines[1][12] = true;
lines[1][17] = true; lines[1][17] = true;
lines[1][18] = true; lines[1][18] = true;
lines[1][19] = true; lines[1][19] = true;
lines[2][1] = true; lines[2][1] = true;
lines[2][4] = true; lines[2][4] = true;
lines[2][7] = true; lines[2][7] = true;
lines[2][10] = true; lines[2][10] = true;
lines[2][13] = true; lines[2][13] = true;
lines[2][16] = true; lines[2][16] = true;
Self { lines } Self { lines }
}; };
fn _flip_y(self) -> Self { fn _flip_y(self) -> Self {
let [a,b,c] = self.lines; let [a,b,c] = self.lines;
Self { lines: [c, b, a] } Self { lines: [c, b, a] }
} }
fn _flip_x(self) -> Self { fn _flip_x(self) -> Self {
let [mut a, mut b, mut c] = self.lines; let [mut a, mut b, mut c] = self.lines;
a.reverse(); a.reverse();
b.reverse(); b.reverse();
c.reverse(); c.reverse();
Self { lines: [a, b, c] } Self { lines: [a, b, c] }
} }
fn all_masks() -> Vec<Mask> { fn all_masks() -> Vec<Mask> {
let def = Self::DEFAULT; let def = Self::DEFAULT;
let flip_x = def._flip_x(); let flip_x = def._flip_x();
vec![ vec![
def, def,
def._flip_y(), def._flip_y(),
flip_x, flip_x,
flip_x._flip_y(), flip_x._flip_y(),
] ]
} }
fn matches_horizontal(&self, tile: &Tile, row: usize, col: usize) -> bool { fn matches_horizontal(&self, tile: &Tile, row: usize, col: usize) -> bool {
if row + 3 > tile.width || col + 20 > tile.width { if row + 3 > tile.width || col + 20 > tile.width {
return false; return false;
} }
for y in 0..3 { for y in 0..3 {
for x in 0..20 { for x in 0..20 {
if self.lines[y][x] && !tile[(row+y, col+x)] { if self.lines[y][x] && !tile[(row+y, col+x)] {
return false; return false;
} }
} }
} }
true true
} }
fn mark_horizontal(&self, tile: &mut Tile, row: usize, col: usize) { fn mark_horizontal(&self, tile: &mut Tile, row: usize, col: usize) {
for y in 0..3 { for y in 0..3 {
for x in 0..20 { for x in 0..20 {
if self.lines[y][x] { if self.lines[y][x] {
tile[(row+y, col+x)] = true; tile[(row+y, col+x)] = true;
} }
} }
} }
} }
fn matches_vertical(&self, tile: &Tile, row: usize, col: usize) -> bool { fn matches_vertical(&self, tile: &Tile, row: usize, col: usize) -> bool {
if row + 20 > tile.width || col + 3 > tile.width { if row + 20 > tile.width || col + 3 > tile.width {
return false; return false;
} }
for y in 0..3 { for y in 0..3 {
for x in 0..20 { for x in 0..20 {
if self.lines[y][x] && !tile[(row+x, col+y)] { if self.lines[y][x] && !tile[(row+x, col+y)] {
return false; return false;
} }
} }
} }
true true
} }
fn mark_vertical(&self, tile: &mut Tile, row: usize, col: usize) { fn mark_vertical(&self, tile: &mut Tile, row: usize, col: usize) {
for y in 0..3 { for y in 0..3 {
for x in 0..20 { for x in 0..20 {
if self.lines[y][x] { if self.lines[y][x] {
tile[(row+x, col+y)] = true; tile[(row+x, col+y)] = true;
} }
} }
} }
} }
} }
impl std::fmt::Debug for Mask { impl std::fmt::Debug for Mask {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
use std::fmt::Write; use std::fmt::Write;
f.write_char('\n')?; f.write_char('\n')?;
for y in 0..3 { for y in 0..3 {
for x in 0..20 { for x in 0..20 {
f.write_char(if self.lines[y][x] { '#' } else { '.' })?; f.write_char(if self.lines[y][x] { '#' } else { '.' })?;
} }
f.write_char('\n')?; f.write_char('\n')?;
} }
Ok(()) Ok(())
} }
} }
#[derive(Clone, PartialEq, Eq, Hash)] #[derive(Clone, PartialEq, Eq, Hash)]
struct Tile { struct Tile {
width: usize, width: usize,
data: Vec<bool>, data: Vec<bool>,
} }
impl Tile { impl Tile {
fn parse(block: &str) -> (u32, Self) { fn parse(block: &str) -> (u32, Self) {
let head_split = block.find(":\n").unwrap(); let head_split = block.find(":\n").unwrap();
let id = block[..head_split].strip_prefix("Tile ").unwrap().parse::<u32>().unwrap(); let id = block[..head_split].strip_prefix("Tile ").unwrap().parse::<u32>().unwrap();
let block = &block[head_split+2..]; let block = &block[head_split+2..];
let first_line = block.lines().next().unwrap(); let first_line = block.lines().next().unwrap();
let width = first_line.len(); let width = first_line.len();
let data = block.lines() let data = block.lines()
.map(|l| { .map(|l| {
assert_eq!(l.len(), width); assert_eq!(l.len(), width);
l.chars().map(|c| c == '#') l.chars().map(|c| c == '#')
}) })
.flatten().collect::<Vec<_>>(); .flatten().collect::<Vec<_>>();
assert_eq!(data.len(), width * width); assert_eq!(data.len(), width * width);
(id, Self { width, data }) (id, Self { width, data })
} }
fn edge(&self, side: Side) -> Edge { fn edge(&self, side: Side) -> Edge {
match side { match side {
Side::Top => Edge::new(self.data[..self.width].iter().copied()), Side::Top => Edge::new(self.data[..self.width].iter().copied()),
Side::Bottom => Edge::new(self.data[self.width*(self.width-1)..].iter().copied()), Side::Bottom => Edge::new(self.data[self.width*(self.width-1)..].iter().copied()),
Side::Left => Edge::new((0..self.width).map(|n| self.data[n*self.width])), Side::Left => Edge::new((0..self.width).map(|n| self.data[n*self.width])),
Side::Right => Edge::new((0..self.width).map(|n| self.data[(n+1)*self.width-1])), Side::Right => Edge::new((0..self.width).map(|n| self.data[(n+1)*self.width-1])),
} }
} }
// rotate/flip given previous sides to top and left // rotate/flip given previous sides to top and left
fn rotate(&self, top: Side, left: Side) -> Self { fn rotate(&self, top: Side, left: Side) -> Self {
let flip = match (top, left) { let flip = match (top, left) {
(Side::Top, Side::Left) => false, (Side::Top, Side::Left) => false,
(Side::Top, Side::Right) => true, (Side::Top, Side::Right) => true,
(Side::Right, Side::Top) => false, (Side::Right, Side::Top) => false,
(Side::Right, Side::Bottom) => true, (Side::Right, Side::Bottom) => true,
(Side::Bottom, Side::Right) => false, (Side::Bottom, Side::Right) => false,
(Side::Bottom, Side::Left) => true, (Side::Bottom, Side::Left) => true,
(Side::Left, Side::Bottom) => false, (Side::Left, Side::Bottom) => false,
(Side::Left, Side::Top) => true, (Side::Left, Side::Top) => true,
_ => panic!("invalid rotation: {:?} -> Top and {:?} -> Left", top, left), _ => panic!("invalid rotation: {:?} -> Top and {:?} -> Left", top, left),
}; };
let data = (0..self.width).map(|row| { let data = (0..self.width).map(|row| {
(0..self.width).map(move |col| { (0..self.width).map(move |col| {
let (row, col) = top.transform(self.width, flip, row, col); let (row, col) = top.transform(self.width, flip, row, col);
self[(row, col)] self[(row, col)]
}) })
}).flatten().collect(); }).flatten().collect();
Self { width: self.width, data } Self { width: self.width, data }
} }
fn find_monster(&self) -> Tile { fn find_monster(&self) -> Tile {
let mut marked = Self { width: self.width, data: self.data.iter().map(|_| false).collect() }; let mut marked = Self { width: self.width, data: self.data.iter().map(|_| false).collect() };
let patterns = Mask::all_masks(); let patterns = Mask::all_masks();
for row in 0..self.width { for row in 0..self.width {
for col in 0..self.width { for col in 0..self.width {
for pattern in &patterns { for pattern in &patterns {
if pattern.matches_horizontal(self, row, col) { if pattern.matches_horizontal(self, row, col) {
pattern.mark_horizontal(&mut marked, row, col); pattern.mark_horizontal(&mut marked, row, col);
} }
if pattern.matches_vertical(self, row, col) { if pattern.matches_vertical(self, row, col) {
pattern.mark_vertical(&mut marked, row, col); pattern.mark_vertical(&mut marked, row, col);
} }
} }
} }
} }
marked marked
} }
} }
impl std::ops::Index<(usize, usize)> for Tile { impl std::ops::Index<(usize, usize)> for Tile {
type Output = bool; type Output = bool;
fn index(&self, (row, col): (usize, usize)) -> &Self::Output { fn index(&self, (row, col): (usize, usize)) -> &Self::Output {
assert!(col <= self.width); assert!(col <= self.width);
&self.data[row * self.width + col] &self.data[row * self.width + col]
} }
} }
impl std::ops::IndexMut<(usize, usize)> for Tile { impl std::ops::IndexMut<(usize, usize)> for Tile {
fn index_mut(&mut self, (row, col): (usize, usize)) -> &mut Self::Output { fn index_mut(&mut self, (row, col): (usize, usize)) -> &mut Self::Output {
assert!(col <= self.width); assert!(col <= self.width);
&mut self.data[row * self.width + col] &mut self.data[row * self.width + col]
} }
} }
impl std::fmt::Debug for Tile { impl std::fmt::Debug for Tile {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
use std::fmt::Write; use std::fmt::Write;
f.write_char('\n')?; f.write_char('\n')?;
for y in 0..self.width { for y in 0..self.width {
for x in 0..self.width { for x in 0..self.width {
f.write_char(if self[(y, x)] { '#' } else { '.' })?; f.write_char(if self[(y, x)] { '#' } else { '.' })?;
} }
f.write_char('\n')?; f.write_char('\n')?;
} }
Ok(()) Ok(())
} }
} }
#[derive(Clone, PartialEq, Eq, Debug)] #[derive(Clone, PartialEq, Eq, Debug)]
struct TileDetailed { struct TileDetailed {
tile: Tile, tile: Tile,
edge_to_side: HashMap<Edge, Side>, edge_to_side: HashMap<Edge, Side>,
side_to_edge: HashMap<Side, Edge>, side_to_edge: HashMap<Side, Edge>,
} }
impl TileDetailed { impl TileDetailed {
fn edge(&self, side: Side) -> Edge { fn edge(&self, side: Side) -> Edge {
*self.side_to_edge.get(&side).unwrap() *self.side_to_edge.get(&side).unwrap()
} }
fn parse(block: &str) -> (u32, Self) { fn parse(block: &str) -> (u32, Self) {
let (id, tile) = Tile::parse(block); let (id, tile) = Tile::parse(block);
let edges = vec![ let edges = vec![
(Side::Top, tile.edge(Side::Top).norm_dir()), (Side::Top, tile.edge(Side::Top).norm_dir()),
(Side::Right, tile.edge(Side::Right).norm_dir()), (Side::Right, tile.edge(Side::Right).norm_dir()),
(Side::Bottom, tile.edge(Side::Bottom).norm_dir()), (Side::Bottom, tile.edge(Side::Bottom).norm_dir()),
(Side::Left, tile.edge(Side::Left).norm_dir()), (Side::Left, tile.edge(Side::Left).norm_dir()),
]; ];
let edge_to_side = edges.iter().map(|&(side, edge)| (edge, side)).collect(); let edge_to_side = edges.iter().map(|&(side, edge)| (edge, side)).collect();
let side_to_edge = edges.into_iter().collect(); let side_to_edge = edges.into_iter().collect();
(id, Self { tile, edge_to_side, side_to_edge }) (id, Self { tile, edge_to_side, side_to_edge })
} }
} }
#[derive(Clone, Copy, Debug)] #[derive(Clone, Copy, Debug)]
struct TilePlacement { struct TilePlacement {
tile: u32, tile: u32,
top_side: Side, top_side: Side,
bottom_edge: Edge, bottom_edge: Edge,
left_side: Side, left_side: Side,
right_edge: Edge, right_edge: Edge,
} }
fn main() { fn main() {
let tiles: HashMap<u32, TileDetailed> = INPUT.trim().split("\n\n").map(TileDetailed::parse).collect(); let tiles: HashMap<u32, TileDetailed> = INPUT.trim().split("\n\n").map(TileDetailed::parse).collect();
let mut edges: HashMap<Edge, Vec<(u32, Side)>> = HashMap::new(); let mut edges: HashMap<Edge, Vec<(u32, Side)>> = HashMap::new();
for (&id, tile) in &tiles { for (&id, tile) in &tiles {
for (&side, &edge) in &tile.side_to_edge { for (&side, &edge) in &tile.side_to_edge {
edges.entry(edge).or_default().push((id, side)); edges.entry(edge).or_default().push((id, side));
} }
} }
let single_edges = edges.iter().filter_map(|(_edge, id_sides)| { let single_edges = edges.iter().filter_map(|(_edge, id_sides)| {
if id_sides.len() == 1 { if id_sides.len() == 1 {
Some(id_sides[0]) Some(id_sides[0])
} else { } else {
assert!(id_sides.len() == 2); assert!(id_sides.len() == 2);
None None
} }
}).collect::<Vec<_>>(); }).collect::<Vec<_>>();
let mut single_edges_per_tile = HashMap::<u32, Vec<Side>>::new(); let mut single_edges_per_tile = HashMap::<u32, Vec<Side>>::new();
for &(tile_id, side) in &single_edges { for &(tile_id, side) in &single_edges {
single_edges_per_tile.entry(tile_id).or_default().push(side); single_edges_per_tile.entry(tile_id).or_default().push(side);
} }
let corners = single_edges_per_tile.iter().filter_map(|(&tile_id, sides)| { let corners = single_edges_per_tile.iter().filter_map(|(&tile_id, sides)| {
if sides.len() == 2 { if sides.len() == 2 {
Some(tile_id) Some(tile_id)
} else { } else {
None None
} }
}).collect::<Vec<_>>(); }).collect::<Vec<_>>();
let corners_prod = corners.iter().map(|&tile_id| tile_id as u64).product::<u64>(); let corners_prod = corners.iter().map(|&tile_id| tile_id as u64).product::<u64>();
println!("Product of corner tile ids: {}", corners_prod); println!("Product of corner tile ids: {}", corners_prod);
let mut grid_tiles: [[Option<TilePlacement>; 12]; 12] = [[None; 12]; 12]; let mut grid_tiles: [[Option<TilePlacement>; 12]; 12] = [[None; 12]; 12];
for row in 0..12 { for row in 0..12 {
if row == 0 { if row == 0 {
// orient top left tile // orient top left tile
let tile: u32 = corners.iter().copied().min().unwrap(); let tile: u32 = corners.iter().copied().min().unwrap();
let top_side: Side = *single_edges_per_tile.get(&tile).unwrap().iter().min().unwrap(); let top_side: Side = *single_edges_per_tile.get(&tile).unwrap().iter().min().unwrap();
let bottom_edge = tiles.get(&tile).unwrap().edge(top_side.opposite()); let bottom_edge = tiles.get(&tile).unwrap().edge(top_side.opposite());
let left_side: Side = *single_edges_per_tile.get(&tile).unwrap().iter().max().unwrap(); let left_side: Side = *single_edges_per_tile.get(&tile).unwrap().iter().max().unwrap();
let right_edge = tiles.get(&tile).unwrap().edge(left_side.opposite()); let right_edge = tiles.get(&tile).unwrap().edge(left_side.opposite());
grid_tiles[0][0] = Some(TilePlacement { tile, top_side, bottom_edge, left_side, right_edge }); grid_tiles[0][0] = Some(TilePlacement { tile, top_side, bottom_edge, left_side, right_edge });
} else { } else {
// left column; look at tile above // left column; look at tile above
let tile_top = grid_tiles[row-1][0].unwrap().tile; let tile_top = grid_tiles[row-1][0].unwrap().tile;
let top_edge = grid_tiles[row-1][0].unwrap().bottom_edge; let top_edge = grid_tiles[row-1][0].unwrap().bottom_edge;
let (tile, top_side) = edges.get(&top_edge).unwrap().iter().filter_map(|&(tile_id, side)| { let (tile, top_side) = edges.get(&top_edge).unwrap().iter().filter_map(|&(tile_id, side)| {
if tile_id != tile_top { if tile_id != tile_top {
Some((tile_id, side)) Some((tile_id, side))
} else { } else {
None None
} }
}).next().unwrap(); }).next().unwrap();
let bottom_side = top_side.opposite(); let bottom_side = top_side.opposite();
let left_side = *single_edges_per_tile.get(&tile).unwrap().iter().filter(|&&side| side != bottom_side).next().unwrap(); let left_side = *single_edges_per_tile.get(&tile).unwrap().iter().filter(|&&side| side != bottom_side).next().unwrap();
assert_ne!(top_side, left_side); assert_ne!(top_side, left_side);
assert_ne!(top_side, left_side.opposite()); assert_ne!(top_side, left_side.opposite());
let bottom_edge = tiles.get(&tile).unwrap().edge(top_side.opposite()); let bottom_edge = tiles.get(&tile).unwrap().edge(top_side.opposite());
let right_edge = tiles.get(&tile).unwrap().edge(left_side.opposite()); let right_edge = tiles.get(&tile).unwrap().edge(left_side.opposite());
grid_tiles[row][0] = Some(TilePlacement { tile, top_side, bottom_edge, left_side, right_edge }); grid_tiles[row][0] = Some(TilePlacement { tile, top_side, bottom_edge, left_side, right_edge });
} }
// complete row // complete row
for col in 1..12 { for col in 1..12 {
// look at tile to the left // look at tile to the left
let tile_left = grid_tiles[row][col-1].unwrap().tile; let tile_left = grid_tiles[row][col-1].unwrap().tile;
let left_edge = grid_tiles[row][col-1].unwrap().right_edge; let left_edge = grid_tiles[row][col-1].unwrap().right_edge;
let (tile, left_side) = edges.get(&left_edge).unwrap().iter().filter_map(|&(tile_id, side)| { let (tile, left_side) = edges.get(&left_edge).unwrap().iter().filter_map(|&(tile_id, side)| {
if tile_id != tile_left { if tile_id != tile_left {
Some((tile_id, side)) Some((tile_id, side))
} else { } else {
None None
} }
}).next().unwrap(); }).next().unwrap();
let right_edge = tiles.get(&tile).unwrap().edge(left_side.opposite()); let right_edge = tiles.get(&tile).unwrap().edge(left_side.opposite());
let top_side; let top_side;
if row == 0 { if row == 0 {
if col == 11 { if col == 11 {
// top right corner // top right corner
let right_side = left_side.opposite(); let right_side = left_side.opposite();
top_side = *single_edges_per_tile.get(&tile).unwrap().iter().filter(|&&side| side != right_side).next().unwrap(); top_side = *single_edges_per_tile.get(&tile).unwrap().iter().filter(|&&side| side != right_side).next().unwrap();
} else { } else {
top_side = *single_edges_per_tile.get(&tile).unwrap().iter().next().unwrap(); top_side = *single_edges_per_tile.get(&tile).unwrap().iter().next().unwrap();
} }
} else { } else {
let tile_top = grid_tiles[row-1][col].unwrap().tile; let tile_top = grid_tiles[row-1][col].unwrap().tile;
let top_edge = grid_tiles[row-1][col].unwrap().bottom_edge; let top_edge = grid_tiles[row-1][col].unwrap().bottom_edge;
top_side = edges.get(&top_edge).unwrap().iter().filter_map(|&(tile_id, side)| { top_side = edges.get(&top_edge).unwrap().iter().filter_map(|&(tile_id, side)| {
if tile_id != tile_top { if tile_id != tile_top {
assert_eq!(tile, tile_id); assert_eq!(tile, tile_id);
Some(side) Some(side)
} else { } else {
None None
} }
}).next().unwrap(); }).next().unwrap();
} }
assert_ne!(left_side, top_side); assert_ne!(left_side, top_side);
assert_ne!(left_side, top_side.opposite()); assert_ne!(left_side, top_side.opposite());
let bottom_edge = tiles.get(&tile).unwrap().edge(top_side.opposite()); let bottom_edge = tiles.get(&tile).unwrap().edge(top_side.opposite());
grid_tiles[row][col] = Some(TilePlacement { tile, top_side, bottom_edge, left_side, right_edge }); grid_tiles[row][col] = Some(TilePlacement { tile, top_side, bottom_edge, left_side, right_edge });
} }
} }
let mut rotated_tiles = Vec::new(); let mut rotated_tiles = Vec::new();
let mut picture = Tile { width: 12 * 8, data: Vec::new() }; let mut picture = Tile { width: 12 * 8, data: Vec::new() };
picture.data.resize(picture.width * picture.width, false); picture.data.resize(picture.width * picture.width, false);
for row in 0..12 { for row in 0..12 {
rotated_tiles.push(Vec::new()); rotated_tiles.push(Vec::new());
for col in 0..12 { for col in 0..12 {
let tp = grid_tiles[row][col].unwrap(); let tp = grid_tiles[row][col].unwrap();
let tile = tiles.get(&tp.tile).unwrap().tile.rotate(tp.top_side, tp.left_side); let tile = tiles.get(&tp.tile).unwrap().tile.rotate(tp.top_side, tp.left_side);
rotated_tiles[row].push(tile.clone()); rotated_tiles[row].push(tile.clone());
assert_eq!(rotated_tiles[row][col], tile); assert_eq!(rotated_tiles[row][col], tile);
// check edges // check edges
if row > 0 { if row > 0 {
assert_eq!(rotated_tiles[row-1][col].edge(Side::Bottom), tile.edge(Side::Top)); assert_eq!(rotated_tiles[row-1][col].edge(Side::Bottom), tile.edge(Side::Top));
} }
if col > 0 { if col > 0 {
assert_eq!(rotated_tiles[row][col-1].edge(Side::Right), tile.edge(Side::Left)); assert_eq!(rotated_tiles[row][col-1].edge(Side::Right), tile.edge(Side::Left));
} }
// assemble full picture // assemble full picture
for y in 0..tile.width-2 { for y in 0..tile.width-2 {
for x in 0..tile.width-2 { for x in 0..tile.width-2 {
picture[(row*8+y, col*8+x)] = tile[(y+1, x+1)]; picture[(row*8+y, col*8+x)] = tile[(y+1, x+1)];
} }
} }
} }
} }
println!("{:?}", picture); println!("{:?}", picture);
let monsters = picture.find_monster(); let monsters = picture.find_monster();
println!("{:?}", monsters); println!("{:?}", monsters);
let sea_count = picture.data.iter().filter(|&&v| v).count(); let sea_count = picture.data.iter().filter(|&&v| v).count();
let monster_count = monsters.data.iter().filter(|&&v| v).count(); let monster_count = monsters.data.iter().filter(|&&v| v).count();
println!("Roughness: {}", sea_count - monster_count); println!("Roughness: {}", sea_count - monster_count);
} }

View File

@ -1,57 +1,57 @@
use std::collections::{BTreeMap, HashMap, HashSet}; use std::collections::{BTreeMap, HashMap, HashSet};
const INPUT: &str = include_str!("../../data/day21"); const INPUT: &str = include_str!("../../data/day21");
struct FoodDescription<'a> { struct FoodDescription<'a> {
ingredients: HashSet<&'a str>, ingredients: HashSet<&'a str>,
allergens: HashSet<&'a str>, allergens: HashSet<&'a str>,
} }
impl<'a> FoodDescription<'a> { impl<'a> FoodDescription<'a> {
fn parse(line: &'a str) -> Self { fn parse(line: &'a str) -> Self {
let pos = line.find(" (contains ").unwrap(); let pos = line.find(" (contains ").unwrap();
let ingredients = &line[..pos]; let ingredients = &line[..pos];
let allergens = line[pos..].strip_prefix(" (contains ").unwrap().strip_suffix(")").unwrap(); let allergens = line[pos..].strip_prefix(" (contains ").unwrap().strip_suffix(")").unwrap();
FoodDescription { FoodDescription {
ingredients: ingredients.split_whitespace().collect(), ingredients: ingredients.split_whitespace().collect(),
allergens: allergens.split(", ").collect(), allergens: allergens.split(", ").collect(),
} }
} }
} }
fn main() { fn main() {
let fds = INPUT.lines().map(FoodDescription::parse).collect::<Vec<_>>(); let fds = INPUT.lines().map(FoodDescription::parse).collect::<Vec<_>>();
let allergens: HashSet<&str> = fds.iter().map(|fd| fd.allergens.iter().cloned()).flatten().collect(); let allergens: HashSet<&str> = fds.iter().map(|fd| fd.allergens.iter().cloned()).flatten().collect();
let mut allergen_map_poss: HashMap<&str, HashSet<&str>> = allergens.iter().map(|&a| { let mut allergen_map_poss: HashMap<&str, HashSet<&str>> = allergens.iter().map(|&a| {
let mut is = fds.iter() let mut is = fds.iter()
.filter(|fd| fd.allergens.contains(a)) .filter(|fd| fd.allergens.contains(a))
.map(|fd| &fd.ingredients); .map(|fd| &fd.ingredients);
let first_ingr: HashSet<&str> = is.next().unwrap().clone(); let first_ingr: HashSet<&str> = is.next().unwrap().clone();
let poss = is.fold(first_ingr, |memo, ingr| { let poss = is.fold(first_ingr, |memo, ingr| {
memo.intersection(ingr).cloned().collect::<HashSet<_>>() memo.intersection(ingr).cloned().collect::<HashSet<_>>()
}); });
(a, poss) (a, poss)
}).collect(); }).collect();
let mut allergen_map: BTreeMap<&str, &str> = BTreeMap::new(); let mut allergen_map: BTreeMap<&str, &str> = BTreeMap::new();
while !allergen_map_poss.is_empty() { while !allergen_map_poss.is_empty() {
let (next_allg, next_ingr) = allergen_map_poss.iter().filter_map(|(&a, is)| { let (next_allg, next_ingr) = allergen_map_poss.iter().filter_map(|(&a, is)| {
if is.len() == 1 { if is.len() == 1 {
Some((a, *is.iter().next().unwrap())) Some((a, *is.iter().next().unwrap()))
} else { } else {
None None
} }
}).next().unwrap(); }).next().unwrap();
allergen_map.insert(next_allg, next_ingr); allergen_map.insert(next_allg, next_ingr);
allergen_map_poss.remove(next_allg); allergen_map_poss.remove(next_allg);
for ingr in allergen_map_poss.values_mut() { for ingr in allergen_map_poss.values_mut() {
ingr.remove(next_ingr); ingr.remove(next_ingr);
} }
} }
println!("Allergens contained by ingredients: {:?}", allergen_map); println!("Allergens contained by ingredients: {:?}", allergen_map);
let all_ingredients: HashSet<&str> = allergen_map.values().cloned().collect(); let all_ingredients: HashSet<&str> = allergen_map.values().cloned().collect();
println!( println!(
"Ingredients not allergens used {} times", "Ingredients not allergens used {} times",
fds.iter().map(|fd| fd.ingredients.difference(&all_ingredients).count()).sum::<usize>(), fds.iter().map(|fd| fd.ingredients.difference(&all_ingredients).count()).sum::<usize>(),
); );
println!("Dangerous ingredients: {}", allergen_map.values().cloned().collect::<Vec<_>>().join(",")); println!("Dangerous ingredients: {}", allergen_map.values().cloned().collect::<Vec<_>>().join(","));
} }

View File

@ -1,98 +1,98 @@
use std::collections::{VecDeque, HashSet}; use std::collections::{VecDeque, HashSet};
type Deck = VecDeque<u32>; type Deck = VecDeque<u32>;
const INPUT: &str = include_str!("../../data/day22"); const INPUT: &str = include_str!("../../data/day22");
fn parse_player(block: &str) -> (u32, Deck) { fn parse_player(block: &str) -> (u32, Deck) {
let block = block.strip_prefix("Player ").unwrap(); let block = block.strip_prefix("Player ").unwrap();
let player = block[..1].parse::<u32>().unwrap(); let player = block[..1].parse::<u32>().unwrap();
let block = block[1..].strip_prefix(":").unwrap().trim(); let block = block[1..].strip_prefix(":").unwrap().trim();
let deck = block.lines().map(|s| s.parse::<u32>().unwrap()).collect(); let deck = block.lines().map(|s| s.parse::<u32>().unwrap()).collect();
(player, deck) (player, deck)
} }
fn score(deck: &Deck) -> u64 { fn score(deck: &Deck) -> u64 {
deck.iter().rev().enumerate().map(|(pos, &value)| { deck.iter().rev().enumerate().map(|(pos, &value)| {
((pos + 1) as u64) * (value as u64) ((pos + 1) as u64) * (value as u64)
}).sum::<u64>() }).sum::<u64>()
} }
fn crab(mut deck1: Deck, mut deck2: Deck) { fn crab(mut deck1: Deck, mut deck2: Deck) {
while !deck1.is_empty() && !deck2.is_empty() { while !deck1.is_empty() && !deck2.is_empty() {
let c1 = deck1.pop_front().unwrap(); let c1 = deck1.pop_front().unwrap();
let c2 = deck2.pop_front().unwrap(); let c2 = deck2.pop_front().unwrap();
if c1 > c2 { if c1 > c2 {
deck1.push_back(c1); deck1.push_back(c1);
deck1.push_back(c2); deck1.push_back(c2);
} else { } else {
assert!(c2 > c1); assert!(c2 > c1);
deck2.push_back(c2); deck2.push_back(c2);
deck2.push_back(c1); deck2.push_back(c1);
} }
} }
if deck2.is_empty() { if deck2.is_empty() {
println!("Player 1 won: {:?}", deck1); println!("Player 1 won: {:?}", deck1);
println!("Score: {}", score(&deck1)); println!("Score: {}", score(&deck1));
} else { } else {
println!("Player 2 won: {:?}", deck2); println!("Player 2 won: {:?}", deck2);
println!("Score: {}", score(&deck2)); println!("Score: {}", score(&deck2));
} }
} }
fn _crab_rec(deck1: &mut Deck, deck2: &mut Deck) -> bool { fn _crab_rec(deck1: &mut Deck, deck2: &mut Deck) -> bool {
// println!("Rec Combat: {:?}, {:?}", deck1, deck2); // println!("Rec Combat: {:?}, {:?}", deck1, deck2);
let mut previous_rounds = HashSet::new(); let mut previous_rounds = HashSet::new();
while !deck1.is_empty() && !deck2.is_empty() { while !deck1.is_empty() && !deck2.is_empty() {
if !previous_rounds.insert((deck1.clone(), deck2.clone())) { if !previous_rounds.insert((deck1.clone(), deck2.clone())) {
// prevent infinite loop; player 1 wins // prevent infinite loop; player 1 wins
return true; return true;
} }
let c1 = deck1.pop_front().unwrap(); let c1 = deck1.pop_front().unwrap();
let c2 = deck2.pop_front().unwrap(); let c2 = deck2.pop_front().unwrap();
let p1_won = if deck1.len() >= c1 as usize && deck2.len() >= c2 as usize { let p1_won = if deck1.len() >= c1 as usize && deck2.len() >= c2 as usize {
let mut deck1: Deck = deck1.iter().copied().take(c1 as usize).collect(); let mut deck1: Deck = deck1.iter().copied().take(c1 as usize).collect();
let mut deck2: Deck = deck2.iter().copied().take(c2 as usize).collect(); let mut deck2: Deck = deck2.iter().copied().take(c2 as usize).collect();
assert_eq!(deck1.len(), c1 as usize); assert_eq!(deck1.len(), c1 as usize);
assert_eq!(deck2.len(), c2 as usize); assert_eq!(deck2.len(), c2 as usize);
// recurse // recurse
_crab_rec(&mut deck1, &mut deck2) _crab_rec(&mut deck1, &mut deck2)
} else { } else {
assert_ne!(c1, c2); assert_ne!(c1, c2);
c1 > c2 c1 > c2
}; };
if p1_won { if p1_won {
deck1.push_back(c1); deck1.push_back(c1);
deck1.push_back(c2); deck1.push_back(c2);
} else { } else {
deck2.push_back(c2); deck2.push_back(c2);
deck2.push_back(c1); deck2.push_back(c1);
} }
} }
deck2.is_empty() deck2.is_empty()
} }
fn crab_rec(mut deck1: Deck, mut deck2: Deck) { fn crab_rec(mut deck1: Deck, mut deck2: Deck) {
let p1_won = _crab_rec(&mut deck1, &mut deck2); let p1_won = _crab_rec(&mut deck1, &mut deck2);
if p1_won { if p1_won {
println!("Player 1 won recursive combat: {:?}", deck1); println!("Player 1 won recursive combat: {:?}", deck1);
println!("Score: {}", score(&deck1)); println!("Score: {}", score(&deck1));
} else { } else {
println!("Player 2 won recursive combat: {:?}", deck2); println!("Player 2 won recursive combat: {:?}", deck2);
println!("Score: {}", score(&deck2)); println!("Score: {}", score(&deck2));
} }
} }
fn main() { fn main() {
let (deck1, deck2) = { let (deck1, deck2) = {
let pos = INPUT.find("\n\n").unwrap(); let pos = INPUT.find("\n\n").unwrap();
let (id1, deck1) = parse_player(&INPUT[..pos]); let (id1, deck1) = parse_player(&INPUT[..pos]);
let (id2, deck2) = parse_player(&INPUT[pos+2..]); let (id2, deck2) = parse_player(&INPUT[pos+2..]);
assert_eq!(id1, 1); assert_eq!(id1, 1);
assert_eq!(id2, 2); assert_eq!(id2, 2);
(deck1, deck2) (deck1, deck2)
}; };
crab(deck1.clone(), deck2.clone()); crab(deck1.clone(), deck2.clone());
crab_rec(deck1, deck2); crab_rec(deck1, deck2);
} }

View File

@ -1,217 +1,217 @@
use std::mem::replace; use std::mem::replace;
const INPUT: &str = include_str!("../../data/day23"); const INPUT: &str = include_str!("../../data/day23");
type Item = usize; type Item = usize;
const MARK_MISSING: usize = !0; const MARK_MISSING: usize = !0;
#[derive(Clone, Copy, PartialEq, Eq, Debug)] #[derive(Clone, Copy, PartialEq, Eq, Debug)]
struct OptionItem(Item); struct OptionItem(Item);
impl OptionItem { impl OptionItem {
pub const NONE: Self = Self(MARK_MISSING); pub const NONE: Self = Self(MARK_MISSING);
fn item(self) -> Option<Item> { fn item(self) -> Option<Item> {
if self.0 == MARK_MISSING { if self.0 == MARK_MISSING {
None None
} else { } else {
Some(self.0) Some(self.0)
} }
} }
} }
impl From<Item> for OptionItem { impl From<Item> for OptionItem {
fn from(i: Item) -> Self { fn from(i: Item) -> Self {
assert_ne!(i, MARK_MISSING); assert_ne!(i, MARK_MISSING);
Self(i) Self(i)
} }
} }
impl From<Option<Item>> for OptionItem { impl From<Option<Item>> for OptionItem {
fn from(i: Option<Item>) -> Self { fn from(i: Option<Item>) -> Self {
match i { match i {
None => Self(MARK_MISSING), None => Self(MARK_MISSING),
Some(i) => Self::from(i), Some(i) => Self::from(i),
} }
} }
} }
#[derive(Clone, PartialEq, Eq, Debug)] #[derive(Clone, PartialEq, Eq, Debug)]
struct Cups { struct Cups {
current: OptionItem, current: OptionItem,
last: OptionItem, last: OptionItem,
next_cup: Vec<OptionItem>, next_cup: Vec<OptionItem>,
} }
impl Default for Cups { impl Default for Cups {
fn default() -> Self { fn default() -> Self {
Self { Self {
current: OptionItem::NONE, current: OptionItem::NONE,
last: OptionItem::NONE, last: OptionItem::NONE,
next_cup: Vec::new(), next_cup: Vec::new(),
} }
} }
} }
impl Cups { impl Cups {
fn last(&self) -> Option<Item> { fn last(&self) -> Option<Item> {
self.last.item() self.last.item()
} }
fn insert_after(&mut self, prev: Item, value: Item) { fn insert_after(&mut self, prev: Item, value: Item) {
let next = replace(&mut self.next_cup[prev], value.into()).item().unwrap(); let next = replace(&mut self.next_cup[prev], value.into()).item().unwrap();
if value >= self.next_cup.len() { if value >= self.next_cup.len() {
self.next_cup.resize(value, OptionItem::NONE); self.next_cup.resize(value, OptionItem::NONE);
self.next_cup.push(next.into()); self.next_cup.push(next.into());
assert_eq!(self.next_cup[value], next.into()); assert_eq!(self.next_cup[value], next.into());
} else { } else {
assert_eq!(self.next_cup[value], OptionItem::NONE); assert_eq!(self.next_cup[value], OptionItem::NONE);
self.next_cup[value] = next.into(); self.next_cup[value] = next.into();
} }
if self.last.item().unwrap() == prev { if self.last.item().unwrap() == prev {
self.last = value.into(); self.last = value.into();
} }
} }
fn extend_after<I>(&mut self, mut after: Item, iter: I) fn extend_after<I>(&mut self, mut after: Item, iter: I)
where where
I: IntoIterator<Item=Item>, I: IntoIterator<Item=Item>,
{ {
for item in iter { for item in iter {
self.insert_after(after, item); self.insert_after(after, item);
after = item; after = item;
} }
} }
fn extend<I>(&mut self, iter: I) fn extend<I>(&mut self, iter: I)
where where
I: IntoIterator<Item=Item>, I: IntoIterator<Item=Item>,
{ {
let mut iter = iter.into_iter(); let mut iter = iter.into_iter();
if let Some(first) = iter.next() { if let Some(first) = iter.next() {
if let Some(prev) = self.last() { if let Some(prev) = self.last() {
self.insert_after(prev, first); self.insert_after(prev, first);
} else { } else {
assert!(self.next_cup.is_empty()); assert!(self.next_cup.is_empty());
self.next_cup.resize(first, OptionItem::NONE); self.next_cup.resize(first, OptionItem::NONE);
self.next_cup.push(first.into()); self.next_cup.push(first.into());
self.current = first.into(); self.current = first.into();
self.last = first.into(); self.last = first.into();
} }
self.extend_after(first, iter); self.extend_after(first, iter);
} }
} }
fn iter_from(&self, start: Item) -> impl Iterator<Item=Item> + '_ { fn iter_from(&self, start: Item) -> impl Iterator<Item=Item> + '_ {
let mut current = start; let mut current = start;
let mut first = true; let mut first = true;
std::iter::from_fn(move || { std::iter::from_fn(move || {
if !first && current == start { if !first && current == start {
return None; return None;
} }
first = false; first = false;
let item = current; let item = current;
current = self.next_cup[item].item().unwrap(); current = self.next_cup[item].item().unwrap();
Some(item) Some(item)
}) })
} }
fn iter(&self) -> impl Iterator<Item=Item> + '_ { fn iter(&self) -> impl Iterator<Item=Item> + '_ {
self.iter_from(self.current.item().unwrap()) self.iter_from(self.current.item().unwrap())
} }
fn remove_after(&mut self, previous: Item) -> Item { fn remove_after(&mut self, previous: Item) -> Item {
let item = self.next_cup[previous].item().unwrap(); let item = self.next_cup[previous].item().unwrap();
let follow = self.next_cup[item].item().unwrap(); let follow = self.next_cup[item].item().unwrap();
self.next_cup[item] = OptionItem::NONE; self.next_cup[item] = OptionItem::NONE;
self.next_cup[previous] = follow.into(); self.next_cup[previous] = follow.into();
if item == self.current.item().unwrap() { if item == self.current.item().unwrap() {
if item == previous { if item == previous {
// last item // last item
self.current = OptionItem::NONE; self.current = OptionItem::NONE;
self.last = OptionItem::NONE; self.last = OptionItem::NONE;
self.next_cup.clear(); self.next_cup.clear();
} else { } else {
// if we removed the "current" item: move current pointer forward // if we removed the "current" item: move current pointer forward
self.current = follow.into(); self.current = follow.into();
} }
} else if item == self.last.item().unwrap() { } else if item == self.last.item().unwrap() {
// just removed the last item // just removed the last item
self.last = previous.into(); self.last = previous.into();
} }
item item
} }
fn drain_after(&mut self, previous: Item, mut len: usize) -> impl Iterator<Item=Item> + '_ { fn drain_after(&mut self, previous: Item, mut len: usize) -> impl Iterator<Item=Item> + '_ {
std::iter::from_fn(move || { std::iter::from_fn(move || {
if len == 0 { if len == 0 {
return None; return None;
} }
len -= 1; len -= 1;
Some(self.remove_after(previous)) Some(self.remove_after(previous))
}) })
} }
fn drain(&mut self, range: std::ops::Range<usize>) -> impl Iterator<Item=Item> + '_ { fn drain(&mut self, range: std::ops::Range<usize>) -> impl Iterator<Item=Item> + '_ {
let len = range.end - range.start; let len = range.end - range.start;
let after = if len == 0 { let after = if len == 0 {
0 // don't care 0 // don't care
} else if range.start > 0 { } else if range.start > 0 {
self.iter().skip(range.start - 1).next().unwrap() self.iter().skip(range.start - 1).next().unwrap()
} else { } else {
self.last().unwrap() self.last().unwrap()
}; };
self.drain_after(after, len) self.drain_after(after, len)
} }
fn parse(cups: &str) -> Self { fn parse(cups: &str) -> Self {
let mut result = Cups::default(); let mut result = Cups::default();
result.extend(cups.bytes().map(|c| (c - b'0') as Item)); result.extend(cups.bytes().map(|c| (c - b'0') as Item));
result result
} }
fn rotate_left(&mut self, items: usize) { fn rotate_left(&mut self, items: usize) {
let next = self.iter().skip(items).next().unwrap(); let next = self.iter().skip(items).next().unwrap();
self.current = next.into(); self.current = next.into();
} }
fn mix(&mut self) { fn mix(&mut self) {
let max_entry = self.next_cup.len() - 1; let max_entry = self.next_cup.len() - 1;
let pickup = self.drain(1..4).collect::<Vec<_>>(); let pickup = self.drain(1..4).collect::<Vec<_>>();
let mut insert_after = self.current.item().unwrap(); let mut insert_after = self.current.item().unwrap();
loop { loop {
insert_after = match insert_after.checked_sub(1) { insert_after = match insert_after.checked_sub(1) {
Some(n) => n, Some(n) => n,
None => max_entry, None => max_entry,
}; };
if self.next_cup[insert_after] != OptionItem::NONE { if self.next_cup[insert_after] != OptionItem::NONE {
break; break;
} }
} }
self.extend_after(insert_after, pickup); self.extend_after(insert_after, pickup);
self.rotate_left(1); self.rotate_left(1);
} }
fn rotate_to_one(&mut self) { fn rotate_to_one(&mut self) {
assert_ne!(self.next_cup[1], OptionItem::NONE); assert_ne!(self.next_cup[1], OptionItem::NONE);
self.current = 1.into(); self.current = 1.into();
} }
} }
fn main() { fn main() {
let mut cups = Cups::parse(INPUT.trim()); let mut cups = Cups::parse(INPUT.trim());
for _ in 0..100 { for _ in 0..100 {
cups.mix(); cups.mix();
} }
cups.rotate_to_one(); cups.rotate_to_one();
println!("Cups: {}", cups.iter().skip(1).map(|c| format!("{}", c)).collect::<Vec<_>>().join("")); println!("Cups: {}", cups.iter().skip(1).map(|c| format!("{}", c)).collect::<Vec<_>>().join(""));
let mut cups = Cups::parse(INPUT.trim()); let mut cups = Cups::parse(INPUT.trim());
cups.extend(10..=1000000); cups.extend(10..=1000000);
for _ in 0..10000000 { for _ in 0..10000000 {
cups.mix() cups.mix()
} }
cups.rotate_to_one(); cups.rotate_to_one();
println!("Cup product: {}", cups.drain(1..3).map(|c| c as u64).product::<u64>()); println!("Cup product: {}", cups.drain(1..3).map(|c| c as u64).product::<u64>());
} }

View File

@ -1,140 +1,140 @@
use std::collections::HashSet; use std::collections::HashSet;
const INPUT: &str = include_str!("../../data/day24"); const INPUT: &str = include_str!("../../data/day24");
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)] #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
enum Direction { enum Direction {
East, East,
SouthEast, SouthEast,
SouthWest, SouthWest,
West, West,
NorthWest, NorthWest,
NorthEast, NorthEast,
} }
impl Direction { impl Direction {
const ALL: [Direction; 6] = [ const ALL: [Direction; 6] = [
Self::East, Self::East,
Self::SouthEast, Self::SouthEast,
Self::SouthWest, Self::SouthWest,
Self::West, Self::West,
Self::NorthWest, Self::NorthWest,
Self::NorthEast, Self::NorthEast,
]; ];
fn symbol(self) -> &'static str { fn symbol(self) -> &'static str {
match self { match self {
Self::East => "e", Self::East => "e",
Self::SouthEast => "se", Self::SouthEast => "se",
Self::SouthWest => "sw", Self::SouthWest => "sw",
Self::West => "w", Self::West => "w",
Self::NorthWest => "nw", Self::NorthWest => "nw",
Self::NorthEast => "ne", Self::NorthEast => "ne",
} }
} }
fn parse_list(line: &str) -> Vec<Self> { fn parse_list(line: &str) -> Vec<Self> {
let mut line = line.trim(); let mut line = line.trim();
let mut result = Vec::new(); let mut result = Vec::new();
'outer: while !line.is_empty() { 'outer: while !line.is_empty() {
for &d in Self::ALL.iter() { for &d in Self::ALL.iter() {
if let Some(rem) = line.strip_prefix(d.symbol()) { if let Some(rem) = line.strip_prefix(d.symbol()) {
result.push(d); result.push(d);
line = rem; line = rem;
continue 'outer; continue 'outer;
} }
} }
panic!("Invalid directions: {:?}", line); panic!("Invalid directions: {:?}", line);
} }
result result
} }
// -> (row, col) // -> (row, col)
fn as_offset(self) -> (i32, i32) { fn as_offset(self) -> (i32, i32) {
match self { match self {
Self::East => (0, 1), Self::East => (0, 1),
Self::SouthEast => (1, 1), Self::SouthEast => (1, 1),
Self::SouthWest => (1, 0), Self::SouthWest => (1, 0),
Self::West => (0, -1), Self::West => (0, -1),
Self::NorthWest => (-1, -1), Self::NorthWest => (-1, -1),
Self::NorthEast => (-1, 0), Self::NorthEast => (-1, 0),
} }
} }
} }
#[derive(Clone, Copy, PartialEq, Eq, Hash, Default, Debug)] #[derive(Clone, Copy, PartialEq, Eq, Hash, Default, Debug)]
struct Position { struct Position {
row: i32, row: i32,
// the "0"-column goes from south-west to north-east through (0/0) // the "0"-column goes from south-west to north-east through (0/0)
col: i32, col: i32,
} }
impl Position { impl Position {
fn adjacent(self) -> impl Iterator<Item=Self> { fn adjacent(self) -> impl Iterator<Item=Self> {
Direction::ALL.iter().map(move |&d| self + d) Direction::ALL.iter().map(move |&d| self + d)
} }
} }
impl std::ops::Add<Direction> for Position { impl std::ops::Add<Direction> for Position {
type Output = Position; type Output = Position;
fn add(self, rhs: Direction) -> Self::Output { fn add(self, rhs: Direction) -> Self::Output {
let (row, col) = rhs.as_offset(); let (row, col) = rhs.as_offset();
Self { Self {
row: self.row + row, row: self.row + row,
col: self.col + col, col: self.col + col,
} }
} }
} }
fn daily_flip(black_tiles: HashSet<Position>) -> HashSet<Position> { fn daily_flip(black_tiles: HashSet<Position>) -> HashSet<Position> {
let mut new_black_tiles: HashSet<Position> = HashSet::new(); let mut new_black_tiles: HashSet<Position> = HashSet::new();
let mut dont_flip_to_black: HashSet<Position> = HashSet::new(); let mut dont_flip_to_black: HashSet<Position> = HashSet::new();
for &tile in &black_tiles { for &tile in &black_tiles {
let mut count_adj_black = 0; let mut count_adj_black = 0;
for adj_tile in tile.adjacent() { for adj_tile in tile.adjacent() {
if black_tiles.contains(&adj_tile) { if black_tiles.contains(&adj_tile) {
count_adj_black += 1; count_adj_black += 1;
} else if !new_black_tiles.contains(&adj_tile) && !dont_flip_to_black.contains(&adj_tile) { } else if !new_black_tiles.contains(&adj_tile) && !dont_flip_to_black.contains(&adj_tile) {
// white adjacent: maybe needs to be flipped to black // white adjacent: maybe needs to be flipped to black
let mut white_count_adj_black = 0; let mut white_count_adj_black = 0;
for adj_tile_2 in adj_tile.adjacent() { for adj_tile_2 in adj_tile.adjacent() {
if black_tiles.contains(&adj_tile_2) { if black_tiles.contains(&adj_tile_2) {
white_count_adj_black += 1; white_count_adj_black += 1;
if white_count_adj_black > 2 { break; } if white_count_adj_black > 2 { break; }
} }
} }
if white_count_adj_black == 2 { if white_count_adj_black == 2 {
new_black_tiles.insert(adj_tile); new_black_tiles.insert(adj_tile);
} else { } else {
// don't check again // don't check again
dont_flip_to_black.insert(adj_tile); dont_flip_to_black.insert(adj_tile);
} }
} }
} }
if count_adj_black == 1 || count_adj_black == 2 { if count_adj_black == 1 || count_adj_black == 2 {
new_black_tiles.insert(tile); new_black_tiles.insert(tile);
} }
} }
new_black_tiles new_black_tiles
} }
fn main() { fn main() {
let directions = INPUT.lines().map(Direction::parse_list).collect::<Vec<_>>(); let directions = INPUT.lines().map(Direction::parse_list).collect::<Vec<_>>();
let flip_tiles = directions.iter().map(|tile| { let flip_tiles = directions.iter().map(|tile| {
tile.iter().fold(Position::default(), |tile, &dir| tile + dir) tile.iter().fold(Position::default(), |tile, &dir| tile + dir)
}).collect::<Vec<_>>(); }).collect::<Vec<_>>();
let mut black_tiles = HashSet::new(); let mut black_tiles = HashSet::new();
for &tile in &flip_tiles { for &tile in &flip_tiles {
if black_tiles.contains(&tile) { if black_tiles.contains(&tile) {
black_tiles.remove(&tile); black_tiles.remove(&tile);
} else { } else {
black_tiles.insert(tile); black_tiles.insert(tile);
} }
} }
println!("Black tiles: {}", black_tiles.len()); println!("Black tiles: {}", black_tiles.len());
for _ in 0..100 { for _ in 0..100 {
black_tiles = daily_flip(black_tiles); black_tiles = daily_flip(black_tiles);
} }
println!("Black tiles: {}", black_tiles.len()); println!("Black tiles: {}", black_tiles.len());
} }

View File

@ -1,44 +1,44 @@
const INPUT: &str = include_str!("../../data/day25"); const INPUT: &str = include_str!("../../data/day25");
const MOD: u32 = 20201227; const MOD: u32 = 20201227;
// transform(subject, key) = (subject ** key) mod 20201227 // transform(subject, key) = (subject ** key) mod 20201227
// //
// public_key := transform(7, private_key) // public_key := transform(7, private_key)
// shared_secret := transform(peer_public_key, local_private_key) // shared_secret := transform(peer_public_key, local_private_key)
fn transform(subject: u32, key: u32) -> u32 { fn transform(subject: u32, key: u32) -> u32 {
let mut value = 1u64; let mut value = 1u64;
let subject = subject as u64; let subject = subject as u64;
for _ in 0..key { for _ in 0..key {
value = (value * subject) % (MOD as u64); value = (value * subject) % (MOD as u64);
} }
value as u32 value as u32
} }
fn find_key(subject: u32, transformed: u32) -> u32 { fn find_key(subject: u32, transformed: u32) -> u32 {
let mut value = 1u64; let mut value = 1u64;
let mut key = 0; let mut key = 0;
let subject = subject as u64; let subject = subject as u64;
let transformed = transformed as u64; let transformed = transformed as u64;
while value != transformed { while value != transformed {
value = (value * subject) % (MOD as u64); value = (value * subject) % (MOD as u64);
key += 1; key += 1;
} }
key key
} }
fn main() { fn main() {
let card_pub_key; let card_pub_key;
let door_pub_key; let door_pub_key;
{ {
let mut lines = INPUT.lines(); let mut lines = INPUT.lines();
card_pub_key = lines.next().unwrap().parse::<u32>().unwrap(); card_pub_key = lines.next().unwrap().parse::<u32>().unwrap();
door_pub_key = lines.next().unwrap().parse::<u32>().unwrap(); door_pub_key = lines.next().unwrap().parse::<u32>().unwrap();
assert!(lines.next().is_none()); assert!(lines.next().is_none());
} }
let card_priv_key = find_key(7, card_pub_key); let card_priv_key = find_key(7, card_pub_key);
println!("Card private key: {}", card_priv_key); println!("Card private key: {}", card_priv_key);
let shared_secret = transform(door_pub_key, card_priv_key); let shared_secret = transform(door_pub_key, card_priv_key);
println!("Shared secret: {}", shared_secret); println!("Shared secret: {}", shared_secret);
} }