Powered by AppSignal & Oban Pro

AOC 2024

2024/aoc2024.livemd

AOC 2024

AOC helper

defmodule AOCHelper do
  def to_int_list(txt_file) do
    txt_file
    |> File.read!()
    |> String.split("\n", trim: true)
    |> Enum.map(fn list ->
      String.split(list) |> Enum.map(&String.to_integer/1)
    end)
  end
end
# defmodule Day do
#   @file_path "/Users/leizhou/Dev/learn_x/aoc/2024/day.txt"
#   def part1 do
#   end

#   def part2 do
#   end
# end

# IO.inspect(%{day_part1: Day.part1()})
# IO.inspect(%{day_part2: Day.part2()})

Day 1

day1_file = "/Users/leizhou/Dev/learn_x/aoc/2024/day1.txt"

[group_1, group_2] =
  day1_file
  |> AOCHelper.to_int_list()
  |> Enum.reduce([[], []], fn [num1, num2], [group1, group2] ->
    [[num1 | group1], [num2 | group2]]
  end)
  |> Enum.map(&Enum.sort/1)

[group_1, group_2]
|> Enum.zip()
|> Enum.reduce(0, fn elements, acc ->
  acc + abs(elem(elements, 0) - elem(elements, 1))
end)
|> IO.inspect(label: "day1 part1")

count_map = Enum.frequencies(group_2)

Enum.reduce(group_1, 0, fn num, acc ->
  acc + num * Map.get(count_map, num, 0)
end)
|> IO.inspect(label: "day1 part2")

Day 2

# part 2
defmodule Day2 do
  @file_path "/Users/leizhou/Dev/learn_x/aoc/2024/day2.txt"

  def check_sign(list), do: Enum.all?(list, fn x -> x >= 0 end) or Enum.all?(list, fn x -> x <= 0 end)

  def check_abs(list), do: Enum.all?(list, fn x -> abs(x) >= 1 and abs(x) <= 3 end)

  def check_cond(list), do: check_sign(list) and check_abs(list)

  def diff_report(list) do
    Enum.reduce(Enum.zip([list, tl(list)]), [], fn {prev, current}, acc ->
      [current - prev | acc]
    end)
  end

  def part1_check(list) do
    list
    |> diff_report()
    |> check_cond()
  end

  def part1() do
    @file_path
    |> AOCHelper.to_int_list()
    |> Enum.map(fn list ->
      part1_check(list)
    end)
    |> Enum.frequencies()
  end

  def part2_check(list) do
    1..length(list)
    |> Enum.map(fn index ->
      List.delete_at(list, index - 1)
      |> part1_check()
    end)
    |> Enum.any?()
  end

  def part2() do
    @file_path
    |> AOCHelper.to_int_list()
    |> Enum.map(fn list ->
      part2_check(list)
    end)
    |> Enum.frequencies()
  end
end

%{day2_part1: Day2.part1(), day2_part2: Day2.part2()}

Day 3

