LoginSignup
9
1

闘魂Elixir ── Advent of code 2023 Day 8 Part 2 を Livebook で楽しむ

Last updated at Posted at 2023-12-26

$\huge{元氣ですかーーーーッ!!!}$
$\huge{元氣があればなんでもできる!}$

$\huge{闘魂とは己に打ち克つこと。}$
$\huge{そして闘いを通じて己の魂を磨いていく}$
$\huge{ことだと思います}$

はじめに

@torifukukaiou さんの パク リスペクト記事です

Elixir Livebook で Advent of Code 2023 の問題を解いてみます

実装したノートブックはこちら

問題はこちら

Part 1 はこちら

セットアップ

Kino AOC をインストールします

Mix.install([
  {:kino_aoc, "~> 0.1.5"}
])

Kino AOC の使い方はこちらを参照

入力の取得

Day 8 の入力を取得します

スクリーンショット 2023-12-27 1.13.08.png

私の答え

私の答えです。
折りたたんでおきます。
▶を押して開いてください。

details

回答用のモジュールです

入力を扱いやすい形にするための parse と、回答を作るための resolve 関数を持っています

defmodule Resolver do
  def parse(input) do
    [instructions | maps] = String.split(input, "\n\n")

    instructions = String.codepoints(instructions)

    maps =
      maps
      |> hd()
      |> String.split("\n")
      |> Enum.into(%{}, fn line ->
        parse_map(line)
      end)

    {instructions, maps}
  end

  defp parse_map(line) do
    [current, left, right] =
      line
      |> String.replace("=", "")
      |> String.replace("(", "")
      |> String.replace(")", "")
      |> String.replace(",", "")
      |> String.split(" ", trim: true)
      |> Enum.map(&String.reverse(&1))

    {current, %{"L" => left, "R" => right}}
  end

  def resolve(instructions, maps) do
    start_points =
      maps
      |> Map.keys()
      |> Enum.filter(fn key ->
        String.starts_with?(key, "A")
      end)

    start_points
    |> Enum.reduce(1, fn start_point, acc ->
      start_point
      |> step(maps, 0, instructions, 0)
      |> lcm(acc)
    end)
  end

  defp step("Z" <> _, _, _, _, acc), do: acc
  defp step(current, maps, inst_index, instructions, acc) do
    instruction = Enum.at(instructions, inst_index)

    next = maps[current][instruction]
    next_inst_index =
      if inst_index == Enum.count(instructions) -1 do
        0
      else
        inst_index + 1
      end

    step(next, maps, next_inst_index, instructions, acc + 1)
  end

  def lcm(a, b), do: div((a * b), Integer.gcd(a, b))
end

parse は Part1 とほぼ同じですが、パターンマッチで終了(ノード名が "Z" で終わる)を判定するため、ノード名を反転させています

defp step("Z" <> _, _, _, _, acc), do: acc のように、先頭でのパターンマッチは有効です

スタート地点は反転したので "A" で始まるノードになります

各スタートから移動して全てが同時にゴールするまで継続するため、答えは各スタートからゴールまでの移動回数の最小公倍数になります

Integer.gcd で最大公約数を求めることができるため、それを利用して最小公倍数を取得します

def lcm(a, b), do: div((a * b), Integer.gcd(a, b))

まとめ

問題は非常にややこしかったのですが、結果的には Part 1 に少し手を加えるだけで解けました

9
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
9
1