103 lines
3.5 KiB
OCaml
103 lines
3.5 KiB
OCaml
(* Improved version of day1.ml with better code quality *)
|
|
|
|
let input = Reuse.split_lines "inputs/day1.txt"
|
|
|
|
(* Improved: Better function name and documentation *)
|
|
let parse_direction (s : string) : int =
|
|
if String.length s = 0 then
|
|
invalid_arg "Empty direction string"
|
|
else
|
|
let direction = s.[0] in
|
|
let distance_str = String.sub s 1 (String.length s - 1) in
|
|
let distance = int_of_string distance_str in
|
|
match direction with
|
|
| 'L' -> -distance (* Negative for left turns *)
|
|
| 'R' -> distance (* Positive for right turns *)
|
|
| _ -> invalid_arg ("Invalid direction: " ^ String.make 1 direction)
|
|
|
|
(* Improved: More descriptive function name *)
|
|
let count_if_at_origin (counter : int) (position : int) : int =
|
|
if position = 0 then counter + 1 else counter
|
|
|
|
(* Improved: Better function name and clearer logic *)
|
|
let normalize_position (position : int) (modulus : int) : int =
|
|
let result = position mod modulus in
|
|
if result >= 0 then result else result + modulus
|
|
|
|
(* Improved: More descriptive function name and clearer logic *)
|
|
let count_boundary_crossings (counter : int) (old_position : int) (movement : int) : int =
|
|
Printf.printf "Position %i, movement %i, current counter %i\n" old_position movement counter;
|
|
|
|
(* Count full rotations (every 100 units) *)
|
|
let full_rotations = abs movement / 100 in
|
|
let remaining_movement = movement mod 100 in
|
|
let new_position = old_position + remaining_movement in
|
|
|
|
(* Count boundary crossings *)
|
|
let boundary_crossings =
|
|
if (new_position >= 100 && old_position < 100) ||
|
|
(new_position <= 0 && old_position > 0) then 1 else 0
|
|
in
|
|
|
|
counter + full_rotations + boundary_crossings
|
|
|
|
(* Improved: More descriptive function names *)
|
|
let rec solve_with_origin_counting (movements : int list) (position : int) (counter : int) : int =
|
|
match movements with
|
|
| [] -> counter
|
|
| movement :: remaining ->
|
|
let new_position = normalize_position (position + movement) 100 in
|
|
solve_with_origin_counting remaining new_position (count_if_at_origin counter new_position)
|
|
|
|
let rec solve_with_boundary_counting (movements : int list) (position : int) (counter : int) : int =
|
|
match movements with
|
|
| [] -> counter
|
|
| movement :: remaining ->
|
|
let new_position = normalize_position (position + movement) 100 in
|
|
let new_counter = count_boundary_crossings counter position movement in
|
|
solve_with_boundary_counting remaining new_position new_counter
|
|
|
|
(* Improved: Clear, descriptive function names *)
|
|
let solve_part1 (lines : string list) : int =
|
|
let movements = List.map parse_direction lines in
|
|
solve_with_origin_counting movements 50 0
|
|
|
|
let solve_part2 (lines : string list) : int =
|
|
let movements = List.map parse_direction lines in
|
|
solve_with_boundary_counting movements 50 0
|
|
|
|
(* Main solutions *)
|
|
let day1_part1 : int = solve_part1 input
|
|
let day1_part2 : int = solve_part2 input
|
|
|
|
(* Improved: Better test organization *)
|
|
module Tests = struct
|
|
let test_input = "L268
|
|
L30
|
|
R48
|
|
L5
|
|
R60
|
|
L55
|
|
L1
|
|
L99
|
|
R14
|
|
L82"
|
|
|
|
let test_lines = String.split_on_char '\n' test_input
|
|
|
|
let run_tests () =
|
|
let part1_result = solve_part1 test_lines in
|
|
let part2_result = solve_part2 test_lines in
|
|
|
|
if part1_result <> 3 then
|
|
failwith ("Part 1 test failed: expected 3, got " ^ string_of_int part1_result);
|
|
|
|
if part2_result <> 8 then
|
|
failwith ("Part 2 test failed: expected 8, got " ^ string_of_int part2_result);
|
|
|
|
Printf.printf "All tests passed!\n"
|
|
end
|
|
|
|
(* Run tests *)
|
|
let () = Tests.run_tests ()
|