- 📅 2024-08-16T18:04:26.130Z
- 👁️ 139 katselukertaa
- 🔓 Julkinen
open Core
open Utils
module Day17 : Day = struct
type rock = Minus | Plus | Corner | Pipe | Square
type direction = Left | Right | Down
type map = { mutable rocks : (int * int) Hash_set.t; mutable highest : int }
type moves = { move_items : direction array; mutable next_move : int }
type rocks = { rock_items : rock list; mutable next_rock : int }
type input = Input of moves
let parse_char = function
| '<' -> Left
| '>' -> Right
| _ -> failwith "Invalid direction"
let parse_input t =
let move_items = Array.of_list @@ List.map (String.to_list t) ~f:parse_char in
Input { move_items; next_move = 0 }
let create_rock (x, y) =
let rock positions = Hash_set.Poly.of_list positions in
function
| Minus -> rock [ (x + 3, y); (x + 4, y); (x + 5, y); (x + 6, y) ]
| Plus -> rock [ (x + 4, y); (x + 3, y + 1); (x + 4, y + 1); (x + 5, y + 1); (x + 4, y + 2) ]
| Corner -> rock [ (x + 3, y); (x + 4, y); (x + 5, y); (x + 5, y + 1); (x + 5, y + 2) ]
| Pipe -> rock [ (x + 3, y); (x + 3, y + 1); (x + 3, y + 2); (x + 3, y + 3) ]
| Square -> rock [ (x + 3, y); (x + 4, y); (x + 3, y + 1); (x + 4, y + 1) ]
let get_next_move moves =
moves.next_move <- (moves.next_move mod Array.length moves.move_items) + 1;
Array.get moves.move_items (moves.next_move - 1)
let get_next_rock rocks =
rocks.next_rock <- (rocks.next_rock mod List.length rocks.rock_items) + 1;
List.nth_exn rocks.rock_items (rocks.next_rock - 1)
let move (x, y) = function
| Left -> (x - 1, y)
| Right -> (x + 1, y)
| Down -> (x, y - 1)
let move_rock rock direction =
let move pos = move pos direction in
Hash_set.Poly.of_list @@ List.map (Hash_set.to_list rock) ~f:move
let collides rock map =
let min_max f rock =
( Option.value_exn (Hash_set.min_elt ~compare:f rock),
Option.value_exn (Hash_set.max_elt ~compare:f rock) )
in
let left, right = min_max (fun (x1, _) (x2, _) -> Int.compare x1 x2) rock in
let bottom, _ = min_max (fun (_, y1) (_, y2) -> Int.compare y1 y2) rock in
fst left <= 0
|| fst right >= 8
|| snd bottom <= 0
|| Hash_set.length (Hash_set.inter rock map.rocks) > 0
let add_rock rock map =
map.rocks <- Hash_set.union rock map.rocks;
Hash_set.filter_inplace map.rocks ~f:(fun (_, y) -> y > map.highest - 50)
let rec place_rock rock map moves =
let next_move = get_next_move moves in
let next_rock = move_rock rock next_move in
let next_rock = if collides next_rock map then rock else next_rock in
let next_rock = move_rock next_rock Down in
if collides next_rock map then (
add_rock next_rock map;
let highest =
Option.value_exn
(Hash_set.max_elt ~compare:(fun (_, y1) (_, y2) -> Int.compare y1 y2) map.rocks)
in
map.highest <- snd highest)
else place_rock next_rock map moves
let solve_part1 (Input input) =
let figures = { rock_items = [ Minus; Plus; Corner; Pipe; Square ]; next_rock = 0 } in
let map = { rocks = Hash_set.Poly.create (); highest = 0 } in
for _ = 1 to 2022 do
let rock = create_rock (0, map.highest + 4) (get_next_rock figures) in
place_rock rock map input
done;
AnswerInt map.highest
let solve_part2 (Input input) =
let figures = { rock_items = [ Minus; Plus; Corner; Pipe; Square ]; next_rock = 0 } in
let map = { rocks = Hash_set.Poly.create (); highest = 0 } in
for _ = 1 to 1_000_000_000_000 do
let next_rock = get_next_rock figures in
let rock = create_rock (0, map.highest + 4) next_rock in
place_rock rock map input
done;
AnswerInt map.highest
let part1 input_str = input_str |> parse_input |> solve_part1 |> answer_to_string
let part2 input_str = input_str |> parse_input |> solve_part2 |> answer_to_string
end