diff --git a/AdventOfCode2024/bin/main.ml b/AdventOfCode2024/bin/main.ml index bd1253b..1582b6d 100644 --- a/AdventOfCode2024/bin/main.ml +++ b/AdventOfCode2024/bin/main.ml @@ -13,7 +13,7 @@ let read_lines name = let () = print_endline "\nAdvent of Code 2024"; let part1, part2 = - read_lines "./inputs/07.txt" |> AdventOfCode2024.Day_07.solve + read_lines "./inputs/08.txt" |> AdventOfCode2024.Day_08.solve in Printf.printf "Part 1: %s\nPart 2: %s\n" (string_of_int part1) (string_of_int part2) diff --git a/AdventOfCode2024/lib/day_08.ml b/AdventOfCode2024/lib/day_08.ml new file mode 100644 index 0000000..caf0465 --- /dev/null +++ b/AdventOfCode2024/lib/day_08.ml @@ -0,0 +1,147 @@ +module IntTuple = struct + type t = int * int + + let compare (x0, y0) (x1, y1) = if x0 == x1 && y0 == y1 then 0 else 1 +end + +module TupleSet = Set.Make (IntTuple) +module CharMap = Map.Make (Char) + +let exponent x n = + let rec loop acc n = match n with 0 -> acc | _ -> loop (acc * x) (n - 1) in + loop 1 n + +let abs x = if x < 0 then -x else x + +let get_coordinates lines freq = + let rec loop acc y lines = + match lines with + | [] -> acc + | head :: tail -> + let _, coords = + String.fold_left + (fun (i, accc) c -> + if c == freq then (i + 1, [ (i, y) ] @ accc) else (i + 1, accc)) + (0, []) head + in + loop (acc @ coords) (y + 1) tail + in + loop [] 0 lines + +let distances (x1, y1) (x2, y2) = (x2 - x1, y2 - y1) + +let get_anti_nodes c1 c2 = + let x, y = c1 in + let dx, dy = distances c1 c2 in + [ (x + (2 * dx), y + (2 * dy)); (x - dx, y - dy) ] + +let get_part2_anti_nodes x_limit y_limit c1 c2 = + let dx, dy = distances c1 c2 in + + let rec loop (x, y) (dx, dy) acc multiplier = + let new_x, new_y = (x + (multiplier * dx), y + (multiplier * dy)) in + if new_x >= 0 && new_x < x_limit && new_y >= 0 && new_y < y_limit then + loop (x, y) (dx, dy) ([ (new_x, new_y) ] @ acc) (multiplier + 1) + else acc + in + + let first = loop c1 (dx, dy) [] 1 in + let second = loop c2 (-dx, -dy) [] 1 in + first @ second + +let get_frequencies lines = + let rec loop y map lines = + match lines with + | [] -> map + | head :: tail -> + let _, m = + String.fold_left + (fun (i, acc) c -> + if c != '.' then + let new_coord = (i, y) in + let new_map = + CharMap.update c + (fun i -> + match i with + | None -> Some [ new_coord ] + | Some l -> Some ([ new_coord ] @ l)) + acc + in + + (i + 1, new_map) + else (i + 1, acc)) + (0, map) head + in + loop (y + 1) m tail + in + loop 0 CharMap.empty lines + +let character_anti_nodes fn m c = + let coords = CharMap.find c m in + + let rec loop c1 acc node_list = + match node_list with + | [] -> acc + | c2 :: tail -> + let anti_nodes = fn c1 c2 in + loop c1 (anti_nodes @ acc) tail + in + + let rec get_all acc node_list = + match node_list with + | [] -> acc + | c1 :: tail -> get_all (loop c1 [] tail @ acc) tail + in + get_all [] coords + +let rec exists l (x1, y1) = + match l with + | [] -> false + | (x2, y2) :: tail -> + if x1 == x2 && y1 == y2 then true else exists tail (x1, y1) + +let remove_duplicates l seen = + let rec loop acc seen l = + match l with + | [] -> (acc, seen) + | head :: tail -> + let found = exists seen head in + if not found then + let new_seen = [ head ] @ seen in + loop ([ head ] @ acc) new_seen tail + else loop acc seen tail + in + loop [] seen l + +(* Gradient signs are all reversed because y going down is + *) +let solve lines = + let m = get_frequencies lines in + let all_keys = CharMap.to_list m |> List.map (fun (k, _) -> k) in + let part1_anti_nodes = character_anti_nodes get_anti_nodes in + let part2_anti_nodes = + character_anti_nodes + (get_part2_anti_nodes + (String.length (List.nth lines 0)) + (List.length lines)) + in + + let sum_anti_nodes fn = + List.fold_left + (fun (acc, seen) c -> + let anti_nodes, new_seen = remove_duplicates (fn m c) seen in + let valid_anti_nodes = + List.filter + (fun (x, y) -> + x >= 0 + && x < String.length (List.nth lines 0) + && y >= 0 + && y < List.length lines) + anti_nodes + in + (acc + List.length valid_anti_nodes, new_seen)) + (0, []) all_keys + in + + let part1, _ = sum_anti_nodes part1_anti_nodes in + let part2, _ = sum_anti_nodes part2_anti_nodes in + (part1, part2)