speed up day 15.1 with intersections
also update the vec module to use Iter in some places
This commit is contained in:
parent
ff4bb0ae89
commit
6758130700
21
src/day14.ml
21
src/day14.ml
|
|
@ -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;
|
||||
|
|
|
|||
201
src/day15.ml
201
src/day15.ml
|
|
@ -20,35 +20,9 @@ let parse_sensor line =
|
|||
| _ -> failwith "parse error"
|
||||
;;
|
||||
|
||||
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"
|
||||
; "Sensor at x=13, y=2: closest beacon is at x=15, y=3"
|
||||
; "Sensor at x=12, y=14: closest beacon is at x=10, y=16"
|
||||
; "Sensor at x=10, y=20: closest beacon is at x=10, y=16"
|
||||
; "Sensor at x=14, y=17: closest beacon is at x=10, y=16"
|
||||
; "Sensor at x=8, y=7: closest beacon is at x=2, y=10"
|
||||
; "Sensor at x=2, y=0: closest beacon is at x=2, y=10"
|
||||
; "Sensor at x=0, y=11: closest beacon is at x=2, y=10"
|
||||
; "Sensor at x=20, y=14: closest beacon is at x=25, y=17"
|
||||
; "Sensor at x=17, y=20: closest beacon is at x=21, y=22"
|
||||
; "Sensor at x=16, y=7: closest beacon is at x=15, y=3"
|
||||
; "Sensor at x=14, y=3: closest beacon is at x=15, y=3"
|
||||
; "Sensor at x=20, y=1: closest beacon is at x=15, y=3"
|
||||
]
|
||||
;;
|
||||
|
||||
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
|
||||
;;
|
||||
|
||||
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))
|
||||
|
|
@ -57,95 +31,108 @@ 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 constant given slope, plug 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 store separately for pos and neg slope boundaries
|
||||
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_coeffs sensors =
|
||||
sensors
|
||||
|> 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)
|
||||
;;
|
||||
|
||||
module IntSet = Set.Make (Int)
|
||||
let%expect_test "Day 15.1" =
|
||||
let sensors = Utils.lines_of_input 15 |> Iter.of_list |> Iter.map parse_sensor in
|
||||
let scan_y = 2000000 in
|
||||
let pos_coeffs, neg_coeffs = get_coeffs sensors in
|
||||
(*{[
|
||||
for a line y = c to intersect with a positive slope boundary:
|
||||
y = c
|
||||
y = x + n
|
||||
c = x + p
|
||||
x = c - p
|
||||
|
||||
and for a negative slop boundary
|
||||
y = c
|
||||
y = -x + n
|
||||
c = -x + n
|
||||
x = n - c
|
||||
]}*)
|
||||
let intersection_xs =
|
||||
Iter.append
|
||||
(Iter.map (fun p -> scan_y - p) pos_coeffs)
|
||||
(Iter.map (fun n -> n - scan_y) neg_coeffs)
|
||||
|> Iter.sort_uniq ~cmp:Int.compare
|
||||
in
|
||||
(* iterate thru the intersections by ascending x.
|
||||
if a point in the region can be a beacon,
|
||||
then every point in the region is. *)
|
||||
let rec count_not_beacons count xs =
|
||||
match xs with
|
||||
| lx :: hx :: rest ->
|
||||
let count =
|
||||
if could_be_beacon sensors false @@ Vec2.of_tuple (lx + 1, scan_y)
|
||||
then count
|
||||
else count + hx - lx
|
||||
in
|
||||
count_not_beacons count @@ (hx :: rest)
|
||||
| _ -> count
|
||||
in
|
||||
let possible_beacons = count_not_beacons 0 @@ Iter.to_list intersection_xs in
|
||||
Printf.printf "%i\n" @@ possible_beacons;
|
||||
[%expect {| 5525990 |}]
|
||||
;;
|
||||
|
||||
let%expect_test "Day 15.2" =
|
||||
let sensors = Utils.lines_of_input 15 |> List.map parse_sensor in
|
||||
let sensors = Utils.lines_of_input 15 |> Iter.of_list |> Iter.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)
|
||||
in
|
||||
let pos_coeffs, neg_coeffs = get_coeffs sensors in
|
||||
(*{[
|
||||
boundaries only itersect if they have opposite slope.
|
||||
to find the intersection, set equations to be equal and solve.
|
||||
y = 1*x + p
|
||||
y = -1*x + n // set equal
|
||||
x + p = -x + n // add x
|
||||
2x + p = n // 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
|
||||
|
||||
intersection point is at
|
||||
x = (n - p)/2
|
||||
y = (n + p)/2
|
||||
]}*)
|
||||
let point =
|
||||
let open Iter in
|
||||
product pos_coeffs neg_coeffs
|
||||
|> fold_while
|
||||
Iter.product pos_coeffs neg_coeffs
|
||||
|> 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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user