호두나무 공방/Advent of Code

Advent of Code 2022 Day 20 in Elixir - Grove Positioning System 풀이

2023. 1. 23. 18:00

문제 보기

요정들의 암호를 복호화 어쩌고 하는 명목으로 숫자 위치 옮기기 퍼즐을 푸는 문제였다. 각 숫자를 숫자 크기만큼 위치를 바꾸되(1인 경우 오른쪽으로 한 칸, -2인 경우 왼쪽으로 두 칸), 옮기는 숫자는 원래 주어진 숫자의 순서대로(중요)여야 했다. 즉 3번째 차례에서 옮겨야 하는 숫자가 반드시 3번째 자리에 있지는 않다는 뜻이므로, 숫자를 섞으면서도 원래 이 숫자가 어디에 있었는지를 기억해야 했다. 그나마 다행인 것은 숫자 목록의 끝과 끝이 이어져 있어 순서만 맞다면 무엇이 첫 숫자로 오든 큰 상관이 없었다는 점 정도.

1번과 2번 문제의 푸는 방법이 약간 다른데, 1번은 위치 바꾸기를 숫자 목록의 각 숫자에 대해 한 번씩만 하면 되어 이진 플래그를 썼고, 2번은 열 바퀴 돌려야 했기 때문에 그냥 마음 편히 숫자의 원래 위치를 기억하는 인덱스를 썼다는 정도. 그렇게 보면 2번 문제 푸는 방법으로도 1번 문제를 충분히 풀 수 있다. 오히려 그 쪽이 확실하다.

defmodule Omicron.Day20 do
  @dir "data/day20/"

  def q1(file_name \\ "q.txt") do
    list =
      File.read!(@dir <> file_name)
      |> String.split("\n")
      |> Enum.map(&String.to_integer/1)
      |> Enum.map(fn v -> {v, false} end)

    size = length(list)

    result =
      list
      |> Enum.reduce(list, fn {value, false}, acc ->
        idx = Enum.find_index(acc, &(elem(&1, 1) == false))

        new_idx = Integer.mod(idx + value, size - 1)

        # IO.inspect(acc, label: "Next number: #{value} at #{idx} to #{new_idx}")

        next_list =
          if idx == 0 do
            Enum.slice(acc, (idx + 1)..-1)
          else
            Enum.slice(acc, 0..(idx - 1)) ++ Enum.slice(acc, (idx + 1)..-1)
          end
          |> List.insert_at(new_idx, {value, true})

        next_list
      end)
      |> IO.inspect()

    zero_index = Enum.find_index(result, &(&1 == {0, true})) |> IO.inspect()
    num_1000 = Enum.at(result, rem(1000 + zero_index, size)) |> elem(0)
    num_2000 = Enum.at(result, rem(2000 + zero_index, size)) |> elem(0)
    num_3000 = Enum.at(result, rem(3000 + zero_index, size)) |> elem(0)

    num_1000 + num_2000 + num_3000
  end

  def q2(file_name \\ "q.txt") do
    multiplier = 811_589_153

    list =
      File.read!(@dir <> file_name)
      |> String.split("\n")
      |> Enum.map(&String.to_integer/1)
      |> Enum.map(fn v -> v * multiplier end)
      |> Enum.with_index()

    size = length(list)

    result =
      1..10
      |> Enum.flat_map(fn _ -> list end)
      |> Enum.reduce(list, fn {value, idx}, acc ->
        if idx == 0 do
          IO.inspect(acc, label: "Round finished")
        end

        real_index = Enum.find_index(acc, fn {v, i} -> i == idx end)
        new_idx = Integer.mod(real_index + value, size - 1)

        # IO.inspect(acc)
        # IO.puts("Next number: #{value} at #{real_index} to #{new_idx}")

        next_list =
          if real_index == 0 do
            Enum.slice(acc, (real_index + 1)..-1)
          else
            Enum.slice(acc, 0..(real_index - 1)) ++ Enum.slice(acc, (real_index + 1)..-1)
          end
          |> List.insert_at(new_idx, {value, idx})

        next_list
      end)
      |> IO.inspect()

    zero_index = Enum.find_index(result, &(elem(&1, 0) == 0))
    num_1000 = Enum.at(result, rem(1000 + zero_index, size)) |> elem(0) |> IO.inspect()
    num_2000 = Enum.at(result, rem(2000 + zero_index, size)) |> elem(0) |> IO.inspect()
    num_3000 = Enum.at(result, rem(3000 + zero_index, size)) |> elem(0) |> IO.inspect()

    (num_1000 + num_2000 + num_3000) * 1
  end
end