day14
This commit is contained in:
parent
c45c7b55fd
commit
1b57b12794
2 changed files with 155 additions and 0 deletions
2
aoc.m
2
aoc.m
|
@ -49,6 +49,7 @@ run_day(Day, Part, Lines, Out) :-
|
|||
:- import_module day11.
|
||||
% :- import_module day12.
|
||||
:- import_module day13.
|
||||
:- import_module day14.
|
||||
|
||||
:- pred solution(int::in, sol::out(sol)) is semidet.
|
||||
solution(1, day1.run).
|
||||
|
@ -64,3 +65,4 @@ solution(10, day10.run).
|
|||
solution(11, day11.run).
|
||||
% solution(12, day12.run).
|
||||
solution(13, day13.run).
|
||||
solution(14, day14.run).
|
||||
|
|
153
day14.m
Normal file
153
day14.m
Normal file
|
@ -0,0 +1,153 @@
|
|||
:- module day14.
|
||||
:- interface.
|
||||
:- import_module basics.
|
||||
|
||||
:- pred run(part::in, lines::in, answer::out) is cc_multi.
|
||||
|
||||
:- implementation.
|
||||
:- import_module int.
|
||||
:- import_module char.
|
||||
:- import_module string.
|
||||
:- import_module list.
|
||||
:- import_module array2d.
|
||||
:- import_module ranges.
|
||||
|
||||
:- import_module set_bbbtree.
|
||||
:- type set(T) == set_bbbtree(T).
|
||||
|
||||
|
||||
:- type cell ---> empty; rock; sand.
|
||||
|
||||
:- type data == array2d(cell).
|
||||
|
||||
:- type grid --->
|
||||
g(start_x :: int, end_x :: int,
|
||||
start_y :: int, end_y :: int,
|
||||
data :: data).
|
||||
|
||||
:- inst grid for grid/0 ---> g(ground, ground, ground, ground, array2d).
|
||||
:- mode grid_di == di(grid).
|
||||
:- mode grid_uo == out(grid).
|
||||
|
||||
:- type point == {int, int}.
|
||||
|
||||
holeX = 500. holeY = 0. hole = {holeX, holeY}.
|
||||
|
||||
|
||||
:- use_module day4.
|
||||
:- pred line(list(point)::out, chars::in, chars::out) is semidet.
|
||||
line([{X, Y} | Rest]) -->
|
||||
day4.number(X), [','], day4.number(Y),
|
||||
(if [' ','-','>',' '] then line(Rest) else {Rest = []}).
|
||||
|
||||
:- pred line(string::in, list(point)::out) is semidet.
|
||||
line(Line, Points) :- line(Points, to_char_list(Line), []).
|
||||
|
||||
:- pred lines(lines::in, list(list(point))::out) is semidet.
|
||||
lines(Lines, Points) :- map(line, Lines, Points).
|
||||
|
||||
|
||||
:- func expand(list(list(point))) = set(point).
|
||||
expand(Points) = from_list(condense(map(expand1, Points))).
|
||||
|
||||
:- func expand1(list(point)) = list(point).
|
||||
expand1([]) = [].
|
||||
expand1([P]) = [P].
|
||||
expand1([P, Q | Rest]) = expand(P, Q) ++ expand1([Q | Rest]).
|
||||
|
||||
:- func expand(point, point) = list(point).
|
||||
expand({X1, Y1}, {X2, Y2}) = List :-
|
||||
if X1 = X2 then List = map(func(Y) = {X1, Y}, between(Y1, Y2))
|
||||
else if Y1 = Y2 then List = map(func(X) = {X, Y1}, between(X1, X2))
|
||||
else die("expand/2: non-orthogonal line").
|
||||
|
||||
:- func between(int, int) = list(int).
|
||||
between(I, J) = to_sorted_list(range(min(I, J), max(I, J))).
|
||||
|
||||
|
||||
:- func bounds(set(point)) = {point, point}.
|
||||
bounds(Points) = {{LoX, LoY}, {HiX, HiY}} :-
|
||||
Points0 = insert(Points, hole),
|
||||
Xs = to_sorted_list(map(func({X, _}) = X, Points0)),
|
||||
Ys = to_sorted_list(map(func({_, Y}) = Y, Points0)),
|
||||
LoX = det_head(Xs), det_last(Xs, HiX),
|
||||
LoY = det_head(Ys), det_last(Ys, HiY).
|
||||
|
||||
|
||||
:- func add_floor(set(point)) = set(point).
|
||||
add_floor(Points) = insert_list(Points, Floor) :-
|
||||
{_, {_, Lo}} = bounds(Points),
|
||||
FloorY = Lo + 2, Start = holeX - FloorY, End = holeX + FloorY,
|
||||
Floor = map(func(X) = {X, FloorY}, between(Start, End)).
|
||||
|
||||
|
||||
:- func get(grid, point) = cell.
|
||||
get(G, {X, Y}) = G^data^elem(Y - G^start_y, X - G^start_x).
|
||||
|
||||
:- pred set_d(point::in, point::in, cell::in,
|
||||
data::array2d_di, data::array2d_uo) is det.
|
||||
set_d({StartX, StartY}, {X, Y}, A, !Data) :-
|
||||
!Data^elem(Y - StartY, X - StartX) := A.
|
||||
|
||||
:- pred set(point::in, cell::in, grid::grid_di, grid::grid_uo) is det.
|
||||
set(XY, A, !Grid) :- some [!Data] (
|
||||
!:Data = !.Grid^data,
|
||||
set_d({!.Grid^start_x, !.Grid^start_y}, XY, A, !Data),
|
||||
!Grid^data := !.Data
|
||||
).
|
||||
|
||||
:- pred in_bounds(grid::in, point::in) is semidet.
|
||||
in_bounds(G, {X, Y}) :-
|
||||
G^start_x =< X, X =< G^end_x,
|
||||
G^start_y =< Y, Y =< G^end_y.
|
||||
|
||||
:- pred place_rocks(point::in, list(point)::in,
|
||||
data::array2d_di, data::array2d_uo) is det.
|
||||
place_rocks(_, [], !Data).
|
||||
place_rocks(Start, [XY | Rest], !Data) :-
|
||||
set_d(Start, XY, rock, !Data),
|
||||
place_rocks(Start, Rest, !Data).
|
||||
|
||||
:- func grid_from_set(set(point)) = grid.
|
||||
:- mode grid_from_set(in) = grid_uo.
|
||||
grid_from_set(Points) = G :-
|
||||
{{StartX, StartY}, {EndX, EndY}} = bounds(Points),
|
||||
Width = EndX - StartX + 1, Height = EndY - StartY + 1,
|
||||
place_rocks({StartX, StartY}, to_sorted_list(Points),
|
||||
init(Height, Width, empty), Arr),
|
||||
G = g(StartX, EndX, StartY, EndY, Arr).
|
||||
|
||||
:- func make_grid(lines) = grid.
|
||||
:- mode make_grid(in) = grid_uo is semidet.
|
||||
make_grid(Lines) = grid_from_set(expand(Points)) :-
|
||||
lines(Lines, Points).
|
||||
|
||||
:- func make_floor_grid(lines) = grid.
|
||||
:- mode make_floor_grid(in) = grid_uo is semidet.
|
||||
make_floor_grid(Lines) = grid_from_set(add_floor(expand(Points))) :-
|
||||
lines(Lines, Points).
|
||||
|
||||
|
||||
:- type fall ---> stop(point); overflow.
|
||||
|
||||
:- pred move(point::in, fall::out, grid::grid_di, grid::grid_uo) is semidet.
|
||||
move({X, Y}, B, !Grid) :-
|
||||
if not in_bounds(!.Grid, {X, Y}) then B = overflow
|
||||
else if get(!.Grid, {X, Y}) \= empty then fail
|
||||
else if move({X, Y+1}, B0, !Grid) then B = B0
|
||||
else if move({X-1, Y+1}, B0, !Grid) then B = B0
|
||||
else if move({X+1, Y+1}, B0, !Grid) then B = B0
|
||||
else set({X, Y}, sand, !Grid), B = stop({X, Y}).
|
||||
|
||||
:- pred fill(int::out, grid::grid_di, grid::grid_uo) is det.
|
||||
fill(N, !Grid) :-
|
||||
if move(hole, stop(_), !Grid) then fill(N0, !Grid), N = N0 + 1
|
||||
else N = 0.
|
||||
|
||||
:- pragma no_determinism_warning(run/3).
|
||||
run(one, Lines, int(Res)) :-
|
||||
if Grid = make_grid(Lines) then fill(Res, Grid, _)
|
||||
else die("bad input").
|
||||
run(two, Lines, int(Res)) :-
|
||||
if Grid = make_floor_grid(Lines) then fill(Res, Grid, _)
|
||||
else die("bad input").
|
Loading…
Reference in a new issue