(* 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 ()