defmodule Day3 do
  @file_path "/Users/leizhou/Dev/learn_x/aoc/2024/day3.txt"

  def part1 do
    @file_path
    |> File.read!()
    |> then(fn string -> Regex.scan(~r/mul\((?<num1>\d{1,3}),(?<num2>\d{1,3})\)/, string) end)
    |> Enum.reduce(0, fn [_, num_string1, num_string2], acc ->
      acc + String.to_integer(num_string1) * String.to_integer(num_string2)
    end)
  end

  def make_instruct_map(text) do
    map_do =
      Regex.scan(~r/do\(\)/, text, return: :index)
      |> Enum.reduce(%{}, fn [{index, _}], acc ->
        Map.put(acc, index, 1)
      end)

    map_not =
      Regex.scan(~r/don't\(\)/, text, return: :index)
      |> Enum.reduce(%{}, fn [{index, _}], acc ->
        Map.put(acc, index, 0)
      end)

    Map.merge(map_do, map_not) |> Map.put(0, 1)
  end

  def make_instruct_index_list(map_instruct) do
    Map.keys(map_instruct) |> Enum.sort()
  end

  def make_mul_index_list(text) do
    text
    |> then(fn string ->
      Regex.scan(~r/mul\((?<num1>\d{1,3}),(?<num2>\d{1,3})\)/, string, return: :index)
    end)
    |> Enum.reduce([], fn [{index, _}, _, _], acc ->
      [index | acc]
    end)
    |> Enum.reverse()
  end

  def make_mul_number_list(text) do
    text
    |> then(fn string -> Regex.scan(~r/mul\((?<num1>\d{1,3}),(?<num2>\d{1,3})\)/, string) end)
    |> Enum.reduce([], fn [_, num_string1, num_string2], acc ->
      [String.to_integer(num_string1) * String.to_integer(num_string2) | acc]
    end)
    |> Enum.reverse()
  end

  def make_instuct_ind_list(mul_index_list, list_instruct_index, map_instruct) do
    mul_index_list
    |> Enum.map(fn index ->
      Enum.reduce_while(list_instruct_index, nil, fn instruct_index, acc ->
        if index - instruct_index > 0, do: {:cont, instruct_index}, else: {:halt, acc}
      end)
      |> then(fn index -> Map.get(map_instruct, index) end)
    end)
  end

  def calculate_sum(instruct_ind_list, mul_number_list) do
    Enum.zip(instruct_ind_list, mul_number_list)
    |> Enum.reduce(0, fn {ind, num}, acc ->
      acc + ind * num
    end)
  end

  def process_text(text) do
    [make_instruct_map(text), make_mul_index_list(text), make_mul_number_list(text)]
  end

  def part2 do
    text = File.read!(@file_path)

    [map_instruct, mul_index_list, mul_number_list] = process_text(text)

    list_instruct_index = make_instruct_index_list(map_instruct)

    instruct_ind_list = make_instuct_ind_list(mul_index_list, list_instruct_index, map_instruct)

    calculate_sum(instruct_ind_list, mul_number_list)
  end
end

IO.inspect(%{day3_part1: Day3.part1()})
IO.inspect(%{day3_part2: Day3.part2()})

Day 4

defmodule Day4 do
  @file_path "/Users/leizhou/Dev/learn_x/aoc/2024/day4.txt"

  def to_mat(file_path) do
    File.read!(file_path)
    |> String.split("\n", trim: true)
    |> Enum.map(&(String.split(&1, "", trim: true) |> List.to_tuple()))
    |> List.to_tuple()
  end

  def mat_elem(mat, x, y) do
    elem(elem(mat, x), y)
  end

  def to_four_letter(mat, x, y, :lr) do
    0..3
    |> Enum.reduce("", fn index, acc ->
      acc <> mat_elem(mat, x, y + index)
    end)
  end

  def to_four_letter(mat, x, y, :td) do
    0..3
    |> Enum.reduce("", fn index, acc ->
      acc <> mat_elem(mat, x + index, y)
    end)
  end

  def to_four_letter(mat, x, y, :nw_se) do
    0..3
    |> Enum.reduce("", fn index, acc ->
      acc <> mat_elem(mat, x + index, y + index)
    end)
  end

  def to_four_letter(mat, x, y, :sw_ne) do
    0..3
    |> Enum.reduce("", fn index, acc ->
      acc <> mat_elem(mat, x - index, y + index)
    end)
  end

  def to_xmas_ind(string) do
    case string do
      "XMAS" -> 1
      "SAMX" -> 1
      _ -> 0
    end
  end

  def check_four_letter(mat, x, y, direction) do
    to_four_letter(mat, x, y, direction) |> to_xmas_ind()
  end

  def count_row(mat, x) do
    0..(tuple_size(elem(mat, x)) - 4)
    |> Enum.map(fn index ->
      check_four_letter(mat, x, index, :lr)
    end)
    |> Enum.sum()
  end

  def count_rows(mat) do
    0..(tuple_size(mat) - 1)
    |> Enum.reduce(0, fn x, acc ->
      acc + count_row(mat, x)
    end)
  end

  def count_column(mat, y) do
    0..(tuple_size(mat) - 4)
    |> Enum.map(fn index ->
      check_four_letter(mat, index, y, :td)
    end)
    |> Enum.sum()
  end

  def count_columns(mat) do
    0..(tuple_size(elem(mat, 0)) - 1)
    |> Enum.reduce(0, fn y, acc ->
      acc + count_column(mat, y)
    end)
  end

  def count_nw_se(mat, x) when x <= tuple_size(mat) - 4 do
    0..(tuple_size(elem(mat, x)) - 4)
    |> Enum.map(fn index ->
      check_four_letter(mat, x, index, :nw_se)
    end)
    |> Enum.sum()
  end

  def count_nw_se(_mat, _x), do: 0

  def count_sw_ne(mat, x) when x >= 3 do
    0..(tuple_size(elem(mat, x)) - 4)
    |> Enum.map(fn index ->
      check_four_letter(mat, x, index, :sw_ne)
    end)
    |> Enum.sum()
  end

  def count_sw_ne(_mat, _x), do: 0

  def count_diags(mat) do
    0..(tuple_size(mat) - 1)
    |> Enum.reduce(0, fn x, acc ->
      acc + count_nw_se(mat, x) + count_sw_ne(mat, x)
    end)
  end

  def count_all(mat) do
    count_rows(mat) + count_columns(mat) + count_diags(mat)
  end

  def part1() do
    @file_path
    |> to_mat()
    |> count_all()
  end

  def to_mas_string(mat, x, y, :nw_se) do
    -1..1
    |> Enum.reduce("", fn index, acc ->
      acc <> mat_elem(mat, x + index, y + index)
    end)
  end

  def to_mas_string(mat, x, y, :sw_ne) do
    -1..1
    |> Enum.reduce("", fn index, acc ->
      acc <> mat_elem(mat, x - index, y + index)
    end)
  end

  def to_mas_ind(string) do
    case string do
      "MAS" -> 1
      "SAM" -> 1
      _ -> 0
    end
  end

  def count_nw_se_mas(mat, x, y), do: to_mas_string(mat, x, y, :nw_se) |> to_mas_ind()

  def count_sw_ne_mas(mat, x, y), do: to_mas_string(mat, x, y, :sw_ne) |> to_mas_ind()

  def count_mas(mat, x, y), do: (if count_nw_se_mas(mat, x, y) + count_sw_ne_mas(mat, x, y) == 2, do: 1, else: 0)

  def count_mas_all(mat) do
    for x <- 1..(tuple_size(mat)-2), y <- 1..(tuple_size(elem(mat, 0))-2), reduce: 0 do
      acc -> acc + count_mas(mat, x, y)
    end 
  end

  def part2 do
    @file_path
    |> to_mat()
    |> count_mas_all()
  end
end

IO.inspect(%{day4_part1: Day4.part1()})
IO.inspect(%{day4_part2: Day4.part2()})