Powered by AppSignal & Oban Pro
Would you like to see your link here? Contact us

Advent of Code - Day 1

2023_day1.livemd

Advent of Code - Day 1

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

Introduction

–> Content

Puzzle

{:ok, puzzle_input} =
  KinoAOC.download_puzzle("2023", "1", System.fetch_env!("LB_AOC_SESSION"))

Parser

Code - Parser

defmodule Parser do
  def parse(input) do
  end
end

Tests - Parser

ExUnit.start(autorun: false)

defmodule ParserTest do
  use ExUnit.Case, async: true
  import Parser

  @input ""
  @expected nil

  test "parse test" do
    actual = parse(@input)
    assert actual == @expected
  end
end

ExUnit.run()

Part One

Code - Part 1

defmodule PartOne do
  def solve(input) do
    IO.puts("--- Part One ---")
    IO.puts("Result: #{run(input)}")
  end

  def run(input) do
    input_list = String.split(input, "\n", trim: true)

    Enum.map(input_list, fn string ->
      extract_value(string)
    end)
    |> Enum.sum()
  end

  defp extract_value(string) do
    digits = Regex.scan(~r/\d/, string)
    first = hd(digits) |> hd()
    last = List.last(digits) |> hd()
    (first <> last) |> String.to_integer()
  end
end

Tests - Part 1

ExUnit.start(autorun: false)

defmodule PartOneTest do
  use ExUnit.Case, async: true
  import PartOne

  @input """
  1abc2
  pqr3stu8vwx
  a1b2c3d4e5f
  treb7uchet
  """
  @expected 142

  test "simple example" do
    actual = run(@input)
    assert actual == @expected
  end
end

ExUnit.run()

Solution - Part 1

PartOne.solve(puzzle_input)
String.split(puzzle_input, "\n", trim: true)

Part Two

Code - Part 2

defmodule PartTwo do
  def solve(input) do
    IO.puts("--- Part Two ---")
    IO.puts("Result: #{run(input)}")
  end

  def run(input) do
    input_list = String.split(input, "\n", trim: true)

    Enum.map(input_list, fn string ->
      IO.puts(string <> " : " <> Integer.to_string(extract_value(string)))
      extract_value(string)
    end)
    |> Enum.sum()
  end

  defp extract_value(string) do
    matches = Regex.scan(~r/(?=(\d|zero|one|two|three|four|five|six|seven|eight|nine))/, string)
    first = hd(matches) |> List.last() |> convert_word_to_digit()
    last = List.last(matches) |> List.last() |> convert_word_to_digit()
    (first <> last) |> String.to_integer()
  end

  @word_to_digit %{
    "zero" => "0",
    "one" => "1",
    "two" => "2",
    "three" => "3",
    "four" => "4",
    "five" => "5",
    "six" => "6",
    "seven" => "7",
    "eight" => "8",
    "nine" => "9"
  }

  defp convert_word_to_digit(string) do
    @word_to_digit[string] || string
  end
end

Tests - Part 2

ExUnit.start(autorun: false)

defmodule PartTwoTest do
  use ExUnit.Case, async: true
  import PartTwo

  @input """
  two1nine
  eightwothree
  abcone2threexyz
  xtwone3four
  4nineeightseven2
  zoneight234
  7pqrstsixteen
  """
  @expected 281

  test "part two" do
    actual = run(@input)
    assert actual == @expected
  end

  test "overlapping strings" do
    actual = run("sevenine")
    assert actual == 79
  end
end

ExUnit.run()

Solution - Part 2

PartTwo.solve(puzzle_input)