Skip to content

Commit

Permalink
✨ Add solution to day 3 part 1
Browse files Browse the repository at this point in the history
  • Loading branch information
simonrainerson committed Dec 16, 2023
1 parent d639d84 commit cc0b610
Show file tree
Hide file tree
Showing 3 changed files with 172 additions and 0 deletions.
1 change: 1 addition & 0 deletions advent_of_code/lib/advent_of_code.ex
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,6 @@ defmodule AdventOfCode do
print(1, 2, Day1.part2())
print(2, 1, Day2.part1())
print(2, 2, Day2.part2())
print(3, 1, Day3.part1())
end
end
98 changes: 98 additions & 0 deletions advent_of_code/lib/day_3.ex
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
defmodule EnginePart do
@moduledoc """
"""
@enforce_keys [:value, :top_left, :bottom_right]
defstruct @enforce_keys

@doc """
Get the value for the engine part, an engine part is only worth something if
it is next to a symbol.
"""
def value_for(%EnginePart{value: value, top_left: {l, t}, bottom_right: {r, b}}, symbols) do
to_check = for x <- l..r, y <- t..b, do: {x, y}

if Enum.any?(to_check, &(&1 in symbols)) do
value
else
0
end
end

@doc """
Create an EnginePart from a sequence of string digits,
a start number (index into the row, i.e. x position) and
row number, i.e. y position.
"""
def from_sequence(seq, start, row) do
%EnginePart{
value: seq |> Enum.reduce("", fn x, str -> str <> x end) |> String.to_integer(),
top_left: {start - 1, row - 1},
bottom_right: {start + Enum.count(seq), row + 1}
}
end
end

defmodule Day3 do
@moduledoc """
Find numbers adjecent to symbols, orthogonally or diagonally.
Add these numbers up.
"""

defp classify(char) do
char
|> case do
"." -> :dot
x when x in ["1", "2", "3", "4", "5", "6", "7", "8", "9", "0"] -> :number
_ -> :symbol
end
end

defp find_engine_parts_and_symbols({text, row}, {engine_parts, symbols}) do
{_, engine_parts, symbols} =
text
|> String.codepoints()
|> Enum.chunk_by(&classify/1)
|> Enum.reduce(
{0, engine_parts, symbols},
fn sequence, {i, engine_parts, symbols} ->
next = i + Enum.count(sequence)

case hd(sequence) |> classify() do
:dot ->
{next, engine_parts, symbols}

:number ->
{next, [EnginePart.from_sequence(sequence, i, row) | engine_parts], symbols}

:symbol ->
{next, engine_parts,
(sequence
|> Enum.with_index()
|> Enum.map(fn {_, offset} -> {i + offset, row} end)) ++ symbols}
end
end
)

{engine_parts, symbols}
end

@doc """
Parse input into [%EnginePart{}] and [{x, y}]
"""
def parse_input(stream) do
stream
|> Stream.with_index()
|> Enum.reduce({[], []}, &find_engine_parts_and_symbols/2)
end

@doc """
Solve part1 of Day 3.
"""
def part1(stream \\ Inputs.stream(3)) do
{engine_parts, symbols} = parse_input(stream)

engine_parts
|> Enum.map(&EnginePart.value_for(&1, symbols))
|> Enum.sum()
end
end
73 changes: 73 additions & 0 deletions advent_of_code/test/day3_test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
defmodule Day3Test do
use ExUnit.Case

describe "describe day 3" do
test "EniginePart.value_for/2" do
engine_part = %EnginePart{
value: 321,
top_left: {4, 3},
bottom_right: {8, 5}
}

symbols = [{1, 1}, {3, 4}, {1, 4}]
assert 0 = engine_part |> EnginePart.value_for(symbols)
symbols = [{2, 2}, {5, 4}, {9, 9}]
assert 321 = engine_part |> EnginePart.value_for(symbols)
symbols = [{0, 0}, {4, 5}]
assert 321 = engine_part |> EnginePart.value_for(symbols)
end

test "EnginePart.from_sequence/3" do
%EnginePart{
value: v,
top_left: {l, t},
bottom_right: {r, b}
} = EnginePart.from_sequence(["1", "2", "4"], 6, 3)

assert v == 124
assert l == 5
assert r == 9
assert t == 2
assert b == 4
end

test "Day3.parse_input/1" do
{_engine_parts, symbols} =
Day3.parse_input([
"467..114..",
"...*......",
"..35..633.",
"......#...",
"617*......",
".....+.58.",
"..592.....",
"......755.",
"...$.*....",
".664.598.."
])

assert {3, 1} in symbols
assert {6, 3} in symbols
assert {3, 4} in symbols
assert {5, 5} in symbols
assert {3, 8} in symbols
assert {5, 8} in symbols
end

test "Day3.part1/1" do
assert 4361 =
Day3.part1([
"467..114..",
"...*......",
"..35..633.",
"......#...",
"617*......",
".....+.58.",
"..592.....",
"......755.",
"...$.*....",
".664.598.."
])
end
end
end

0 comments on commit cc0b610

Please sign in to comment.