Skip to content

Instantly share code, notes, and snippets.

@pmbanka
Last active March 4, 2017 12:29
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 pmbanka/8f0f201fac5f07eb7825c1e999b3f79a to your computer and use it in GitHub Desktop.
Save pmbanka/8f0f201fac5f07eb7825c1e999b3f79a to your computer and use it in GitHub Desktop.
Game of Life in F#
open System
let cartesian xs ys =
xs |> List.collect (fun x -> ys |> List.map (fun y -> x, y))
type Coords = { X:int; Y:int } with
static member FromTuple (x, y) =
{ X = x; Y = y }
type State = { Alive:Set<Coords>; XMax:int; YMax:int } with
member this.AllCells =
cartesian [ 0 .. this.XMax ] [ 0 .. this.YMax ]
|> List.map Coords.FromTuple
type Input = { AliveMarker:char; Grid:string }
type OutputConfig = { AliveMarker:char; DeadMarker:char; MaxIter: int; Delay:TimeSpan }
let fromString input =
let lines = input.Grid.Split ([| "\n"; "\r\n" |], StringSplitOptions.RemoveEmptyEntries) |> List.ofSeq
let coords =
lines
|> Seq.mapi (fun lineIdx line ->
line
|> Seq.mapi (fun chIdx ch -> if ch = input.AliveMarker then Some { X = chIdx; Y = lineIdx } else None)
|> Seq.choose id)
|> Seq.concat
|> Set.ofSeq
let xMax = (lines |> Seq.maxBy (fun line -> line.Length)).Length-1
{ Alive = coords; XMax = xMax; YMax = lines.Length-1 }
let toString (aliveMarker:char) (deadMarker:char) state =
let createLine y =
[| 0 .. state.XMax |]
|> Array.map (fun x -> if state.Alive.Contains { X=x; Y=y } then aliveMarker else deadMarker)
|> String
let lines =
[| 0 .. state.YMax |]
|> Array.map createLine
String.concat Environment.NewLine lines
let step state =
let aliveNeighbors coord =
cartesian [coord.X-1 .. coord.X+1] [coord.Y-1 .. coord.Y+1]
|> List.map Coords.FromTuple
|> List.where (fun c -> c.X >= 0 && c.X <= state.XMax && c.Y >= 0 && c.Y <= state.YMax && c <> coord)
|> List.where state.Alive.Contains
|> List.length
let isAliveInNextRound coord =
let isAliveNow = state.Alive.Contains coord
let aliveNeighbors = aliveNeighbors coord
match isAliveNow, aliveNeighbors with
| true, 2 | true, 3 | false, 3 -> true
| _ -> false
let newAlive =
state.AllCells
|> Seq.where isAliveInNextRound
|> Set.ofSeq
{ state with Alive = newAlive }
let animate input cfg =
let rec loop currentState currentIter = async {
System.Console.Clear ()
printfn "%s" <| toString cfg.AliveMarker cfg.DeadMarker currentState
do! Async.Sleep (int cfg.Delay.TotalMilliseconds)
if currentIter < cfg.MaxIter then
let nextState = step currentState
return! loop nextState (currentIter+1)
else
return ()
}
let initialState = fromString input
loop initialState 0
let grid = """
........................................................................O....................
......................................................................OO.O...................
......................................................................OO...O.................
....................................................................O...OO.O.....O...........
....................................................................OOOO.OO...OOOO.......O.O.
..................................................................O......O....OOO.....O.O..O.
..................................................................OOOOOOO...O...O....O..O....
...............................................................O.O......OO..O...O.O.OO....O..
..............................................................OOOOOOOOO.....O..OO........O...
.............................................................OO..............O.OO.OOOO...O..O
............................................................OO....OO.O..........O...O..O.O...
.............................................................OO....O........OOO......O.O.O..O
.....................................................................O......OO......O....OO..
.............................................................OO....O........OOO......O.O.O..O
............................................................OO....OO.O..........O...O..O.O...
.............................................................OO..............O.OO.OOOO...O..O
..............................................................OOOOOOOOO.....O..OO........O...
...............................................................O.O......OO..O...O.O.OO....O..
..................................................................OOOOOOO...O...O....O..O....
..................................................................O......O....OOO.....O.O..O.
....................................................................OOOO.OO...OOOO.......O.O.
....................................................................O...OO.O.....O...........
......................................................................OO...O.................
......................................................................OO.O...................
........................................................................O...................."""
let input = { AliveMarker = 'O'; Grid = grid }
let cfg = { AliveMarker = 'o'; DeadMarker = '.'; Delay = TimeSpan.FromMilliseconds 10.; MaxIter = 300 }
animate input cfg |> Async.RunSynchronously
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment