advent_of_code_2022

My (attempted) solutions to the 2022 Advent of Code
git clone https://git.eamoncaddigan.net/advent_of_code_2022.git
Log | Files | Refs | README

day_11.jl (5173B)


      1 #!/usr/bin/env julia
      2 # https://adventofcode.com/2022/day/11
      3 using AdventOfCode
      4 
      5 example = readlines(IOBuffer("""
      6     Monkey 0:
      7       Starting items: 79, 98
      8       Operation: new = old * 19
      9       Test: divisible by 23
     10         If true: throw to monkey 2
     11         If false: throw to monkey 3
     12 
     13     Monkey 1:
     14       Starting items: 54, 65, 75, 74
     15       Operation: new = old + 6
     16       Test: divisible by 19
     17         If true: throw to monkey 2
     18         If false: throw to monkey 0
     19 
     20     Monkey 2:
     21       Starting items: 79, 60, 97
     22       Operation: new = old * old
     23       Test: divisible by 13
     24         If true: throw to monkey 1
     25         If false: throw to monkey 3
     26 
     27     Monkey 3:
     28       Starting items: 74
     29       Operation: new = old + 3
     30       Test: divisible by 17
     31         If true: throw to monkey 0
     32         If false: throw to monkey 1"""))
     33 input = readlines("data/day_11.txt")
     34 
     35 # Parsing this input is going to be a job, so I'm going to give myself
     36 # permission to write as many tiny functions as I want to (easier to debug).
     37 
     38 module MonkeyBusiness
     39     mutable struct Monkey
     40         id::Int
     41         items::Vector{Int}
     42         operation::Tuple{<:AbstractString,<:AbstractString}
     43         divisible::Int
     44         throw_id::Tuple{Int,Int}
     45         considerations::Int
     46     end
     47 
     48     # This parses the text and creates a Monkey. Not sure if this is the normal
     49     # Julia approach, but outer constructors are so flexible it seems
     50     # reasonable?
     51     function Monkey(monkeytext::Vector{<:AbstractString})
     52         monkeyregexes = [
     53             r"^Monkey (\d+):$",
     54             r"^  Starting items: ([0-9, ]*)$",
     55             r"^  Operation: new = old ([+*]) (old|\d+)$",
     56             r"^  Test: divisible by (\d+)$",
     57             r"^    If true: throw to monkey (\d+)$",
     58             r"^    If false: throw to monkey (\d+)$"
     59         ]
     60         monkeymatches = map(match, monkeyregexes, monkeytext)
     61         @assert !any(map(isnothing, monkeymatches))
     62 
     63         Monkey(
     64             parse(Int, monkeymatches[1].captures[1]),
     65             parse.(Int, split(monkeymatches[2].captures[1], ", ")),
     66             Tuple(monkeymatches[3].captures),
     67             parse(Int, monkeymatches[4].captures[1]),
     68             (
     69                 parse(Int, monkeymatches[5].captures[1]),
     70                 parse(Int, monkeymatches[6].captures[1])
     71             ),
     72             0
     73         )
     74     end
     75 
     76     """
     77     Apply the operation to see how the worry level changes.
     78     """
     79     function monkeyinspection!(monkey::Monkey, worry::Int)
     80         if monkey.operation[2] == "old"
     81             operand = worry
     82         else
     83             operand = parse(Int, monkey.operation[2])
     84         end
     85 
     86         if monkey.operation[1] == "*"
     87             worry * operand
     88         else
     89             worry + operand
     90         end
     91     end
     92 
     93     """
     94     Test worry level and return the id of the monkey that will receive the item.
     95     """
     96     function monkeytest(monkey::Monkey, worry::Int)
     97         worry % monkey.divisible == 0 ? monkey.throw_id[1] : monkey.throw_id[2]
     98     end
     99 
    100     """
    101     Process one monkey's turn.
    102     """
    103     function monkeyturn!(monkey::Monkey, 
    104                          monkeys::Dict{Int, Monkey},
    105                          part::Int)
    106         while !isempty(monkey.items)
    107             item = mod(monkeyinspection!(monkey, popfirst!(monkey.items)),
    108                        mapreduce(x->x.divisible, *, values(monkeys)))
    109             part == 1 && (item รท= 3)
    110             push!(monkeys[monkeytest(monkey, item)].items, item)
    111             monkey.considerations += 1
    112         end
    113     end
    114 
    115     function monkeyrounds!(monkeys::Dict{Int, Monkey}, numrounds::Int,
    116                            part::Int = 1)
    117         monkeyids = sort(collect(keys(monkeys)))
    118         for _ = 1:numrounds
    119             for monkeyid = monkeyids
    120                 monkeyturn!(monkeys[monkeyid], monkeys, part)
    121             end
    122         end
    123     end
    124 
    125     """
    126     Split the input lines into (unparsed text representations) of each monkey.
    127 
    128     Returns an array of string arrays.
    129     """
    130     function split_monkeys(input::Vector{<:AbstractString})
    131         monkeystart = 1
    132         monkeys = Vector{Vector{<:AbstractString}}()
    133         for monkeyend = [findall(==(""), input); lastindex(input)+1]
    134             push!(monkeys, input[monkeystart:monkeyend-1])
    135             monkeystart = monkeyend+1
    136         end
    137         monkeys
    138     end
    139 
    140     """
    141     Read the input and return a dictionary of monkeys.
    142     """
    143     function makemonkeys(input::Vector{<:AbstractString})
    144         monkeys = map(Monkey, split_monkeys(input))
    145         Dict(zip([m.id for m in monkeys], monkeys))
    146     end
    147 end
    148 
    149 function part_1(input)
    150     monkeys = MonkeyBusiness.makemonkeys(input)
    151     MonkeyBusiness.monkeyrounds!(monkeys, 20)
    152     reduce(
    153         *,
    154         partialsort(map(x->x.considerations, values(monkeys)),
    155                     1:2, rev = true)
    156     )
    157 end
    158 @assert part_1(example) == 10605
    159 @info part_1(input)
    160 
    161 function part_2(input)
    162     monkeys = MonkeyBusiness.makemonkeys(input)
    163     MonkeyBusiness.monkeyrounds!(monkeys, 10_000, 2)
    164     reduce(
    165         *,
    166         partialsort(map(x->x.considerations, values(monkeys)),
    167                     1:2, rev = true)
    168     )
    169 end
    170 @assert part_2(example) == 2713310158
    171 @info part_2(input)