diff options
| author | DJ O'Leary <dijitol@proton.me> | 2025-12-08 01:55:28 +0100 |
|---|---|---|
| committer | DJ O'Leary <dijitol@proton.me> | 2025-12-08 01:57:20 +0100 |
| commit | 0f34d9a931791406d2cabf282742530195f71927 (patch) | |
| tree | 076e546f64f8f541e91bf135d5d5c78cde173ccf /2025/ocaml | |
| parent | 63c4dea442acb32db82318b4399ac05fb955e6d0 (diff) | |
refactor(2025,ocaml): use result type for answers to allow for returning errors
Diffstat (limited to '2025/ocaml')
| -rw-r--r-- | 2025/ocaml/lib/application/d01p01.ml | 14 | ||||
| -rw-r--r-- | 2025/ocaml/lib/application/d01p01.mli | 4 | ||||
| -rw-r--r-- | 2025/ocaml/lib/application/d01p02.ml | 14 | ||||
| -rw-r--r-- | 2025/ocaml/lib/application/d01p02.mli | 4 | ||||
| -rw-r--r-- | 2025/ocaml/lib/application/d02p01.ml | 23 | ||||
| -rw-r--r-- | 2025/ocaml/lib/application/d02p01.mli | 4 | ||||
| -rw-r--r-- | 2025/ocaml/lib/application/d02p02.ml | 25 | ||||
| -rw-r--r-- | 2025/ocaml/lib/application/d02p02.mli | 4 | ||||
| -rw-r--r-- | 2025/ocaml/lib/application/d03p01.ml | 46 | ||||
| -rw-r--r-- | 2025/ocaml/lib/application/d03p01.mli | 4 | ||||
| -rw-r--r-- | 2025/ocaml/lib/application/d04p01.ml | 46 | ||||
| -rw-r--r-- | 2025/ocaml/lib/application/d04p01.mli | 4 | ||||
| -rw-r--r-- | 2025/ocaml/lib/application/d04p02.ml | 46 | ||||
| -rw-r--r-- | 2025/ocaml/lib/application/d04p02.mli | 4 | ||||
| -rw-r--r-- | 2025/ocaml/lib/presentation/cli.ml | 7 | ||||
| -rw-r--r-- | 2025/ocaml/test/application/test_d01p01.ml | 4 | ||||
| -rw-r--r-- | 2025/ocaml/test/application/test_d01p02.ml | 4 | ||||
| -rw-r--r-- | 2025/ocaml/test/application/test_d04p01.ml | 2 |
18 files changed, 156 insertions, 103 deletions
diff --git a/2025/ocaml/lib/application/d01p01.ml b/2025/ocaml/lib/application/d01p01.ml index 75e1b17..206e757 100644 --- a/2025/ocaml/lib/application/d01p01.ml +++ b/2025/ocaml/lib/application/d01p01.ml @@ -1,5 +1,7 @@ open Domain.D01p01 +let err_empty_input = "input is empty" + let rec input_to_lines input = let lines = String.split_on_char '\n' input |> List.filter (fun l -> l <> "") @@ -42,8 +44,10 @@ let rec turn_aux count current = function let turn current = turn_aux 0 current let solve input () = - let lines = input_to_lines input in - let turns = lines_to_turns lines in - let start = Dial.start in - let answer = turn start turns in - answer + if input = "" then Error err_empty_input + else + let lines = input_to_lines input in + let turns = lines_to_turns lines in + let start = Dial.start in + let answer = turn start turns in + Ok answer diff --git a/2025/ocaml/lib/application/d01p01.mli b/2025/ocaml/lib/application/d01p01.mli index 30ece9d..2eecf19 100644 --- a/2025/ocaml/lib/application/d01p01.mli +++ b/2025/ocaml/lib/application/d01p01.mli @@ -1,4 +1,4 @@ -val solve : string -> unit -> int +val solve : string -> unit -> (int, string) result (** [solve input ()] is the solution for the first part of the first day of advent of code 2025 for [input]. - The solution for an empty input is 0. + The solution for an empty input is [None]. Requires: [input] to be a multiline string using `\n` as it's line separator. *) diff --git a/2025/ocaml/lib/application/d01p02.ml b/2025/ocaml/lib/application/d01p02.ml index 6f935a4..7910486 100644 --- a/2025/ocaml/lib/application/d01p02.ml +++ b/2025/ocaml/lib/application/d01p02.ml @@ -1,5 +1,7 @@ open Domain.D01p02 +let err_empty_input = "input is empty" + let rec input_to_lines input = let lines = String.split_on_char '\n' input |> List.filter (fun l -> l <> "") @@ -44,8 +46,10 @@ let rec turn_aux count dial = function let turn current = turn_aux 0 current let solve input () = - let lines = input_to_lines input in - let turns = lines_to_turns lines in - let start = Dial.start in - let answer = turn start turns in - answer + if input = "" then Error err_empty_input + else + let lines = input_to_lines input in + let turns = lines_to_turns lines in + let start = Dial.start in + let answer = turn start turns in + Ok answer diff --git a/2025/ocaml/lib/application/d01p02.mli b/2025/ocaml/lib/application/d01p02.mli index 33aa63f..9e9a2f2 100644 --- a/2025/ocaml/lib/application/d01p02.mli +++ b/2025/ocaml/lib/application/d01p02.mli @@ -1,4 +1,4 @@ -val solve : string -> unit -> int +val solve : string -> unit -> (int, string) result (** [solve input ()] is the solution for the second part of the first day of advent of code 2025 for [input]. - The solution for an empty input is 0. + The solution for an empty input is [Error msg]. Requires: [input] to be a multiline string using `\n` as it's line separator. *) diff --git a/2025/ocaml/lib/application/d02p01.ml b/2025/ocaml/lib/application/d02p01.ml index ce8778d..f313149 100644 --- a/2025/ocaml/lib/application/d02p01.ml +++ b/2025/ocaml/lib/application/d02p01.ml @@ -1,5 +1,7 @@ open Domain.D02p01 +let err_empty_input = "input is empty" + (** [input_to_lines input] is a list of strings that have been split on ','. Example: [input_to_lines "12-34,56-78,90-100"] becomes the list \[ "12-34"; "56-78"; "90-100" \]. Requires: [input] to be a string in the format "12-34,56-78,90-100". *) @@ -37,11 +39,16 @@ let is_invalid = ID.validate rules let passthrough_with_logs logger lst = logger lst ; lst let solve input () = - input |> input_to_lines |> List.to_seq - |> passthrough_with_logs pp_lines - |> lines_to_ranges - |> passthrough_with_logs pp_ranges - |> collect_all - |> passthrough_with_logs pp_collection - |> Seq.filter is_invalid - |> Seq.fold_left (fun acc id -> acc + ID.to_int id) 0 + if input = "" then Error err_empty_input + else + let answer = + input |> input_to_lines |> List.to_seq + |> passthrough_with_logs pp_lines + |> lines_to_ranges + |> passthrough_with_logs pp_ranges + |> collect_all + |> passthrough_with_logs pp_collection + |> Seq.filter is_invalid + |> Seq.fold_left (fun acc id -> acc + ID.to_int id) 0 + in + Ok answer diff --git a/2025/ocaml/lib/application/d02p01.mli b/2025/ocaml/lib/application/d02p01.mli index 27f85d4..8a8eb32 100644 --- a/2025/ocaml/lib/application/d02p01.mli +++ b/2025/ocaml/lib/application/d02p01.mli @@ -1,4 +1,4 @@ -val solve : string -> unit -> int +val solve : string -> unit -> (int, string) result (** [solve input ()] is the solution for the first part of the second day of advent of code 2025 for [input]. - The solution for an empty input is 0. + The solution for an empty input is [Error msg]. Requires: [input] to be a string. *) diff --git a/2025/ocaml/lib/application/d02p02.ml b/2025/ocaml/lib/application/d02p02.ml index 29e3877..d062f7b 100644 --- a/2025/ocaml/lib/application/d02p02.ml +++ b/2025/ocaml/lib/application/d02p02.ml @@ -1,5 +1,9 @@ open Domain.D02p02 +(* Errors *) + +let err_empty_input = "input is empty" + (* Logging *) (** [passthrough_with_logs logger lst] is [lst] with the side-effects caused by calling [logger] on [lst]. *) @@ -67,11 +71,16 @@ let is_invalid id = List.length repeating_patterns > 0 let solve input () = - input |> input_to_lines |> List.to_seq - |> passthrough_with_logs pp_lines - |> lines_to_ranges - |> passthrough_with_logs pp_ranges - |> collect_all - |> passthrough_with_logs pp_collection - |> Seq.map ID.to_string |> Seq.filter is_invalid - |> Seq.fold_left (fun acc id -> acc + int_of_string id) 0 + if input = "" then Error err_empty_input + else + let answer = + input |> input_to_lines |> List.to_seq + |> passthrough_with_logs pp_lines + |> lines_to_ranges + |> passthrough_with_logs pp_ranges + |> collect_all + |> passthrough_with_logs pp_collection + |> Seq.map ID.to_string |> Seq.filter is_invalid + |> Seq.fold_left (fun acc id -> acc + int_of_string id) 0 + in + Ok answer diff --git a/2025/ocaml/lib/application/d02p02.mli b/2025/ocaml/lib/application/d02p02.mli index 27f85d4..8a8eb32 100644 --- a/2025/ocaml/lib/application/d02p02.mli +++ b/2025/ocaml/lib/application/d02p02.mli @@ -1,4 +1,4 @@ -val solve : string -> unit -> int +val solve : string -> unit -> (int, string) result (** [solve input ()] is the solution for the first part of the second day of advent of code 2025 for [input]. - The solution for an empty input is 0. + The solution for an empty input is [Error msg]. Requires: [input] to be a string. *) diff --git a/2025/ocaml/lib/application/d03p01.ml b/2025/ocaml/lib/application/d03p01.ml index 691fe65..14f49e8 100644 --- a/2025/ocaml/lib/application/d03p01.ml +++ b/2025/ocaml/lib/application/d03p01.ml @@ -28,20 +28,27 @@ open Domain.D03p01 +let err_empty_input = "input is empty" + let to_lines s = s |> String.split_on_char '\n' |> List.map String.trim |> List.filter (fun l -> l <> "") let solve input () = - input |> to_lines |> List.to_seq - |> Seq.map BatteryBank.of_string - |> Seq.mapi (fun idx bb -> - let joltage = BatteryBank.highest_joltage bb in - Logs.debug (fun m -> - m "%d line=%s highest_joltage=%d" (idx + 1) (BatteryBank.pp bb) - joltage ) ; - joltage ) - |> Seq.fold_left ( + ) 0 + if input = "" then Error err_empty_input + else + let answer = + input |> to_lines |> List.to_seq + |> Seq.map BatteryBank.of_string + |> Seq.mapi (fun idx bb -> + let joltage = BatteryBank.highest_joltage bb in + Logs.debug (fun m -> + m "%d line=%s highest_joltage=%d" (idx + 1) (BatteryBank.pp bb) + joltage ) ; + joltage ) + |> Seq.fold_left ( + ) 0 + in + Ok answer let print_occurring_digits input () = input |> to_lines |> List.to_seq @@ -52,14 +59,17 @@ let print_occurring_digits input () = |> Seq.iteri (fun idx bb -> Logs.debug (fun m -> m "%d %s" (idx + 1) (BatteryBank.pp bb)) ) ; -1 +(* disables unused function warning *) +[@@warning "-32"] -[@@warning "-32"] (* disables unused function warning *) let _solve' input () = - let banks = - input |> to_lines |> List.to_seq - |> Seq.map BatteryBank.of_string - |> Seq.map BatteryBank.to_list - |> Seq.map (fun bb -> List.map Battery.to_int bb) - |> List.of_seq - in - banks + if input = "" then Error err_empty_input + else + let banks = + input |> to_lines |> List.to_seq + |> Seq.map BatteryBank.of_string + |> Seq.map BatteryBank.to_list + |> Seq.map (fun bb -> List.map Battery.to_int bb) + |> List.of_seq + in + Ok banks diff --git a/2025/ocaml/lib/application/d03p01.mli b/2025/ocaml/lib/application/d03p01.mli index d875e40..b492753 100644 --- a/2025/ocaml/lib/application/d03p01.mli +++ b/2025/ocaml/lib/application/d03p01.mli @@ -1,6 +1,6 @@ -val solve : string -> unit -> int +val solve : string -> unit -> (int, string) result (** [solve input ()] is the solution for the first part of the third day of advent of code 2025 for [input]. - The solution for an empty input is 0. + The solution for an empty input is [Error msg]. Requires: [input] to be a string. *) val print_occurring_digits : string -> unit -> int diff --git a/2025/ocaml/lib/application/d04p01.ml b/2025/ocaml/lib/application/d04p01.ml index 260c9e9..7c69de4 100644 --- a/2025/ocaml/lib/application/d04p01.ml +++ b/2025/ocaml/lib/application/d04p01.ml @@ -1,26 +1,32 @@ open Domain +let err_empty_input = "input is empty" + let rec solve input () = - let matrix = Matrix.M.of_string input in - let count_paper lst = List.length @@ List.filter (fun c -> c <> ".") lst in - let accessible_rolls_of_paper = ref 0 in - for y = 0 to Array.length matrix - 1 do - for x = 0 to Array.length matrix.(y) - 1 do - if matrix.(y).(x) = "." then () - else - let neighbour_coords = Matrix.M.get_valid_neighbours matrix x y in - let neighbours = - List.map (fun (x', y') -> Matrix.M.get matrix x' y') neighbour_coords - in - let paper_count = count_paper neighbours in - let is_accessible = paper_count < 4 in - if is_accessible then - accessible_rolls_of_paper := !accessible_rolls_of_paper + 1 ; - print_iteration_debug_msg x y neighbour_coords neighbours paper_count - is_accessible - done - done ; - !accessible_rolls_of_paper + if input = "" then Error err_empty_input + else + let matrix = Matrix.M.of_string input in + let count_paper lst = List.length @@ List.filter (fun c -> c <> ".") lst in + let accessible_rolls_of_paper = ref 0 in + for y = 0 to Array.length matrix - 1 do + for x = 0 to Array.length matrix.(y) - 1 do + if matrix.(y).(x) = "." then () + else + let neighbour_coords = Matrix.M.get_valid_neighbours matrix x y in + let neighbours = + List.map + (fun (x', y') -> Matrix.M.get matrix x' y') + neighbour_coords + in + let paper_count = count_paper neighbours in + let is_accessible = paper_count < 4 in + if is_accessible then + accessible_rolls_of_paper := !accessible_rolls_of_paper + 1 ; + print_iteration_debug_msg x y neighbour_coords neighbours paper_count + is_accessible + done + done ; + Ok !accessible_rolls_of_paper and print_iteration_debug_msg x y neighbour_coords neighbours paper_count is_accessible = diff --git a/2025/ocaml/lib/application/d04p01.mli b/2025/ocaml/lib/application/d04p01.mli index 2fd4e44..873acff 100644 --- a/2025/ocaml/lib/application/d04p01.mli +++ b/2025/ocaml/lib/application/d04p01.mli @@ -1,4 +1,4 @@ -val solve : string -> unit -> int +val solve : string -> unit -> (int, string) result (** [solve input ()] is the solution for the first part of the fourth day of advent of code 2025 for [input]. - The solution for an empty input is 0. + The solution for an empty input is [Error msg]. Requires: [input] to be a string. *) diff --git a/2025/ocaml/lib/application/d04p02.ml b/2025/ocaml/lib/application/d04p02.ml index 260c9e9..7c69de4 100644 --- a/2025/ocaml/lib/application/d04p02.ml +++ b/2025/ocaml/lib/application/d04p02.ml @@ -1,26 +1,32 @@ open Domain +let err_empty_input = "input is empty" + let rec solve input () = - let matrix = Matrix.M.of_string input in - let count_paper lst = List.length @@ List.filter (fun c -> c <> ".") lst in - let accessible_rolls_of_paper = ref 0 in - for y = 0 to Array.length matrix - 1 do - for x = 0 to Array.length matrix.(y) - 1 do - if matrix.(y).(x) = "." then () - else - let neighbour_coords = Matrix.M.get_valid_neighbours matrix x y in - let neighbours = - List.map (fun (x', y') -> Matrix.M.get matrix x' y') neighbour_coords - in - let paper_count = count_paper neighbours in - let is_accessible = paper_count < 4 in - if is_accessible then - accessible_rolls_of_paper := !accessible_rolls_of_paper + 1 ; - print_iteration_debug_msg x y neighbour_coords neighbours paper_count - is_accessible - done - done ; - !accessible_rolls_of_paper + if input = "" then Error err_empty_input + else + let matrix = Matrix.M.of_string input in + let count_paper lst = List.length @@ List.filter (fun c -> c <> ".") lst in + let accessible_rolls_of_paper = ref 0 in + for y = 0 to Array.length matrix - 1 do + for x = 0 to Array.length matrix.(y) - 1 do + if matrix.(y).(x) = "." then () + else + let neighbour_coords = Matrix.M.get_valid_neighbours matrix x y in + let neighbours = + List.map + (fun (x', y') -> Matrix.M.get matrix x' y') + neighbour_coords + in + let paper_count = count_paper neighbours in + let is_accessible = paper_count < 4 in + if is_accessible then + accessible_rolls_of_paper := !accessible_rolls_of_paper + 1 ; + print_iteration_debug_msg x y neighbour_coords neighbours paper_count + is_accessible + done + done ; + Ok !accessible_rolls_of_paper and print_iteration_debug_msg x y neighbour_coords neighbours paper_count is_accessible = diff --git a/2025/ocaml/lib/application/d04p02.mli b/2025/ocaml/lib/application/d04p02.mli index 69c5181..388ec56 100644 --- a/2025/ocaml/lib/application/d04p02.mli +++ b/2025/ocaml/lib/application/d04p02.mli @@ -1,4 +1,4 @@ -val solve : string -> unit -> int +val solve : string -> unit -> (int, string) result (** [solve input ()] is the solution for the second part of the fourth day of advent of code 2025 for [input]. - The solution for an empty input is 0. + The solution for an empty input is [Error msg]. Requires: [input] to be a string. *) diff --git a/2025/ocaml/lib/presentation/cli.ml b/2025/ocaml/lib/presentation/cli.ml index 371f199..b4c990c 100644 --- a/2025/ocaml/lib/presentation/cli.ml +++ b/2025/ocaml/lib/presentation/cli.ml @@ -2,8 +2,11 @@ let run_solver name solver filepath = let content = Infrastructure.File.read filepath in - let answer = solver content () in - Logs.app (fun m -> m "[%s] ANSWER=%d" name answer) + match solver content () with + | Error msg -> + Logs.app (fun m -> m "[%s] Err=%s" name msg) + | Ok answer -> + Logs.app (fun m -> m "[%s] ANSWER=%d" name answer) let run_day _ = function | 1 -> diff --git a/2025/ocaml/test/application/test_d01p01.ml b/2025/ocaml/test/application/test_d01p01.ml index 70f5ab2..e57db01 100644 --- a/2025/ocaml/test/application/test_d01p01.ml +++ b/2025/ocaml/test/application/test_d01p01.ml @@ -12,7 +12,9 @@ R14 L82|} let test_full_given_example () = - Alcotest.(check int) "full given example" 3 (solve example_data ()) + Alcotest.(check int) + "full given example" 3 + (match solve example_data () with Error _ -> 0 | Ok ans -> ans) let () = let open Alcotest in diff --git a/2025/ocaml/test/application/test_d01p02.ml b/2025/ocaml/test/application/test_d01p02.ml index e29c932..bbda035 100644 --- a/2025/ocaml/test/application/test_d01p02.ml +++ b/2025/ocaml/test/application/test_d01p02.ml @@ -12,7 +12,9 @@ R14 L82|} let test_full_given_example () = - Alcotest.(check int) "full given example" 6 (solve example_data ()) + Alcotest.(check int) + "full given example" 6 + (match solve example_data () with Error _ -> 0 | Ok ans -> ans) let () = let open Alcotest in diff --git a/2025/ocaml/test/application/test_d04p01.ml b/2025/ocaml/test/application/test_d04p01.ml index 783659e..ffbe102 100644 --- a/2025/ocaml/test/application/test_d04p01.ml +++ b/2025/ocaml/test/application/test_d04p01.ml @@ -14,7 +14,7 @@ let example = let test_input_to_matrix have want () = Alcotest.(check int) "number of rolls of paper that are accessible" want - @@ solve have () + @@ match solve have () with Error _ -> 0 | Ok ans -> ans let () = let open Alcotest in |
