diff --git a/day_2/.github/workflows/test.yml b/day_2/.github/workflows/test.yml new file mode 100644 index 0000000..f6e42b5 --- /dev/null +++ b/day_2/.github/workflows/test.yml @@ -0,0 +1,23 @@ +name: test + +on: + push: + branches: + - master + - main + pull_request: + +jobs: + test: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: erlef/setup-beam@v1 + with: + otp-version: "28" + gleam-version: "1.13.0" + rebar3-version: "3" + # elixir-version: "1" + - run: gleam deps download + - run: gleam test + - run: gleam format --check src test diff --git a/day_2/.gitignore b/day_2/.gitignore new file mode 100644 index 0000000..599be4e --- /dev/null +++ b/day_2/.gitignore @@ -0,0 +1,4 @@ +*.beam +*.ez +/build +erl_crash.dump diff --git a/day_2/README.md b/day_2/README.md new file mode 100644 index 0000000..964517a --- /dev/null +++ b/day_2/README.md @@ -0,0 +1,24 @@ +# day_2 + +[![Package Version](https://img.shields.io/hexpm/v/day_2)](https://hex.pm/packages/day_2) +[![Hex Docs](https://img.shields.io/badge/hex-docs-ffaff3)](https://hexdocs.pm/day_2/) + +```sh +gleam add day_2@1 +``` +```gleam +import day_2 + +pub fn main() -> Nil { + // TODO: An example of the project in use +} +``` + +Further documentation can be found at . + +## Development + +```sh +gleam run # Run the project +gleam test # Run the tests +``` diff --git a/day_2/gleam.toml b/day_2/gleam.toml new file mode 100644 index 0000000..67bb76f --- /dev/null +++ b/day_2/gleam.toml @@ -0,0 +1,20 @@ +name = "day_2" +version = "1.0.0" + +# Fill out these fields if you intend to generate HTML documentation or publish +# your project to the Hex package manager. +# +# description = "" +# licences = ["Apache-2.0"] +# repository = { type = "github", user = "", repo = "" } +# links = [{ title = "Website", href = "" }] +# +# For a full reference of all the available options, you can have a look at +# https://gleam.run/writing-gleam/gleam-toml/. + +[dependencies] +gleam_stdlib = ">= 0.44.0 and < 2.0.0" +simplifile = ">= 2.3.1 and < 3.0.0" + +[dev-dependencies] +gleeunit = ">= 1.0.0 and < 2.0.0" diff --git a/day_2/input.txt b/day_2/input.txt new file mode 100644 index 0000000..825afb6 --- /dev/null +++ b/day_2/input.txt @@ -0,0 +1 @@ +24-46,124420-259708,584447-720297,51051-105889,6868562486-6868811237,55-116,895924-1049139,307156-347325,372342678-372437056,1791-5048,3172595555-3172666604,866800081-866923262,5446793-5524858,6077-10442,419-818,57540345-57638189,2143479-2274980,683602048-683810921,966-1697,56537997-56591017,1084127-1135835,1-14,2318887654-2318959425,1919154462-1919225485,351261-558210,769193-807148,4355566991-4355749498,809094-894510,11116-39985,9898980197-9898998927,99828221-99856128,9706624-9874989,119-335 diff --git a/day_2/manifest.toml b/day_2/manifest.toml new file mode 100644 index 0000000..6139ca9 --- /dev/null +++ b/day_2/manifest.toml @@ -0,0 +1,14 @@ +# This file was generated by Gleam +# You typically do not need to edit this file + +packages = [ + { name = "filepath", version = "1.1.2", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "filepath", source = "hex", outer_checksum = "B06A9AF0BF10E51401D64B98E4B627F1D2E48C154967DA7AF4D0914780A6D40A" }, + { name = "gleam_stdlib", version = "0.67.0", build_tools = ["gleam"], requirements = [], otp_app = "gleam_stdlib", source = "hex", outer_checksum = "6368313DB35963DC02F677A513BB0D95D58A34ED0A9436C8116820BF94BE3511" }, + { name = "gleeunit", version = "1.9.0", build_tools = ["gleam"], requirements = ["gleam_stdlib"], otp_app = "gleeunit", source = "hex", outer_checksum = "DA9553CE58B67924B3C631F96FE3370C49EB6D6DC6B384EC4862CC4AAA718F3C" }, + { name = "simplifile", version = "2.3.1", build_tools = ["gleam"], requirements = ["filepath", "gleam_stdlib"], otp_app = "simplifile", source = "hex", outer_checksum = "957E0E5B75927659F1D2A1B7B75D7B9BA96FAA8D0C53EA71C4AD9CD0C6B848F6" }, +] + +[requirements] +gleam_stdlib = { version = ">= 0.44.0 and < 2.0.0" } +gleeunit = { version = ">= 1.0.0 and < 2.0.0" } +simplifile = { version = ">= 2.3.1 and < 3.0.0" } diff --git a/day_2/src/day_2.gleam b/day_2/src/day_2.gleam new file mode 100644 index 0000000..b1fbe3d --- /dev/null +++ b/day_2/src/day_2.gleam @@ -0,0 +1,77 @@ +import gleam/int +import gleam/io +import gleam/list +import gleam/result +import gleam/string +import simplifile + +pub fn digits(x: Int, base: Int) -> Result(List(Int), Nil) { + case base < 2 { + True -> Error(Nil) + False -> Ok(digits_loop(x, base, [])) + } +} + +fn digits_loop(x: Int, base: Int, acc: List(Int)) -> List(Int) { + case int.absolute_value(x) < base { + True -> [x, ..acc] + False -> digits_loop(x / base, base, [x % base, ..acc]) + } +} + +pub fn int_to_half_length_pair(i: Int) -> Result(#(List(Int), List(Int)), Nil) { + digits(i, 10) + |> result.map(fn(l) { list.split(list: l, at: list.length(l) / 2) }) +} + +pub fn int_is_doubled_string(i: Int) -> Result(Bool, Nil) { + int_to_half_length_pair(i) + |> result.map(fn(p) { + let #(start, end) = p + start == end + }) +} + +pub fn filter_for_bad_ids(ids: List(Int)) -> List(Int) { + list.filter(ids, fn(id) { int_is_doubled_string(id) |> result.unwrap(False) }) +} + +pub fn ranges_to_list_of_ids(ids: List(#(Int, Int))) -> List(Int) { + list.map(ids, fn(id_pair) { + let #(first, last) = id_pair + list.range(first, last) + }) + |> list.flatten +} + +pub fn ranges_to_bad_ids(ids: List(#(Int, Int))) -> List(Int) { + ranges_to_list_of_ids(ids) |> filter_for_bad_ids +} + +pub fn sum_of_bad_ids(ids: List(#(Int, Int))) -> Int { + ranges_to_bad_ids(ids) |> list.fold(0, int.add) +} + +pub fn range_string_to_int_pair(s: String) -> #(Int, Int) { + let assert [a, b] = + string.split(s, on: "-") + |> list.map(int.parse) + |> list.map(result.unwrap(_, 0)) + #(a, b) +} + +pub fn list_of_ranges_to_int_pairs(s: String) -> List(#(Int, Int)) { + string.split(s, on: ",") |> list.map(range_string_to_int_pair) +} + +pub fn file_to_sum_of_bad_ids(f: String) -> Int { + simplifile.read(f) + |> result.unwrap("") + |> string.trim + |> list_of_ranges_to_int_pairs + |> sum_of_bad_ids +} + +pub fn main() -> Nil { + file_to_sum_of_bad_ids("input.txt") |> int.to_string |> io.println +} diff --git a/day_2/test/calibration.txt b/day_2/test/calibration.txt new file mode 100644 index 0000000..a3f22ef --- /dev/null +++ b/day_2/test/calibration.txt @@ -0,0 +1 @@ +11-22,95-115,998-1012,1188511880-1188511890,222220-222224,1698522-1698528,446443-446449,38593856-38593862,565653-565659,824824821-824824827,2121212118-2121212124 diff --git a/day_2/test/day_2_test.gleam b/day_2/test/day_2_test.gleam new file mode 100644 index 0000000..63d6d35 --- /dev/null +++ b/day_2/test/day_2_test.gleam @@ -0,0 +1,115 @@ +import day_2 +import gleeunit + +pub fn main() -> Nil { + gleeunit.main() +} + +pub fn trivial_digits_test() { + assert day_2.int_to_half_length_pair(1) == Ok(#([], [1])) +} + +pub fn two_digit_number_test() { + assert day_2.int_to_half_length_pair(12) == Ok(#([1], [2])) +} + +pub fn ten_digit_number_test() { + assert day_2.int_to_half_length_pair(1_234_567_890) + == Ok(#([1, 2, 3, 4, 5], [6, 7, 8, 9, 0])) +} + +pub fn eleven_digit_number_test() { + assert day_2.int_to_half_length_pair(12_345_678_901) + == Ok(#([1, 2, 3, 4, 5], [6, 7, 8, 9, 0, 1])) +} + +pub fn bad_number_test() { + assert day_2.int_is_doubled_string(123_123) == Ok(True) +} + +pub fn good_number_test() { + assert day_2.int_is_doubled_string(123_456) == Ok(False) +} + +pub fn list_of_ranges_test() { + assert day_2.ranges_to_list_of_ids([#(10, 20), #(30, 40)]) + == [ + 10, + 11, + 12, + 13, + 14, + 15, + 16, + 17, + 18, + 19, + 20, + 30, + 31, + 32, + 33, + 34, + 35, + 36, + 37, + 38, + 39, + 40, + ] +} + +pub fn ranges_with_bad_ids_test() { + assert day_2.ranges_to_bad_ids([ + #(11, 22), + #(95, 115), + #(998, 1012), + #(1_188_511_880, 1_188_511_890), + #(222_220, 222_224), + #(1_698_522, 1_698_528), + #(446_443, 446_449), + #(38_593_856, 38_593_862), + ]) + == [11, 22, 99, 1010, 1_188_511_885, 222_222, 446_446, 38_593_859] +} + +pub fn sum_of_bad_ids_test() { + assert day_2.sum_of_bad_ids([ + #(11, 22), + #(95, 115), + #(998, 1012), + #(1_188_511_880, 1_188_511_890), + #(222_220, 222_224), + #(1_698_522, 1_698_528), + #(446_443, 446_449), + #(38_593_856, 38_593_862), + ]) + == 1_227_775_554 +} + +pub fn range_parse_test() { + assert day_2.range_string_to_int_pair("111-333") == #(111, 333) +} + +pub fn ranges_parse_test() { + let ranges = + "11-22,95-115,998-1012,1188511880-1188511890,222220-222224,1698522-1698528,446443-446449,38593856-38593862,565653-565659,824824821-824824827,2121212118-2121212124" + assert day_2.list_of_ranges_to_int_pairs(ranges) + == [ + #(11, 22), + #(95, 115), + #(998, 1012), + #(1_188_511_880, 1_188_511_890), + #(222_220, 222_224), + #(1_698_522, 1_698_528), + #(446_443, 446_449), + #(38_593_856, 38_593_862), + #(565_653, 565_659), + #(824_824_821, 824_824_827), + #(2_121_212_118, 2_121_212_124), + ] +} + +pub fn calibrate() { + assert day_2.file_to_sum_of_bad_ids("test/calibration.txt") == 1_227_775_554 +}