-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
d639d84
commit cc0b610
Showing
3 changed files
with
172 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |