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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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