From 3b877bf4cc667eb8bcc787d145203600a4dba2d2 Mon Sep 17 00:00:00 2001
From: Prefetch
Date: Sat, 25 Feb 2023 11:41:27 +0100
Subject: Initial commit

---
 d10/src/main.rs | 173 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 173 insertions(+)
 create mode 100644 d10/src/main.rs

(limited to 'd10/src')

diff --git a/d10/src/main.rs b/d10/src/main.rs
new file mode 100644
index 0000000..650c146
--- /dev/null
+++ b/d10/src/main.rs
@@ -0,0 +1,173 @@
+// Let the record show that I hated every second of solving this puzzle.
+
+use std::fs;
+
+#[derive(Copy, Clone, Debug)]
+struct Bot {
+    lo: Option<usize>,
+    hi: Option<usize>,
+}
+
+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<usize> {
+        let result = self.lo;
+        if result.is_some() {
+            self.lo = None;
+        }
+        result
+    }
+
+    fn give_chip_hi(&mut self) -> Option<usize> {
+        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> = [Bot::new()].repeat(300);
+    let mut bins: Vec<Option<usize>> = [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));
+    }
+}
-- 
cgit v1.2.3