Compare commits
29
Commits
421616a282
..
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8ec58edd08 | ||
|
|
1904239ca7 | ||
|
|
46d49dc3fe | ||
|
|
950f2860df | ||
|
|
d203c9a520 | ||
|
|
8b54ae782c | ||
|
|
9f1499a1d3 | ||
|
|
490696c63d | ||
|
|
a7c039197b | ||
|
|
94f4badcc4 | ||
|
|
4e57ad4072 | ||
|
|
f2f3e497d6 | ||
|
|
2a515a5323 | ||
|
|
5c0d7867a4 | ||
|
|
1df5532c24 | ||
|
|
8db68c9baf | ||
|
|
360be1a368 | ||
|
|
ae2acb87fa | ||
|
|
c7eae53378 | ||
|
|
b9a36a476a | ||
|
|
8270ee7aa8 | ||
|
|
894e83fe12 | ||
|
|
11546bff5f | ||
|
|
d1e69ba66e | ||
|
|
2887274ad9 | ||
|
|
6758130700 | ||
|
|
ff4bb0ae89 | ||
|
|
b69f867423 | ||
|
|
a652ec69da |
No files matched your search
@@ -1,2 +1 @@
|
||||
profile = janestreet
|
||||
version = 0.26.0
|
||||
+1
-1
@@ -1,2 +1,2 @@
|
||||
#install_printer Aoc.Vec2.pp;;
|
||||
#install_printer Aoc.Vec2.pp_grid;;
|
||||
#install_printer Aoc.Grid.pp;;
|
||||
@@ -10,11 +10,11 @@ homepage: "https://github.com/username/reponame"
|
||||
doc: "https://url/to/documentation"
|
||||
bug-reports: "https://github.com/username/reponame/issues"
|
||||
depends: [
|
||||
"ocaml"
|
||||
"dune" {>= "3.10"}
|
||||
"utop"
|
||||
"ocaml-lsp-server"
|
||||
"ocamlformat"
|
||||
"ocaml"
|
||||
"dune" {>= "3.10"}
|
||||
"re"
|
||||
"containers"
|
||||
"iter"
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
Valve XB has flow rate=0; tunnels lead to valves WZ, LE
|
||||
Valve BM has flow rate=0; tunnels lead to valves PL, RI
|
||||
Valve GC has flow rate=0; tunnels lead to valves HN, IT
|
||||
Valve RM has flow rate=0; tunnels lead to valves ZQ, YL
|
||||
Valve ZM has flow rate=5; tunnels lead to valves SN, KE, UW, MY, GW
|
||||
Valve UH has flow rate=0; tunnels lead to valves HM, HN
|
||||
Valve GW has flow rate=0; tunnels lead to valves LE, ZM
|
||||
Valve HN has flow rate=19; tunnels lead to valves UW, UH, GL, WZ, GC
|
||||
Valve VT has flow rate=0; tunnels lead to valves ZD, PE
|
||||
Valve VI has flow rate=0; tunnels lead to valves JS, AA
|
||||
Valve YL has flow rate=12; tunnels lead to valves PM, MH, RM, CM
|
||||
Valve LA has flow rate=0; tunnels lead to valves SN, IY
|
||||
Valve CM has flow rate=0; tunnels lead to valves YL, UL
|
||||
Valve JI has flow rate=24; tunnels lead to valves UV, WH, XW, OJ
|
||||
Valve ZD has flow rate=25; tunnels lead to valves VB, XW, VT
|
||||
Valve VB has flow rate=0; tunnels lead to valves QX, ZD
|
||||
Valve FO has flow rate=0; tunnels lead to valves WH, PE
|
||||
Valve JS has flow rate=0; tunnels lead to valves VI, IY
|
||||
Valve RI has flow rate=0; tunnels lead to valves PE, BM
|
||||
Valve XD has flow rate=14; tunnel leads to valve CP
|
||||
Valve ES has flow rate=11; tunnels lead to valves GU, WU
|
||||
Valve WZ has flow rate=0; tunnels lead to valves XB, HN
|
||||
Valve HW has flow rate=0; tunnels lead to valves QY, LE
|
||||
Valve KE has flow rate=0; tunnels lead to valves AA, ZM
|
||||
Valve IY has flow rate=22; tunnels lead to valves JS, AB, LA, IT
|
||||
Valve XW has flow rate=0; tunnels lead to valves ZD, JI
|
||||
Valve BE has flow rate=0; tunnels lead to valves AB, LE
|
||||
Valve QY has flow rate=23; tunnels lead to valves AM, HW, MY
|
||||
Valve MH has flow rate=0; tunnels lead to valves YL, AM
|
||||
Valve IT has flow rate=0; tunnels lead to valves GC, IY
|
||||
Valve DA has flow rate=0; tunnels lead to valves QR, CP
|
||||
Valve PM has flow rate=0; tunnels lead to valves LE, YL
|
||||
Valve WU has flow rate=0; tunnels lead to valves SM, ES
|
||||
Valve UL has flow rate=0; tunnels lead to valves CM, QR
|
||||
Valve SM has flow rate=13; tunnel leads to valve WU
|
||||
Valve XC has flow rate=0; tunnels lead to valves ZJ, AA
|
||||
Valve OJ has flow rate=0; tunnels lead to valves GI, JI
|
||||
Valve SN has flow rate=0; tunnels lead to valves LA, ZM
|
||||
Valve WH has flow rate=0; tunnels lead to valves JI, FO
|
||||
Valve UW has flow rate=0; tunnels lead to valves HN, ZM
|
||||
Valve HM has flow rate=0; tunnels lead to valves UH, PE
|
||||
Valve AB has flow rate=0; tunnels lead to valves BE, IY
|
||||
Valve QR has flow rate=8; tunnels lead to valves WW, UL, DA
|
||||
Valve UV has flow rate=0; tunnels lead to valves JI, FF
|
||||
Valve ZQ has flow rate=20; tunnel leads to valve RM
|
||||
Valve ZJ has flow rate=0; tunnels lead to valves PE, XC
|
||||
Valve GL has flow rate=0; tunnels lead to valves HN, PL
|
||||
Valve CP has flow rate=0; tunnels lead to valves DA, XD
|
||||
Valve AM has flow rate=0; tunnels lead to valves QY, MH
|
||||
Valve PL has flow rate=17; tunnels lead to valves GL, YE, BM, FF, QX
|
||||
Valve YE has flow rate=0; tunnels lead to valves PL, AA
|
||||
Valve PE has flow rate=4; tunnels lead to valves FO, RI, ZJ, VT, HM
|
||||
Valve MY has flow rate=0; tunnels lead to valves QY, ZM
|
||||
Valve QX has flow rate=0; tunnels lead to valves VB, PL
|
||||
Valve GI has flow rate=0; tunnels lead to valves OJ, AA
|
||||
Valve WW has flow rate=0; tunnels lead to valves GU, QR
|
||||
Valve FF has flow rate=0; tunnels lead to valves UV, PL
|
||||
Valve LE has flow rate=6; tunnels lead to valves HW, GW, XB, BE, PM
|
||||
Valve GU has flow rate=0; tunnels lead to valves ES, WW
|
||||
Valve AA has flow rate=0; tunnels lead to valves KE, GI, VI, YE, XC
|
||||
@@ -0,0 +1 @@
|
||||
><<<<>>>><>>><<><<<<>>>><<>>><>>><<<<>>>><<>>>><<<>>>><<<>>>><<<>>><<<<>><>><<<<>>><><<<><><>>>><<<<><<<<>><<<>>>><<>><><>>>><<<>>><<<>>><<>>>><>>>><>><>>><<>><><<<<><>><<<>>>><<<>>><<<>><>><<<>>><<<<>>>><><<<>>><<<>>><<<<><<<<>>><<<<>>><<<>>><<<<>>><<>>><<><<<>><<<>>><<<<><<<<>>><<<><<>>>><<<>><>>><<>>><<<><<<<>>><<><<<<>>>><<>>>><<>>>><<<<>>><><<<<><>><<<>>>><<<<>><>>>><>><>><>><<<>><<<>>>><<<>><<>><<<<><<<<><<>>>><<<<><<<>>><>><<<>>><<<><<<><<<<><<><<<>>><<<<>>>><<<<><<>><<<<>>><>>><<<<><<<>>>><<><>><<<<>><<<<>>>><<<><<<<><<<<>>><<<><<>>><>>><<<<>>><<>><<<<>>><<<><<<>><<<<>>>><<<<><<><<<<>>>><>>>><<<<>>>><<<>>>><<<><<>>><>>><<<<>><<<<>>><><>><<<>>>><<<<>><<<>><<>><<<>>><<<>>>><<<<>>>><<>>>><<>>>><><<<<>><<<<>>>><<<<>>><<<<>><>><<<>>><>>>><><<>><<>>><<<>>>><>>><<>><<<>><>>><>>><><>><>><<<><<<>>>><>><<<><<<>>><<<<><<>>>><<<><<<>>>><>><<<>>>><<<>>>><>>><><>>>><>><<><<<>>>><<>>>><<<>>><<<<>>><<<>><>>>><<<<>>>><>><<>>><<<<>>>><<<<><>>>><<>><<>>>><>><<<<>>>><<<<>><<<>>>><<>><><>><<<<>><<<>>>><<<>>><<>><><<<>>>><<>><>><<>><>><<<<>><<<>>><<<<>>><<>>>><<>>><<<<>>><>><<><<<>><>>><<<>><<<<>>>><<<>>><<>>><<<>>><<<<>>>><<<<>>><<<>>><<>>>><<>>><<<>>><<><<>>>><<>>>><<><<>>><<><<<>>>><<<<><<<<>><<<>>>><>>><<<<>>><<<<>>>><<>><<<<>>>><<<><<<><<>>>><<<>>>><<<>>>><<<>><<>>><>>><<<<>>><<<<>><<<>>><<<>><<<<>>><<<<><<><>>><<<<>>><<<<>>>><>><<<<>><<<<>>>><<<<><<<>>>><>><<<<><<<<>>>><>><<><<<>>><<>>><<<<>><<<><<<><>><<>>>><<<><<<<><<<>>>><<<<>><<>><<<<>>>><><<<><<<>>>><<>>>><<>><<<<>>>><><><<<<><><<>>><<><<>>><>>><<>><><<><<>>>><<<<><<<>>>><<>><><<>>>><<<<><><<<>>>><<>>><<<><<<>>>><>><<<<>>>><>>><<><>>>><>>>><<<><>>><>>>><<<>><<<>>><<<<>>>><<>>>><<<<>>>><<><>><<<<>><<<<>>><<>>><>>>><<><<>>><>>>><<><<><<><><<<>><>><<<<>>><<<>>>><<<><<<<>><>><>>><<<<><<<>>>><<<>>><><<<<>>><<<<><<<<>>>><>><>>><<<><<>><<<><>>><<<<>><><<<<><>><<>><<>><>><<<<><<<>><>>>><<>><<<<>>><><<<>><<>><<>><>>><>>>><<><>><<<<>>><>>><<>>><<>>>><<>><<<<>>><>><<<>>><<<>>><<<<>>>><<<><<<<>><<><<<><<<>>><<><<>><>><>>><<<>>><<>><<>>><<>><<>><>><<<<>>>><<<<>>><<<>>>><<<<>>><<<>><<<>><<<<>>>><<>><<<<><<<<>><<>>><><<><<<<>>>><><>>>><<<<>>>><<<<>>>><<>><<<<>>>><<<><>><<>>>><<>>>><<<<>>>><<<>>><>><<>>>><<<>>>><<<<>>><<<<>><<<<>>>><<>>><<>>>><>>><>>>><><>><<<<>>>><<<>>>><<<>><<>>><<<<>>><>>>><<>>>><<<><>>><><<>>>><<>>>><<<>>>><<<<><<<<>>>><>>>><>><<<>>><<<>>>><<<>><<<>>><>><<>>><<><><<><<>>>><>>><<<<>><><>><>>><<<<><>>><<<>>><<<><<<<>>><<<>><<<>>><<>>><<<<><<<<><>><><<<<>>><<<>><<>><<<>><<<<>><>><>>>><<>>><<<>><<<>><<<<>><><<<<>>><<<>>>><<<<>>><<>><>><<>>><><<>>><<<>>><<<>><<>>>><<<<>><<<<>>>><<<><<><<<<>><<<<>><<<>>>><>><<<><>>>><<>>>><<><<<>>>><<<<>><<<>>>><<>>><>><<<<>>>><<<<>>><>>>><<<<><<><<>><>><>>><<<><<<<><<<<>><<<>>><<<<>><<><<<<>>>><<<>>>><<<>>>><<<<>>><<><<>><<><<<<>>>><<<><<<<>>>><<>>><<<<>><>>>><<<<>><<<>>>><<><<<<>>>><<<>><>>>><><<<>><<<><<<>>><>>>><>>><<<<><<<<>>><<>>><<<>>><<<<><<<<>>>><<>>><<<>><<>>><><<<>>>><<><>>><<>><<<>>>><<<><>><<<>>><<>>>><<<>>>><>><<><>><<>>>><<<>>>><<<<>>>><<<>>><<<>><<<<><<<<>><><<><<<>><>>><<<<>>><<<<>><<<>>><<>><<<<><<<><<<><<<>>><<<<>>><<<<>><<>>><<>><<<<>>>><<>>>><<>>>><<>><>>><>>>><<>>>><<<>>><<<<>>><>>><>>>><<<>><<>>><<>>>><<>>>><<<>>><<>>><<<><<<>><>>>><<<>><>>><<<>>><>>>><>><<>>><<<<>>><<<<>><>>><<<<>>><<<<><<<>>><><>>><<<>>><>><>>>><<>>>><<<>><<>>><<>><<<<>><<<<><>>><<>><<<<>>><<<>>><<>>>><>>>><<<><>>>><<><><>>><<>>>><<<>>>><><>><<<<>><<<>>>><<<<><<<<>>>><<<<>><>>>><<<>>>><<><<>>>><<<<>>>><<<<>>><>><>>>><>>>><>><<>>>><>>>><<>>><<<<>>>><<>>><><<>>>><<>><<<<><<><>>>><<<<>><<><<><<>><<<><<<>><<<<>>><<<<>><<>><>>><<<<>>>><<<<>>>><<<<>>><><<<<><<<<><><<<<><<<<>>>><<<<>>>><><>>><<<<><<<<><<<><><<<<>><><<<><<<<>><>>><><><<<><>>><<><<>><<<>><<<>>>><<<<>>><<>><<<<><>>><>>><<>>>><<<<>>><<<>>>><<<>>>><<<>><<<>><<<<>>><<<<>><>>><<<<><>>>><<<>><>>><<>>><<<<>><<><<<>><>>><<<>><<<<>>><<<>>>><>>><><<<><<<>>>><<>>><<><>>>><>>>><<<<>>><><><>>>><<<<>><<<<>>><<>>><<<><<<>>>><<><<<>><<<>>>><<<<>>>><>>><<<>>><><<<>><<<<>><<<>>>><>><<><><<>><<<>><<<>>><<<<>>>><<<><<<<>><<<<>>>><<<<>>>><<><<<<>>><<<<>><<<<>><<<>><<<<>>><<>>>><<>>><>><<>>>><>>><>>><<<><>>>><<<>>><<<<><<<<>>><<><><>>>><<>>>><<>>>><<<<>>>><<><<><<<>>><<<>>>><<><<<><<>>><<<><<<>>>><<<<>>><<>>>><<>><><<<><<>><<>>><<<<>><>><<<>><<<<>><<<<><<>>><<><<<<>>>><<<>><<<<>><<>>>><>>>><>>><>>>><<<>>><<<>>>><<>>>><>><>><<<>><<<>>><<>>><<<<>>><<<<>>><<>>>><>>><>><<<<>>>><<>>>><<>>>><<<<>>>><>>>><><>>><<>>>><<<<><<>>>><<<<>><<<<>>>><<>><<<<><<<<>>><<<<>><<>>>><<<>>>><<<><>><<<><<>>><<<<>><<<>>><><<<<>><<<<>><>>><<<<>>><<<<>>>><>>>><<>>>><<>>><>>>><<<<>>><<>><<<><<><<<>>>><<<>>>><<<<><<>>><>><<<<><<<>>><>><><<<><<>>>><>>>><>><<>>><<>>>><<>>>><<<<>><<>><><<>>><<>><<>><<>>><>><<<<>>>><<>>>><<<<>>><<>><><<<><>><<<>><>>><>><<>>><><>>><<>>>><<<>>><<<<>>><<<>><<>><<<<>>><<<><<<>><<<<><<>><>><<<<>><<>>>><<<>><<<>>>><<<<>>>><<<<>><<>>><<<>>><<<>>>><<<<>>><<<<>>>><>><<<<>>>><<>>><><<>>>><<<<>>>><<<<>>>><>><<>><<<<><<<<>>><><>>><<<<>>><<><>>>><<<<><<<>>>><>>>><<<>>>><<<>>>><<>>><<>>><<<<><<<>><>>><<<><<<>>><Line truncated
|
||||
+2025
File diff suppressed because it is too large.
Load diff
+63
-45
@@ -1,7 +1,5 @@
|
||||
open Containers
|
||||
|
||||
let infinity = 1000000000
|
||||
|
||||
type node =
|
||||
{ char : char
|
||||
; mutable visited : bool
|
||||
@@ -10,25 +8,20 @@ type node =
|
||||
[@@deriving show]
|
||||
|
||||
let parse_grid start_anywhere =
|
||||
let s = ref @@ Vec2.origin in
|
||||
let e = ref @@ Vec2.origin in
|
||||
let unvisited = ref [] in
|
||||
let start_nodes = ref [] in
|
||||
let end_node = ref @@ Vec2.origin in
|
||||
let grid =
|
||||
Utils.lines_of_input 12
|
||||
|> Grid.of_lines (fun char -> { char; visited = false; distance = infinity })
|
||||
|> Grid.of_lines (fun char -> { char; visited = false; distance = Int.max_int })
|
||||
in
|
||||
Grid.iter grid (fun pos ->
|
||||
let n = Grid.at_e grid pos in
|
||||
match n.char with
|
||||
| 'E' -> e := pos
|
||||
| 'S' ->
|
||||
n.distance <- 0;
|
||||
s := pos
|
||||
| 'a' when start_anywhere ->
|
||||
n.distance <- 0;
|
||||
unvisited := pos :: !unvisited
|
||||
let node = Grid.at_e grid pos in
|
||||
match node.char with
|
||||
| 'E' -> end_node := pos
|
||||
| 'S' -> start_nodes := pos :: !start_nodes
|
||||
| 'a' when start_anywhere -> start_nodes := pos :: !start_nodes
|
||||
| _ -> ());
|
||||
grid, !e, !s, !unvisited
|
||||
grid, !end_node, !start_nodes
|
||||
;;
|
||||
|
||||
let reachable f t =
|
||||
@@ -41,42 +34,67 @@ let reachable f t =
|
||||
Char.compare (height f) (height t) >= -1
|
||||
;;
|
||||
|
||||
let rec path grid goal current unvisited =
|
||||
let current_node = Grid.at_e grid current in
|
||||
let get_neighbors grid node_pos =
|
||||
let node = Grid.at_e grid node_pos in
|
||||
Vec2.directions
|
||||
|> List.map (fun dir -> Vec2.(current + dir))
|
||||
|> List.iter (fun neighbor ->
|
||||
match Grid.at grid neighbor with
|
||||
| Some node when (not node.visited) && reachable current_node.char node.char ->
|
||||
let new_distance = current_node.distance + 1 in
|
||||
if new_distance < node.distance then node.distance <- new_distance;
|
||||
if Option.is_none @@ List.find_opt (fun x -> Vec2.(x = neighbor)) !unvisited
|
||||
then unvisited := neighbor :: !unvisited
|
||||
| _ -> ());
|
||||
current_node.visited <- true;
|
||||
unvisited
|
||||
:= List.sort
|
||||
(fun a b ->
|
||||
let a = Grid.at_e grid a in
|
||||
let b = Grid.at_e grid b in
|
||||
a.distance - b.distance)
|
||||
!unvisited;
|
||||
match !unvisited with
|
||||
| shortest :: _ when Vec2.(shortest = goal) -> Grid.at grid shortest
|
||||
| shortest :: rest -> path grid goal shortest @@ ref rest
|
||||
| [] -> None
|
||||
|> Iter.of_list
|
||||
|> Iter.filter_map (fun dir ->
|
||||
let neighbor_pos = Vec2.(node_pos + dir) in
|
||||
match Grid.at grid neighbor_pos with
|
||||
| Some neighbor when reachable node.char neighbor.char -> Some neighbor_pos
|
||||
| _ -> None)
|
||||
;;
|
||||
|
||||
let path_distance grid goal start_nodes =
|
||||
let set_info position (dist, visited) =
|
||||
let node = Grid.at_e grid position in
|
||||
node.distance <- dist;
|
||||
node.visited <- visited
|
||||
in
|
||||
let get_info position =
|
||||
let { char = _; distance; visited } = Grid.at_e grid position in
|
||||
distance, visited
|
||||
in
|
||||
Utils.djikstra
|
||||
~set_info
|
||||
~get_info
|
||||
~eq:Vec2.( = )
|
||||
~get_weight:(fun _ _ -> 1)
|
||||
~get_neighbors:(get_neighbors grid)
|
||||
~goal
|
||||
~start_nodes
|
||||
|> Option.get_exn_or "couldn't find"
|
||||
;;
|
||||
|
||||
let%expect_test "Day 12.1" =
|
||||
let grid, e, s, unvisited = parse_grid false in
|
||||
let solution = Option.get_exn_or "coulnd't find" @@ path grid e s @@ ref unvisited in
|
||||
Printf.printf "%d" solution.distance;
|
||||
let grid, goal, start_nodes = parse_grid false in
|
||||
Printf.printf "%i" @@ path_distance grid (Some goal) start_nodes;
|
||||
[%expect {| 497 |}]
|
||||
;;
|
||||
|
||||
let%expect_test "Day 12.2" =
|
||||
let grid, e, s, unvisited = parse_grid true in
|
||||
let solution = Option.get_exn_or "coulnd't find" @@ path grid e s @@ ref unvisited in
|
||||
Printf.printf "%d" solution.distance;
|
||||
let grid, goal, start_nodes = parse_grid true in
|
||||
Printf.printf "%i" @@ path_distance grid (Some goal) start_nodes;
|
||||
[%expect {| 492 |}]
|
||||
;;
|
||||
|
||||
let%expect_test "Day 12 test" =
|
||||
let grid, goal, start_nodes = parse_grid false in
|
||||
let _ = path_distance grid None start_nodes in
|
||||
let print_dist_to pos =
|
||||
let node = Option.get_exn_or "invalid pos!" grid.Grid.%(pos) in
|
||||
Printf.printf "%s %c %i\n" (Vec2.show pos) node.char node.distance
|
||||
in
|
||||
print_dist_to goal;
|
||||
print_dist_to Vec2.(goal + right);
|
||||
print_dist_to Vec2.(goal + down);
|
||||
print_dist_to Vec2.(of_tuple (121, 23));
|
||||
print_dist_to Vec2.(origin + right);
|
||||
[%expect
|
||||
{|
|
||||
(149, 20) E 497
|
||||
(150, 20) z 498
|
||||
(149, 21) y 496
|
||||
(121, 23) a 138
|
||||
(1, 0) b 21 |}]
|
||||
;;
|
||||
+14
-9
@@ -9,7 +9,7 @@ type tile =
|
||||
| Path_L
|
||||
| Path_R
|
||||
|
||||
let char_of_tile = function
|
||||
let char_of_tile _ = function
|
||||
| Empty -> '-'
|
||||
| Rock -> '#'
|
||||
| Sand -> 'o'
|
||||
@@ -43,9 +43,9 @@ let draw_rocks grid points =
|
||||
;;
|
||||
|
||||
let parse_grid lines start =
|
||||
let points = List.map parse_line lines in
|
||||
let points = Iter.map Fun.(parse_line %> Iter.of_list) lines in
|
||||
let Vec2.{ x = lx; y = ly }, Vec2.{ x = hx; y = hy } =
|
||||
points |> List.flatten |> List.append [ start ] |> Vec2.bounds 0
|
||||
points |> Iter.flatten |> Iter.cons start |> Vec2.find_bounds 0
|
||||
in
|
||||
let pad_floor = 1 in
|
||||
let pad_x = 2 in
|
||||
@@ -58,8 +58,9 @@ let parse_grid lines start =
|
||||
let open Grid in
|
||||
let grid = init ~width ~height (fun _ -> Empty) in
|
||||
grid.%(start) <- Start;
|
||||
points
|
||||
|> List.iter Fun.(List.map (fun point -> Vec2.(point - extra)) %> draw_rocks grid);
|
||||
Iter.iter
|
||||
Fun.(Iter.map (fun point -> Vec2.(point - extra)) %> Iter.to_list %> draw_rocks grid)
|
||||
points;
|
||||
grid, start
|
||||
;;
|
||||
|
||||
@@ -118,7 +119,9 @@ let rec drop_all_sand drop grid start =
|
||||
;;
|
||||
|
||||
let%expect_test "Day 14.1 example" =
|
||||
let lines = [ "498,4 -> 498,6 -> 496,6"; "503,4 -> 502,4 -> 502,9 -> 494,9" ] in
|
||||
let lines =
|
||||
Iter.of_list [ "498,4 -> 498,6 -> 496,6"; "503,4 -> 502,4 -> 502,9 -> 494,9" ]
|
||||
in
|
||||
let start = Vec2.of_tuple (500, 0) in
|
||||
let grid, start = parse_grid lines start in
|
||||
drop_all_sand (drop_sand_abyss false) grid start;
|
||||
@@ -142,7 +145,9 @@ let%expect_test "Day 14.1 example" =
|
||||
;;
|
||||
|
||||
let%expect_test "Day 14.2 example" =
|
||||
let lines = [ "498,4 -> 498,6 -> 496,6"; "503,4 -> 502,4 -> 502,9 -> 494,9" ] in
|
||||
let lines =
|
||||
Iter.of_list [ "498,4 -> 498,6 -> 496,6"; "503,4 -> 502,4 -> 502,9 -> 494,9" ]
|
||||
in
|
||||
let start = Vec2.of_tuple (500, 0) in
|
||||
let grid, start = parse_grid lines start in
|
||||
drop_all_sand drop_sand_floor grid start;
|
||||
@@ -165,7 +170,7 @@ let%expect_test "Day 14.2 example" =
|
||||
;;
|
||||
|
||||
let%expect_test "Day 14.1" =
|
||||
let lines = Utils.lines_of_input 14 in
|
||||
let lines = Iter.of_list @@ Utils.lines_of_input 14 in
|
||||
let start = Vec2.of_tuple (500, 0) in
|
||||
let grid, start = parse_grid lines start in
|
||||
drop_all_sand (drop_sand_abyss false) grid start;
|
||||
@@ -175,7 +180,7 @@ let%expect_test "Day 14.1" =
|
||||
;;
|
||||
|
||||
let%expect_test "Day 14.2" =
|
||||
let lines = Utils.lines_of_input 14 in
|
||||
let lines = Iter.of_list @@ Utils.lines_of_input 14 in
|
||||
let start = Vec2.of_tuple (500, 0) in
|
||||
let grid, start = parse_grid lines start in
|
||||
drop_all_sand drop_sand_floor grid start;
|
||||
|
||||
+119
-117
@@ -1,31 +1,5 @@
|
||||
open Containers
|
||||
|
||||
type sensor =
|
||||
{ pos : Vec2.t
|
||||
; beacon : Vec2.t
|
||||
; dist : int
|
||||
}
|
||||
[@@deriving show { with_path = false }]
|
||||
|
||||
let taxicab_dist a b =
|
||||
let x, y = Vec2.(abs (a - b) |> to_tuple) in
|
||||
x + y
|
||||
;;
|
||||
|
||||
let parse_sensor line =
|
||||
let open Utils.Parse in
|
||||
let parse =
|
||||
many1 (not_int *> int)
|
||||
>>= function
|
||||
| [ sx; sy; bx; by ] ->
|
||||
let pos = Vec2.of_tuple (sx, sy) in
|
||||
let beacon = Vec2.of_tuple (bx, by) in
|
||||
return { pos; beacon; dist = taxicab_dist pos beacon }
|
||||
| _ -> failwith "should be four ints"
|
||||
in
|
||||
Result.get_or_failwith @@ parse_string ~consume:All parse line
|
||||
;;
|
||||
|
||||
let example_lines =
|
||||
[ "Sensor at x=2, y=18: closest beacon is at x=-2, y=15"
|
||||
; "Sensor at x=9, y=16: closest beacon is at x=10, y=16"
|
||||
@@ -44,17 +18,29 @@ let example_lines =
|
||||
]
|
||||
;;
|
||||
|
||||
let bounds sensors =
|
||||
let max_dist = sensors |> List.fold_left (fun acc s -> max acc s.dist) 0 in
|
||||
sensors
|
||||
|> List.flat_map (fun sensor -> [ sensor.pos; sensor.beacon ])
|
||||
|> Vec2.bounds max_dist
|
||||
type sensor =
|
||||
{ pos : Vec2.t
|
||||
; beacon : Vec2.t
|
||||
; dist : int
|
||||
}
|
||||
[@@deriving show { with_path = false }]
|
||||
|
||||
let taxicab_dist a b =
|
||||
let x, y = Vec2.(abs (a - b) |> to_tuple) in
|
||||
x + y
|
||||
;;
|
||||
|
||||
let parse_sensor line =
|
||||
match List.map int_of_string Re.(matches (Pcre.regexp {|-?\d+|}) line) with
|
||||
| [ sx; sy; bx; by ] ->
|
||||
let pos, beacon = Vec2.(of_tuple (sx, sy), of_tuple (bx, by)) in
|
||||
{ pos; beacon; dist = taxicab_dist pos beacon }
|
||||
| _ -> failwith "parse error"
|
||||
;;
|
||||
|
||||
let could_be_beacon sensors ignore_known pos =
|
||||
let open Iter in
|
||||
of_list sensors
|
||||
|> fold_while
|
||||
sensors
|
||||
|> Iter.fold_while
|
||||
(fun _ s ->
|
||||
let d = taxicab_dist pos s.pos in
|
||||
if d <= s.dist && (ignore_known || not Vec2.(pos = s.beacon))
|
||||
@@ -63,95 +49,102 @@ let could_be_beacon sensors ignore_known pos =
|
||||
true
|
||||
;;
|
||||
|
||||
(* let%expect_test "Day 15.1" = *)
|
||||
(* let sensors = Utils.lines_of_input 15 |> List.map parse_sensor in *)
|
||||
(* let scan_row = 2000000 in *)
|
||||
(* let Vec2.{ x = lx; _ }, Vec2.{ x = hx; _ } = bounds sensors in *)
|
||||
(* Vec2.fold_region *)
|
||||
(* Vec2.{ x = lx; y = scan_row } *)
|
||||
(* Vec2.{ x = hx; y = scan_row } *)
|
||||
(* (fun acc pos -> if could_be_beacon sensors false pos then acc else acc + 1) *)
|
||||
(* 0 *)
|
||||
(* |> Printf.printf "%i\n"; *)
|
||||
(* [%expect {| 5525990 |}] *)
|
||||
(* ;; *)
|
||||
(*{[
|
||||
equation for line
|
||||
y = m*x + c
|
||||
|
||||
(* module VecTbl = Hashtbl.Make (Vec2) *)
|
||||
beacon range boundaries are made up of four lines:
|
||||
slope 1 passing thru bx + dist + 1, by
|
||||
slope 1 passing thru bx - dist - 1, by
|
||||
slope -1 passing thru bx + dist + 1, by
|
||||
slope -1 passing thru bx - dist - 1, by
|
||||
|
||||
(* let points_on_border tbl sensor = *)
|
||||
(* let points = ref [] in *)
|
||||
(* let dist = sensor.dist + 1 in *)
|
||||
(* for d1 = 0 to dist do *)
|
||||
(* let d2 = dist - d1 in *)
|
||||
(* let open Vec2 in *)
|
||||
(* let p1 = of_tuple (d1, d2) in *)
|
||||
(* let p2 = of_tuple (d1, Int.(-d2)) in *)
|
||||
(* let new_points = *)
|
||||
(* [ sensor.pos + p1; sensor.pos - p1; sensor.pos + p2; sensor.pos - p2 ] *)
|
||||
(* |> List.fold_left *)
|
||||
(* (fun acc point -> *)
|
||||
(* match VecTbl.find_opt tbl point with *)
|
||||
(* | Some false -> acc *)
|
||||
(* | Some true -> *)
|
||||
(* VecTbl.replace tbl point false; *)
|
||||
(* point :: acc *)
|
||||
(* | None -> *)
|
||||
(* VecTbl.add tbl point true; *)
|
||||
(* acc) *)
|
||||
(* [] *)
|
||||
(* in *)
|
||||
(* points := !points @ new_points *)
|
||||
(* done; *)
|
||||
(* !points *)
|
||||
(* ;; *)
|
||||
to find the constant given a slope, plug a point into
|
||||
c = y - m*x
|
||||
|
||||
(* let%expect_test "Day 15.2 brute force" = *)
|
||||
(* let sensors = Utils.lines_of_input 15 |> List.map parse_sensor in *)
|
||||
(* let mult = 4000000 in *)
|
||||
(* let range = mult in *)
|
||||
(* let in_bounds = Vec2.(in_bounds origin @@ of_tuple (range, range)) in *)
|
||||
(* let tbl = VecTbl.create (range * 10) in *)
|
||||
(* let point = *)
|
||||
(* sensors *)
|
||||
(* |> Iter.of_list *)
|
||||
(* |> Iter.flat_map_l @@ points_on_border tbl *)
|
||||
(* |> Iter.fold_while *)
|
||||
(* (fun _ v -> *)
|
||||
(* if in_bounds v && could_be_beacon sensors true v *)
|
||||
(* then Some v, `Stop *)
|
||||
(* else None, `Continue) *)
|
||||
(* None *)
|
||||
(* |> Option.get_exn_or "fucc :(" *)
|
||||
(* in *)
|
||||
(* Printf.printf "%i\n" ((mult * point.x) + point.y); *)
|
||||
(* [%expect {| 11756174628223 |}] *)
|
||||
(* ;; *)
|
||||
constants are stored separately for boundaries with positive and negative
|
||||
slope so that they can be handled separately
|
||||
p1 = y - bx - dist - 1
|
||||
p2 = y - bx + dist + 1
|
||||
n1 = y + bx + dist + 1
|
||||
n2 = y + bx - dist - 1
|
||||
]}*)
|
||||
let get_consts sensors extra =
|
||||
sensors
|
||||
|> Iter.fold
|
||||
(fun (p_slope_consts, n_slope_consts) { pos; beacon = _; dist } ->
|
||||
let p1 = pos.y - pos.x + dist + extra in
|
||||
let p2 = pos.y - pos.x - dist - extra in
|
||||
let n1 = pos.y + pos.x + dist + extra in
|
||||
let n2 = pos.y + pos.x - dist - extra in
|
||||
Iter.(
|
||||
( append p_slope_consts (of_list [ p1; p2 ])
|
||||
, append n_slope_consts (of_list [ n1; n2 ]) )))
|
||||
(Iter.empty, Iter.empty)
|
||||
;;
|
||||
|
||||
module IntSet = Set.Make (Int)
|
||||
let solve_part_1 lines scan_y =
|
||||
let sensors = lines |> Iter.of_list |> Iter.map parse_sensor in
|
||||
let p_slope_consts, n_slope_consts = get_consts sensors 0 in
|
||||
(*{[
|
||||
for a known y to intersect with a positive slope boundary:
|
||||
y = x + p
|
||||
x = y - p
|
||||
|
||||
let%expect_test "Day 15.2" =
|
||||
let sensors = Utils.lines_of_input 15 |> List.map parse_sensor in
|
||||
let mult = 4000000 in
|
||||
let range = mult in
|
||||
let in_bounds = Vec2.(in_bounds origin @@ of_tuple (range, range)) in
|
||||
let pos_coeffs, neg_coeffs =
|
||||
sensors
|
||||
|> Iter.of_list
|
||||
|> Iter.fold
|
||||
(fun (pos_coeffs, neg_coeffs) { pos; beacon = _; dist } ->
|
||||
let p1 = pos.y - pos.x + dist + 1 in
|
||||
let p2 = pos.y - pos.x - dist - 1 in
|
||||
let n1 = pos.y + pos.x + dist + 1 in
|
||||
let n2 = pos.y + pos.x - dist - 1 in
|
||||
Iter.(
|
||||
( append pos_coeffs (of_list [ p1; p2 ])
|
||||
, append neg_coeffs (of_list [ n1; n2 ]) )))
|
||||
(Iter.empty, Iter.empty)
|
||||
and for a negative slope boundary:
|
||||
y = -x + n
|
||||
x = n - y
|
||||
]}*)
|
||||
let intersection_xs =
|
||||
Iter.append
|
||||
(Iter.map (fun p -> scan_y - p) p_slope_consts)
|
||||
(Iter.map (fun n -> n - scan_y) n_slope_consts)
|
||||
|> Iter.sort_uniq ~cmp:Int.compare
|
||||
in
|
||||
let check x = could_be_beacon sensors false @@ Vec2.of_tuple (x, scan_y) in
|
||||
let rec count_not_beacons count xs =
|
||||
match xs with
|
||||
| x :: next :: rest ->
|
||||
(* if there is a region [x+1, next), the value of check
|
||||
is the same for each point within it. intersection points
|
||||
must be checked separately from the regions between them *)
|
||||
let intersection = if check x then 0 else 1 in
|
||||
let region = if x + 1 < next && check (x + 1) then 0 else next - (x + 1) in
|
||||
count_not_beacons (count + intersection + region) (next :: rest)
|
||||
| _ -> count
|
||||
in
|
||||
count_not_beacons 0 @@ Iter.to_list intersection_xs
|
||||
;;
|
||||
|
||||
let%expect_test "Day 15.1 example" =
|
||||
Printf.printf "%i\n" @@ solve_part_1 example_lines 10;
|
||||
[%expect {| 26 |}]
|
||||
;;
|
||||
|
||||
let%expect_test "Day 15.1" =
|
||||
Printf.printf "%i\n" @@ solve_part_1 (Utils.lines_of_input 15) 2000000;
|
||||
[%expect {| 5525990 |}]
|
||||
;;
|
||||
|
||||
let solve_part_2 lines range mult =
|
||||
let sensors = lines |> Iter.of_list |> Iter.map parse_sensor in
|
||||
let in_bounds = Vec2.(in_bounds origin @@ of_tuple (range, range)) in
|
||||
let p_slope_consts, n_slope_consts = get_consts sensors 1 in
|
||||
(*{[
|
||||
boundaries only itersect if they have opposite slope.
|
||||
to find the intersection, set equations to be equal and solve.
|
||||
y = x + p
|
||||
y = -x + n // set equal
|
||||
x + p = -x + n // add x and subtract p
|
||||
2x = n - p // divide 2
|
||||
-> x = (n - p)/2 // substitute x into original
|
||||
y = (n - p)/2 + p // multiply 2
|
||||
2y = n + p // div 2
|
||||
-> y = (n + p)/2
|
||||
]}*)
|
||||
let point =
|
||||
let open Iter in
|
||||
product pos_coeffs neg_coeffs
|
||||
|> fold_while
|
||||
Iter.product p_slope_consts n_slope_consts
|
||||
|> Iter.fold_while
|
||||
(fun _ (p, n) ->
|
||||
let i = Vec2.of_tuple ((n - p) / 2, (n + p) / 2) in
|
||||
if in_bounds i && could_be_beacon sensors true i
|
||||
@@ -160,6 +153,15 @@ let%expect_test "Day 15.2" =
|
||||
None
|
||||
|> Option.get_exn_or "no distress beacon found :("
|
||||
in
|
||||
Printf.printf "%i\n" ((mult * point.x) + point.y);
|
||||
(mult * point.x) + point.y
|
||||
;;
|
||||
|
||||
let%expect_test "Day 15.2 example" =
|
||||
Printf.printf "%i\n" @@ solve_part_2 example_lines 20 4000000;
|
||||
[%expect {| 56000011 |}]
|
||||
;;
|
||||
|
||||
let%expect_test "Day 15.2" =
|
||||
Printf.printf "%i\n" @@ solve_part_2 (Utils.lines_of_input 15) 4000000 4000000;
|
||||
[%expect {| 11756174628223 |}]
|
||||
;;
|
||||
+141
@@ -0,0 +1,141 @@
|
||||
open Containers
|
||||
|
||||
let example_lines =
|
||||
String.lines
|
||||
{|Valve AA has flow rate=0; tunnels lead to valves DD, II, BB
|
||||
Valve BB has flow rate=13; tunnels lead to valves CC, AA
|
||||
Valve CC has flow rate=2; tunnels lead to valves DD, BB
|
||||
Valve DD has flow rate=20; tunnels lead to valves CC, AA, EE
|
||||
Valve EE has flow rate=3; tunnels lead to valves FF, DD
|
||||
Valve FF has flow rate=0; tunnels lead to valves EE, GG
|
||||
Valve GG has flow rate=0; tunnels lead to valves FF, HH
|
||||
Valve HH has flow rate=22; tunnel leads to valve GG
|
||||
Valve II has flow rate=0; tunnels lead to valves AA, JJ
|
||||
Valve JJ has flow rate=21; tunnel leads to valve II
|
||||
|}
|
||||
;;
|
||||
|
||||
type valve =
|
||||
{ flow : int
|
||||
; mask : int
|
||||
}
|
||||
|
||||
let parse_valves lines =
|
||||
let rooms = Hashtbl.create 100 in
|
||||
lines
|
||||
|> List.iter (fun line ->
|
||||
let int_matches = List.map int_of_string Re.(matches (Pcre.regexp {|\d+|}) line) in
|
||||
let valve_matches = Re.(matches (Pcre.regexp {|[A-Z]{2}|}) line) in
|
||||
match int_matches, valve_matches with
|
||||
| [ flow ], name :: tunnels -> Hashtbl.add rooms name (flow, tunnels)
|
||||
| _ -> failwith "parse error");
|
||||
let valves =
|
||||
Hashtbl.to_list rooms
|
||||
|> List.filter (fun (_, (flow, _)) -> flow > 0)
|
||||
|> List.mapi (fun i (name, (flow, _)) ->
|
||||
let mask = Int.(1 lsl i) in
|
||||
name, { flow; mask })
|
||||
in
|
||||
let distances = Hashtbl.create 100 in
|
||||
valves
|
||||
|> List.map (fun (valve, _) -> valve)
|
||||
|> List.cons "AA"
|
||||
|> List.iter (fun from ->
|
||||
let dist_of =
|
||||
Fun.(
|
||||
Utils.djikstra_hash
|
||||
~get_weight:(fun _ _ -> 1)
|
||||
~get_neighbors:(fun room ->
|
||||
match Hashtbl.get rooms room with
|
||||
| Some (_, tunnels) -> Iter.of_list tunnels
|
||||
| None -> failwith "sad")
|
||||
~start_nodes:[ from ]
|
||||
()
|
||||
%> Option.get_exn_or "big sad")
|
||||
in
|
||||
valves
|
||||
|> List.remove_assoc ~eq:String.equal from
|
||||
|> List.iter (fun (valve, _) ->
|
||||
Hashtbl.replace distances (from, valve) (dist_of valve)));
|
||||
let get_dist f t =
|
||||
match Hashtbl.get distances (f, t) with
|
||||
| Some dist -> dist
|
||||
| None -> failwith @@ Printf.sprintf "cant find distance %s -> %s" f t
|
||||
in
|
||||
valves, get_dist
|
||||
;;
|
||||
|
||||
let traverse
|
||||
get_dist
|
||||
(valves : (string * valve) list)
|
||||
?(current = "AA")
|
||||
?(released = 0)
|
||||
?(opened = 0)
|
||||
time
|
||||
=
|
||||
let states = Hashtbl.create 1000 in
|
||||
let rec traverse' current time released opened =
|
||||
Hashtbl.update
|
||||
states
|
||||
~f:(fun _ current ->
|
||||
match current with
|
||||
| Some r when r >= released -> Some r
|
||||
| _ -> Some released)
|
||||
~k:opened;
|
||||
valves
|
||||
|> List.iter (fun (name, { flow; mask }) ->
|
||||
if Int.(mask land opened) = 0
|
||||
then (
|
||||
let dist = get_dist current name in
|
||||
let time' = time - (dist + 1) in
|
||||
if time' > 0
|
||||
then (
|
||||
let released' = released + (flow * time') in
|
||||
let opened' = Int.(mask lor opened) in
|
||||
traverse' name time' released' opened')))
|
||||
in
|
||||
traverse' current time released opened;
|
||||
states
|
||||
|> Iter.of_hashtbl
|
||||
|> Iter.sort ~cmp:(fun (_, a_score) (_, b_score) -> b_score - a_score)
|
||||
;;
|
||||
|
||||
let solve_part_1 lines =
|
||||
let valves, get_dist = parse_valves lines in
|
||||
let states = traverse get_dist valves 30 in
|
||||
match states |> Iter.take 1 |> Iter.to_list with
|
||||
| [ (_, score) ] -> score
|
||||
| _ -> failwith "no traversals ?"
|
||||
;;
|
||||
|
||||
let%expect_test "Day 16.1 example" =
|
||||
Printf.printf "%i\n" @@ solve_part_1 example_lines;
|
||||
[%expect {| 1651 |}]
|
||||
;;
|
||||
|
||||
let%expect_test "Day 16.1" =
|
||||
Printf.printf "%i\n" @@ solve_part_1 @@ Utils.lines_of_input 16;
|
||||
[%expect {| 2265 |}]
|
||||
;;
|
||||
|
||||
let solve_part_2 lines =
|
||||
let valves, get_dist = parse_valves @@ lines in
|
||||
let states = traverse get_dist valves 26 in
|
||||
states
|
||||
|> Iter.filter_map (fun (a_set, a_score) ->
|
||||
match Iter.find_pred (fun (b_set, _) -> Int.(a_set land b_set) = 0) states with
|
||||
| Some (_, b_score) -> Some (a_score + b_score)
|
||||
| None -> None)
|
||||
|> Iter.fold Int.max 0
|
||||
;;
|
||||
|
||||
let%expect_test "Day 16.2 example" =
|
||||
Printf.printf "%i\n" @@ solve_part_2 example_lines;
|
||||
[%expect {| 1707 |}]
|
||||
;;
|
||||
|
||||
(* god damn bitmasks are fast, 30x speedup from StrSet! down to 50ms now *)
|
||||
let%expect_test "Day 16.2" =
|
||||
Printf.printf "%i\n" @@ solve_part_2 @@ Utils.lines_of_input 16;
|
||||
[%expect {| 2811 |}]
|
||||
;;
|
||||
+201
@@ -0,0 +1,201 @@
|
||||
open Containers
|
||||
|
||||
let cave_width = 7
|
||||
let example_lines = [ ">>><<><>><<<>><>>><<<>>><<<><<<>><>><<>>" ]
|
||||
|
||||
let dir_of_char = function
|
||||
| '<' -> Vec2.left
|
||||
| '>' -> Vec2.right
|
||||
| _ -> failwith "oof"
|
||||
;;
|
||||
|
||||
let next_stream_idx stream idx = (idx + 1) mod Array.length stream
|
||||
|
||||
(*[{
|
||||
#
|
||||
# # #
|
||||
### # # ##
|
||||
#### # ### # ##
|
||||
|
||||
row plus angle col square
|
||||
|
||||
}]*)
|
||||
type shape =
|
||||
| Row
|
||||
| Plus
|
||||
| Angle
|
||||
| Col
|
||||
| Square
|
||||
|
||||
let get_shape idx = [| Row; Plus; Angle; Col; Square |].(idx mod 5)
|
||||
|
||||
let shape_points shape pos =
|
||||
let points =
|
||||
match shape with
|
||||
| Row -> [ 0, 0; 1, 0; 2, 0; 3, 0 ]
|
||||
| Plus -> [ 0, 1; 1, 0; 1, 1; 1, 2; 2, 1 ]
|
||||
| Angle -> [ 0, 0; 1, 0; 2, 0; 2, 1; 2, 2 ]
|
||||
| Col -> [ 0, 0; 0, 1; 0, 2; 0, 3 ]
|
||||
| Square -> [ 0, 0; 1, 0; 0, 1; 1, 1 ]
|
||||
in
|
||||
points |> List.map (fun point -> Vec2.(pos + of_tuple point))
|
||||
;;
|
||||
|
||||
type loop_key =
|
||||
{ rock : shape
|
||||
; stream_idx : int
|
||||
; normalized_topo : int array
|
||||
}
|
||||
|
||||
type loop =
|
||||
{ rock_idx : int
|
||||
; height : int
|
||||
}
|
||||
|
||||
type cave =
|
||||
{ grid : char Grid.t
|
||||
; mutable grid_topo : int array
|
||||
; mutable grid_height : int
|
||||
; total_rocks : int
|
||||
; mutable rocks : int
|
||||
; mutable stream_idx : int
|
||||
; loop_cache : (loop_key, loop) Hashtbl.t
|
||||
; mutable height : int
|
||||
}
|
||||
|
||||
let init_cave max_height total_rocks =
|
||||
let grid = Grid.init ~width:7 ~height:max_height (Fun.const '.') in
|
||||
{ grid
|
||||
; height = -1
|
||||
; grid_height = -1
|
||||
; rocks = 0
|
||||
; stream_idx = 0
|
||||
; grid_topo = Array.init cave_width (Fun.const 0)
|
||||
; loop_cache = Hashtbl.create max_height
|
||||
; total_rocks
|
||||
}
|
||||
;;
|
||||
|
||||
let draw_cave cave = Grid.draw cave.grid ~rev_y:true (fun _ contents -> contents)
|
||||
|
||||
let place_shape cave shape pos =
|
||||
shape_points shape pos
|
||||
|> List.iter (fun point ->
|
||||
let delta = point.Vec2.y - cave.grid_height in
|
||||
if delta > 0
|
||||
then (
|
||||
cave.height <- cave.height + delta;
|
||||
cave.grid_height <- cave.grid_height + delta);
|
||||
cave.grid_topo.(point.x) <- max point.y cave.grid_topo.(point.x);
|
||||
Grid.set_e cave.grid point '#')
|
||||
;;
|
||||
|
||||
let can_place_shape cave shape pos =
|
||||
shape_points shape pos
|
||||
|> List.fold_while
|
||||
(fun _ point ->
|
||||
match cave.grid.Grid.%(point) with
|
||||
| Some '.' -> true, `Continue
|
||||
| _ -> false, `Stop)
|
||||
false
|
||||
;;
|
||||
|
||||
let drop_shape cave stream =
|
||||
let shape = get_shape cave.rocks in
|
||||
let rec drop pos =
|
||||
let stream_pos = Vec2.(pos + stream.(cave.stream_idx)) in
|
||||
cave.stream_idx <- next_stream_idx stream cave.stream_idx;
|
||||
let pos = if can_place_shape cave shape stream_pos then stream_pos else pos in
|
||||
let drop_pos = Vec2.(pos + Vec2.up) in
|
||||
if can_place_shape cave shape drop_pos
|
||||
then drop drop_pos
|
||||
else place_shape cave shape pos
|
||||
in
|
||||
cave.rocks <- cave.rocks + 1;
|
||||
drop Vec2.{ x = 2; y = Int.(cave.grid_height + 4) }
|
||||
;;
|
||||
|
||||
let normalize_topo topo =
|
||||
let min = Array.min_exn Int.compare topo in
|
||||
Array.map (fun x -> x - min) topo
|
||||
;;
|
||||
|
||||
let rec drop_shapes cave stream =
|
||||
let loop_key =
|
||||
{ rock = get_shape cave.rocks
|
||||
; stream_idx = cave.stream_idx
|
||||
; normalized_topo = normalize_topo cave.grid_topo
|
||||
}
|
||||
in
|
||||
let remaining_rocks cave = cave.total_rocks - cave.rocks in
|
||||
let loop_rocks loop = cave.rocks - loop.rock_idx in
|
||||
let loop_fits loop = loop_rocks loop < remaining_rocks cave in
|
||||
match Hashtbl.get cave.loop_cache loop_key with
|
||||
| Some loop when loop_fits loop ->
|
||||
let loop_height = cave.height - loop.height in
|
||||
cave.height <- cave.height + loop_height;
|
||||
cave.rocks <- cave.rocks + loop_rocks loop;
|
||||
drop_shapes cave stream
|
||||
| _ when remaining_rocks cave > 0 ->
|
||||
let loop = { height = cave.height; rock_idx = cave.rocks } in
|
||||
Hashtbl.add cave.loop_cache loop_key loop;
|
||||
drop_shape cave stream;
|
||||
drop_shapes cave stream
|
||||
| _ -> ()
|
||||
;;
|
||||
|
||||
let solve remaining_rocks lines =
|
||||
let stream = lines |> List.hd |> Iter.of_str |> Iter.map dir_of_char |> Iter.to_array in
|
||||
let cave = init_cave 100_000 remaining_rocks in
|
||||
drop_shapes cave stream;
|
||||
cave.height + 1
|
||||
;;
|
||||
|
||||
let%expect_test "Day 17.1 Example" =
|
||||
Printf.printf "height: %i\n" @@ solve 2022 example_lines;
|
||||
[%expect {| height: 3068 |}]
|
||||
;;
|
||||
|
||||
let%expect_test "Day 17.1" =
|
||||
Printf.printf "height: %i\n" @@ solve 2022 (Utils.lines_of_input 17);
|
||||
[%expect {| height: 3227 |}]
|
||||
;;
|
||||
|
||||
let%expect_test "Day 17.2 Example" =
|
||||
Printf.printf "height: %i\n" @@ solve 1_000_000_000_000 example_lines;
|
||||
[%expect {| height: 1514285714288 |}]
|
||||
;;
|
||||
|
||||
let%expect_test "Day 17.2" =
|
||||
Printf.printf "height: %i\n" @@ solve 1_000_000_000_000 (Utils.lines_of_input 17);
|
||||
[%expect {| height: 1597714285698 |}]
|
||||
;;
|
||||
|
||||
let%expect_test "Smol tower" =
|
||||
let stream =
|
||||
example_lines |> List.hd |> Iter.of_str |> Iter.map dir_of_char |> Iter.to_array
|
||||
in
|
||||
let cave = init_cave 18 8 in
|
||||
drop_shapes cave stream;
|
||||
Printf.printf "%s" @@ draw_cave cave;
|
||||
[%expect
|
||||
{|
|
||||
.......
|
||||
.......
|
||||
.......
|
||||
.....#.
|
||||
.....#.
|
||||
..####.
|
||||
.###...
|
||||
..#....
|
||||
.####..
|
||||
....##.
|
||||
....##.
|
||||
....#..
|
||||
..#.#..
|
||||
..#.#..
|
||||
#####..
|
||||
..###..
|
||||
...#...
|
||||
..####. |}]
|
||||
;;
|
||||
+121
@@ -0,0 +1,121 @@
|
||||
open Containers
|
||||
|
||||
let example_lines =
|
||||
String.lines
|
||||
{|2,2,2
|
||||
1,2,2
|
||||
3,2,2
|
||||
2,1,2
|
||||
2,3,2
|
||||
2,2,1
|
||||
2,2,3
|
||||
2,2,4
|
||||
2,2,6
|
||||
1,2,5
|
||||
3,2,5
|
||||
2,1,5
|
||||
2,3,5|}
|
||||
;;
|
||||
|
||||
module Vec3 = struct
|
||||
type t = int * int * int [@@deriving show, eq, ord, hash]
|
||||
|
||||
let directions = [ 1, 0, 0; -1, 0, 0; 0, 1, 0; 0, -1, 0; 0, 0, 1; 0, 0, -1 ]
|
||||
let ( + ) (x1, y1, z1) (x2, y2, z2) = x1 + x2, y1 + y2, z1 + z2
|
||||
let ( - ) (x1, y1, z1) (x2, y2, z2) = x1 - x2, y1 - y2, z1 - z2
|
||||
let ( = ) = equal
|
||||
|
||||
let iter_region (ax, ay, az) (bx, by, bz) callback =
|
||||
for z = az to bz do
|
||||
for y = ay to by do
|
||||
for x = ax to bx do
|
||||
let p = x, y, z in
|
||||
callback p
|
||||
done
|
||||
done
|
||||
done
|
||||
;;
|
||||
|
||||
let in_bounds (lx, ly, lz) (hx, hy, hz) (x, y, z) =
|
||||
not (x < lx || x > hx || y < ly || y > hy || z < lz || z > hz)
|
||||
;;
|
||||
|
||||
let find_bounds points =
|
||||
let first = Iter.head_exn points in
|
||||
let padding = 1, 1, 1 in
|
||||
let min, max =
|
||||
points
|
||||
|> Iter.drop 1
|
||||
|> Iter.fold
|
||||
(fun ((lx, ly, lz), (hx, hy, hz)) (x, y, z) ->
|
||||
(min lx x, min ly y, min lz z), (max hx x, max hy y, max hz z))
|
||||
(first, first)
|
||||
in
|
||||
min - padding, max + padding
|
||||
;;
|
||||
end
|
||||
|
||||
module Vec3Set = Set.Make (Vec3)
|
||||
|
||||
let parse_points lines =
|
||||
lines
|
||||
|> List.map (fun line ->
|
||||
match List.map Int.of_string_exn Re.(matches (Pcre.regexp {|\d+|}) line) with
|
||||
| [ x; y; z ] -> x, y, z
|
||||
| _ -> failwith "oopzi doopers")
|
||||
|> Vec3Set.of_list
|
||||
;;
|
||||
|
||||
let solve_part_1 lines =
|
||||
let points = parse_points lines in
|
||||
let min, max = Vec3.find_bounds @@ Vec3Set.to_iter points in
|
||||
let surface_area = ref 0 in
|
||||
Vec3.iter_region min max (fun point ->
|
||||
if not @@ Vec3Set.mem point points
|
||||
then
|
||||
Vec3.directions
|
||||
|> List.iter (fun dir ->
|
||||
let n = Vec3.(point + dir) in
|
||||
if Vec3Set.mem n points then incr surface_area));
|
||||
!surface_area
|
||||
;;
|
||||
|
||||
let%expect_test "Day 18.1 example" =
|
||||
Format.printf "%i" @@ solve_part_1 example_lines;
|
||||
[%expect {| 64 |}]
|
||||
;;
|
||||
|
||||
let%expect_test "Day 18.1" =
|
||||
Format.printf "%i" @@ solve_part_1 @@ Utils.lines_of_input 18;
|
||||
[%expect {| 3662 |}]
|
||||
;;
|
||||
|
||||
let solve_part_2 lines =
|
||||
let points = parse_points lines in
|
||||
let min, max = Vec3.find_bounds @@ Vec3Set.to_iter points in
|
||||
let in_bounds = Vec3.in_bounds min max in
|
||||
let surface_area = ref 0 in
|
||||
let directions_iter = Iter.of_list Vec3.directions in
|
||||
let get_neighbors point =
|
||||
directions_iter
|
||||
|> Iter.map Vec3.(fun dir -> point + dir)
|
||||
|> Iter.filter (fun neighbor ->
|
||||
let in_droplet = Vec3Set.mem neighbor points in
|
||||
if in_droplet then incr surface_area;
|
||||
(not in_droplet) && in_bounds neighbor)
|
||||
in
|
||||
let _ignore =
|
||||
Utils.djikstra_hash ~get_weight:(fun _ _ -> 1) ~get_neighbors ~start_nodes:[ min ] ()
|
||||
in
|
||||
!surface_area
|
||||
;;
|
||||
|
||||
let%expect_test "Day 18.2 example" =
|
||||
Format.printf "%i" @@ solve_part_2 example_lines;
|
||||
[%expect {| 58 |}]
|
||||
;;
|
||||
|
||||
let%expect_test "Day 18.2" =
|
||||
Format.printf "%i" @@ solve_part_2 @@ Utils.lines_of_input 18;
|
||||
[%expect {| 2060 |}]
|
||||
;;
|
||||
+14
-8
@@ -27,13 +27,15 @@ let at grid point =
|
||||
;;
|
||||
|
||||
let at_e (grid : 'a t) (point : Vec2.t) =
|
||||
Option.get_exn_or "point out of bounds!" @@ at grid point
|
||||
match at grid point with
|
||||
| Some point -> point
|
||||
| None -> failwith @@ Format.sprintf "point %a out of bounds!" Vec2.pp point
|
||||
;;
|
||||
|
||||
let set_e grid point item =
|
||||
if in_bounds grid point
|
||||
then Array.set grid.items (idx_of_vec2 grid.width point) item
|
||||
else failwith "point out of bounds!"
|
||||
else failwith (Format.sprintf "point %a out of bounds!" Vec2.pp point)
|
||||
;;
|
||||
|
||||
let ( .%() ) = at
|
||||
@@ -82,12 +84,16 @@ let iter grid callback =
|
||||
iter_region grid a b callback
|
||||
;;
|
||||
|
||||
let draw grid printer =
|
||||
let str = ref [] in
|
||||
let draw grid ?(rev_y = false) ?(rev_x = false) printer =
|
||||
let line = ref [] in
|
||||
let lines = ref [] in
|
||||
iter grid (fun point ->
|
||||
let item = grid.%(point) in
|
||||
let char = printer @@ Option.get_exn_or "iter is broken" item in
|
||||
str := !str @ [ char ];
|
||||
if point.x = grid.width - 1 then str := !str @ [ '\n' ]);
|
||||
String.of_list !str
|
||||
let char = printer point (Option.get_exn_or "iter is broken" item) in
|
||||
line := if rev_x then char :: !line else !line @ [ char ];
|
||||
if List.length !line = grid.width
|
||||
then (
|
||||
lines := !lines @ [ String.of_list !line ];
|
||||
line := []));
|
||||
String.concat "\n" (if rev_y then List.rev !lines else !lines)
|
||||
;;
|
||||
+1
-1
@@ -11,4 +11,4 @@ val find : 'a t -> ('a -> bool) -> (Vec2.t * 'a) option
|
||||
val count : 'a t -> ('a -> bool) -> int
|
||||
val iter_region : 'a t -> Vec2.t -> Vec2.t -> (Vec2.t -> unit) -> unit
|
||||
val iter : 'a t -> (Vec2.t -> unit) -> unit
|
||||
val draw : 'a t -> ('a -> char) -> string
|
||||
val draw : 'a t -> ?rev_y:bool -> ?rev_x:bool -> (Vec2.t -> 'a -> char) -> string
|
||||
+100
@@ -10,6 +10,106 @@ let lines_of_input day =
|
||||
IO.(with_in path read_lines_l)
|
||||
;;
|
||||
|
||||
(** [f] in [memo f] should be a function that makes recursive calls
|
||||
via the memoized function passed as its first argument. ie:
|
||||
{[
|
||||
let fib_m =
|
||||
memo (fun self x ->
|
||||
match x with
|
||||
| 0 | 1 -> 1
|
||||
| x -> self (x - 1) + self (x - 2))
|
||||
;;
|
||||
]} *)
|
||||
let memo f =
|
||||
let open Hashtbl in
|
||||
let cache = create 100000 in
|
||||
let rec f_mem k =
|
||||
try find cache k with
|
||||
| Not_found ->
|
||||
let v = f f_mem k in
|
||||
add cache k v;
|
||||
v
|
||||
in
|
||||
f_mem
|
||||
;;
|
||||
|
||||
let%expect_test "fib memoized" =
|
||||
let fib_m =
|
||||
memo (fun fib_m x ->
|
||||
match x with
|
||||
| 0 | 1 -> 1
|
||||
| x -> fib_m (x - 1) + fib_m (x - 2))
|
||||
in
|
||||
Printf.printf "%i\n" @@ fib_m 100;
|
||||
[%expect {| 1298777728820984005 |}]
|
||||
;;
|
||||
|
||||
let djikstra ~eq ~get_info ~set_info ~get_weight ~get_neighbors ~goal ~start_nodes =
|
||||
let seen = ref [] in
|
||||
let rec djikstra' c_node =
|
||||
let c_dist, _ = get_info c_node in
|
||||
set_info c_node (c_dist, true);
|
||||
get_neighbors c_node
|
||||
|> Iter.iter (fun neighbor ->
|
||||
let n_dist, n_visited = get_info neighbor in
|
||||
if not n_visited
|
||||
then (
|
||||
let new_dist = c_dist + get_weight c_node neighbor in
|
||||
if new_dist < n_dist then set_info neighbor (new_dist, n_visited);
|
||||
seen := List.add_nodup ~eq neighbor !seen));
|
||||
seen
|
||||
:= List.fast_sort
|
||||
(fun a b ->
|
||||
let a_dist, _ = get_info a in
|
||||
let b_dist, _ = get_info b in
|
||||
Int.compare a_dist b_dist)
|
||||
!seen;
|
||||
match goal, !seen with
|
||||
| Some goal_node, next :: _ when eq goal_node next ->
|
||||
let dist, _ = get_info next in
|
||||
Some dist
|
||||
| None, [] -> Some 0
|
||||
| _, next :: rest ->
|
||||
seen := rest;
|
||||
djikstra' next
|
||||
| Some _, [] -> None
|
||||
in
|
||||
List.iter (fun node -> set_info node (0, false)) start_nodes;
|
||||
match start_nodes with
|
||||
| first :: rest ->
|
||||
seen := rest;
|
||||
djikstra' first
|
||||
| _ -> failwith "start_nodes cannot be empty"
|
||||
;;
|
||||
|
||||
let djikstra_hash
|
||||
(type k)
|
||||
?(hash = Hashtbl.hash)
|
||||
?(eq = Stdlib.( = ))
|
||||
?(slots = 100)
|
||||
~get_weight
|
||||
~get_neighbors
|
||||
~start_nodes
|
||||
()
|
||||
=
|
||||
let module Tbl =
|
||||
CCHashtbl.Make (struct
|
||||
type t = k
|
||||
|
||||
let equal = eq
|
||||
let hash = hash
|
||||
end)
|
||||
in
|
||||
let table = Tbl.create slots in
|
||||
let get_info node = Tbl.get_or ~default:(Int.max_int, false) table node in
|
||||
let set_info node info = Tbl.replace table node info in
|
||||
let dist_of node = Option.(Tbl.get table node >|= fun (dist, _) -> dist) in
|
||||
let _ =
|
||||
djikstra ~eq ~get_info ~set_info ~get_weight ~get_neighbors ~goal:None ~start_nodes
|
||||
in
|
||||
dist_of
|
||||
;;
|
||||
|
||||
module Parse = struct
|
||||
include Angstrom
|
||||
|
||||
|
||||
+2
-2
@@ -35,10 +35,10 @@ let fold_region a b callback init =
|
||||
!acc
|
||||
;;
|
||||
|
||||
let bounds padding points =
|
||||
let find_bounds padding points =
|
||||
let lx, hx, ly, hy =
|
||||
points
|
||||
|> List.fold_left
|
||||
|> Iter.fold
|
||||
(fun (lx, hx, ly, hy) { x; y } ->
|
||||
let lx = min lx x in
|
||||
let hx = max hx x in
|
||||
|
||||
+1
-1
@@ -20,5 +20,5 @@ val ( = ) : t -> t -> bool
|
||||
val abs : t -> t
|
||||
val iter_region : t -> t -> (t -> unit) -> unit
|
||||
val fold_region : t -> t -> ('a -> t -> 'a) -> 'a -> 'a
|
||||
val bounds : int -> t list -> t * t
|
||||
val find_bounds : int -> t Iter.t -> t * t
|
||||
val in_bounds : t -> t -> t -> bool
|
||||
Reference in new issue
Block a user