summaryrefslogtreecommitdiff
path: root/2025
diff options
context:
space:
mode:
authorDJ O'Leary <dijitol@proton.me>2025-12-08 01:55:28 +0100
committerDJ O'Leary <dijitol@proton.me>2025-12-08 01:57:20 +0100
commit0f34d9a931791406d2cabf282742530195f71927 (patch)
tree076e546f64f8f541e91bf135d5d5c78cde173ccf /2025
parent63c4dea442acb32db82318b4399ac05fb955e6d0 (diff)
refactor(2025,ocaml): use result type for answers to allow for returning errors
Diffstat (limited to '2025')
-rw-r--r--2025/ocaml/lib/application/d01p01.ml14
-rw-r--r--2025/ocaml/lib/application/d01p01.mli4
-rw-r--r--2025/ocaml/lib/application/d01p02.ml14
-rw-r--r--2025/ocaml/lib/application/d01p02.mli4
-rw-r--r--2025/ocaml/lib/application/d02p01.ml23
-rw-r--r--2025/ocaml/lib/application/d02p01.mli4
-rw-r--r--2025/ocaml/lib/application/d02p02.ml25
-rw-r--r--2025/ocaml/lib/application/d02p02.mli4
-rw-r--r--2025/ocaml/lib/application/d03p01.ml46
-rw-r--r--2025/ocaml/lib/application/d03p01.mli4
-rw-r--r--2025/ocaml/lib/application/d04p01.ml46
-rw-r--r--2025/ocaml/lib/application/d04p01.mli4
-rw-r--r--2025/ocaml/lib/application/d04p02.ml46
-rw-r--r--2025/ocaml/lib/application/d04p02.mli4
-rw-r--r--2025/ocaml/lib/presentation/cli.ml7
-rw-r--r--2025/ocaml/test/application/test_d01p01.ml4
-rw-r--r--2025/ocaml/test/application/test_d01p02.ml4
-rw-r--r--2025/ocaml/test/application/test_d04p01.ml2
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