Run this notebook

Use Livebook to open this notebook and explore new ideas.

It is easy to get started, on your machine or the cloud.

Click below to open and run it in your Livebook at .

(or change your Livebook location)

<!-- livebook:{"file_entries":[{"file":{"file_system_id":"local","file_system_type":"local","path":"/home/bre/Dev/advent-of-code-2023/day 10/input.txt"},"name":"day_10_input.txt","type":"file"},{"file":{"file_system_id":"local","file_system_type":"local","path":"/home/bre/Dev/advent-of-code-2023/day 11/input.txt"},"name":"day_11_input.txt","type":"file"},{"file":{"file_system_id":"local","file_system_type":"local","path":"/home/bre/Dev/advent-of-code-2023/day 1/input.txt"},"name":"day_1_input.txt","type":"file"},{"file":{"file_system_id":"local","file_system_type":"local","path":"/home/bre/Dev/advent-of-code-2023/day 2/input.txt"},"name":"day_2_input.txt","type":"file"},{"file":{"file_system_id":"local","file_system_type":"local","path":"/home/bre/Dev/advent-of-code-2023/day 3/input.txt"},"name":"day_3_input.txt","type":"file"},{"file":{"file_system_id":"local","file_system_type":"local","path":"/home/bre/Dev/advent-of-code-2023/day 4/input.txt"},"name":"day_4_input.txt","type":"file"},{"file":{"file_system_id":"local","file_system_type":"local","path":"/home/bre/Dev/advent-of-code-2023/day 5/input.txt"},"name":"day_5_input.txt","type":"file"},{"file":{"file_system_id":"local","file_system_type":"local","path":"/home/bre/Dev/advent-of-code-2023/day 6/input.txt"},"name":"day_6_input.txt","type":"file"},{"file":{"file_system_id":"local","file_system_type":"local","path":"/home/bre/Dev/advent-of-code-2023/day 7/input.txt"},"name":"day_7_input.txt","type":"file"},{"file":{"file_system_id":"local","file_system_type":"local","path":"/home/bre/Dev/advent-of-code-2023/day 8/input.txt"},"name":"day_8_input.txt","type":"file"},{"file":{"file_system_id":"local","file_system_type":"local","path":"/home/bre/Dev/advent-of-code-2023/day 9/input.txt"},"name":"day_9_input.txt","type":"file"},{"file":{"file_system_id":"local","file_system_type":"local","path":"/home/bre/Dev/advent-of-code-2023/day 11/example1.txt"},"name":"example1.txt","type":"file"}]} --> # AoC 2023 ```elixir Mix.install([ {:kino, "~> 0.11.3"} ]) ``` ## Day 1 ```elixir input = Kino.FS.file_path("day_1_input.txt") |> File.read!() |> String.split("\n") ``` ```elixir defmodule Day1 do def run(input) do part_1 = input |> Enum.map(&leave_only_numbers/1) |> Enum.map(&format_numbers/1) |> Enum.sum() part_2 = input |> Enum.map(&written_number_to_number_string/1) |> Enum.map(&leave_only_numbers/1) |> Enum.map(&format_numbers/1) |> Enum.sum() {part_1, part_2} end defp written_number_to_number_string(string) do string |> String.replace("one", "o1e") |> String.replace("two", "t2o") |> String.replace("three", "t3e") |> String.replace("four", "f4r") |> String.replace("five", "f5e") |> String.replace("six", "s6x") |> String.replace("seven", "s7n") |> String.replace("eight", "e8t") |> String.replace("nine", "n9e") end defp leave_only_numbers(string) do String.replace(string, ~r/[^0-9]/, "") end defp format_numbers(""), do: 0 defp format_numbers(string) do (String.at(string, 0) <> String.at(string, String.length(string) - 1)) |> Integer.parse() |> elem(0) end end ``` ```elixir Day1.run(input) ``` ## Day 2 ```elixir constrains = %{ red: 12, green: 13, blue: 14 } input = Kino.FS.file_path("day_2_input.txt") |> File.read!() |> String.split("\n") ``` ```elixir defmodule Day2 do def run(input, constrains) do part_1 = input |> Enum.reduce(%{}, &get_game_info/2) |> Enum.filter(&is_game_possible?(&1, constrains)) |> Enum.map(&elem(&1, 0)) |> Enum.sum() part_2 = input |> Enum.reduce(%{}, &get_game_info/2) |> Enum.map( &(elem(&1, 1) |> calculate_fewest_to_make_possible() |> Map.values() |> Enum.product()) ) |> Enum.sum() {part_1, part_2} end defp calculate_fewest_to_make_possible(game_info) do Enum.reduce(game_info, %{red: 0, green: 0, blue: 0}, fn play, acc -> %{ red: Enum.max([Map.get(play, :red) || 0, Map.get(acc, :red)]), green: Enum.max([Map.get(play, :green) || 0, Map.get(acc, :green)]), blue: Enum.max([Map.get(play, :blue) || 0, Map.get(acc, :blue)]) } end) end defp is_game_possible?(game, constrains) do game |> elem(1) |> Enum.map(fn play -> (Map.get(play, :red) || 0) <= (Map.get(constrains, :red) || 0) && (Map.get(play, :green) || 0) <= (Map.get(constrains, :green) || 0) && (Map.get(play, :blue) || 0) <= (Map.get(constrains, :blue) || 0) end) |> Enum.all?() end # "Game 1: 3 blue, 4 red; 1 red, 2 green, 6 blue; 2 green" defp get_game_info(game_string, recursive_map) do [game_hd | game_tl] = String.split(game_string, ": ") game_tl = Enum.at(game_tl, 0) game_number = game_hd |> String.replace("Game ", "") |> Integer.parse() |> elem(0) game_info = game_tl |> String.split("; ") |> Enum.map(&get_play_info/1) Map.put(recursive_map, game_number, game_info) end # "3 blue, 4 red" defp get_play_info(play_string) do play_string |> String.split(", ") |> Enum.reduce(%{}, &accumulate_play_info/2) end # "3 blue, %{} -> %{blue: 3} defp accumulate_play_info(play, acc) do [number, color] = String.split(play, " ") Map.put(acc, String.to_atom(color), Integer.parse(number) |> elem(0)) end end ``` ```elixir Day2.run(input, constrains) ``` ## Day 3 ```elixir input = Kino.FS.file_path("day_3_input.txt") |> File.read!() |> String.split("\n") |> Enum.filter(&(&1 != "")) ``` ```elixir defmodule Day3 do require Logger def run(input) do row_range = Enum.to_list(0..(length(input) - 1)) part_1 = row_range |> Enum.reduce([], &generate_all_checks(input, &1, &2)) |> Enum.filter(&filter_with_checks(input, &1)) |> Enum.map(&Map.get(&1, :number)) |> Enum.sum() part_2 = row_range |> Enum.reduce([], &generate_all_checks(input, &1, &2)) |> Enum.reduce(%{}, &asterisk_transformation(input, &1, &2)) |> Map.values() |> Enum.filter(&(length(&1) === 2)) |> Enum.map(&Enum.product/1) |> Enum.sum() {part_1, part_2} end defp asterisk_transformation(input, check_map, accumulator) do checks = Map.get(check_map, :check) number = Map.get(check_map, :number) checks |> Enum.reduce(accumulator, fn {row, col}, acc -> line = Enum.at(input, row) char = String.at(line, col) with :error <- Integer.parse(char), true <- char === "*" do key = inspect({row, col}) value = Map.get(acc, key, []) ++ [number] Map.put(acc, key, value) else _ -> acc end end) end defp filter_with_checks(input, check_map) do Map.get(check_map, :check) |> Enum.map(fn {row, col} -> line = Enum.at(input, row) char = String.at(line, col) with :error <- Integer.parse(char), true <- char !== "." do true else _ -> false end end) |> Enum.any?(& &1) end defp generate_all_checks(input, row_number, accumulator) do numbers_col_start_and_size = get_numbers_placement_per_row(input, row_number) line = Enum.at(input, row_number) row_checks = Enum.reduce(numbers_col_start_and_size, [], fn tuple, acc -> [col_start, size] = [elem(tuple, 0), elem(tuple, 1)] col_end = col_start + size max_row = length(input) - 1 max_col = String.length(line) - 1 [ %{ number: String.slice(line, col_start, size) |> String.to_integer(), check: generate_el_checks(row_number, col_start, col_end, max_row, max_col) } | acc ] end) row_checks ++ accumulator end defp generate_el_checks(row, col_start, col_end, max_row, max_col) do row_above = Enum.max([0, row - 1]) row_below = Enum.min([max_row, row + 1]) col_before = Enum.max([0, col_start - 1]) col_after = Enum.min([max_col, col_end]) range = Enum.to_list(col_before..col_after) [row_above, row, row_below] |> Enum.uniq_by(& &1) |> Enum.flat_map(fn row_number -> Enum.map(range, fn index -> {row_number, index} end) end) end defp get_numbers_placement_per_row(input, row) do Regex.scan(~r/\d+/, Enum.at(input, row), return: :index) |> Enum.map(&Enum.at(&1, 0)) end end ``` ```elixir Day3.run(input) ``` ## Day 4 ```elixir input = Kino.FS.file_path("day_4_input.txt") |> File.read!() |> String.split("\n") |> Enum.filter(&(&1 != "")) ``` ```elixir require Logger defmodule Day4 do def run(input) do part_1 = input |> Enum.map(fn card -> [hd, tl] = String.split(card, " | ") get_winning_numbers(hd) |> calculate_num_of_matches(get_my_numbers(tl)) |> calculate_score() end) |> Enum.sum() quantities_per_card = input |> Enum.map(fn _ -> 1 end) part_2 = input |> Enum.with_index() |> Enum.reduce(quantities_per_card, fn {card, card_index}, acc -> [hd, tl] = String.split(card, " | ") num_of_matches = calculate_num_of_matches(get_winning_numbers(hd), get_my_numbers(tl)) case num_of_matches do 0 -> acc n -> curr_list = acc positions_to_increase = Enum.to_list((card_index + 1)..(card_index + n)) amount_to_increase = Enum.at(acc, card_index) increase_numerical_list_at_positions( curr_list, positions_to_increase, amount_to_increase ) end end) |> Enum.sum() {part_1, part_2} end defp get_winning_numbers(string) do string |> String.split(": ") |> Enum.at(1) |> String.split(" ") |> Enum.filter(&(&1 != "")) |> Enum.map(&String.to_integer/1) end defp get_my_numbers(string) do string |> String.split(" ") |> Enum.filter(&(&1 != "")) |> Enum.map(&String.to_integer/1) end defp calculate_num_of_matches(winning_numbers, my_numbers) do Enum.reduce(my_numbers, 0, fn my_num, acc -> case Enum.find(winning_numbers, &(&1 === my_num)) do nil -> acc _ -> acc + 1 end end) end defp calculate_score(num_of_matches) do case num_of_matches do 0 -> 0 p -> 2 ** (p - 1) end end defp increase_numerical_list_at_positions(list, positions, increment) do Enum.with_index(list, fn el, idx -> case Enum.find(positions, &(&1 === idx)) do nil -> el _ -> el + increment end end) end end ``` ```elixir Day4.run(input) ``` ## Day 5 ```elixir input = Kino.FS.file_path("day_5_input.txt") |> File.read!() |> String.split("\n") ``` ```elixir defmodule Day5 do def run(input) do triples = get_triples(input) part_1 = exec(get_tuples_for_part_1(input), triples) part_2 = exec(get_tuples(input), triples) {part_1, part_2} end defp exec(tuples, triples) do triples |> Enum.reduce(tuples, fn triple, acc -> acc |> chop_tuples_from_triples(triple) |> apply_triples_in_tuples(triple) end) |> Enum.sort(fn {minA, _}, {minB, _} -> minA <= minB end) |> Enum.at(0) |> elem(0) end defp get_tuples_for_part_1(input) do input |> Enum.at(0) |> String.split(": ") |> Enum.at(1) |> String.split(" ") |> Enum.map(&String.to_integer/1) |> Enum.map(fn el -> {el, el} end) end defp get_tuples(input) do input |> Enum.at(0) |> String.split(": ") |> Enum.at(1) |> String.split(" ") |> Enum.map(&String.to_integer/1) |> Enum.chunk_every(2) |> Enum.map(fn chunk -> {Enum.at(chunk, 0), Enum.at(chunk, 0) + Enum.at(chunk, 1) - 1} end) end defp get_triples(input) do [_ | maps] = input |> Enum.chunk_by(&(&1 === "")) |> Enum.filter(fn arr -> Enum.all?(arr, fn el -> el !== "" end) end) maps |> Enum.map(fn [_ | tl] -> Enum.map(tl, fn el -> el |> String.split(" ") |> Enum.map(&String.to_integer/1) end) |> Enum.map(fn [target, source, length] -> {source, source + length - 1, target - source} end) end) end defp chop_tuple_from_triple(tuple, triple) do {triple_min, triple_max, _} = triple {tuple_min, tuple_max} = tuple cond do tuple_max < triple_min or tuple_min > triple_max -> [tuple] triple_min <= tuple_min and tuple_max <= triple_max -> [tuple] tuple_min < triple_min and tuple_max <= triple_max -> [ {tuple_min, triple_min - 1}, {triple_min, tuple_max} ] triple_min <= tuple_min and triple_max < tuple_max -> [ {tuple_min, triple_max}, {triple_max + 1, tuple_max} ] tuple_min < triple_min and triple_max < tuple_max -> [ {tuple_min, triple_min - 1}, {triple_min, triple_max}, {triple_max + 1, tuple_max} ] end end defp chop_tuples_from_triple(tuples, triple) do Enum.flat_map(tuples, &chop_tuple_from_triple(&1, triple)) end defp chop_tuple_from_triples(tuple, triples) do Enum.reduce(triples, [tuple], &chop_tuples_from_triple(&2, &1)) end defp chop_tuples_from_triples(tuples, triples) do Enum.flat_map(tuples, &chop_tuple_from_triples(&1, triples)) end defp apply_triple_in_tuple(tuple, triple) do {triple_min, triple_max, increment} = triple {tuple_min, tuple_max} = tuple cond do triple_min <= tuple_min and tuple_max <= triple_max -> new_tuple_min = tuple_min + increment new_tuple_max = tuple_max + increment {Enum.min([new_tuple_min, new_tuple_max]), Enum.max([new_tuple_min, new_tuple_max])} true -> tuple end end defp apply_triples_in_tuple(tuple, triples) do new_tuples = Enum.map(triples, &apply_triple_in_tuple(tuple, &1)) contain_new? = Enum.any?(new_tuples, fn t -> t !== tuple end) case contain_new? do true -> Enum.filter(new_tuples, fn t -> t !== tuple end) false -> new_tuples |> Enum.uniq() end end defp apply_triples_in_tuples(tuples, triples) do Enum.flat_map(tuples, &apply_triples_in_tuple(&1, triples)) end end ``` ```elixir Day5.run(input) ``` ## Day 6 ```elixir input = Kino.FS.file_path("day_6_input.txt") |> File.read!() |> String.split("\n") ``` ```elixir defmodule Day6 do def run(input) do part_1 = get_list_of_tuples_part_1(input) |> exec() part_2 = get_list_of_tuples_part_2(input) |> exec() {part_1, part_2} end defp exec(list_of_tuples) do list_of_tuples |> Enum.map(fn {max_time, distance_record} -> Enum.to_list(0..max_time) |> Enum.map(&calculate_distance(&1, max_time)) |> Enum.count(&(&1 > distance_record)) end) |> Enum.product() end defp get_list_of_tuples_part_1(input) do [times, distances] = input |> Enum.map(fn str -> str |> String.replace(~r/\s\s+/, " ") |> String.split(": ") |> Enum.at(1) |> String.split(" ") |> Enum.map(&String.to_integer/1) end) Enum.zip(times, distances) end defp get_list_of_tuples_part_2(input) do [times, distances] = input |> Enum.map(fn str -> str |> String.replace(~r/\s\s+/, " ") |> String.split(": ") |> Enum.at(1) |> String.replace(" ", "") |> String.to_integer() end) [{times, distances}] end defp calculate_distance(time_holding_button, max_time) do cond do time_holding_button === 0 -> 0 true -> (max_time - time_holding_button) * time_holding_button end end end ``` ```elixir # Day6.run(input) ``` ## Day 7 ```elixir input = Kino.FS.file_path("day_7_input.txt") |> File.read!() |> String.split("\n") ``` ```elixir defmodule Cartesian do def product(characters, n) when is_integer(n) and n > 0 do Enum.chunk_every(List.flatten(generate_cartesian([], characters, n)), n) end defp generate_cartesian(acc, _characters, 0), do: [acc] defp generate_cartesian(acc, characters, n) do Enum.reduce(characters, [], fn char, result -> generate_cartesian([char | acc], characters, n - 1) ++ result end) end end defmodule CharReplacer do def exec(string, character, list_of_chars) do Enum.reduce(list_of_chars, string, fn letter, acc -> String.replace(acc, ~r/#{character}/, letter, global: false) end) end end defmodule Day7 do require Logger alias Cartesian @alphabet_1 %{ "A" => 12, "K" => 11, "Q" => 10, "J" => 9, "T" => 8, "9" => 7, "8" => 6, "7" => 5, "6" => 4, "5" => 3, "4" => 2, "3" => 1, "2" => 0 } @alphabet_2 %{ "A" => 12, "K" => 11, "Q" => 10, "T" => 9, "9" => 8, "8" => 7, "7" => 6, "6" => 5, "5" => 4, "4" => 3, "3" => 2, "2" => 1, "J" => 0 } @pc1 Cartesian.product(Map.keys(@alphabet_2) |> Enum.filter(&(&1 !== "J")), 1) @pc2 Cartesian.product(Map.keys(@alphabet_2) |> Enum.filter(&(&1 !== "J")), 2) @pc3 Cartesian.product(Map.keys(@alphabet_2) |> Enum.filter(&(&1 !== "J")), 3) @group_order %{ "5" => 6, "41" => 5, "32" => 4, "311" => 3, "221" => 2, "2111" => 1, "11111" => 0 } def run(input) do card_bid_list_map = get_card_bid_list_map(input) part_1 = exec(card_bid_list_map, &group_cards_1/1, @alphabet_1) part_2 = exec(card_bid_list_map, &group_cards_2/1, @alphabet_2) {part_1, part_2} end defp exec(card_bid_list_map, group_cards_fn, alphabet) do card_bid_list_map |> get_cards_from_card_bid_map_list() |> group_cards_fn.() |> sort_cards_on_each_group(alphabet) |> sort_groups() |> calculate(card_bid_list_map) end defp get_card_bid_list_map(input) do input |> Enum.map(fn s -> [card, bid] = String.split(s, " ") %{card => bid} end) end defp get_cards_from_card_bid_map_list(card_bid_list_map) do Enum.flat_map(card_bid_list_map, &Map.keys/1) end defp get_card_group(card) do card |> String.split("") |> Enum.filter(&(&1 !== "")) |> Enum.reduce(%{}, fn char, acc -> case Map.get(acc, char, nil) do nil -> Map.put(acc, char, 1) _ -> Map.put(acc, char, Map.get(acc, char) + 1) end end) |> Map.values() |> Enum.sort(fn a, b -> b < a end) |> Enum.map(&Integer.to_string/1) |> Enum.join() end defp group_cards_1(cards) do Enum.reduce(cards, %{}, fn card, acc -> group = get_card_group(card) case Map.get(acc, group, nil) do nil -> Map.put(acc, group, [card]) _ -> Map.put(acc, group, [card | Map.get(acc, group)]) end end) end defp get_card_group_2(card) do num_of_js = card |> String.split("") |> Enum.filter(&(&1 !== "")) |> Enum.count(fn s -> s === "J" end) case num_of_js do 0 -> get_card_group(card) 5 -> "5" 4 -> "5" n -> [0, @pc1, @pc2, @pc3] |> Enum.at(n) |> Enum.map(fn letters -> get_card_group(CharReplacer.exec(card, "J", letters)) end) |> Enum.sort(fn g_a, g_b -> Map.get(@group_order, g_a, 0) > Map.get(@group_order, g_b, 0) end) |> Enum.at(0) end end defp group_cards_2(cards) do Enum.reduce(cards, %{}, fn card, acc -> group = get_card_group_2(card) case Map.get(acc, group, nil) do nil -> Map.put(acc, group, [card]) _ -> Map.put(acc, group, [card | Map.get(acc, group)]) end end) end defp get_card_sorting_boolean(card_a, card_b, alphabet) do Enum.reduce_while(0..4, true, fn index, _ -> char_a = String.at(card_a, index) char_b = String.at(card_b, index) char_a_value = Map.get(alphabet, char_a, 0) char_b_value = Map.get(alphabet, char_b, 0) # Logger.info("Comparing: #{card_a}, #{char_a}, #{char_a_value} with #{card_b} #{char_b} #{char_b_value}") cond do char_a_value > char_b_value -> {:halt, false} char_a_value < char_b_value -> {:halt, true} true -> {:cont, true} end end) end defp get_group_sorting_boolean(group_a_label, group_b_label) do group_a_value = Map.get(@group_order, group_a_label, 0) group_b_value = Map.get(@group_order, group_b_label, 0) group_a_value < group_b_value end defp sort_cards_by_group(card_group, alphabet) do Enum.sort(card_group, &get_card_sorting_boolean(&1, &2, alphabet)) end defp sort_cards_on_each_group(groups, alphabet) do Enum.map(groups, fn {label, group} -> %{label => sort_cards_by_group(group, alphabet)} end) end defp sort_groups(card_bid_map_sorted_by_cards) do Enum.sort(card_bid_map_sorted_by_cards, fn g_a, g_b -> group_a_label = Map.keys(g_a) |> Enum.at(0) group_b_label = Map.keys(g_b) |> Enum.at(0) get_group_sorting_boolean(group_a_label, group_b_label) end) end defp calculate(full_sorted, card_bid_list_map) do card_bid_map = Enum.reduce(card_bid_list_map, %{}, fn m, acc -> key = Map.keys(m) |> Enum.at(0) Map.put(acc, key, Map.get(m, key)) end) full_sorted |> Enum.flat_map(fn m -> Map.values(m) end) |> Enum.flat_map(fn m -> m end) |> Enum.with_index() |> Enum.reduce(0, fn {card, index}, acc -> rank = index + 1 bid = Map.get(card_bid_map, card, 0) acc + rank * String.to_integer(bid) end) end end ``` ```elixir Day7.run(input) ``` ## Day 8 ```elixir input = Kino.FS.file_path("day_8_input.txt") |> File.read!() |> String.split("\n") |> Enum.filter(&(&1 !== "")) ``` ```elixir defmodule BasicMath do 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: trunc(a * b / gcd(a, b)) end defmodule Day8 do require Logger alias BasicMath def run(input) do instructions = get_instructions(input) map = get_map(input) part_1 = navigate("AAA", "ZZZ", map, instructions) part_2 = get_origins(Map.keys(map)) |> get_cycle_size(map, instructions) |> lcm {part_1, part_2} end defp get_instructions(input) do [instructions | _] = input instructions end defp get_map(input) do [_ | raw_map] = input Enum.reduce(raw_map, %{}, fn entry, acc -> [key, lr] = String.split(entry, " = ") l = String.split(lr, ", ") |> Enum.at(0) |> String.replace("(", "") r = String.split(lr, ", ") |> Enum.at(1) |> String.replace(")", "") case Map.get(acc, key, nil) do nil -> Map.put(acc, key, {l, r}) _ -> acc end end) end defp get_origins(origins) do Enum.filter(origins, &(String.at(&1, 2) === "A")) end defp navigate(origin, destiny, map, instructions, steps \\ 0) do instructions_list = String.split(instructions, "") |> Enum.filter(&(&1 !== "")) {updated_origin, updated_steps} = Enum.reduce(instructions_list, {origin, steps}, fn dir, acc -> idx = if dir === "L", do: 0, else: 1 new_location = Map.get(map, elem(acc, 0)) |> elem(idx) {new_location, elem(acc, 1) + 1} end) case updated_origin do ^destiny -> updated_steps _ -> navigate(updated_origin, destiny, map, instructions, updated_steps) end end defp navigate_2(origin, map, instructions, steps \\ 0) do instructions_list = String.split(instructions, "") |> Enum.filter(&(&1 !== "")) {updated_origin, updated_steps} = Enum.reduce(instructions_list, {origin, steps}, fn dir, acc -> idx = if dir === "L", do: 0, else: 1 new_location = Map.get(map, elem(acc, 0)) |> elem(idx) {new_location, elem(acc, 1) + 1} end) if String.at(updated_origin, 2) === "Z", do: updated_steps, else: navigate_2(updated_origin, map, instructions, updated_steps) end defp get_cycle_size(origins, map, instructions) do Enum.map(origins, fn origin -> navigate_2(origin, map, instructions) end) end defp lcm(list_of_numbers) do sorted_list_of_numbers = list_of_numbers |> Enum.sort(:asc) sorted_list_of_numbers |> Enum.reduce(Enum.at(sorted_list_of_numbers, 0), &BasicMath.lcm(&1, &2)) end end ``` ```elixir Day8.run(input) ``` ## Day 9 ```elixir input = Kino.FS.file_path("day_9_input.txt") |> File.read!() |> String.split("\n") |> Enum.filter(&(&1 !== "")) ``` ```elixir defmodule Day9 do def run(input) do part_1 = input |> Enum.map(&convert_to_list_of_numbers/1) |> Enum.map(&convert_to_pyramid(&1, [&1])) |> Enum.map(&sum_pyramid_last_digits(&1)) |> Enum.sum() part_2 = input |> Enum.map(&convert_to_list_of_numbers/1) |> Enum.map(&convert_to_pyramid(&1, [&1])) |> Enum.map(&smart_sum_pyramid_first_digits(&1)) |> Enum.sum() {part_1, part_2} end defp convert_to_list_of_numbers(string) do String.split(string, " ") |> Enum.map(&String.to_integer/1) end defp convert_to_pyramid(list_of_numbers, acc \\ []) do list_of_differences = Enum.map(0..(length(list_of_numbers) - 2), fn idx -> Enum.at(list_of_numbers, idx + 1) - Enum.at(list_of_numbers, idx) end) case Enum.all?(list_of_differences, &(&1 === 0)) do true -> acc false -> convert_to_pyramid(list_of_differences, [list_of_differences] ++ acc) end end defp sum_pyramid_last_digits(pyramid) do pyramid |> Enum.map(&List.last/1) |> Enum.sum() end defp smart_sum_pyramid_first_digits(pyramid) do Enum.reduce(pyramid, 0, fn list_of_numbers, acc -> List.first(list_of_numbers) - acc end) end end ``` ```elixir Day9.run(input) ``` ## Day 10 ```elixir input = Kino.FS.file_path("day_10_input.txt") |> File.read!() |> String.split("\n") |> Enum.filter(&(&1 !== "")) |> Enum.map(fn line -> String.split(line, "") |> Enum.filter(&(&1 !== "")) end) ``` ```elixir defmodule Day10 do require Logger def run(input) do map = get_map(input) start_pos = get_start_pos(input) [dir_a_results, dir_b_results] = get_start_dirs(map, start_pos) |> Enum.map(&move(map, start_pos, &1)) final = Map.keys(dir_b_results) |> Enum.map(fn key -> [dir_a_results, dir_b_results] |> Enum.map(&Map.get(&1, key)) |> Enum.min() end) part_1 = Enum.max(final) ### coords = get_coords(map, start_pos, Enum.at(get_start_dirs(map, start_pos), 0)) part_2 = coords |> shoelace() |> pick(coords) {part_1, part_2} end defp get_map(input) do Enum.reduce(Enum.with_index(input), %{}, fn {row, row_idx}, acc1 -> Enum.reduce(Enum.with_index(row), acc1, fn {col, col_idx}, acc2 -> Map.put(acc2, inspect({row_idx, col_idx}), col) end) end) end defp get_start_pos(input) do row_idx = Enum.find_index(input, &Enum.member?(&1, "S")) col_idx = Enum.find_index(Enum.at(input, row_idx), &(&1 === "S")) {row_idx, col_idx} end defp get_start_dirs(map, {s_r, s_c}) do up = {s_r - 1, s_c} down = {s_r + 1, s_c} left = {s_r, s_c - 1} right = {s_r, s_c + 1} is_up? = Enum.member?(["|", "F", "7"], Map.get(map, tts(up))) is_down? = Enum.member?(["|", "J", "L"], Map.get(map, tts(down))) is_left? = Enum.member?(["-", "F", "L"], Map.get(map, tts(left))) is_right? = Enum.member?(["-", "J", "7"], Map.get(map, tts(right))) [ {up, is_up?}, {down, is_down?}, {left, is_left?}, {right, is_right?} ] |> Enum.filter(&elem(&1, 1)) |> Enum.map(&elem(&1, 0)) end defp get_coords(map, p, c, coords \\ []) do case Map.get(map, tts(c)) === "S" do true -> coords false -> get_coords(map, c, calculate(map, p, c), coords ++ [c]) end end defp move(map, p, c, result_map \\ %{}) do case Map.get(map, tts(c)) === "S" do true -> result_map false -> value = Map.get(result_map, tts(p), 0) + 1 result_map = Map.put(result_map, tts(c), value) move(map, c, calculate(map, p, c), result_map) end end defp shoelace(coords) do Enum.reduce(Enum.with_index(coords), 0, fn {coord, idx}, acc -> case idx === length(coords) - 1 do true -> acc + det(coord, Enum.at(coords, 0)) false -> acc + det(coord, Enum.at(coords, idx + 1)) end end) end defp pick(twoA, coords) do abs(twoA) / 2 + 1 - (length(coords) + 1) / 2 end ### defp calculate(map, {p_x, p_y}, {c_x, c_y} = c) do pipe = Map.get(map, tts(c)) case pipe do "." -> c "S" -> c "-" -> if p_y < c_y, do: {c_x, c_y + 1}, else: {c_x, c_y - 1} "|" -> if p_x < c_x, do: {c_x + 1, c_y}, else: {c_x - 1, c_y} "L" -> if p_x < c_x, do: {c_x, c_y + 1}, else: {c_x - 1, c_y} "F" -> if p_x === c_x, do: {c_x + 1, c_y}, else: {c_x, c_y + 1} "J" -> if p_y < c_y, do: {c_x - 1, c_y}, else: {c_x, c_y - 1} "7" -> if p_y < c_y, do: {c_x + 1, c_y}, else: {c_x, c_y - 1} end end # tuple to string defp tts({x, y}) do inspect({x, y}) end # string to tuple defp stt(str) do [a, b] = String.split(str, ", ") { String.replace(a, "{", "") |> String.to_integer(), String.replace(b, "}", "") |> String.to_integer() } end def det({p_x, p_y}, {c_x, c_y}) do {x_1, y_1} = {p_y, p_x} {x_2, y_2} = {c_y, c_x} x_1 * y_2 - x_2 * y_1 end end ``` ```elixir Day10.run(input) ``` ## Day 11 ```elixir input = Kino.FS.file_path("day_11_input.txt") |> File.read!() |> String.split("\n") |> Enum.filter(&(&1 !== "")) |> Enum.map(fn row -> String.split(row, "") |> Enum.filter(&(&1 !== "")) end) ``` ```elixir defmodule Day11 do def run(input) do sum_of_all_galaxy_pair_distances = input |> get_expanded_universe() |> get_universe_map() |> remove_empty_spaces() |> calculate_min_distances() |> Map.values() |> Enum.sum() part_1 = sum_of_all_galaxy_pair_distances / 2 end defp get_universe_map(universe) do Enum.with_index(universe) |> Enum.reduce(%{}, fn {row, row_idx}, acc_1 -> Enum.reduce(Enum.with_index(row), acc_1, fn {col, col_idx}, acc_2 -> Map.put(acc_2, {row_idx, col_idx}, col) end) end) end defp get_universe_dimensions(universe) do {length(universe) - 1, length(Enum.at(universe, 0)) - 1} end defp get_expanded_universe(universe) do universe |> expand_universe_on_rows() |> expand_universe_on_cols() end defp expand_universe_on_rows(universe) do {max_rows, _} = get_universe_dimensions(universe) row_slices_idx = Enum.reduce(0..max_rows, [], fn row_idx, acc -> row = Enum.at(universe, row_idx) if Enum.all?(row, &(&1 === ".")), do: acc ++ [row_idx], else: acc end) ++ [max_rows] {_, expanded_result} = Enum.reduce(row_slices_idx, {0, []}, fn empty_row_idx, {last_row_idx, universe_slice} -> {empty_row_idx, universe_slice ++ Enum.slice(universe, last_row_idx..empty_row_idx)} end) expanded_result end defp expand_universe_on_cols(universe) do {_, max_cols} = get_universe_dimensions(universe) col_slices_idx = Enum.reduce(0..max_cols, [], fn col_idx, acc -> col = Enum.map(universe, &Enum.at(&1, col_idx)) if Enum.all?(col, &(&1 === ".")), do: acc ++ [col_idx], else: acc end) ++ [max_cols] Enum.map(universe, fn row -> {_, col_expansion} = Enum.reduce(col_slices_idx, {0, []}, fn empty_col_idx, {last_col_idx, universe_slice} -> {empty_col_idx, universe_slice ++ Enum.slice(row, last_col_idx..empty_col_idx)} end) col_expansion end) end defp remove_empty_spaces(universe_map) do Enum.reduce(universe_map, %{}, fn {key, value}, acc -> if value !== ".", do: Map.put(acc, key, value), else: acc end) end defp calculate_min_distances(universe_map) do galaxies_coordinates = Map.keys(universe_map) Enum.reduce(universe_map, %{}, fn {key, _}, acc -> min_distance = Enum.map(galaxies_coordinates, fn {g_x, g_y} -> abs(g_x - elem(key, 0)) + abs(g_y - elem(key, 1)) end) |> Enum.filter(&(&1 > 0)) |> Enum.sum() Map.put(acc, key, min_distance) end) end end ``` ```elixir Day11.run(input) ``` <!-- livebook:{"offset":32702,"stamp":{"token":"XCP.0xO5ixclrh1zqlsE3dDzul_Tz9Q5muiaXsGcZPs6IW-0TlbLcEGxOf4IT9XQMN9g5culTkiNwRfNCxkoLR0u2HPJrNIQDNLfqzWzgTk","version":2}} -->
See source

Have you already installed Livebook?

If you already installed Livebook, you can configure the default Livebook location where you want to open notebooks.
Livebook up Checking status We can't reach this Livebook (but we saved your preference anyway)
Run notebook

Not yet? Install Livebook in just a minute

Livebook is open source, free, and ready to run anywhere.

Run in the cloud

on select platforms

To run on Linux, Docker, embedded devices, or Elixir’s Mix, check our README.

PLATINUM SPONSORS
SPONSORS
Code navigation with go to definition of modules and functions Read More