LoginSignup
8
1

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

Last updated at Posted at 2023-12-27

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

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

はじめに

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

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

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

問題はこちら

セットアップ

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

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

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

入力の取得

Day 10 の入力を取得します

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

私の答え

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

details

回答用のモジュールです

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

defmodule Resolver do
  @pipe_map %{
    "-" => %{left: true, right: true},
    "|" => %{up: true, down: true},
    "F" => %{down: true, right: true},
    "7" => %{down: true, left: true},
    "L" => %{up: true, right: true},
    "J" => %{up: true, left: true},
    "S" => %{start: true, up: true, down: true, left: true, right: true}
  }

  @next_direction %{
    up: :down,
    down: :up,
    left: :right,
    right: :left,
  }

  def parse(input) do
    input
    |> String.split("\n")
    |> Enum.with_index()
    |> Enum.reduce(%{}, fn {line, row_index}, acc ->
      line_maze =
        line
        |> String.codepoints()
        |> Enum.with_index()
        |> Enum.into(%{}, fn {pipe, col_index} ->
          {
            {row_index, col_index},
            Map.get(@pipe_map, pipe, nil)
          }
        end)
        |> Map.filter(fn {_, route} -> !is_nil(route) end)

      Map.merge(acc, line_maze)
    end)
  end

  def resolve(maze) do
    {position, route} =
      maze
      |> Enum.filter(fn {_, route} ->
        Map.has_key?(route, :start)
      end)
      |> hd()

    Stream.iterate(0, & &1 + 1)
    |> Enum.reduce_while({position, route, :start}, fn index, {next_position, next_route, pre_direction} ->
      {next_position, next_route, pre_direction} =
        search_next(next_position, next_route, pre_direction, maze)
  
      IO.inspect({next_position, next_route, pre_direction})
  
      if Map.has_key?(next_route, :start) do
        {:halt, div(index + 1, 2)}
      else
        {:cont, {next_position, next_route, pre_direction}}
      end
    end)
  end

  defp search_next(position, route, pre_direction, maze) do
    [:up, :down, :left, :right]
    |> Enum.filter(& (&1 != pre_direction))
    |> Enum.map(fn direction ->
      get_next(position, route, direction, maze)
    end)
    |> Enum.filter(fn {_, next_route, _} ->
      !is_nil(next_route)
    end)
    |> hd()
  end

  defp get_next({row_index, col_index}, route, direction, maze) when is_map_key(route, direction) do
    next_position =
      case direction do
        :up -> {row_index - 1, col_index}
        :down -> {row_index + 1, col_index}
        :left -> {row_index, col_index - 1}
        :right -> {row_index, col_index + 1}
      end

    next_direction = Map.get(@next_direction, direction)

    next_route =
      maze
      |> Map.get(next_position)
      |> check_next(next_direction)

    {next_position, next_route, next_direction}
  end
  defp get_next(_, _, _, _), do: {nil, nil, nil}

  defp check_next(nil, _), do: nil
  defp check_next(next_route, next_direction) when is_map_key(next_route, next_direction) do
    next_route
  end
  defp check_next(_, _), do: nil
end

parse で各パイプを座標とルート(どの方向に行けるか)として読み取ります
スタート地点はとりあえず全方向に移動可能としておきます

入力

.....
.S-7.
.|.|.
.L-J.
.....

パース結果

%{
  {1, 1} => %{start: true, up: true, down: true, left: true, right: true},
  {1, 2} => %{left: true, right: true},
  {1, 3} => %{down: true, left: true},
  {2, 1} => %{up: true, down: true},
  {2, 3} => %{up: true, down: true},
  {3, 1} => %{up: true, right: true},
  {3, 2} => %{left: true, right: true},
  {3, 3} => %{up: true, left: true}
}

resolve では、スタート地点から開始して、次に進めるパイプを探索していきます

スタート地点に到達したらループの完成です

行き止まりがなく、パイプが必ずループしている前提なので、スタート地点から最も遠い地点の距離 = ループの全長 / 2 となります

まとめ

今回もかなり愚直に解きました

前のトランプと同様、迷路というテーマが面白かったです

実装としては関数の whenis_map_key を使えたのが良かったです

Part 2 はこちら

8
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
8
1