2 Commits
1c4d96edf4
...
6452527a35
| Author | SHA1 | Message | Date |
|---|---|---|---|
|
|
6452527a35 |
Day 12-2
|
3 years ago |
|
|
5b56fd2746 |
Day 12-1
|
3 years ago |
1 changed files with 122 additions and 0 deletions
-
122day12.erl
@ -0,0 +1,122 @@ |
|||
-module(day12). |
|||
|
|||
-export([solve/1]). |
|||
|
|||
solve(Input) -> |
|||
Board = parse_board(Input), |
|||
Result1 = find_path(find_start(Board), find_goal(Board), Board), |
|||
%Part |
|||
Part2Goal = fun(Node) -> value(Board, Node) == $a end, |
|||
Part2Neighbors = fun(Node) -> neighbors2(Board, Node) end, |
|||
{ok, Result2} = bfs(find_goal(Board), Part2Neighbors, Part2Goal), |
|||
{Result1, Result2}. |
|||
|
|||
find_start({W, H, _Data} = Board) -> |
|||
[Pos] = lists:filter(fun(P) -> raw_value(Board, P) == $S end, [ {X, Y} || X <- lists:seq(0, W-1), Y <- lists:seq(0, H-1) ]), |
|||
Pos. |
|||
find_goal({W, H, _Data} = Board) -> |
|||
[Pos] = lists:filter(fun(P) -> raw_value(Board, P) == $E end, [ {X, Y} || X <- lists:seq(0, W-1), Y <- lists:seq(0, H-1) ]), |
|||
Pos. |
|||
|
|||
parse_board(Input) -> |
|||
Parsed = binary:split(Input, <<"\n">>, [global, trim_all]), |
|||
{byte_size(hd(Parsed)), length(Parsed), Input}. |
|||
|
|||
raw_value({W, _H, Data}, {X, Y}) -> |
|||
Pos = Y * (W + 1) + X, |
|||
<<_Before:Pos/binary, RawValue, _After/binary>> = Data, |
|||
RawValue. |
|||
|
|||
value(Board, Pos) -> |
|||
case raw_value(Board, Pos) of |
|||
$S -> $a; |
|||
$E -> $z; |
|||
Letter -> Letter |
|||
end. |
|||
|
|||
valid_position({_W, _H, _Data}, {X, Y}) when X < 0 orelse Y < 0 -> false; |
|||
valid_position({W, H, _Data}, {X, Y}) when X >= W orelse Y >= H -> false; |
|||
valid_position({_W, _H, _Data}, {_X, _Y}) -> true. |
|||
|
|||
valid_move(State, {StartX, StartY}=Start, {MoveX, MoveY} = Move) -> |
|||
StartValue = value(State, Start), |
|||
MoveValue = value(State, Move), |
|||
if |
|||
abs(StartX - MoveX) + abs(StartY - MoveY) > 1 -> false; |
|||
MoveValue - StartValue > 1 -> false; |
|||
true -> true |
|||
end. |
|||
|
|||
valid_move2(State, Start, Move) -> |
|||
value(State, Move) - value(State, Start) >= -1. |
|||
|
|||
neighbors(State, Pos) -> |
|||
PossibleNeighbors0 = [ vec_add(Pos, Dir) || Dir <- [{0,-1}, {0, 1}, {1, 0}, {-1, 0}]], |
|||
PossibleNeighbors1 = lists:filter(fun(Move) -> valid_position(State, Move) end, PossibleNeighbors0), |
|||
lists:filter(fun(Move) -> valid_move(State, Pos, Move) end, PossibleNeighbors1). |
|||
|
|||
neighbors2(State, Pos) -> |
|||
PossibleNeighbors0 = [ vec_add(Pos, Dir) || Dir <- [{0,-1}, {0, 1}, {1, 0}, {-1, 0}]], |
|||
PossibleNeighbors1 = lists:filter(fun(Move) -> valid_position(State, Move) end, PossibleNeighbors0), |
|||
lists:filter(fun(Move) -> valid_move2(State, Pos, Move) end, PossibleNeighbors1). |
|||
|
|||
vec_add({X1, Y1}, {X2, Y2}) -> {X1+X2, Y1+Y2}. |
|||
vec_sub({X1, Y1}, {X2, Y2}) -> {X1-X2, Y1-Y2}. |
|||
vec_norm({X1, Y1}) -> abs(X1)+abs(Y1). |
|||
|
|||
cartesian(P1, P2) -> vec_norm(vec_sub(P1, P2)). |
|||
|
|||
enqueue({_Priority, _Distance, _Parent, Pos}, {Map, Priq}) when is_map_key(Pos, Map) -> {Map, Priq}; |
|||
enqueue({Priority, Distance, Parent, Pos}, {Map, Priq}) -> |
|||
{Map#{Pos => {Distance, Parent}}, priq:insert({Priority, Pos}, Priq)}. |
|||
|
|||
dequeue({_Map, empty}) -> error; |
|||
dequeue({Map, Priq}) -> |
|||
{ok, {Priority, Pos}} = priq:peek_min(Priq), |
|||
{ok, Rest} = priq:delete_min(Priq), |
|||
#{Pos := {Distance, Parent}} = Map, |
|||
{ok, {Priority, Distance, Parent, Pos}, {Map, Rest}}. |
|||
|
|||
find_path(Start, Goal, Board) -> |
|||
a_star({cartesian(Start, Goal), 0, start, Start}, Goal, {#{}, empty}, #{}, Board). |
|||
|
|||
% Closed: A map of Node -> Node (parent). Used to check for visitation. Forms the path. |
|||
a_star({_Heuristic, Distance, _Parent, Goal}, Goal, _Open, _Closed, _State) -> |
|||
Distance; |
|||
a_star({_Heuristic, _Distance, _Parent, Curr}, Goal, Open, Closed, State) when is_map_key(Curr, Closed) -> |
|||
case dequeue(Open) of |
|||
error -> {error, no_path}; |
|||
{ok, NextCurrNode, NextOpen} -> a_star(NextCurrNode, Goal, NextOpen, Closed, State) |
|||
end; |
|||
a_star({_Heuristic, Distance, Parent, Curr}, Goal, Open, Closed, State) -> |
|||
OpenNeighbors = neighbors(State, Curr), |
|||
OpenNeighborNodes = lists:map(fun(Neighbor) -> {cartesian(Neighbor, Goal)+Distance+1, Distance+1, Curr, Neighbor} end, OpenNeighbors), |
|||
AddedOpen = lists:foldl(fun enqueue/2, Open, OpenNeighborNodes), |
|||
case dequeue(AddedOpen) of |
|||
error -> {error, no_path}; |
|||
{ok, NextCurrNode, NextOpen} -> a_star(NextCurrNode, Goal, NextOpen, Closed#{Curr=>{Distance, Parent}}, State) |
|||
end. |
|||
|
|||
bfs(Position, NeighborFn, GoalFn) -> |
|||
do_bfs0(Position, NeighborFn, GoalFn, 0, priq:new(), #{}). |
|||
do_bfs0(Position, NeighborFn, GoalFn, Distance, Queue, Visited) when is_map_key(Position, Visited) -> |
|||
do_bfs2(Position, NeighborFn, GoalFn, Distance, Queue, Visited); |
|||
do_bfs0(Position, NeighborFn, GoalFn, Distance, Queue, Visited) -> |
|||
case GoalFn(Position) of |
|||
true -> {ok, Distance}; |
|||
false -> do_bfs1(Position, NeighborFn, GoalFn, Distance, Queue, Visited) |
|||
end. |
|||
|
|||
do_bfs1(Position, NeighborFn, GoalFn, Distance, Queue, Visited) -> |
|||
WeightedNeighbors = lists:map(fun (Node) -> {Distance+1, Node} end, NeighborFn(Position)), |
|||
UpdatedQueue = lists:foldl(fun priq:insert/2, Queue, WeightedNeighbors), |
|||
do_bfs2(Position, NeighborFn, GoalFn, Distance, UpdatedQueue, Visited). |
|||
|
|||
do_bfs2(_Position, _NeighborFn, _GoalFn, _Distance, empty, _Visited) -> |
|||
{error, no_path}; |
|||
do_bfs2(Position, NeighborFn, GoalFn, _Distance, Queue, Visited) -> |
|||
{ok, {NextDistance, NextPosition}} = priq:peek_min(Queue), |
|||
{ok, NextQueue} = priq:delete_min(Queue), |
|||
do_bfs0(NextPosition, NeighborFn, GoalFn, NextDistance, NextQueue, Visited#{Position => []}). |
|||
|
|||
%calculate_path(Goal, Closed) -> {Goal, Closed}. |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue