LoginSignup
9
1

闘魂Elixir ── Advent of code 2023 Day 4 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 4 の入力を取得します

スクリーンショット 2023-12-26 13.10.10.png

私の答え

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

details

回答用のモジュールです

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

defmodule Resolver do
  def parse(input) do
    input
    |> String.split("\n")
    |> Enum.with_index()
    |> Enum.into(%{}, fn {line, index} ->
      [_, card] = String.split(line, ":")
      [winning_card, my_card] = String.split(card, "|")
      winning_card = parse_card(winning_card)
      my_card = parse_card(my_card)

      nuber_of_matches =
        winning_card
        |> Enum.count(fn winnig_number ->
          Enum.member?(my_card, winnig_number)
        end)

      copies = get_copies(index + 1, nuber_of_matches)

      {index + 1, copies}
    end)
  end

  defp get_copies(_, 0), do: []
  defp get_copies(current_index, nuber_of_matches) do
    Enum.to_list((current_index + 1)..(current_index + nuber_of_matches))
  end

  defp parse_card(card) do
    card
    |> String.split(" ")
    |> Enum.filter(fn value -> value != "" end)
    |> Enum.map(fn value -> String.to_integer(value) end)
  end

  def resolve(games) do
    games
    |> Enum.map(fn {card_index, _} ->
      get_card(games, card_index, 0)
    end)
    |> Enum.sum()
  end

  defp get_card(games, card_index, acc) do
    games[card_index]
    |> Enum.map(fn next_card_index ->
      get_card(games, next_card_index, acc)
    end)
    |> Enum.sum()
    |> Kernel.+(1)
  end
end

今回は parse も Part 1 から変えています

パース時点でマッチ数を取得し、カード毎に次に取得できるカードの番号の配列を設定しておきます

入力

Card 1: 41 48 83 86 17 | 83 86  6 31 17  9 48 53
Card 2: 13 32 20 16 61 | 61 30 68 82 17 32 24 19
Card 3:  1 21 53 59 44 | 69 82 63 72 16 21 14  1
Card 4: 41 92 73 84 69 | 59 84 76 51 58  5 54 83
Card 5: 87 83 26 28 32 | 88 30 70 12 93 22 82 36
Card 6: 31 18 13 56 72 | 74 77 10 23 35 67 36 11

パース結果

%{1 => [2, 3, 4, 5], 2 => [3, 4], 3 => [4, 5], 4 => [5], 5 => [], 6 => []}

1 のカードからは 2, 3, 4, 5 のカードが取得でき、 2 のカードからは 3, 4 のカードが取得できる、という情報担っています

resolve でパース結果の情報を再起的に処理し、最終的な枚数を取得します

再起処理の中では、自分から連なるカードの枚数 + 1(自分自身)という計算をしています

  defp get_card(games, card_index, acc) do
    games[card_index]
    |> Enum.map(fn next_card_index ->
      get_card(games, next_card_index, acc)
    end)
    |> Enum.sum()
    |> Kernel.+(1)
  end

おそらく最後から逆に計算して、計算結果を覚えておいた方が効率的に計算できそうですが、今回はそこまでやらずに愚直に毎回計算しています

まとめ

ちょっとややこしい再帰処理が出てきました

徐々に難しくなっている感じがしますね

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