Exercise from advent of code (day 11)
You can find the exercise on: https://adventofcode.com/2022/day/11
This example can best been run locally on your machine, you'll need two files:
- main.rs
- input.txt
Solve the error in the rust code.
Monkey 0:
Starting items: 79, 98
Operation: new = old * 19
Test: divisible by 23
If true: throw to monkey 2
If false: throw to monkey 3
Monkey 1:
Starting items: 54, 65, 75, 74
Operation: new = old + 6
Test: divisible by 19
If true: throw to monkey 2
If false: throw to monkey 0
Monkey 2:
Starting items: 79, 60, 97
Operation: new = old * old
Test: divisible by 13
If true: throw to monkey 1
If false: throw to monkey 3
Monkey 3:
Starting items: 74
Operation: new = old + 3
Test: divisible by 17
If true: throw to monkey 0
If false: throw to monkey 1
use std::collections::{BTreeMap, HashMap}; use std::fs::File; use std::io; use std::io::BufRead; use std::num::ParseIntError; use std::path::Path; #[derive(Debug, Copy, Clone)] enum Operation { Add(i32), Multiply(i32), AddSelf(), MultiplySelf(), } impl Operation { fn apply(&self, worry_level: i32) -> i32 { match self { Operation::Add(x) => x + worry_level, Operation::Multiply(x) => x * worry_level, Operation::AddSelf() => worry_level + worry_level, Operation::MultiplySelf() => worry_level * worry_level, } } } #[derive(Debug, Copy, Clone)] struct Test { divisible: i32, next_monkey_when_true: char, next_monkey_when_false: char, } impl Test { fn test(&self, worry_level: i32) -> char { if worry_level % self.divisible == 0 { self.next_monkey_when_true } else { self.next_monkey_when_false } } } #[derive(Debug, Clone)] struct Monkey { id: char, items: Vec<i32>, operation: Operation, test: Test, inspects: i32, } impl Monkey { fn catch(&self, item: i32) -> Monkey { let mut items_to_mutate = self.items.clone(); items_to_mutate.push(item); Monkey { id: self.id, items: items_to_mutate, operation: self.operation, test: self.test, inspects: self.inspects, } } fn throw(&self) -> (Monkey, char, i32) { let (first, tail) = self.items.split_first().unwrap(); let after_operation = self.operation.apply(*first) / 3; let new_monkey = Monkey { id: self.id.clone(), items: tail.to_vec(), operation: self.operation, test: self.test, inspects: self.inspects + 1, }; (new_monkey, self.test.test(after_operation), after_operation) } } fn process_items(monkey_id: char, start_play: BTreeMap<char, Monkey>) -> BTreeMap<char, Monkey> { let mut end_play = start_play.clone(); let monkey = start_play.get(&monkey_id).unwrap(); let (updated_monkey, next_monkey_id, item) = monkey.throw(); end_play.insert(updated_monkey.id, updated_monkey); let monkey_to_receive_item = start_play.get(&next_monkey_id).unwrap(); let updated_monkey_receive = monkey_to_receive_item.catch(item); end_play.insert(updated_monkey_receive.id, updated_monkey_receive); end_play } fn play_game(start_play: BTreeMap<char, Monkey>) -> BTreeMap<char, Monkey> { let mut end_play = start_play.clone(); for _ in 0..20 { for monkey_turn in start_play.keys() { let monkey = end_play.get(monkey_turn).unwrap(); for _ in 0..monkey.items.len() { end_play = process_items(*monkey_turn, end_play); } } } end_play.clone() } fn calculate_score(play: BTreeMap<char, Monkey>) -> i32 { let mut scores: Vec<i32> = Vec::new(); for (_, monkey) in play { scores.push(monkey.inspects); } scores.sort_by(|a, b| b.cmp(a)); let first = scores.get(0).unwrap(); let second = scores.get(1).unwrap(); first * second } fn read_from_file(name_file: &str) { let mut monkey_id: char = 'q'; let mut operation = Operation::Add(0); let mut test_divisor: i32 = 0; let mut test_true_throw: char = 'q'; let mut test_false_throw: char = 'q'; let mut items: Vec<i32> = Vec::new(); let mut all_monkeys: BTreeMap<char, Monkey> = BTreeMap::new(); if let Ok(lines) = read_lines(name_file) { for line in lines { if let Ok(input_line) = line { if input_line.starts_with("Monkey ") { let possible_monkey_id: Vec<char> = input_line.chars().collect(); monkey_id = possible_monkey_id[7].clone(); } let starting_items_header = " Starting items: "; if input_line.starts_with(starting_items_header) { items = input_line.replace(starting_items_header, "") .split(", ") .map(|e| e.parse::<i32>().unwrap()) .collect(); } let operation_header = " Operation: new = "; if input_line.starts_with(operation_header) { let number_as_string: String = input_line.chars() .filter(|c| c.is_numeric()) .collect(); match number_as_string.parse::<i32>() { Ok(number) => { if input_line.contains("*") { operation = Operation::Multiply(number); } if input_line.contains("+") { operation = Operation::Add(number); } } Err(_) => { if input_line.contains("*") { operation = Operation::MultiplySelf(); } if input_line.contains("+") { operation = Operation::AddSelf(); } } }; } let test_header = " Test: divisible by "; if input_line.starts_with(test_header) { test_divisor = input_line.replace(test_header, "").parse::<i32>().unwrap(); } let test_true_header = " If true: throw to monkey "; if input_line.starts_with(test_true_header) { let number_as_string: String = input_line.chars() .filter(|c| c.is_numeric()) .collect(); test_true_throw = number_as_string.parse::<char>().unwrap(); } let test_false_header = " If false: throw to monkey "; if input_line.starts_with(test_false_header) { let number_as_string: String = input_line.chars() .filter(|c| c.is_numeric()) .collect(); test_false_throw = number_as_string.parse::<char>().unwrap(); } // println!("*** {:?}", input_line); if input_line.trim().is_empty() { let test = Test { divisible: test_divisor, next_monkey_when_true: test_true_throw, next_monkey_when_false: test_false_throw, }; let monkey = Monkey { id: monkey_id, items: items.clone(), inspects: 0, operation, test, }; // println!("{:?}", monkey); all_monkeys.insert(monkey_id, monkey); } } } } let map = play_game(all_monkeys); for (c, m) in map { println!("{:?} --- {:?} --- {:?} --- {:?}", c, m.id, m.inspects, m.items); } println!("total score: {}", calculate_score(map)); } fn main() { if let Ok(lines) = read_lines("./input.txt") { for line in lines { if let Ok(input_line) = line { // do something with input_line } } } // write a message with result let message = "message with result"; println!("message: {:?}", message); } fn read_lines<P>(filename: P) -> io::Result<io::Lines<io::BufReader<File>>> where P: AsRef<Path>, { let file = File::open(filename)?; Ok(io::BufReader::new(file).lines()) } #[cfg(test)] mod tests { // Note this useful idiom: importing names from outer (for mod tests) scope. use super::*; #[test] fn test_add_operation() { let ops1 = Operation::Add(4); assert_eq!(ops1.apply(5), 9); } #[test] fn test_multiply_operation() { let ops1 = Operation::Multiply(4); assert_eq!(ops1.apply(5), 20); } #[test] fn test_test_struct_when_false() { let test = Test { divisible: 11, next_monkey_when_true: 'a', next_monkey_when_false: 'b', }; assert_eq!(test.test(2), 'b'); } #[test] fn test_test_struct_when_true() { let test = Test { divisible: 11, next_monkey_when_true: 'a', next_monkey_when_false: 'b', }; assert_eq!(test.test(220), 'a'); } #[test] fn test_example() { read_from_file("./test-input.txt"); } // #[test] // fn test_solution() { // read_from_file("./input.txt"); // } }