Skip to content

Instantly share code, notes, and snippets.

@voyce
Created January 22, 2012 23:03
Show Gist options
  • Save voyce/1659242 to your computer and use it in GitHub Desktop.
Save voyce/1659242 to your computer and use it in GitHub Desktop.
Generic recursive path finder in F#
open System
/// Given a starting location, and functions for determining possible
/// moves and whether we're reached the goal.
/// Returns a tuple of boolean and a list of locations in the path
/// to the goal.
let tryFindPath (start : 'T) (neighbours : 'T -> 'T []) (isGoal : 'T -> bool) =
let rec tryPath l visited =
if isGoal l
then
true, visited
else
let paths =
// Get valid adjacent cells
neighbours l
// Remove ones that we've visited already on this path
|> Array.filter (fun l -> not <| List.exists ((=) l) visited)
// And recurse
|> Array.map (fun l -> tryPath l (l :: visited))
match paths |> Array.tryFind (fun path -> fst path = true) with
| Some path -> path
| None -> false, []
tryPath start []
/// Example usage: a 2d maze
type Location =
{ X : int; Y: int }
let getNeighbours, isGoal =
let maze =
let rows =
[|
[| '.';'.';'#';'.';'.';'#' |];
[| '#';'.';'.';'.';'#';'#' |];
[| '#';'.';'#';'.';'#';'#' |];
[| '#';'.';'.';'.';'.';'.' |];
[| '#';'#';'#';'#';'#';'.' |];
[| '.';'.';'#';'.';'G';'.' |]
|];
Array2D.init (rows.Length) (rows.[0].Length) (fun r c -> rows.[r].[c])
// is the location within the bounds, and not a wall?
let valid = fun l -> (l.X >= 0 && l.Y >= 0 && l.X <= 5 && l.Y <= 5) && maze.[l.Y,l.X] <> '#'
// get the viable neighbours
(fun l ->
if maze.[l.Y, l.X] = '#'
then
[||]
else
[|{ l with Y = l.Y - 1 }
{ l with Y = l.Y + 1 }
{ l with X = l.X - 1 }
{ l with X = l.X + 1 }|]
|> Array.filter valid),
// is this the goal?
(fun l -> maze.[l.Y, l.X] = 'G')
tryFindPath {X=0;Y=0} getNeighbours isGoal
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment