defmodule Day9 do def init_state(p) when p == :part1 do {[{0, 0}, {0, 0}], MapSet.new()} end def init_state(p) when p == :part2 do {[{0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}, {0, 0}], MapSet.new()} end def execute(line, state) when line == "" do state end def execute(line, state) do [movement, count] = line |> String.split(" ") {count, _} = Integer.parse(count) Enum.reduce( 1..count, state, fn _, state -> {rope, tail_positions} = state rope = movement |> string_to_movement_direction() |> move_rope_by_head(rope) {rope, MapSet.put(tail_positions, List.last(rope))} end ) end def get_answer(state) do {_, tail_positions} = state MapSet.size(tail_positions) end defp move_rope_by_head(direction, rope) do [head | tail] = rope reconcile_tail([add_2_tuple(head, direction)], tail) end defp reconcile_tail(_, tail) when tail == [] do tail end defp reconcile_tail(head, tail) do leader = head |> List.last() [follower | tail] = tail reconcile_tail(head ++ [keep_min_distance(follower, leader, 1)], tail) end defp keep_min_distance(tail, head, min_distance) do difference = distance_vector(tail, head) if max_coord_difference(tail, head) <= min_distance do tail else {a, b} = difference add_2_tuple(tail, {megan(a, 0, -1, 1), megan(b, 0, -1, 1)}) end end defp megan(x, _, _, pos) when x > 0 do pos end defp megan(x, _, neg, _) when x < 0 do neg end defp megan(0, zero, _, _) do zero end defp string_to_movement_direction(direction) do case direction do "R" -> {1, 0} "L" -> {-1, 0} "U" -> {0, 1} "D" -> {0, -1} end end defp add_2_tuple(x, y) do {elem(x, 0) + elem(y, 0), elem(x, 1) + elem(y, 1)} end defp distance_vector(x, y) do {elem(y, 0) - elem(x, 0), elem(y, 1) - elem(x, 1)} end defp max_coord_difference(x, y) do {a, b} = {abs(elem(x, 0) - elem(y, 0)), abs(elem(x, 1) - elem(y, 1))} max(a, b) end end