Skip to content

Instantly share code, notes, and snippets.

@jdoig
Created July 29, 2011 07:12
Show Gist options
  • Save jdoig/1113358 to your computer and use it in GitHub Desktop.
Save jdoig/1113358 to your computer and use it in GitHub Desktop.
First attempt at pathfinding in F#
module Pathfinding
open Level
open Tools
(* a wrapper for mapPoint that can contain pathing data as per typical A* pathfinding *)
(* g = cost of path so far, h = estimated cost to goal, parent = tile we came here from *)
type PathingNode =
{point:MapPoint; h:float; g:float; parent:PathingNode option}
member this.f = this.g + this.h
(* returns a pathnode based on a given map point *)
let pointToPathNode parent goal node =
{point=node; h=node.Distance goal; g=(parent.g+1.0); parent=Some(parent)}
let isClear t = t.value = 0 (* Tile is empty *)
let nodePointEquals mapPoint pathNode = pathNode.point = mapPoint
(* A has the same location as B but via a shorter route *)
let isShorter nodeA nodeB= nodeA.point = nodeB.point && nodeA.f < nodeB.f
let rec pathFind (area:Map) goal start (openNodes:PathingNode list) (closedNodes:PathingNode list) =
let pointToPathNode = pointToPathNode openNodes.Head goal
(* Loop over list of neighbors accumalating a list of open nodes *)
let rec checkNeighbors neighbors openNodeAcc=
match module Pathfinding
open Level
open Tools
(* a wrapper for mapPoint that can contain pathing data as per typical A* pathfinding *)
(* g = cost of path so far, h = estimated cost to goal, parent = tile we came here from *)
type PathingNode =
{point:MapPoint; h:float; g:float; parent:PathingNode option}
member this.f = this.g + this.h
(* returns a pathnode based on a given map point *)
let pointToPathNode parent goal node =
{point=node; h=node.Distance goal; g=(parent.g+1.0); parent=Some(parent)}
let isClear t = t.value = 0 (* Tile is empty *)
let nodePointEquals mapPoint pathNode = pathNode.point = mapPoint
(* A has the same location as B but via a shorter route *)
let isShorter nodeA nodeB= nodeA.point = nodeB.point && nodeA.f < nodeB.f
let rec pathFind (area:Map) goal start (openNodes:PathingNode list) (closedNodes:PathingNode list) =
let pointToPathNode = pointToPathNode openNodes.Head goal
(* Loop over list of neighbors accumalating a list of open nodes *)
let rec checkNeighbors neighbors openNodeAcc=
match neighbors with
|[] -> openNodeAcc (* When list of neighbors is exhausted return the open nodes. *)
|hd::tl ->
let checkNeighbors = checkNeighbors tl
let node = {hd with parent = Some(openNodes.Head)}
if (List.exists (isShorter hd) openNodeAcc) then (* if a higher costingnode is in open...*)
let shorterPath = remove openNodeAcc (nodePointEquals hd.point) (*... remove it.. *)
checkNeighbors (node::shorterPath)
elif not(List.exists (nodePointEquals hd.point) closedNodes)
&& not (List.exists (nodePointEquals hd.point) openNodeAcc) then
(* If path is not open or closed... *)
checkNeighbors (node::openNodeAcc)
else checkNeighbors openNodeAcc (* Else carry on. *)
let neighbors =
(* Get the neighbors of our current node...*)
area.GetNeighborsOf openNodes.Head.point
|> List.filter isClear (* ...filter out collidable tiles... *)
|> List.map pointToPathNode (*..for each neighbor we can walk to: generate a node*)
(* Try and find the goal node in the walkable neighbor of this tile. *)
let pathToGoal = List.tryFind (nodePointEquals goal) neighbors
if pathToGoal.IsSome then pathToGoal //If we found our goal return it...
else
let nextSet =
checkNeighbors neighbors openNodes.Tail
|> List.sortBy(fun x -> x.f)
if nextSet.Length > 0 then
pathFind area goal start nextSet (nextSet.Head::closedNodes) (*...Else carry on*)
else None with
|[] -> openNodeAcc (* When list of neighbors is exhausted return the open nodes. *)
|hd::tl ->
let checkNeighbors = checkNeighbors tl
let node = {hd with parent = Some(openNodes.Head)}
if (List.exists (isShorter hd) openNodeAcc) then (* if a higher costingnode is in open...*)
let shorterPath = remove openNodeAcc (nodePointEquals hd.point) (*... remove it.. *)
checkNeighbors (node::shorterPath)
elif not(List.exists (nodePointEquals hd.point) closedNodes)
&& not (List.exists (nodePointEquals hd.point) openNodeAcc) then
(* If path is not open or closed... *)
checkNeighbors (node::openNodeAcc)
else checkNeighbors openNodeAcc (* Else carry on. *)
let neighbors =
(* Get the neighbors of our current node...*)
area.GetNeighborsOf openNodes.Head.point
|> List.filter isClear (* ...filter out collidable tiles... *)
|> List.map pointToPathNode (*..for each neighbor we can walk to: generate a node*)
(* Try and find the goal node in the walkable neighbor of this tile. *)
let pathToGoal = List.tryFind (nodePointEquals goal) neighbors
if pathToGoal.IsSome then pathToGoal (* If we found our goal return it... *)
else
let nextSet =
checkNeighbors neighbors openNodes.Tail
|> List.sortBy(fun x -> x.f)
if nextSet.Length > 0 then
pathFind area goal start nextSet (nextSet.Head::closedNodes) (*...Else carry on*)
else None
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment