-
-
Save reverie/dad1c3bff03b4740ef29ce21fbf16097 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
defmodule BasicMath do | |
# From https://programming-idioms.org/idiom/75/compute-lcm/983/elixir | |
def gcd(a, 0), do: a | |
def gcd(0, b), do: b | |
def gcd(a, b), do: gcd(b, rem(a, b)) | |
def lcm(0, 0), do: 0 | |
def lcm(a, b), do: div(a * b, gcd(a, b)) | |
end | |
defmodule Main do | |
def run() do | |
"input.txt" | |
|> File.read!() | |
|> solve() | |
end | |
def solve(data) do | |
monkeys = | |
data | |
|> String.split("\n\n") | |
|> Enum.map(&parse_block/1) | |
|> Enum.map(&Map.put(&1, :num_inspects, 0)) | |
|> Enum.with_index() | |
|> Map.new(fn {monkey, idx} -> {idx, monkey} end) | |
part1(monkeys) |> IO.inspect(charlists: :as_lists) | |
part2(monkeys) |> IO.inspect(charlists: :as_lists) | |
end | |
def part1(monkeys) do | |
worry_reliever = fn x -> floor(x / 3) end | |
final_state = run_all_rounds(monkeys, worry_reliever, 20) | |
monkey_business(final_state) | |
end | |
def part2(monkeys) do | |
lcm_of_divisors = | |
monkeys | |
|> Enum.map(fn {_, monkey} -> monkey.divisor end) | |
|> Enum.reduce(&BasicMath.lcm/2) | |
worry_reliever = fn x -> rem(x, lcm_of_divisors) end | |
final_state = run_all_rounds(monkeys, worry_reliever, 10_000) | |
monkey_business(final_state) | |
end | |
def parse_block(block) do | |
[_ | lines] = String.split(block, "\n", trim: true) | |
[items_l, operations_l, divisor_l, true_l, false_l] = lines | |
[_, operation_s] = String.split(operations_l, ": ") | |
operation_fn_s = String.replace(operation_s, "new =", "fn (old) ->") <> " end" | |
{operation, _} = Code.eval_string(operation_fn_s) | |
%{ | |
items: ints(items_l), | |
operation: operation, | |
divisor: hd(ints(divisor_l)), | |
true_id: hd(ints(true_l)), | |
false_id: hd(ints(false_l)) | |
} | |
end | |
def ints(string) do | |
regex = ~r/\d+/ | |
matches = Regex.scan(regex, string) | |
Enum.map(List.flatten(matches), &String.to_integer/1) | |
end | |
def run_all_rounds(monkeys, worry_reliever, num_rounds) do | |
Enum.reduce(1..num_rounds, monkeys, fn _, acc -> | |
run_a_round(acc, worry_reliever) | |
end) | |
end | |
def run_a_round(monkeys, worry_reliever) do | |
Enum.reduce(monkeys, monkeys, fn {monkey_id, _monkey}, acc -> | |
do_monkey_throws(acc, worry_reliever, monkey_id) | |
end) | |
end | |
def do_monkey_throws(monkeys, worry_reliever, monkey_id) do | |
Enum.reduce(monkeys[monkey_id].items, monkeys, fn item, acc -> | |
thrower = acc[monkey_id] | |
{receiver_id, new_item} = get_receiver(thrower, worry_reliever, item) | |
receiver = Map.update!(acc[receiver_id], :items, &[new_item | &1]) | |
thrower = %{thrower | items: [], num_inspects: thrower.num_inspects + 1} | |
%{acc | monkey_id => thrower, receiver_id => receiver} | |
end) | |
end | |
def get_receiver(monkey, worry_reliever, item) do | |
item = monkey.operation.(item) | |
item = worry_reliever.(item) | |
case rem(item, monkey.divisor) == 0 do | |
true -> {monkey.true_id, item} | |
false -> {monkey.false_id, item} | |
end | |
end | |
def monkey_business(monkeys) do | |
# the product of the number of inspections of the two most active monkeys | |
monkeys | |
|> Enum.map(fn {_, monkey} -> monkey.num_inspects end) | |
|> Enum.sort(:desc) | |
|> Enum.take(2) | |
|> Enum.product() | |
end | |
end | |
Main.run() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment