Skip to content

Instantly share code, notes, and snippets.

@hakelimopu
Created December 15, 2015 21:53
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save hakelimopu/52fade340b8ca3537d38 to your computer and use it in GitHub Desktop.
Save hakelimopu/52fade340b8ca3537d38 to your computer and use it in GitHub Desktop.
A Second, Better Approach to Immutable Maze Generation
module Cardinal
open Location
type Direction = North | East | South | West
let Walk (location:Location) (direction:Direction) : Location =
match direction with
| North -> {location with Row=location.Row-1}
| East -> {location with Column=location.Column+1 }
| South -> {location with Row=location.Row+1}
| West -> {location with Column=location.Column-1 }
let Values = [North; East; South; West]
module ColumnHex
open Location
type Direction = North | Northeast | Southeast | South | Southwest | Northwest
let Walk (location:Location) (direction:Direction) : Location =
match direction with
| North -> {location with Row = location.Row - 1}
| Northeast -> {location with Column = location.Column + 1 }
| Southeast -> { Column = location.Column + 1; Row = location.Row + 1}
| South -> {location with Row = location.Row + 1}
| Southwest -> {location with Column = location.Column - 1 }
| Northwest -> { Column = location.Column - 1; Row = location.Row - 1}
let Values = [North; Northeast; Southeast; South; Southwest; Northwest]
module Intercardinal
open Location
type Direction = North | Northeast | East | Southeast | South | Southwest | West | Northwest
let Walk (location:Location) (direction:Direction) : Location =
match direction with
| North -> {location with Row = location.Row - 1}
| Northeast -> { Column = location.Column + 1; Row = location.Row - 1}
| East -> {location with Column = location.Column + 1 }
| Southeast -> { Column = location.Column + 1; Row = location.Row + 1}
| South -> {location with Row = location.Row + 1}
| Southwest -> { Column = location.Column - 1; Row = location.Row + 1}
| West -> {location with Column = location.Column - 1 }
| Northwest -> { Column = location.Column - 1; Row = location.Row - 1}
let Values = [North; Northeast; East; Southeast; South; Southwest; West; Northwest]
module Location
type Location = {Column: int; Row: int}
module Neighbors
open Location
let FindAll (walk: Location->'direction->Location) (directions:'direction list) (location:Location) : Location list =
directions
|> List.map(walk location)
let FindAllCardinal = FindAll Cardinal.Walk Cardinal.Values
let FindAllIntercardinal = FindAll Intercardinal.Walk Intercardinal.Values
let FindAllRowHex = FindAll RowHex.Walk RowHex.Values
let FindAllColumnHex = FindAll ColumnHex.Walk ColumnHex.Values
open Location
open Neighbors
let MakeGrid (columns, rows) =
[for c in [0..columns-1] do
for r in [0..rows-1] do
yield {Column=c; Row=r}]
let MakeEmptyMaze (locations: Location list) =
locations
|> List.map(fun item -> (item, Set.empty))
|> Map.ofSeq
let AddConnection (fromLocation: Location) (toLocation:Location) (maze: Map<Location, Set<Location> >) =
let newConnections = maze.[fromLocation]
|> Set.add toLocation
maze
|> Map.add fromLocation newConnections
let AddConnections (fromLocation: Location) (toLocation:Location) (maze: Map<Location, Set<Location> >) =
maze
|> AddConnection fromLocation toLocation
|> AddConnection toLocation fromLocation
let ChooseStart (condition:Location->bool) (maze: Map<Location, Set<Location> >) =
maze
|> Map.toSeq
|> Seq.map (fun (k,v) -> k)
|> Seq.filter(condition)
|> Seq.sortBy (fun e-> System.Guid.NewGuid())
|> Seq.head
let ChooseNeighbor (neighborFinder:Location -> Location list) (maze: Map<Location, Set<Location> >) (location: Location) =
location
|> neighborFinder
|> Seq.filter (fun e-> maze.ContainsKey(e))
|> Seq.sortBy (fun e-> System.Guid.NewGuid())
|> Seq.head
let StartMaze (neighborFinder:Location -> Location list) (maze: Map<Location, Set<Location> >) =
let start =
maze
|> ChooseStart (fun e-> true)
let neighbor =
start
|> ChooseNeighbor neighborFinder maze
maze
|> AddConnections start neighbor
let GetPossibleConnections (neighborFinder:Location -> Location list) (maze: Map<Location, Set<Location> >) (location:Location) =
location
|> neighborFinder
|> List.filter (fun e-> maze.ContainsKey(e))
let CanConnectTo (neighborFinder:Location -> Location list) (maze: Map<Location, Set<Location> >) (location:Location)=
location
|> GetPossibleConnections neighborFinder maze
|> List.isEmpty
|> not
let AddRoom (neighborFinder:Location -> Location list) (maze: Map<Location, Set<Location> >) =
let outside, inside =
maze
|> Map.partition (fun k v -> v.IsEmpty)
let start =
outside
|> ChooseStart (fun e-> e |> CanConnectTo neighborFinder inside)
let neighbor =
start
|> ChooseNeighbor neighborFinder inside
maze
|> AddConnections start neighbor
let (|Complete|InProgress|Empty|) (maze: Map<Location, Set<Location> >) =
let outside, inside = maze |> Map.partition (fun k v -> v.IsEmpty)
if inside.IsEmpty then
Empty
elif outside.IsEmpty then
Complete
else
InProgress
let rec MakeMaze (neighborFinder:Location -> Location list) maze =
match maze with
| Complete -> maze
| Empty -> maze |> StartMaze neighborFinder |> MakeMaze neighborFinder
| _ -> maze |> AddRoom neighborFinder |> MakeMaze neighborFinder
[<EntryPoint>]
let main argv =
let maze =
(2,2)
|> MakeGrid
|> MakeEmptyMaze
|> MakeMaze FindAllCardinal
|> Map.toSeq
|> printfn "%A"
0
module RowHex
open Location
type Direction = Northeast | East | Southeast | Southwest | West | Northwest
let Walk (location:Location) (direction:Direction) : Location =
match direction with
| Northeast -> {location with Row = location.Row - 1}
| East -> {location with Column = location.Column + 1 }
| Southeast -> { Column = location.Column + 1; Row = location.Row + 1}
| Southwest -> {location with Row = location.Row + 1}
| West -> {location with Column = location.Column - 1 }
| Northwest -> { Column = location.Column - 1; Row = location.Row - 1}
let Values = [Northeast; East; Southeast; Southwest; West; Northwest]
@hakelimopu
Copy link
Author

Naturally, the Gist screws up the order:

The order it needs to go in:

Location.fs
//the next four's order doesn't really matter. but depend on Location
Cardinal.fs
Intercardinal.fs
RowHex.fs
ColumnHex.fs
//neighbors depends on the previous four
Neighbors.fs
Program.fs

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment