summaryrefslogtreecommitdiff
path: root/2024/OCaml/lib/day_04/part_01.ml
blob: acb3e3b8ff57e489e9b6e8886f909d70bd7ccb3b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
(* Day 4: Part 1 *)

type character = { index : int; character : char }
type direction = N | NE | E | SE | S | SW | W | NW

let all_directions = [ N; NE; E; SE; S; SW; W; NW ]
let needle = "XMAS"

(** [calculate_row_offset lines] is the offset used to move between the rows of
    the concatenated variant of [lines] as though it were a grid. Requires:
    values of [lines] all have the same length *)
let calculate_row_offset lines =
  match lines with [] -> 0 | h :: _ -> String.length h

let join_lines lines =
  let concat_trim acc line = acc ^ String.trim line in
  List.fold_left concat_trim "" lines

let explode s = List.init (String.length s) (String.get s)
let is_in_bounds length index = index >= 0 && index < length

let get_at_index line index =
  let length = String.length line in
  if not (is_in_bounds length index) then "."
  else
    let character = String.get line index in
    Char.escaped character

let get_direction_index row_offset direction index =
  match direction with
  | N -> index - row_offset
  | NE -> index - row_offset + 1
  | E -> index + 1
  | SE -> index + row_offset + 1
  | S -> index + row_offset
  | SW -> index + row_offset - 1
  | W -> index - 1
  | NW -> index - row_offset - 1

let get_direction_string get_char get_offset_index index =
  let first = get_char index in
  let second_index = get_offset_index index in
  let second = get_char second_index in
  let third_index = get_offset_index second_index in
  let third = get_char third_index in
  let fourth_index = get_offset_index third_index in
  let fourth = get_char fourth_index in
  first ^ second ^ third ^ fourth

let check_direction get_from_line get_offset_index index direction =
  get_direction_string get_from_line (get_offset_index direction) index = needle

let calculate_num_of_matches line row_offset character =
  let get_from_line_at_index = get_at_index line in
  let get_offset_direction_index = get_direction_index row_offset in
  let check_direction_from_index =
    check_direction get_from_line_at_index get_offset_direction_index
      character.index
  in
  all_directions
  |> List.map check_direction_from_index
  |> List.map (fun x -> match x with true -> 1 | false -> 0)
  |> List.fold_left ( + ) 0

(* Tried:
   2544 -> too high
   1272 (previous halved) -> too low *)
let solve lines =
  let row_offset = calculate_row_offset lines in
  let line = join_lines lines in
  let chars = explode line in
  let characters = List.mapi (fun i c -> { index = i; character = c }) chars in
  let xs =
    List.filter
      (fun c -> match c.character with 'X' -> true | _ -> false)
      characters
  in
  let calculate_num_of_surrounding_matches_in_line =
    calculate_num_of_matches line row_offset
  in
  let num_of_matches =
    List.map calculate_num_of_surrounding_matches_in_line xs
    |> List.fold_left ( + ) 0
  in
  num_of_matches