// Let the record show that I hated every second of solving this puzzle. use std::fs; #[derive(Copy, Clone, Debug)] struct Bot { lo: Option, hi: Option, } impl Bot { fn new() -> Self { Self { lo: None, hi: None } } fn recv_chip(&mut self, val: usize) { if self.lo.is_none() && self.hi.is_none() { self.lo = Some(val); } else if self.lo.is_some() && self.hi.is_none() { let old_lo = self.lo.unwrap(); if old_lo < val { self.hi = Some(val); } else { self.lo = Some(val); self.hi = Some(old_lo); } } else if self.lo.is_none() && self.hi.is_some() { let old_hi = self.hi.unwrap(); if old_hi > val { self.lo = Some(val); } else { self.lo = Some(old_hi); self.hi = Some(val); } } else if self.lo.is_some() && self.hi.is_some() { // If both slots are occupied, we can't accept any more } } fn give_chip_lo(&mut self) -> Option { let result = self.lo; if result.is_some() { self.lo = None; } result } fn give_chip_hi(&mut self) -> Option { let result = self.hi; if result.is_some() { self.hi = None; } result } fn num_chips(&self) -> usize { let mut result = 0; if self.lo.is_some() { result += 1; } if self.hi.is_some() { result += 1; } result } } fn solve_puzzle(lines: &Vec<&str>, target: (usize, usize)) -> (usize, usize) { let mut bots: Vec = [Bot::new()].repeat(300); let mut bins: Vec> = [None].repeat(300); let mut part1 = None; let mut part2 = None; let mut ln = 0; // Loop 50k times until a self-consistent state is reached for _i in 0..50000 { let words: Vec<&str> = lines[ln].split_ascii_whitespace().collect(); // Take chip from input bin if words[0] == "value" { let to: usize = words[5].parse().unwrap(); bots[to].recv_chip(words[1].parse().unwrap()); } // Give chips to other bots or to output bins if words[0] == "bot" { let from: usize = words[1].parse().unwrap(); // Does the giver have two chips? If not, ignore this line (for now) if bots[from].lo.is_some() && bots[from].hi.is_some() { // Bot/bin indices to give chips to let lo_to: usize = words[6].parse().unwrap(); let hi_to: usize = words[11].parse().unwrap(); // Can the destination accept our chips? let lo_avail = (words[5] == "bot" && bots[lo_to].num_chips() < 2) || words[5] == "output"; let hi_avail = (words[10] == "bot" && bots[hi_to].num_chips() < 2) || words[10] == "output"; if lo_avail && hi_avail { // Give the low chip let lo = bots[from].give_chip_lo().unwrap(); if words[5] == "bot" { bots[lo_to].recv_chip(lo); } else if words[5] == "output" { bins[lo_to] = Some(lo); // overwrite } // Give the high chip let hi = bots[from].give_chip_hi().unwrap(); if words[10] == "bot" { bots[hi_to].recv_chip(hi); } else if words[10] == "output" { bins[hi_to] = Some(hi); // overwrite } } } } // Which bot ends up comparing the target-valued chips? for j in 0..bots.len() { if bots[j].lo.is_some() && bots[j].lo.unwrap() == target.0 { if bots[j].hi.is_some() && bots[j].hi.unwrap() == target.1 { part1 = Some(j); } } } // What is the product of the chips that end up in output bins 0, 1 and 2? if bins[0].is_some() && bins[1].is_some() && bins[2].is_some() { part2 = Some(bins[0].unwrap() * bins[1].unwrap() * bins[2].unwrap()); } ln = (ln + 1) % lines.len(); } (part1.unwrap(), part2.unwrap()) } fn main() { // Read instructions from input text file let input = fs::read_to_string("input.txt").unwrap(); let lines = input.lines().collect(); let (part1, part2) = solve_puzzle(&lines, (17, 61)); // Part 1 gives 101 for me println!("Part 1 solution: {}", part1); // Part 2 gives 37789 for me println!("Part 2 solution: {}", part2); } #[cfg(test)] mod tests { use super::*; #[test] fn part1_example1() { let lines = vec![ "value 5 goes to bot 2", "bot 2 gives low to bot 1 and high to bot 0", "value 3 goes to bot 1", "bot 1 gives low to output 1 and high to bot 0", "bot 0 gives low to output 2 and high to output 0", "value 2 goes to bot 2", ]; assert_eq!(solve_puzzle(&lines, (2, 5)), (2, 30)); } }