$\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 の入力を取得します
私の答え
私の答えです。
折りたたんでおきます。
▶を押して開いてください。
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 となります
まとめ
今回もかなり愚直に解きました
前のトランプと同様、迷路というテーマが面白かったです
実装としては関数の when
で is_map_key
を使えたのが良かったです
Part 2 はこちら