diff options
42 files changed, 351 insertions, 669 deletions
diff --git a/2024/ocaml/.envrc b/2024/ocaml/.envrc deleted file mode 100644 index 3550a30..0000000 --- a/2024/ocaml/.envrc +++ /dev/null @@ -1 +0,0 @@ -use flake diff --git a/2024/ocaml/.gitignore b/2024/ocaml/.gitignore deleted file mode 100644 index a18e084..0000000 --- a/2024/ocaml/.gitignore +++ /dev/null @@ -1,29 +0,0 @@ -*.annot -*.cmo -*.cma -*.cmi -*.a -*.o -*.cmx -*.cmxs -*.cmxa - -# ocamlbuild working directory -_build/ - -# ocamlbuild targets -*.byte -*.native - -# oasis generated files -setup.data -setup.log - -# Merlin configuring file for Vim and Emacs -.merlin - -# Dune generated files -*.install - -# Local OPAM switch -_opam/ diff --git a/2024/ocaml/.ocamlformat b/2024/ocaml/.ocamlformat deleted file mode 100644 index 912bea4..0000000 --- a/2024/ocaml/.ocamlformat +++ /dev/null @@ -1,2 +0,0 @@ -profile = default -version = 0.27.0 diff --git a/2024/ocaml/AoC_2024.opam b/2024/ocaml/AoC_2024.opam deleted file mode 100644 index 6be14e1..0000000 --- a/2024/ocaml/AoC_2024.opam +++ /dev/null @@ -1,32 +0,0 @@ -# This file is generated by dune, edit dune-project instead -opam-version: "2.0" -synopsis: "Advent of Code 2024" -description: "Solutions to the Advent of Code puzzles for 2024" -maintainer: ["D.J. O'Leary"] -authors: ["D.J. O'Leary"] -license: "Unlicense" -tags: ["aoc2024" "Advent of Code 2024"] -homepage: "https://github.com/DJOLEARY/Advent_of_Code" -doc: - "https://github.com/DJOLEARY/Advent_of_Code/blob/main/2024/OCaml/README.md" -bug-reports: "https://github.com/DJOLEARY/Advent_of_Code/issues" -depends: [ - "ocaml" - "dune" {>= "3.16"} - "odoc" {with-doc} -] -build: [ - ["dune" "subst"] {dev} - [ - "dune" - "build" - "-p" - name - "-j" - jobs - "@install" - "@runtest" {with-test} - "@doc" {with-doc} - ] -] -dev-repo: "git+https://github.com/DJOLEARY/Advent_of_Code.git" diff --git a/2024/ocaml/README.md b/2024/ocaml/README.md deleted file mode 100644 index caa88fe..0000000 --- a/2024/ocaml/README.md +++ /dev/null @@ -1,21 +0,0 @@ -# OCaml 2024 - -## Getting Started - -### How to Build - -```sh -task build -``` - -### How to Run - -```sh -task run -``` - -### How to Test - -```sh -task test -``` diff --git a/2024/ocaml/Taskfile.yml b/2024/ocaml/Taskfile.yml deleted file mode 100644 index 1d8a89e..0000000 --- a/2024/ocaml/Taskfile.yml +++ /dev/null @@ -1,56 +0,0 @@ -# https://taskfile.dev - -version: "3" -vars: - PROJECT_NAME: AoC_2024 -tasks: - default: - desc: Prints this message - cmds: - - cmd: task --list-all - build:doc: - desc: Builds documentation using ocamldoc - preconditions: - - which ocamldoc - cmds: - - cmd: ocamldoc -html -d docs bin/main.ml - silent: true - build: - desc: Builds the project - preconditions: - - which dune - cmds: - - cmd: dune build - test: - desc: Runs the tests in the 'tests' directory - ignore_error: true - env: - OUNIT_CI: true - preconditions: - - which dune - deps: - - task: build - cmds: - - cmd: dune test {{.CLI_ARGS}} - test:watch: - desc: Continuously runs the tests in the 'tests' directory - ignore_error: true - sources: - - lib/**/*.ml - - test/**/*.ml - env: - OUNIT_CI: true - preconditions: - - which dune - deps: - - task: build - cmds: - - cmd: dune test --watch {{.CLI_ARGS}} - run: - desc: Runs the project - preconditions: - - which dune - deps: - - task: build - cmds: - - cmd: dune exec {{.PROJECT_NAME}} diff --git a/2024/ocaml/bin/dune b/2024/ocaml/bin/dune deleted file mode 100644 index ee36b46..0000000 --- a/2024/ocaml/bin/dune +++ /dev/null @@ -1,5 +0,0 @@ -(executable - (public_name AoC_2024) - (name main) - (modes byte exe) - (libraries AoC_2024)) diff --git a/2024/ocaml/bin/main.ml b/2024/ocaml/bin/main.ml deleted file mode 100644 index a76e046..0000000 --- a/2024/ocaml/bin/main.ml +++ /dev/null @@ -1,46 +0,0 @@ -open AoC_2024 - -let day_to_path day = - let string_day = string_of_int day in - let formatted_day = if day < 10 then "0" ^ string_day else string_day in - "../_input/day_" ^ formatted_day ^ ".txt" - -let print_solution day_num part_num answer = - print_string "D"; - print_int day_num; - print_string ":P"; - print_int part_num; - print_string " -> "; - print_int answer; - print_newline () - -let day_1 = - let lines = File.Read_input.lines_from_file (day_to_path 1) in - let left, right = Day_01.Part_01.lines_to_lists lines in - let part_01 = Day_01.Part_01.solve left right in - print_solution 1 1 part_01; - let part_02 = Day_01.Part_02.solve left right in - print_solution 1 2 part_02 - -let day_2 = - let lines = File.Read_input.lines_from_file (day_to_path 2) in - let reports = Day_02.Part_01.lines_to_reports lines in - let part_01 = Day_02.Part_01.solve reports in - print_solution 2 1 part_01 - -let day_3 = - let lines = File.Read_input.lines_from_file (day_to_path 3) in - let line = List.fold_left (fun acc line -> acc ^ line) "" lines in - let part_01 = Day_03.Part_01.solve line in - print_solution 3 1 part_01 - -let day_4 = - let lines = File.Read_input.lines_from_file (day_to_path 4) in - let part_01 = Day_04.Part_01.solve lines in - print_solution 4 1 part_01 - -let _ = - day_1; - day_2; - day_3; - day_4 diff --git a/2024/ocaml/dune-project b/2024/ocaml/dune-project deleted file mode 100644 index 3f3a7bc..0000000 --- a/2024/ocaml/dune-project +++ /dev/null @@ -1,28 +0,0 @@ -(lang dune 3.16) - -(name AoC_2024) - -(generate_opam_files true) - -(source - (github DJOLEARY/Advent_of_Code)) - -(authors "D.J. O'Leary") - -(maintainers "D.J. O'Leary") - -(license Unlicense) - -(documentation https://github.com/DJOLEARY/Advent_of_Code/blob/main/2024/OCaml/README.md) - -(map_workspace_root false) - -(package - (name AoC_2024) - (synopsis "Advent of Code 2024") - (description "Solutions to the Advent of Code puzzles for 2024") - (depends ocaml dune) - (tags - (aoc2024 "Advent of Code 2024"))) - -; See the complete stanza docs at https://dune.readthedocs.io/en/stable/reference/dune-project/index.html diff --git a/2024/ocaml/flake.lock b/2024/ocaml/flake.lock deleted file mode 100644 index b7695d9..0000000 --- a/2024/ocaml/flake.lock +++ /dev/null @@ -1,27 +0,0 @@ -{ - "nodes": { - "nixpkgs": { - "locked": { - "lastModified": 1741010256, - "narHash": "sha256-WZNlK/KX7Sni0RyqLSqLPbK8k08Kq7H7RijPJbq9KHM=", - "owner": "nixos", - "repo": "nixpkgs", - "rev": "ba487dbc9d04e0634c64e3b1f0d25839a0a68246", - "type": "github" - }, - "original": { - "owner": "nixos", - "ref": "nixos-unstable", - "repo": "nixpkgs", - "type": "github" - } - }, - "root": { - "inputs": { - "nixpkgs": "nixpkgs" - } - } - }, - "root": "root", - "version": 7 -} diff --git a/2024/ocaml/flake.nix b/2024/ocaml/flake.nix deleted file mode 100644 index 74a5dc6..0000000 --- a/2024/ocaml/flake.nix +++ /dev/null @@ -1,44 +0,0 @@ -{ - description = "Advent of Code 2024: OCaml"; - - inputs = { - nixpkgs.url = "github:nixos/nixpkgs?ref=nixos-unstable"; - }; - - outputs = - { nixpkgs, ... }: - let - pkgs = nixpkgs.legacyPackages.x86_64-linux; - in - { - devShells.x86_64-linux.default = - let - ocamlPackages = pkgs.ocaml-ng.ocamlPackages_5_2; - in - pkgs.mkShell { - # build tools - nativeBuildInputs = with ocamlPackages; [ - pkgs.opam - ocaml - findlib - dune_3 - ]; - # dependencies - buildInputs = with ocamlPackages; [ - bisect_ppx - findlib - menhir - ocaml-lsp - earlybird - ocamlformat - ocamlgraph - odoc - ounit2 - re2 - utop - - pkgs.gh # GitHub CLI - ]; - }; - }; -} diff --git a/2024/ocaml/lib/file/read_input.ml b/2024/ocaml/lib/file/read_input.ml deleted file mode 100644 index 0f7470b..0000000 --- a/2024/ocaml/lib/file/read_input.ml +++ /dev/null @@ -1,11 +0,0 @@ -let rec lines_from_channel ic = - try - let line = input_line ic in - line :: lines_from_channel ic - with End_of_file -> [] - -let lines_from_file filename = - let channel = open_in filename in - let lines = lines_from_channel channel in - close_in channel; - lines diff --git a/2024/ocaml/lib/grid/cell.ml b/2024/ocaml/lib/grid/cell.ml deleted file mode 100644 index 186e451..0000000 --- a/2024/ocaml/lib/grid/cell.ml +++ /dev/null @@ -1,18 +0,0 @@ -type t = { - value : string; - up : t option; - down : t option; - left : t option; - right : t option; -} - -let create value = { value; up = None; down = None; left = None; right = None } -let with_up up c = { c with up = Some up } -let with_down down c = { c with down = Some down } -let with_left left c = { c with left = Some left } -let with_right right c = { c with right = Some right } -let move_up co = match co with Some c -> c.up | None -> None -let move_down co = match co with Some c -> c.down | None -> None -let move_left co = match co with Some c -> c.left | None -> None -let move_right co = match co with Some c -> c.right | None -> None -let ( = ) c1 c2 = c1.value = c2.value diff --git a/2024/ocaml/lib/grid/cell.mli b/2024/ocaml/lib/grid/cell.mli deleted file mode 100644 index e61e34a..0000000 --- a/2024/ocaml/lib/grid/cell.mli +++ /dev/null @@ -1,18 +0,0 @@ -type t = { - value : string; - up : t option; - down : t option; - left : t option; - right : t option; -} - -val create : string -> t -val with_up : t -> t -> t -val with_down : t -> t -> t -val with_left : t -> t -> t -val with_right : t -> t -> t -val move_up : t option -> t option -val move_down : t option -> t option -val move_left : t option -> t option -val move_right : t option -> t option -val ( = ) : t -> t -> bool diff --git a/2024/ocaml/lib/grid/grid.ml b/2024/ocaml/lib/grid/grid.ml deleted file mode 100644 index 0d6bc4c..0000000 --- a/2024/ocaml/lib/grid/grid.ml +++ /dev/null @@ -1,14 +0,0 @@ -let rec to_grid_aux tokens max_width acc = - match tokens with - | [] -> acc - | h :: t -> - let new_cell = Cell.create h |> Cell.with_left acc in - let new_acc = acc |> Cell.with_right new_cell in - to_grid_aux t max_width new_acc - -let to_grid tokens max_width : Cell.t option = - match tokens with - | [] -> None - | h :: t -> - let first_cell = Cell.create h in - Some (to_grid_aux t max_width first_cell) diff --git a/2024/ocaml/lib/grid/grid.mli b/2024/ocaml/lib/grid/grid.mli deleted file mode 100644 index a69fcfe..0000000 --- a/2024/ocaml/lib/grid/grid.mli +++ /dev/null @@ -1,6 +0,0 @@ -val to_grid : string list -> int -> Cell.t option -(** The cell in the top left corner of the grid is returned where each element - of [tokens] is converted into rows with [width]. An empty list of [tokens] - returns None. Requires: [width] is greater than 0 **) - -(* val print : Cell.t -> () *) diff --git a/2024/ocaml/test/day_01_part_01.ml b/2024/ocaml/test/day_01_part_01.ml deleted file mode 100644 index 824c95b..0000000 --- a/2024/ocaml/test/day_01_part_01.ml +++ /dev/null @@ -1,27 +0,0 @@ -open OUnit2 -open AoC_2024.Day_01.Part_01 - -let tests = - "test suite for day 01 part 01" - >::: [ - ("empty list is 0" >:: fun _ -> assert_equal 0 (solve [] [])); - ( "distance between 0 and 1 is 1" >:: fun _ -> - assert_equal 1 (solve [ 0 ] [ 1 ]) ); - ( "distance between 1 and 0 is 1" >:: fun _ -> - assert_equal 1 (solve [ 1 ] [ 0 ]) ); - ( "smallest are paired, then next smallest, ..." >:: fun _ -> - assert_equal 0 (solve [ 1; 2; 3 ] [ 3; 2; 1 ]) ); - (* See https://adventofcode.com/2024/day/1 *) - ( "solves example correctly" >:: fun _ -> - assert_equal 11 (solve [ 3; 4; 2; 1; 3; 3 ] [ 4; 3; 5; 3; 9; 3 ]) ); - ( "same number is 0 distance apart" >:: fun _ -> - assert_equal 0 (calculate_distance 0 0) ); - ( "same number is 0 distance apart" >:: fun _ -> - assert_equal 0 (calculate_distance 1 1) ); - ( "distance is equal to abs(x - y)" >:: fun _ -> - assert_equal 1 (calculate_distance 0 1) ); - ( "order of inputs doesn't matter" >:: fun _ -> - assert_equal 1 (calculate_distance 1 0) ); - ] - -let _ = run_test_tt_main tests diff --git a/2024/ocaml/test/day_01_part_02.ml b/2024/ocaml/test/day_01_part_02.ml deleted file mode 100644 index f7340df..0000000 --- a/2024/ocaml/test/day_01_part_02.ml +++ /dev/null @@ -1,17 +0,0 @@ -open OUnit2 -open AoC_2024.Day_01.Part_02 - -let test_solve = - "test suite for day 01 part 02" - >::: [ - ("empty list is 0" >:: fun _ -> assert_equal 0 (solve [] [])); - ( "ten x zero occurrances of ten" >:: fun _ -> - assert_equal 0 (solve [ 10 ] [ 1; 2; 3 ]) ); - ( "two x three occurrances of two" >:: fun _ -> - assert_equal 6 (solve [ 2 ] [ 2; 2; 2 ]) ); - (* See https://adventofcode.com/2024/day/1#part2 *) - ( "solves example correctly" >:: fun _ -> - assert_equal 31 (solve [ 3; 4; 2; 1; 3; 3 ] [ 4; 3; 5; 3; 9; 3 ]) ); - ] - -let _ = run_test_tt_main test_solve diff --git a/2024/ocaml/test/day_02_part_01.ml b/2024/ocaml/test/day_02_part_01.ml deleted file mode 100644 index 36af248..0000000 --- a/2024/ocaml/test/day_02_part_01.ml +++ /dev/null @@ -1,38 +0,0 @@ -open OUnit2 -open AoC_2024.Day_02.Part_01 - -let tests = - "test suite for day 2 part 1" - >::: [ - ( "empty list should have no valid reports" >:: fun _ -> - assert_equal 0 (solve []) ); - ( "valid report should count as one" >:: fun _ -> - assert_equal 1 (solve [ [ 7; 6; 4; 2; 1 ] ]) ); - ( "example correct" >:: fun _ -> - assert_equal 2 - (solve - [ - [ 7; 6; 4; 2; 1 ]; - [ 1; 2; 7; 8; 9 ]; - [ 9; 7; 6; 2; 1 ]; - [ 1; 3; 2; 4; 5 ]; - [ 8; 6; 4; 4; 1 ]; - [ 1; 3; 6; 7; 9 ]; - ]) ); - ( "all increasing" >:: fun _ -> - assert_equal true (check_report_safety [ 1; 2; 3 ]) ); - ( "all decreasing" >:: fun _ -> - assert_equal true (check_report_safety [ 3; 2; 1 ]) ); - ( "increasing and decreasing" >:: fun _ -> - assert_equal false (check_report_safety [ 1; 3; 2 ]) ); - ( "change of at least one" >:: fun _ -> - assert_equal true (check_report_safety [ 1; 2; 3 ]) ); - ( "no change" >:: fun _ -> - assert_equal false (check_report_safety [ 1; 1; 2 ]) ); - ( "change of at most three" >:: fun _ -> - assert_equal true (check_report_safety [ 1; 2; 4; 7 ]) ); - ( "change of at more than three" >:: fun _ -> - assert_equal false (check_report_safety [ 1; 5 ]) ); - ] - -let _ = run_test_tt_main tests diff --git a/2024/ocaml/test/day_03_part_01.ml b/2024/ocaml/test/day_03_part_01.ml deleted file mode 100644 index 0db039a..0000000 --- a/2024/ocaml/test/day_03_part_01.ml +++ /dev/null @@ -1,29 +0,0 @@ -open OUnit2 -open AoC_2024.Day_03.Part_01 - -let example_input = - "xmul(2,4)%&mul[3,7]!@^do_not_mul(5,5)+mul(32,64]then(mul(11,8)mul(8,5))" - -let test_find_mul _ = - assert_equal - [ "mul(2,4)"; "mul(5,5)"; "mul(11,8)"; "mul(8,5)" ] - (let result = find_mul example_input in - List.map (fun x -> Re2.Match.get_exn x ~sub:(`Index 0)) result) - -let test_extract_pairs _ = - assert_equal - [ (2, 4); (5, 5); (11, 8); (8, 5) ] - (let matches = find_mul example_input in - extract_pairs matches) - -let test_solve _ = assert_equal ~printer:string_of_int 161 (solve example_input) - -let tests = - "test suite for day 3 part 1" - >::: [ - "muls extracted from example correctly" >:: test_find_mul; - "pairs extracted from matches" >:: test_extract_pairs; - "example correctly solved" >:: test_solve; - ] - -let _ = run_test_tt_main tests diff --git a/2024/ocaml/test/day_04_part_01.ml b/2024/ocaml/test/day_04_part_01.ml deleted file mode 100644 index 9550e5b..0000000 --- a/2024/ocaml/test/day_04_part_01.ml +++ /dev/null @@ -1,151 +0,0 @@ -open OUnit2 -open AoC_2024.Day_04.Part_01 - -let example_input = - [ - "MMMSXXMASM"; - "MSAMXMSMSA"; - "AMXSXMAAMM"; - "MSAMASMSMX"; - "XMASAMXAMM"; - "XXAMMXXAMA"; - "SMSMSASXSS"; - "SAXAMASAAA"; - "MAMMMXMMMM"; - "MXMXAXMASX"; - ] - -let is_in_bounds_tests = - let test_is_in_bounds expected length index _ = - let actual = is_in_bounds length index in - assert_equal ~printer:string_of_bool expected actual - in - [ - "not in bounds if below 0" >:: test_is_in_bounds false 1 (-1); - "in bounds if gte 0 and lt length" >:: test_is_in_bounds true 1 0; - "not in bounds if eq length" >:: test_is_in_bounds false 1 1; - "not in bounds if gt length" >:: test_is_in_bounds false 1 2; - ] - -let calculate_num_of_matches_tests = - let test_calculate_num_of_matches expected line row_offset character _ = - let actual = calculate_num_of_matches line row_offset character in - assert_equal ~printer:string_of_int expected actual - in - [ - "is 0 when no matches present" - >:: test_calculate_num_of_matches 0 "SMMX" 1 { index = 3; character = 'X' }; - "is 1 when only one match present - top to bottom" - >:: - (* - |.|X|.| - |.|M|.| - |.|A|.| - |.|S|.| - *) - test_calculate_num_of_matches 1 ".X..M..A..S." 3 - { index = 1; character = 'X' }; - "is 1 when only one match present - top to bottom edge" - >:: - (* - |X|.|.| - |M|.|.| - |A|.|.| - |S|.|.| - *) - test_calculate_num_of_matches 1 "X..M..A..S.." 3 - { index = 0; character = 'X' }; - "is 1 when only one match present - bottom to top" - >:: - (* - |.|S|.| - |.|A|.| - |.|M|.| - |.|X|.| - *) - test_calculate_num_of_matches 1 ".S..A..M..X." 3 - { index = 10; character = 'X' }; - "is 1 when only one match present - left to right" - >:: - (* - |X|M|A|S| - *) - test_calculate_num_of_matches 1 "XMAS" 4 { index = 0; character = 'X' }; - "is 1 when only one match present - right to left" - >:: - (* - |S|A|M|X| - *) - test_calculate_num_of_matches 1 "SAMX" 4 { index = 3; character = 'X' }; - "is 1 when only one match present - bottom right to top left" - >:: - (* - |S|.|.|.| - |.|A|.|.| - |.|.|M|.| - |.|.|.|X| - *) - test_calculate_num_of_matches 1 "S....A....M....X" 4 - { index = 15; character = 'X' }; - "is 1 when only one match present - top left to bottom right" - >:: - (* - |X|.|.|.| - |.|M|.|.| - |.|.|A|.| - |.|.|.|S| - *) - test_calculate_num_of_matches 1 "X....M....A....S" 4 - { index = 0; character = 'X' }; - "is 1 when only one match present - bottom left to top right" - >:: - (* - |.|.|.|S| - |.|.|A|.| - |.|M|.|.| - |X|.|.|.| - *) - test_calculate_num_of_matches 1 "...S..A..M..X..." 4 - { index = 12; character = 'X' }; - "is 1 when only one match present - top right to bottom left" - >:: - (* - |.|.|.|X| - |.|.|M|.| - |.|A|.|.| - |S|.|.|.| - *) - test_calculate_num_of_matches 1 "...X..M..A..S..." 4 - { index = 3; character = 'X' }; - "is 8 when matches present in all possible directions" - >:: - (* - |S|.|.|S|.|.|S| - |.|A|.|A|.|A|.| - |.|.|M|M|M|.|.| - |S|A|M|X|M|A|S| - |.|.|M|M|M|.|.| - |.|A|.|A|.|A|.| - |S|.|.|S|.|.|S| - *) - test_calculate_num_of_matches 8 - "S..S..S.A.A.A...MMM..SAMXMAS..MMM...A.A.A.S..S..S" 7 - { index = 24; character = 'X' }; - ] - -let solve_tests = - let test_solve expected lines _ = - let actual = solve lines in - assert_equal ~printer:string_of_int expected actual - in - [ - "example is solved correctly" >:: test_solve 18 example_input; - "minimal example is solved correctly" - >:: test_solve 4 [ "..X..."; ".SAMX."; ".A..A."; "XMAS.S"; ".X...." ]; - ] - -let tests = - "test suite for day 4 part 1" - >::: is_in_bounds_tests @ calculate_num_of_matches_tests @ solve_tests - -let _ = run_test_tt_main tests diff --git a/2024/ocaml/test/day_05_part_01.ml b/2024/ocaml/test/day_05_part_01.ml deleted file mode 100644 index 0f50476..0000000 --- a/2024/ocaml/test/day_05_part_01.ml +++ /dev/null @@ -1,4 +0,0 @@ -open OUnit2 - -let tests = "test suite for day n part 1" >::: [] -let _ = run_test_tt_main tests diff --git a/2024/ocaml/test/dune b/2024/ocaml/test/dune deleted file mode 100644 index e19cbe9..0000000 --- a/2024/ocaml/test/dune +++ /dev/null @@ -1,10 +0,0 @@ -(tests - (names - day_01_part_01 - day_01_part_02 - day_02_part_01 - day_03_part_01 - day_04_part_01 - grid) - (modes byte exe) - (libraries AoC_2024 ounit2)) diff --git a/2024/ocaml/test/grid.ml b/2024/ocaml/test/grid.ml deleted file mode 100644 index bd384cd..0000000 --- a/2024/ocaml/test/grid.ml +++ /dev/null @@ -1,5 +0,0 @@ -open OUnit2 -open AoC_2024.Grid - -let tests = "" >::: [] -let _ = run_test_tt_main tests diff --git a/ocaml/README.md b/ocaml/README.md index 7d64eb8..a2a6b4f 100644 --- a/ocaml/README.md +++ b/ocaml/README.md @@ -1 +1,21 @@ # Advent of Code: OCaml + +## Getting Started + +### How to Build + +```sh +task build +``` + +### How to Run + +```sh +task run +``` + +### How to Test + +```sh +task test +``` diff --git a/ocaml/flake.nix b/ocaml/flake.nix index 0176014..7b267a1 100644 --- a/ocaml/flake.nix +++ b/ocaml/flake.nix @@ -39,6 +39,7 @@ cmdliner # command line library ppx_deriving # derivers logs # logging library + menhir # parsing library ]; sysTools = with pkgs; [ go-task @@ -48,4 +49,4 @@ buildInputs = ocamlPackages ++ sysTools; }; }; -} +}
\ No newline at end of file diff --git a/2024/ocaml/lib/day_01/part_01.ml b/ocaml/lib/year2024/day01/part01.ml index 100b12a..100b12a 100644 --- a/2024/ocaml/lib/day_01/part_01.ml +++ b/ocaml/lib/year2024/day01/part01.ml diff --git a/2024/ocaml/lib/day_01/part_02.ml b/ocaml/lib/year2024/day01/part02.ml index 25c8b7d..25c8b7d 100644 --- a/2024/ocaml/lib/day_01/part_02.ml +++ b/ocaml/lib/year2024/day01/part02.ml diff --git a/2024/ocaml/lib/day_02/part_01.ml b/ocaml/lib/year2024/day02/part01.ml index 32d7ab8..32d7ab8 100644 --- a/2024/ocaml/lib/day_02/part_01.ml +++ b/ocaml/lib/year2024/day02/part01.ml diff --git a/2024/ocaml/lib/day_03/part_01.ml b/ocaml/lib/year2024/day03/part01.ml index c12b68a..c12b68a 100644 --- a/2024/ocaml/lib/day_03/part_01.ml +++ b/ocaml/lib/year2024/day03/part01.ml diff --git a/2024/ocaml/lib/day_04/part_01.ml b/ocaml/lib/year2024/day04/part01.ml index acb3e3b..acb3e3b 100644 --- a/2024/ocaml/lib/day_04/part_01.ml +++ b/ocaml/lib/year2024/day04/part01.ml diff --git a/2024/ocaml/lib/day_05/part_01.ml b/ocaml/lib/year2024/day05/part01.ml index f32bef1..96ff486 100644 --- a/2024/ocaml/lib/day_05/part_01.ml +++ b/ocaml/lib/year2024/day05/part01.ml @@ -3,14 +3,16 @@ module StringMap = Map.Make (String) let inverse_page_rule page_rule = - String.split_on_char '|' page_rule |> fun lst -> + String.split_on_char '|' page_rule + |> fun lst -> match lst with [] -> "" | _ :: _ -> String.concat "|" (List.rev lst) (** [parse_aux acc page_rules] is a map of all invalid transitions as defined by [page_rules] *) let rec get_invalid_transitions_aux acc page_rules : unit StringMap.t = match page_rules with - | [] -> acc + | [] -> + acc | h :: t -> get_invalid_transitions_aux (acc |> StringMap.add (inverse_page_rule h) ()) @@ -31,32 +33,32 @@ let get_invalid_transitions page_rules = - if yes, then it is an illegal transition - if no, then add the list to a list for further processing *) let solve page_rules _ = - let invalid_transitions = get_invalid_transitions page_rules in + let[@warning "-26"] invalid_transitions = + get_invalid_transitions page_rules + in () let _ = solve - [ - "47|53"; - "97|13"; - "97|61"; - "97|47"; - "75|29"; - "61|13"; - "75|53"; - "29|13"; - "97|29"; - "53|29"; - "61|53"; - "97|53"; - "61|29"; - "47|13"; - "75|47"; - "97|75"; - "47|61"; - "75|61"; - "47|29"; - "75|13"; - "53|13"; - ] + [ "47|53" + ; "97|13" + ; "97|61" + ; "97|47" + ; "75|29" + ; "61|13" + ; "75|53" + ; "29|13" + ; "97|29" + ; "53|29" + ; "61|53" + ; "97|53" + ; "61|29" + ; "47|13" + ; "75|47" + ; "97|75" + ; "47|61" + ; "75|61" + ; "47|29" + ; "75|13" + ; "53|13" ] [] diff --git a/2024/ocaml/lib/dune b/ocaml/lib/year2024/dune index 84e62ca..63bf2a1 100644 --- a/2024/ocaml/lib/dune +++ b/ocaml/lib/year2024/dune @@ -1,5 +1,5 @@ +(include_subdirs qualified) + (library - (name AoC_2024) + (name year2024) (libraries re2)) - -(include_subdirs qualified) diff --git a/ocaml/test/year2024/day01/dune b/ocaml/test/year2024/day01/dune new file mode 100644 index 0000000..8891ccf --- /dev/null +++ b/ocaml/test/year2024/day01/dune @@ -0,0 +1,11 @@ +(tests + (names + test_part01 + test_part02) + (libraries + year2024 + alcotest + qcheck-core + qcheck-alcotest) + (action (run %{test} -e)) + (modes byte exe)) diff --git a/ocaml/test/year2024/day01/test_part01.ml b/ocaml/test/year2024/day01/test_part01.ml new file mode 100644 index 0000000..706284d --- /dev/null +++ b/ocaml/test/year2024/day01/test_part01.ml @@ -0,0 +1,30 @@ +open Year2024.Day01.Part01 + +let test_solve haveLeft haveRight want () = + Alcotest.(check int) "incorrect solution" want @@ solve haveLeft haveRight + +let test_distance_calculation haveX haveY want () = + Alcotest.(check int) "incorrect solution" want + @@ calculate_distance haveX haveY + +let () = + let open Alcotest in + run "test suite for day 01 part 01" + [ ( "test solutions" + , [ test_case "empty list is 0" `Quick @@ (test_solve [] []) 0 + ; test_case "distance between 0 and 1 is 1" `Quick + @@ (test_solve [0] [1]) 1 + ; test_case "distance between 1 and 0 is 1" `Quick + @@ (test_solve [1] [0]) 1 + ; test_case "smallest are paired, then next smallest, ..." `Quick + @@ (test_solve [1; 2; 3] [3; 2; 1]) 0 + ; test_case "solves example correctly" `Quick + @@ (test_solve [3; 4; 2; 1; 3; 3] [4; 3; 5; 3; 9; 3]) 11 + ; test_case "same number is 0 distance apart" `Quick + @@ (test_distance_calculation 0 0) 0 + ; test_case "same number is 0 distance apart" `Quick + @@ (test_distance_calculation 1 1) 0 + ; test_case "distance is equal to abs(x - y)" `Quick + @@ (test_distance_calculation 0 1) 1 + ; test_case "order of inputs doesn't matter" `Quick + @@ (test_distance_calculation 1 0) 1 ] ) ] diff --git a/ocaml/test/year2024/day01/test_part02.ml b/ocaml/test/year2024/day01/test_part02.ml new file mode 100644 index 0000000..142add6 --- /dev/null +++ b/ocaml/test/year2024/day01/test_part02.ml @@ -0,0 +1,16 @@ +open Year2024.Day01.Part02 + +let test_solve haveLeft haveRight want () = + Alcotest.(check int) "incorrect solution" want @@ solve haveLeft haveRight + +let () = + let open Alcotest in + run "test suite for day 01 part 02" + [ ( "test solutions" + , [ test_case "empty list is 0" `Quick @@ (test_solve [] []) 0 + ; test_case "ten x zero occurrances of ten" `Quick + @@ (test_solve [10] [1; 2; 3]) 0 + ; test_case "two x three occurrances of two" `Quick + @@ (test_solve [2] [2; 2; 2]) 6 + ; test_case "solves example correctly" `Quick + @@ (test_solve [3; 4; 2; 1; 3; 3] [4; 3; 5; 3; 9; 3]) 31 ] ) ] diff --git a/ocaml/test/year2024/day02/dune b/ocaml/test/year2024/day02/dune new file mode 100644 index 0000000..c4a75c4 --- /dev/null +++ b/ocaml/test/year2024/day02/dune @@ -0,0 +1,10 @@ +(tests + (names + test_part01) + (libraries + year2024 + alcotest + qcheck-core + qcheck-alcotest) + (action (run %{test} -e)) + (modes byte exe)) diff --git a/ocaml/test/year2024/day02/test_part01.ml b/ocaml/test/year2024/day02/test_part01.ml new file mode 100644 index 0000000..4feea41 --- /dev/null +++ b/ocaml/test/year2024/day02/test_part01.ml @@ -0,0 +1,41 @@ +open Year2024.Day02.Part01 + +let test_solve have want () = + Alcotest.(check int) "incorrect solution" want @@ solve have + +let test_report_safety_check have want () = + Alcotest.(check bool) "incorrect report safety" want + @@ check_report_safety have + +let () = + let open Alcotest in + run "test suite for day 2 part 1" + [ ( "test solutions" + , [ test_case "empty list should have no valid reports" `Quick + @@ test_solve [] 0 + ; test_case "valid report should count as one" `Quick + @@ test_solve [[7; 6; 4; 2; 1]] 1 + ; test_case "provided example" `Quick + @@ test_solve + [ [7; 6; 4; 2; 1] + ; [1; 2; 7; 8; 9] + ; [9; 7; 6; 2; 1] + ; [1; 3; 2; 4; 5] + ; [8; 6; 4; 4; 1] + ; [1; 3; 6; 7; 9] ] + 2 ] ) + ; ( "test report safety check" + , [ test_case "all increasing" `Quick + @@ test_report_safety_check [1; 2; 3] true + ; test_case "all decreasing" `Quick + @@ test_report_safety_check [3; 2; 1] true + ; test_case "increasing and decreasing" `Quick + @@ test_report_safety_check [1; 3; 2] false + ; test_case "change of at least one" `Quick + @@ test_report_safety_check [1; 2; 3] true + ; test_case "no change" `Quick + @@ test_report_safety_check [1; 1; 2] false + ; test_case "change of at most three" `Quick + @@ test_report_safety_check [1; 2; 4; 7] true + ; test_case "change of at more than three" `Quick + @@ test_report_safety_check [1; 5] false ] ) ] diff --git a/ocaml/test/year2024/day03/dune b/ocaml/test/year2024/day03/dune new file mode 100644 index 0000000..c4a75c4 --- /dev/null +++ b/ocaml/test/year2024/day03/dune @@ -0,0 +1,10 @@ +(tests + (names + test_part01) + (libraries + year2024 + alcotest + qcheck-core + qcheck-alcotest) + (action (run %{test} -e)) + (modes byte exe)) diff --git a/ocaml/test/year2024/day03/test_part01.ml b/ocaml/test/year2024/day03/test_part01.ml new file mode 100644 index 0000000..78d04e2 --- /dev/null +++ b/ocaml/test/year2024/day03/test_part01.ml @@ -0,0 +1,29 @@ +open Year2024.Day03.Part01 + +let example_input = + "xmul(2,4)%&mul[3,7]!@^do_not_mul(5,5)+mul(32,64]then(mul(11,8)mul(8,5))" + +let test_find_mul () = + Alcotest.(check (list string)) + "incorrect mul list found" + ["mul(2,4)"; "mul(5,5)"; "mul(11,8)"; "mul(8,5)"] + (let result = find_mul example_input in + List.map (fun x -> Re2.Match.get_exn x ~sub:(`Index 0)) result ) + +let test_extract_pairs () = + Alcotest.(check (list (pair int int))) + "incorrect pairs" + [(2, 4); (5, 5); (11, 8); (8, 5)] + (let matches = find_mul example_input in + extract_pairs matches ) + +let test_solve () = Alcotest.(check int) "" 161 @@ solve example_input + +let () = + let open Alcotest in + run "test suite for day 3 part 1" + [ ( "test implementation" + , [ test_case "muls extracted from example correctly" `Quick + @@ test_find_mul + ; test_case "pairs extracted from matches" `Quick @@ test_extract_pairs + ; test_case "example correctly solved" `Quick @@ test_solve ] ) ] diff --git a/ocaml/test/year2024/day04/dune b/ocaml/test/year2024/day04/dune new file mode 100644 index 0000000..c4a75c4 --- /dev/null +++ b/ocaml/test/year2024/day04/dune @@ -0,0 +1,10 @@ +(tests + (names + test_part01) + (libraries + year2024 + alcotest + qcheck-core + qcheck-alcotest) + (action (run %{test} -e)) + (modes byte exe)) diff --git a/ocaml/test/year2024/day04/test_part01.ml b/ocaml/test/year2024/day04/test_part01.ml new file mode 100644 index 0000000..5776e49 --- /dev/null +++ b/ocaml/test/year2024/day04/test_part01.ml @@ -0,0 +1,141 @@ +open Year2024.Day04.Part01 + +let example_input = + [ "MMMSXXMASM" + ; "MSAMXMSMSA" + ; "AMXSXMAAMM" + ; "MSAMASMSMX" + ; "XMASAMXAMM" + ; "XXAMMXXAMA" + ; "SMSMSASXSS" + ; "SAXAMASAAA" + ; "MAMMMXMMMM" + ; "MXMXAXMASX" ] + +let is_in_bounds_tests = + let test_is_in_bounds expected length index _ = + let actual = is_in_bounds length index in + assert_equal ~printer:string_of_bool expected actual + in + [ "not in bounds if below 0" >:: test_is_in_bounds false 1 (-1) + ; "in bounds if gte 0 and lt length" >:: test_is_in_bounds true 1 0 + ; "not in bounds if eq length" >:: test_is_in_bounds false 1 1 + ; "not in bounds if gt length" >:: test_is_in_bounds false 1 2 ] + +let calculate_num_of_matches_tests = + let test_calculate_num_of_matches expected line row_offset character _ = + let actual = calculate_num_of_matches line row_offset character in + assert_equal ~printer:string_of_int expected actual + in + [ "is 0 when no matches present" + >:: test_calculate_num_of_matches 0 "SMMX" 1 {index= 3; character= 'X'} + ; "is 1 when only one match present - top to bottom" + >:: + (* + |.|X|.| + |.|M|.| + |.|A|.| + |.|S|.| + *) + test_calculate_num_of_matches 1 ".X..M..A..S." 3 {index= 1; character= 'X'} + ; "is 1 when only one match present - top to bottom edge" + >:: + (* + |X|.|.| + |M|.|.| + |A|.|.| + |S|.|.| + *) + test_calculate_num_of_matches 1 "X..M..A..S.." 3 {index= 0; character= 'X'} + ; "is 1 when only one match present - bottom to top" + >:: + (* + |.|S|.| + |.|A|.| + |.|M|.| + |.|X|.| + *) + test_calculate_num_of_matches 1 ".S..A..M..X." 3 {index= 10; character= 'X'} + ; "is 1 when only one match present - left to right" + >:: + (* + |X|M|A|S| + *) + test_calculate_num_of_matches 1 "XMAS" 4 {index= 0; character= 'X'} + ; "is 1 when only one match present - right to left" + >:: + (* + |S|A|M|X| + *) + test_calculate_num_of_matches 1 "SAMX" 4 {index= 3; character= 'X'} + ; "is 1 when only one match present - bottom right to top left" + >:: + (* + |S|.|.|.| + |.|A|.|.| + |.|.|M|.| + |.|.|.|X| + *) + test_calculate_num_of_matches 1 "S....A....M....X" 4 + {index= 15; character= 'X'} + ; "is 1 when only one match present - top left to bottom right" + >:: + (* + |X|.|.|.| + |.|M|.|.| + |.|.|A|.| + |.|.|.|S| + *) + test_calculate_num_of_matches 1 "X....M....A....S" 4 + {index= 0; character= 'X'} + ; "is 1 when only one match present - bottom left to top right" + >:: + (* + |.|.|.|S| + |.|.|A|.| + |.|M|.|.| + |X|.|.|.| + *) + test_calculate_num_of_matches 1 "...S..A..M..X..." 4 + {index= 12; character= 'X'} + ; "is 1 when only one match present - top right to bottom left" + >:: + (* + |.|.|.|X| + |.|.|M|.| + |.|A|.|.| + |S|.|.|.| + *) + test_calculate_num_of_matches 1 "...X..M..A..S..." 4 + {index= 3; character= 'X'} + ; "is 8 when matches present in all possible directions" + >:: + (* + |S|.|.|S|.|.|S| + |.|A|.|A|.|A|.| + |.|.|M|M|M|.|.| + |S|A|M|X|M|A|S| + |.|.|M|M|M|.|.| + |.|A|.|A|.|A|.| + |S|.|.|S|.|.|S| + *) + test_calculate_num_of_matches 8 + "S..S..S.A.A.A...MMM..SAMXMAS..MMM...A.A.A.S..S..S" 7 + {index= 24; character= 'X'} ] + +let solve_tests = + let test_solve have want () = + let actual = solve have in + Alcotest.(check int) "incorrect solution" want actual + in + [ ( "test solution" + , [ Alcotest.test_case "example is solved correctly" `Quick + @@ test_solve example_input 18 + ; Alcotest.test_case "minimal example is solved correctly" `Quick + @@ test_solve ["..X..."; ".SAMX."; ".A..A."; "XMAS.S"; ".X...."] 4 ] ) + ] + +let () = + let open Alcotest in + run "test suite for day 4 part 1" is_in_bounds_tests + @ calculate_num_of_matches_tests @ solve_tests |
