Powered by AppSignal & Oban Pro

Day 03

day03.livemd

Day 03

Mix.install([
  {:kino, "~> 0.14.2"}
])

IEx.Helpers.c("/Users/johnb/dev/2015adventOfCode/advent_of_code.ex")
alias AdventOfCode, as: AOC
alias Kino.Input
require Integer

# Note: when making the next template, something like this works well:
#   `cat day04.livemd | sed 's/03/04/' > day04.livemd`
#

Installation and Data

input_p1example = Kino.Input.textarea("Example Data")
input_p1puzzleInput = Kino.Input.textarea("Puzzle Input")
input_source_select =
  Kino.Input.select("Source", [{:example, "example"}, {:puzzle_input, "puzzle input"}])
p1data = fn ->
  (Kino.Input.read(input_source_select) == :example &&
     Kino.Input.read(input_p1example)) ||
    Kino.Input.read(input_p1puzzleInput)
end

Part 1

defmodule Day03 do
  @mapwidth 10_000 # not quite infinite, but just add zeros
  @north "^"
  @south "v"
  @east ">"
  @west "<"
  @deltas %{@north => -@mapwidth, @south => @mapwidth, @east => 1, @west => -1}

  def split_positions(text) do
    String.split(text, "", trim: true)
  end

  def track_visited_houses(list) do
    list
    |> Enum.reduce(%{0 => 1, :position => 0}, fn direction, acc = %{position: position} ->
      new_position = position + @deltas[direction]
      new_position_count = acc[new_position] || 1
      
      acc
      |> Map.put(:position, new_position)
      |> Map.put(new_position, new_position_count)
    end)    
    |> Map.delete(:position)
  end
  
  def solve1(text) do
    text
    |> split_positions()
    |> track_visited_houses()
    |> Enum.count()
  end

  def split_santa_from_robo_santa(list) do
    list
    |> Enum.reduce([[],[]], fn direction, _acc = [left, right] ->
      # Count the lists so I don't have to use with_index and then remove the index
      if Enum.count(left) > Enum.count(right) do
        [left, [direction | right]]
      else
        [[direction | left], right]
      end
    end)
    # Reverse them back to the original orderings
    |> Enum.map(&Enum.reverse/1)
  end

  def solve2(text) do
    [santa_directions, robo_santa_directions] =
      text
      |> split_positions()
      |> split_santa_from_robo_santa()

    santa_houses = track_visited_houses(santa_directions)
    robo_santa_houses = track_visited_houses(robo_santa_directions)

    Map.merge(santa_houses, robo_santa_houses)
    |> Enum.count()
  end
end

# Example:
# ^v^v^v^v^v
p1data.()
|> Day03.solve1()
|> IO.inspect(label: "\n*** Part 1 solution (example: 2)")
# 2592

p1data.()
|> Day03.solve2()
|> IO.inspect(label: "\n*** Part 2 solution (example: 11)")
# 2360