#!/usr/bin/env julia using Test mutable struct Monkey index :: UInt items :: Vector{Int} op :: Function op_value :: Int test_divisor :: Int test_result :: NTuple{2, UInt} inspected :: Int end function parse_monkey(text) lines = split(text, '\n') # Monkey 0: match_index = match(r"Monkey (\d+):", lines[1]) index = parse(UInt, match_index.captures[1]) # Starting items: 79, 98 match_items = match(r" *Starting items: (.*)", lines[2]) items_list = split(match_items.captures[1], ", ") items = parse.(Int, items_list) # Operation: new = old * 19 match_op = match(r" *Operation: new = old (.) (old|\d+)", lines[3]) if match_op.captures[1] == "+" op = + elseif match_op.captures[1] == "*" op = * else throw(DomainError("Unknown op: " * match_op.captures[1])) end if match_op.captures[2] == "old" op = ^ op_value = 2 else op_value = parse(Int, match_op.captures[2]) end # Test: divisible by 13 match_test = match(r" *Test: divisible by (\d+)", lines[4]) test_divisor = parse(Int, match_test.captures[1]) # If true: throw to monkey 1 # If false: throw to monkey 3 match_true = match(r" *If true: throw to monkey (\d+)", lines[5]) match_false = match(r" *If false: throw to monkey (\d+)", lines[6]) test_result = (parse(UInt, match_true.captures[1]) + 1, parse(UInt, match_false.captures[1]) + 1) return Monkey(index, items, op, op_value, test_divisor, test_result, 0) end function read_monkeys(infile) open(infile) do io monkey_strings = split(read(io, String), "\n\n") return parse_monkey.(monkey_strings) end end function simian_shenanigans(monkeys :: Vector{Monkey}; part=1) modulus = prod(m.test_divisor for m in monkeys) for m in monkeys for item in m.items new_value = m.op(item, m.op_value) if part == 1 new_value = Int(floor(new_value / 3)) else new_value = mod(new_value, modulus) end new_monkey = (mod(new_value, m.test_divisor) == 0 ? m.test_result[1] : m.test_result[2]) push!(monkeys[new_monkey].items, new_value) m.inspected += 1 end empty!(m.items) end end function monkey_business(monkeys; rounds=20, part=1) for _ in 1:rounds simian_shenanigans(monkeys, part=part) end return prod(sort([m.inspected for m in monkeys])[end-1:end]) end function test() @testset "monkey business 1" verbose=true begin @test monkey_business(read_monkeys("example.txt")) == 10605 @test monkey_business(read_monkeys("input.txt")) == 64032 end @testset "monkey business 2" verbose=true begin @test monkey_business( read_monkeys("example.txt"), rounds=10000, part=2) == 2713310158 @test monkey_business( read_monkeys("input.txt"), rounds=10000, part=2) == 12729522272 end end function main() if size(ARGS, 1) == 0 test() else infile = ARGS[1] println("infile = ", infile) monkeys = read_monkeys(infile) display(monkeys) println() println("monkey business 1 ", monkey_business(monkeys, rounds=20, part=1)) monkeys = read_monkeys(infile) println("monkey business 2 ", monkey_business(monkeys, rounds=10000, part=2)) end end main()