Game of Life in PureScript
Riccardo Odone
Posted on June 3, 2019
You can keep reading here or jump to my blog to get the full experience, including the wonderful pink, blue and white palette.
An example run
The code
module Main where
import Prelude
import Effect (Effect)
import Effect.Console (log)
import Data.Tuple (Tuple(..))
import Data.Array.NonEmpty as NEA
import Data.Array as A
import Data.Foldable (elem, length)
import Data.String.Common as S
import Effect.Timer as T
import Control.MonadZero (guard)
maxX :: Int
maxX = 80
maxY :: Int
maxY = 10
type Y = Int
type X = Int
type Pos = Tuple Y X
-- Alive cells
type Board = Array Pos
block :: Board
block =
[ Tuple 0 0
, Tuple 0 1
, Tuple 1 0
, Tuple 1 1
]
blinker :: Board
blinker =
[ Tuple 0 1
, Tuple 1 1
, Tuple 2 1
]
glider :: Board
glider =
[ Tuple 0 1
, Tuple 1 2
, Tuple 2 0
, Tuple 2 1
, Tuple 2 2
]
showBoard :: Board -> String
showBoard board =
S.joinWith "\n" $ map showRow $ A.groupBy haveSameY $ A.sort allPositions
where
haveSameY :: Pos -> Pos -> Boolean
haveSameY (Tuple y1 _) (Tuple y2 _) = y1 == y2
showRow :: NEA.NonEmptyArray Pos -> String
showRow fs = S.joinWith "" $ NEA.toArray $ map (\f -> if elem f board then "O" else "X") fs
nextGeneration :: Board -> Board
nextGeneration board = do
p <- allPositions
guard $ willLive p board
pure p
allPositions :: Board
allPositions = do
y <- A.range 0 maxY
x <- A.range 0 maxX
pure $ Tuple y x
willLive :: Pos -> Board -> Boolean
willLive p board =
case Tuple (aliveNeighbors p board) (isAlive board p) of
Tuple 0 true -> false
Tuple 1 true -> false
Tuple 2 true -> true
Tuple 3 _ -> true
_ -> false
aliveNeighbors :: Pos -> Board -> Int
aliveNeighbors p board =
length $ A.filter (isAlive board) $ neighbors p
isAlive :: Board -> Pos -> Boolean
isAlive board p =
elem p board
neighbors :: Pos -> Array Pos
neighbors p1@(Tuple y1 x1) = do
y2 <- A.range (y1 - 1) (y1 + 1)
x2 <- A.range (x1 - 1) (x1 + 1)
let p2 = Tuple y2 x2
guard $ p1 /= p2
pure p2
loop :: Board -> Effect Unit
loop board = do
log "\x1B[2J\x1B[0f" -- clear terminal
log $ showBoard board
log ""
_ <- T.setTimeout 2000 (loop $ nextGeneration board)
pure unit
main :: Effect Unit
main = do
-- loop block
-- loop blinker
loop glider
pure unit
Get the latest content via email from me personally. Reply with your thoughts. Let's learn from each other. Subscribe to my PinkLetter!
💖 💪 🙅 🚩
Riccardo Odone
Posted on June 3, 2019
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.