Again, nothing really new from an Icon language perspective.
Some convenient features though are that:
- numbers easily extend to "big numbers", so no special consideration needed to represent the worry values
-
optional arguments to procedures are set to null, and
then can be set to a default value by testing if null
procedure fn(required_arg, optional_arg) /optional_arg := "default value" # sets value if null end # called with one value to use default fn("required value") # or two values to set optional argument fn("required value", "sets optional argument")
From a solution perspective, this is the first problem needing some special attention on efficiency. As all the "worry" divisors are primes, we can divide the worry by their product to keep the number to a reasonable size.
procedure main(inputs) local filename if *inputs = 1 then { filename := inputs[1] # Part 1 solution write("Part 1: ", find_worry_levels(filename, 20, "by 3")) # Part 2 solution write("Part 2: ", find_worry_levels(filename, 10000)) } else { write("Provide a filename of data") } end record monkey_defn(index, num_inspections, items, operator, operand, divisible, on_true, on_false) procedure find_worry_levels(filename, num_rounds, divide_by_three) local monkey, monkey_data, most_active monkey_data := read_data(filename) every 1 to num_rounds do do_round(monkey_data, divide_by_three) most_active := [0, 0] every monkey := !monkey_data do { if monkey.num_inspections > most_active[1] then most_active := [monkey.num_inspections, most_active[1]] else if monkey.num_inspections > most_active[2] then most_active := [most_active[1], monkey.num_inspections] } return most_active[1] * most_active[2] end procedure read_data(filename) local file, line local current_monkey, monkey_data file := open(filename) | stop("Cannot open ", filename) monkey_data := list() current_monkey := &null every line := !file do { line ? { tab(many(" \t")) # skip spaces if ="Monkey " then { current_monkey := monkey_defn(tab(upto(":")), 0, list(), &null, &null, &null, &null, &null) } else if ="Starting items: " then { while tab(upto(&digits)) do { put (current_monkey.items, tab(many(&digits))) } } else if ="Operation: " then { ="new = old " current_monkey.operator := move(1) move(1) current_monkey.operand := tab(0) } else if ="Test: " then { ="divisible by " current_monkey.divisible := integer(tab(0)) } else if ="If true: " then { ="throw to monkey " current_monkey.on_true := integer(tab(0)) } else if ="If false: " then { ="throw to monkey " current_monkey.on_false := integer(tab(0)) } else { if \current_monkey then { put(monkey_data, current_monkey) } } } } close(file) return monkey_data end # Given a list of monkey definitions, in index order, # do one round through every monkey and item procedure do_round(monkey_data, divide_by_three) local item, monkey, new_worry, simplifier every monkey := !monkey_data do { while *monkey.items > 0 do { item := pop(monkey.items) monkey.num_inspections +:= 1 new_worry := compute_worry(monkey.operator, monkey.operand, item) if \divide_by_three then new_worry /:= 3 else { # all divisors are primes, so we can modulo by their product simplifier := 1 every simplifier *:= (!monkey_data).divisible new_worry %:= simplifier } if new_worry % monkey.divisible = 0 then { put(monkey_data[monkey.on_true+1].items, new_worry) } else { put(monkey_data[monkey.on_false+1].items, new_worry) } } } end # Computes new worry value procedure compute_worry(operator, operand, old_value) if operand == "old" then { return operator(old_value, old_value) } else { return operator(old_value, integer(operand)) } end