roc/crates/cli/tests/benchmarks/AStar.roc
2025-01-04 15:10:55 -08:00

123 lines
4 KiB
Text

module [find_path, Model, initial_model, cheapest_open, reconstruct_path]
import Quicksort
find_path = \cost_fn, move_fn, start, end ->
astar(cost_fn, move_fn, end, initial_model(start))
Model position : {
evaluated : Set position,
open_set : Set position,
costs : Dict position F64,
came_from : Dict position position,
} where position implements Hash & Eq
initial_model : position -> Model position where position implements Hash & Eq
initial_model = \start -> {
evaluated: Set.empty({}),
open_set: Set.single(start),
costs: Dict.single(start, 0),
came_from: Dict.empty({}),
}
cheapest_open : (position -> F64), Model position -> Result position {} where position implements Hash & Eq
cheapest_open = \cost_fn, model ->
model.open_set
|> Set.toList
|> List.keepOks(
\position ->
when Dict.get(model.costs, position) is
Err(_) -> Err({})
Ok(cost) -> Ok({ cost: cost + cost_fn(position), position }),
)
|> Quicksort.sort_by(.cost)
|> List.first
|> Result.map(.position)
|> Result.mapErr(\_ -> {})
reconstruct_path : Dict position position, position -> List position where position implements Hash & Eq
reconstruct_path = \came_from, goal ->
when Dict.get(came_from, goal) is
Err(_) -> []
Ok(next) -> List.append(reconstruct_path(came_from, next), goal)
update_cost : position, position, Model position -> Model position where position implements Hash & Eq
update_cost = \current, neighbor, model ->
new_came_from =
Dict.insert(model.came_from, neighbor, current)
new_costs =
Dict.insert(model.costs, neighbor, distance_to)
distance_to =
reconstruct_path(new_came_from, neighbor)
|> List.len
|> Num.toFrac
new_model =
{ model &
costs: new_costs,
came_from: new_came_from,
}
when Dict.get(model.costs, neighbor) is
Err(_) ->
new_model
Ok(previous_distance) ->
if distance_to < previous_distance then
new_model
else
model
astar : (position, position -> F64), (position -> Set position), position, Model position -> Result (List position) {} where position implements Hash & Eq
astar = \cost_fn, move_fn, goal, model ->
when cheapest_open(\source -> cost_fn(source, goal), model) is
Err({}) -> Err({})
Ok(current) ->
if current == goal then
Ok(reconstruct_path(model.came_from, goal))
else
model_popped =
{ model &
open_set: Set.remove(model.open_set, current),
evaluated: Set.insert(model.evaluated, current),
}
neighbors =
move_fn(current)
new_neighbors =
Set.difference(neighbors, model_popped.evaluated)
model_with_neighbors : Model position
model_with_neighbors =
model_popped
|> &open_set(Set.union(model_popped.open_set, new_neighbors))
walker : Model position, position -> Model position
walker = \amodel, n -> update_cost(current, n, amodel)
model_with_costs =
Set.walk(new_neighbors, model_with_neighbors, walker)
astar(cost_fn, move_fn, goal, model_with_costs)
# takeStep = \moveFn, _goal, model, current ->
# modelPopped =
# { model &
# openSet: Set.remove model.openSet current,
# evaluated: Set.insert model.evaluated current,
# }
#
# neighbors = moveFn current
#
# newNeighbors = Set.difference neighbors modelPopped.evaluated
#
# modelWithNeighbors = { modelPopped & openSet: Set.union modelPopped.openSet newNeighbors }
#
# # a lot goes wrong here
# modelWithCosts =
# Set.walk newNeighbors modelWithNeighbors (\n, m -> updateCost current n m)
#
# modelWithCosts