import os, argparse from typing import List DEBUG = False #DEBUG = True def debug_print(s): if DEBUG: print(f"DEBUG: {s}") def solve_task(lines): monkeys = [] current_monkey = [] for line in lines: if len(line) == 0: monkeys.append(Monkey.from_line(current_monkey)) current_monkey = [] else: current_monkey.append(line) if(len(current_monkey) != 0): monkeys.append(Monkey.from_line(current_monkey)) for i, monkey in enumerate(monkeys): print(f"Monkey {i}:") print(f"{monkey}\n") game = KeepAwayGame(monkeys) game.do_rounds(10000) for i, monkey in enumerate(monkeys): print(f"Monkey {i} inspected items : {monkey.items_inspected} times.") print(f"Monkey business: {game.monkey_business()}") class Monkey: def __init__(self, items, operation, modulo, true_monkey, false_monkey) -> None: self._items = items self._operation = operation self._modulo = modulo self._true_monkey = true_monkey self._false_monkey = false_monkey self.items_inspected = 0 def __str__(self): to_return = f" Items: {str(self._items)[1:-1]}\n" to_return += f" Operation: {self._operation}\n" to_return += f" Test: divisible by {self._modulo}\n" to_return += f" If true: throw to monkey {self._true_monkey}\n" to_return += f" If false: throw to monkey {self._false_monkey}" return to_return def give(self, item): self._items.append(item) def take_round(self, throw_function): while len(self._items): item = self._items.pop(0) #debug_print(f" Monkey inspects an item with a worry level of {item}.") self.items_inspected += 1 item = self._operation(item) debug_print(f" New worry level: {item}.") #item = item // 3 #debug_print(f" Monkey gets bored with item. Worry level is divided by 3 to {item}.") if (item % self._modulo) == 0: debug_print(f" Test: true") throw_function(self._true_monkey, item) #debug_print(f" Item with worry level {item} is thrown to monkey {self._true_monkey}.") else: #debug_print(f" Test: false") throw_function(self._false_monkey, item) #debug_print(f" Item with worry level {item} is thrown to monkey {self._false_monkey}.") def from_line(lines) -> None: starting_items = [int(i) for i in lines[1].split(':')[1].split(',')] operation = Monkey.create_operation_from_line(lines[2]) modulo = int(lines[3].split("by")[1]) iftrue = int(lines[4].split("monkey")[1]) iffalse = int(lines[5].split("monkey")[1]) return Monkey(starting_items, operation, modulo, iftrue, iffalse) def create_operation_from_line(line): operation = line.split(':')[1].strip() global_dir = {} exec(f"""def func(old): {operation} return new""", global_dir) return global_dir["func"] class KeepAwayGame: def __init__(self, monkeys: List[Monkey]) -> None: self.monkeys = monkeys self.global_modulo = 1 for monkey in monkeys: self.global_modulo *= monkey._modulo print(f"Game initialized with global modulo: {self.global_modulo}") def do_rounds(self, n = 1): for round in range(n): debug_print(f"Round {round}") for i, monkey in enumerate(self.monkeys): debug_print(f"Monkey {i}:") monkey.take_round(self.throw) def monkey_business(self): inspections = [m.items_inspected for m in self.monkeys] inspections.sort() return inspections[-1] * inspections[-2] def throw(self, recipient, worry_level): worry_level = worry_level % self.global_modulo self.monkeys[recipient].give(worry_level) def read_lines(filename): lines = [] with open(filename) as infile: for raw_line in infile: line = raw_line.rstrip() lines.append(line) return lines def parse_arguments(): parser = argparse.ArgumentParser(description="Script that solves the case",epilog="Have a nice day!") parser.add_argument('filename', nargs='?', default="example.txt", help='Input file') args = parser.parse_args() return args def main(): args = parse_arguments() lines = read_lines(args.filename) solve_task(lines) if __name__ == "__main__": main()