diff --git a/sample/day3-sample.txt b/sample/day3-sample.txt new file mode 100644 index 0000000..f274bda --- /dev/null +++ b/sample/day3-sample.txt @@ -0,0 +1 @@ +xmul(2,4)%&mul[3,7]!@^do_not_mul(5,5)+mul(32,64]then(mul(11,8)mul(8,5)) diff --git a/sample/day3-sample2.txt b/sample/day3-sample2.txt new file mode 100644 index 0000000..159557d --- /dev/null +++ b/sample/day3-sample2.txt @@ -0,0 +1,2 @@ +xmul(2,4)&mul[3,7]!^don't()_mul(5,5)+mul(32,64](mul(11,8)undo()?mul(8,5)) + diff --git a/src/day3_p1.gleam b/src/day3_p1.gleam new file mode 100644 index 0000000..983555d --- /dev/null +++ b/src/day3_p1.gleam @@ -0,0 +1,97 @@ +import gleam/int +import gleam/io +import gleam/iterator +import gleam/list.{reverse, take_while} +import gleam/option.{type Option, None, Some} +import gleam/string.{join} +import stdin.{stdin} + +pub fn main() { + stdin() + |> read_input + |> find_and_calculate(0) + |> io.debug +} + +type FindResult { + Expression(Int, Int, String) + Nothing +} + +fn find_and_calculate(s: String, acc: Int) -> Int { + // Drop all until first 'mul(' + let next = + string.crop(from: s, before: "mul(") + |> string.drop_start(up_to: 4) + + case next { + "" -> acc + str -> { + case find_expression(str) { + Expression(a, b, rest) -> find_and_calculate(rest, acc + a * b) + Nothing -> find_and_calculate(next, acc) + } + } + } +} + +fn find_expression(str: String) -> FindResult { + case + str + |> read_num + |> then(read_char(",")) + |> then(read_num) + |> then(read_char(")")) + { + Some(#(#(#(#(a, _), b), _), rest)) -> Expression(a, b, rest) + None -> Nothing + } +} + +fn read_char(char: String) { + fn(str: String) -> Option(#(String, String)) { + case string.pop_grapheme(str) { + Ok(#(ch, rest)) if ch == char -> Some(#(char, rest)) + _ -> None + } + } +} + +fn then( + current: Option(#(a, String)), + next: fn(String) -> Option(#(b, String)), +) -> Option(#(#(a, b), String)) { + case current { + None -> None + Some(#(a, s)) -> + next(s) + |> option.map(fn(result) { + let #(b, rest) = result + #(#(a, b), rest) + }) + } +} + +const numbers = "0123456789" + +fn read_num(str: String) -> Option(#(Int, String)) { + let to_parse = + str + |> string.to_graphemes + |> take_while(fn(c) { string.contains(does: numbers, contain: c) }) + |> join(with: "") + + to_parse + |> int.parse + |> option.from_result + |> option.map(fn(n) { + #(n, string.drop_start(str, up_to: string.length(to_parse))) + }) +} + +fn read_input(it: iterator.Iterator(String)) -> String { + it + |> iterator.fold([], fn(acc, line) { [line, ..acc] }) + |> reverse + |> join(with: "") +} diff --git a/src/day3_p2.gleam b/src/day3_p2.gleam new file mode 100644 index 0000000..2aff4cb --- /dev/null +++ b/src/day3_p2.gleam @@ -0,0 +1,96 @@ +import gleam/int +import gleam/io +import gleam/iterator +import gleam/list.{reverse, take_while} +import gleam/option.{type Option, None, Some} +import gleam/string.{join} +import stdin.{stdin} + +pub fn main() { + stdin() + |> read_input + |> find_and_calculate(0, can_mul: True) + |> io.debug +} + +type FindResult { + Expression(Int, Int, String) + Nothing +} + +fn find_and_calculate(s: String, acc: Int, can_mul mul_enabled: Bool) -> Int { + case s { + "" -> acc + "do()" <> rest -> find_and_calculate(rest, acc, can_mul: True) + "don't()" <> rest -> find_and_calculate(rest, acc, can_mul: False) + "mul(" <> rest if mul_enabled -> { + case find_expression(rest) { + Expression(a, b, rest) -> + find_and_calculate(rest, acc + a * b, can_mul: mul_enabled) + Nothing -> find_and_calculate(rest, acc, can_mul: mul_enabled) + } + } + _ -> find_and_calculate(string.drop_start(s, 1), acc, can_mul: mul_enabled) + } +} + +fn find_expression(str: String) -> FindResult { + case + str + |> read_num + |> then(read_char(",")) + |> then(read_num) + |> then(read_char(")")) + { + Some(#(#(#(#(a, _), b), _), rest)) -> Expression(a, b, rest) + None -> Nothing + } +} + +fn read_char(char: String) { + fn(str: String) -> Option(#(String, String)) { + case string.pop_grapheme(str) { + Ok(#(ch, rest)) if ch == char -> Some(#(char, rest)) + _ -> None + } + } +} + +fn then( + current: Option(#(a, String)), + next: fn(String) -> Option(#(b, String)), +) -> Option(#(#(a, b), String)) { + case current { + None -> None + Some(#(a, s)) -> + next(s) + |> option.map(fn(result) { + let #(b, rest) = result + #(#(a, b), rest) + }) + } +} + +const numbers = "0123456789" + +fn read_num(str: String) -> Option(#(Int, String)) { + let to_parse = + str + |> string.to_graphemes + |> take_while(fn(c) { string.contains(does: numbers, contain: c) }) + |> join(with: "") + + to_parse + |> int.parse + |> option.from_result + |> option.map(fn(n) { + #(n, string.drop_start(str, up_to: string.length(to_parse))) + }) +} + +fn read_input(it: iterator.Iterator(String)) -> String { + it + |> iterator.fold([], fn(acc, line) { [line, ..acc] }) + |> reverse + |> join(with: "") +}