summaryrefslogtreecommitdiffstats
path: root/lib/day09.ml
blob: ad31a48ca320379fac4fe6715ee7d82a79e543dc (plain) (blame)
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
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
(*
 * SPDX-FileCopyrightText: Copyright 2025 Alexandre Jesus <https://adbjesus.com>
 *
 * SPDX-License-Identifier: GPL-3.0-or-later
 *)

let parse ch =
  ch
  |> In_channel.input_lines
  |> List.filter (( <> ) "")
  |> List.map (fun s -> Scanf.sscanf s "%d,%d" (fun x y -> (x, y)))

let part1 ch =
  let coords = parse ch in
  let area (x1, y1) (x2, y2) = (abs (x1 - x2) + 1) * (abs (y1 - y2) + 1) in
  let rec solve acc coords =
    match coords with
    | [] | _ :: [] -> acc
    | h :: t ->
        let acc = List.fold_left (fun acc c -> Int.max acc (area h c)) acc t in
        solve acc t
  in
  solve 0 coords |> Printf.printf "%d\n"

let part2 ch =
  (* This approach assumes the following (which holds true for my input):
     - Points to form the polygon are given clockwise order
       (this can be dealt with by reversing the point list)
     - Every point gives a corner
       (these could be included, but were not needed in my input)
     - There are no adjacent edges
       (this would need a different approach, we could probably build the polygon with coordinate compression)

     The idea is:
     - Compute directions of vertices to know if it is a convex or concave corner
     - For each vertex check all other vertices that make up an interior rectangle,
       taking into account that depending on corner only some vertices give an
       interior rectangle.
     - For each interior rectangle check if there are any overlapping edges. If
       there are no overlapping edges, this is a valid rectangle.
   *)
  let coords = parse ch in
  let n = List.length coords in
  let dir c1 c2 =
    match (c1, c2) with
    | (x1, y1), (x2, y2) when x1 = x2 && y1 < y2 -> `D
    | (x1, y1), (x2, y2) when x1 = x2 && y2 < y1 -> `U
    | (x1, y1), (x2, y2) when y1 = y2 && x1 < x2 -> `R
    | (x1, y1), (x2, y2) when y1 = y2 && x2 < x1 -> `L
    | _ -> failwith "Invalid input (repeated point, or no common coordinate)"
  in
  let ccoords = List.to_seq coords |> Seq.cycle in
  let dirs =
    Seq.map2 dir
      (Seq.take (n + 1) (Seq.drop (n - 1) ccoords))
      (Seq.take (n + 1) ccoords)
    |> Array.of_seq
  in
  let coords = Array.of_list coords in
  let ans = ref 0 in
  let ans1 = ref coords.(0) in
  let ans2 = ref coords.(0) in
  let all_outside (x1, y1) (x2, y2) =
    let xl, xu = if x1 < x2 then (x1, x2) else (x2, x1) in
    let yl, yu = if y1 < y2 then (y1, y2) else (y2, y1) in
    coords
    |> Array.to_seq
    |> Seq.mapi (fun i c -> (i, c))
    |> Seq.for_all (fun (i, (x1, y1)) ->
        let x2, y2 = coords.((i + 1) mod n) in
        let x1, x2 = if x1 < x2 then (x1, x2) else (x2, x1) in
        let y1, y2 = if y1 < y2 then (y1, y2) else (y2, y1) in
        x2 <= xl || xu <= x1 || y2 <= yl || yu <= y1)
  in
  let try_rect c1 condfn =
    Array.iter
      (fun c2 ->
        if condfn c1 c2 && all_outside c1 c2 then
          let xd = abs (fst c1 - fst c2) in
          let yd = abs (snd c1 - snd c2) in
          let a = (xd + 1) * (yd + 1) in
          if a > !ans then (
            ans := a;
            ans1 := c1;
            ans2 := c2))
      coords
  in
  for i = 0 to n - 1 do
    let d1 = dirs.(i) in
    let d2 = dirs.(i + 1) in
    let c = coords.(i) in
    match (d1, d2) with
    | `U, `R -> try_rect c (fun (x1, y1) (x2, y2) -> x1 <= x2 && y1 <= y2)
    | `R, `U -> try_rect c (fun (x1, y1) (x2, y2) -> x1 <= x2 || y1 <= y2)
    | `R, `D -> try_rect c (fun (x1, y1) (x2, y2) -> x2 <= x1 && y1 <= y2)
    | `D, `R -> try_rect c (fun (x1, y1) (x2, y2) -> x2 <= x1 || y1 <= y2)
    | `D, `L -> try_rect c (fun (x1, y1) (x2, y2) -> x2 <= x1 && y2 <= y1)
    | `L, `D -> try_rect c (fun (x1, y1) (x2, y2) -> x2 <= x1 || y2 <= y1)
    | `L, `U -> try_rect c (fun (x1, y1) (x2, y2) -> x1 <= x2 && y2 <= y1)
    | `U, `L -> try_rect c (fun (x1, y1) (x2, y2) -> x1 <= x2 || y2 <= y1)
    | _ -> failwith "Non-corner vertex"
  done;
  Printf.printf "%d\n" !ans