Created
July 29, 2011 07:12
-
-
Save jdoig/1113358 to your computer and use it in GitHub Desktop.
First attempt at pathfinding in F#
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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