diff --git a/src/bin/day23.rs b/src/bin/day23.rs new file mode 100644 index 0000000..98ef396 --- /dev/null +++ b/src/bin/day23.rs @@ -0,0 +1,217 @@ +use std::mem::replace; + +const INPUT: &str = "315679824"; + +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 { + if self.0 == MARK_MISSING { + None + } else { + Some(self.0) + } + } +} + +impl From for OptionItem { + fn from(i: Item) -> Self { + assert_ne!(i, MARK_MISSING); + Self(i) + } +} + +impl From> for OptionItem { + fn from(i: Option) -> 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, +} + +impl Default for Cups { + fn default() -> Self { + Self { + current: OptionItem::NONE, + last: OptionItem::NONE, + next_cup: Vec::new(), + } + } +} + +impl Cups { + fn last(&self) -> Option { + 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(&mut self, mut after: Item, iter: I) + where + I: IntoIterator, + { + for item in iter { + self.insert_after(after, item); + after = item; + } + } + + fn extend(&mut self, iter: I) + where + I: IntoIterator, + { + 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 + '_ { + 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 + '_ { + 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 + '_ { + 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) -> impl Iterator + '_ { + 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::>(); + 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); + for _ in 0..100 { + cups.mix(); + } + cups.rotate_to_one(); + println!("Cups: {}", cups.iter().skip(1).map(|c| format!("{}", c)).collect::>().join("")); + + let mut cups = Cups::parse(INPUT); + 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::()); +}