initial commit with day1 solution
This commit is contained in:
32
aoc25.opam
Normal file
32
aoc25.opam
Normal file
@ -0,0 +1,32 @@
|
||||
# This file is generated by dune, edit dune-project instead
|
||||
opam-version: "2.0"
|
||||
synopsis: "A short synopsis"
|
||||
description: "A longer description"
|
||||
maintainer: ["Maintainer Name <maintainer@example.com>"]
|
||||
authors: ["Author Name <author@example.com>"]
|
||||
license: "LICENSE"
|
||||
tags: ["add topics" "to describe" "your" "project"]
|
||||
homepage: "https://github.com/username/reponame"
|
||||
doc: "https://url/to/documentation"
|
||||
bug-reports: "https://github.com/username/reponame/issues"
|
||||
depends: [
|
||||
"dune" {>= "3.20"}
|
||||
"ocaml"
|
||||
"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/username/reponame.git"
|
||||
x-maintenance-intent: ["(latest)"]
|
||||
4
bin/dune
Normal file
4
bin/dune
Normal file
@ -0,0 +1,4 @@
|
||||
(executable
|
||||
(public_name aoc25)
|
||||
(name main)
|
||||
(libraries aoc25))
|
||||
2
bin/main.ml
Normal file
2
bin/main.ml
Normal file
@ -0,0 +1,2 @@
|
||||
let () = Printf.printf "%i\n" Aoc25.Day1.day1_part1
|
||||
let () = Printf.printf "%i\n" Aoc25.Day1.day1_part2
|
||||
26
dune-project
Normal file
26
dune-project
Normal file
@ -0,0 +1,26 @@
|
||||
(lang dune 3.20)
|
||||
|
||||
(name aoc25)
|
||||
|
||||
(generate_opam_files true)
|
||||
|
||||
(source
|
||||
(github username/reponame))
|
||||
|
||||
(authors "Author Name <author@example.com>")
|
||||
|
||||
(maintainers "Maintainer Name <maintainer@example.com>")
|
||||
|
||||
(license LICENSE)
|
||||
|
||||
(documentation https://url/to/documentation)
|
||||
|
||||
(package
|
||||
(name aoc25)
|
||||
(synopsis "A short synopsis")
|
||||
(description "A longer description")
|
||||
(depends ocaml)
|
||||
(tags
|
||||
("add topics" "to describe" your project)))
|
||||
|
||||
; See the complete stanza docs at https://dune.readthedocs.io/en/stable/reference/dune-project/index.html
|
||||
4498
inputs/day1.txt
Normal file
4498
inputs/day1.txt
Normal file
File diff suppressed because it is too large
Load Diff
86
lib/day1.ml
Normal file
86
lib/day1.ml
Normal file
@ -0,0 +1,86 @@
|
||||
|
||||
let input = Reuse.split_lines "inputs/day1.txt"
|
||||
|
||||
|
||||
let convert (s : string): int =
|
||||
let first_char = s.[0] in
|
||||
let remainder = String.sub s 1 (String.length s - 1) in
|
||||
let distance = int_of_string remainder in
|
||||
match first_char with
|
||||
| 'L' -> -distance
|
||||
| 'R' -> distance
|
||||
| _ -> raise (Invalid_argument "Invalid input")
|
||||
|
||||
|
||||
let count_if_zero (counter: int) (position: int) =
|
||||
if position == 0 then counter + 1 else counter
|
||||
|
||||
let normalize_position (pos : int) (modulus : int) =
|
||||
let result = pos mod modulus in
|
||||
if result >= 0 then result else result + modulus
|
||||
|
||||
let count_boundary_crossings (counter: int) (position: int) (movement: int) =
|
||||
let full_rotations = abs movement / 100 in
|
||||
let remaining_movement = movement mod 100 in
|
||||
let new_position = position + remaining_movement in
|
||||
let boundary_crossings =
|
||||
if (new_position >= 100 && position < 100) ||
|
||||
(new_position <= 0 && position > 0) then 1 else 0
|
||||
in
|
||||
counter + full_rotations + boundary_crossings
|
||||
|
||||
|
||||
|
||||
let rec update (values: int list) (position: int) (counter: int) : int =
|
||||
match values with
|
||||
| [] -> counter
|
||||
| x :: xs -> let new_position = normalize_position (position + x) 100 in
|
||||
update xs new_position (count_if_zero counter new_position)
|
||||
|
||||
let rec update_part2 (values: int list) (position: int) (counter: int) : int =
|
||||
match values with
|
||||
| [] -> counter
|
||||
| x :: xs -> let new_position = normalize_position (position + x) 100 in
|
||||
update_part2 xs new_position (count_boundary_crossings counter position x)
|
||||
|
||||
let solve_part1 (lines: string list) : int =
|
||||
update (List.map convert lines) 50 0
|
||||
|
||||
let solve_part2 (lines: string list) : int =
|
||||
update_part2 (List.map convert lines) 50 0
|
||||
|
||||
let day1_part1 : int =
|
||||
solve_part1 input
|
||||
|
||||
let day1_part2 : int =
|
||||
solve_part2 input
|
||||
|
||||
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
|
||||
|
||||
let () = Tests.run_tests()
|
||||
|
||||
|
||||
102
lib/day1_improved.ml
Normal file
102
lib/day1_improved.ml
Normal file
@ -0,0 +1,102 @@
|
||||
(* 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 ()
|
||||
4
lib/dune
Normal file
4
lib/dune
Normal file
@ -0,0 +1,4 @@
|
||||
(library
|
||||
(name aoc25)
|
||||
(inline_tests)
|
||||
(preprocess (pps ppx_inline_test)))
|
||||
9
lib/reuse.ml
Normal file
9
lib/reuse.ml
Normal file
@ -0,0 +1,9 @@
|
||||
let read filename =
|
||||
let ic = open_in filename in
|
||||
let length = in_channel_length ic in
|
||||
let content = really_input_string ic length in
|
||||
close_in ic;
|
||||
content
|
||||
|
||||
let split_lines filename =
|
||||
String.split_on_char '\n' (read filename)
|
||||
Reference in New Issue
Block a user