summaryrefslogtreecommitdiff
path: root/d10/src/main.rs
diff options
context:
space:
mode:
Diffstat (limited to 'd10/src/main.rs')
-rw-r--r--d10/src/main.rs173
1 files changed, 173 insertions, 0 deletions
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));
+ }
+}