Skip to content

Instantly share code, notes, and snippets.

@beckyconning
Last active March 29, 2016 07:44
Show Gist options
  • Save beckyconning/adef8f3e88da4d1d20e9 to your computer and use it in GitHub Desktop.
Save beckyconning/adef8f3e88da4d1d20e9 to your computer and use it in GitHub Desktop.
Bingo card generator app in both Elm 0.16.0 and Purescript v0.7.6.1 for comparison. The apps are almost identical except that the Elm version uses a random seed and the Purescript version includes a "FREE SPACE" in the middle. Elm version by Charlotte Tortorella, Purescript version by Becky Conning.
import Signal
import Graphics.Element exposing (..)
import Graphics.Input exposing (button)
import Graphics.Collage exposing (collage, toForm)
import List exposing (..)
import Color
import Text
import Random
import Random.Array
import Window
import Task exposing (Task)
import Array exposing (Array)
type alias Model =
{ seed : Random.Seed
, bingoTiles : List String
, generator : Random.Generator (List String)
}
type Action = NoOp
| Shuffle
randomGenerator : Random.Generator (List String)
randomGenerator =
initialNumbers
|> Random.Array.shuffle << Array.fromList
|> Random.map Array.toList
initialNumbers : List String
initialNumbers =
[ "1"
, "2"
, "3"
, "4"
, "5"
, "6"
, "7"
, "8"
, "9"
, "10"
, "11"
, "12"
, "13"
, "14"
, "15"
, "16"
, "17"
, "18"
, "19"
, "20"
, "21"
, "22"
, "23"
, "24"
, "25"
]
main =
Signal.map2 view model Window.dimensions
update : Action -> Model -> Model
update action model =
case action of
NoOp ->
model
Shuffle ->
let (tiles, newSeed) = Random.generate model.generator model.seed
in
{ model
| bingoTiles = tiles
, seed = newSeed
}
initialModel : Model
initialModel =
{ seed = Random.initialSeed 42
, bingoTiles = initialNumbers
, generator = randomGenerator
}
model : Signal Model
model =
Signal.foldp update initialModel actions.signal
slice : List a -> Int -> Int -> List a
slice xs s e = take (e - s + 1) <| drop (s - 1) xs
view : Model -> (Int, Int) -> Element
view model (windowWidth, windowHeight) =
let boxSize = (min windowWidth windowHeight) |> toFloat |> flip (/) 6 |> round
boxSpacing = 2
boxSpacer = spacer boxSpacing boxSpacing
shuffleButton = button (Signal.message actions.address Shuffle) "Shuffle"
sliceTilesAsText start end =
slice model.bingoTiles start end
|> map (centered << Text.height 10 << Text.fromString)
|> map (container boxSize boxSize middle)
|> map (color Color.lightGrey)
|> intersperse boxSpacer
|> flow right
middleAtY = middleAt (relative 0.5)
bingoTiles =
[ sliceTilesAsText 1 5
, sliceTilesAsText 6 10
, sliceTilesAsText 11 15
, sliceTilesAsText 16 20
, sliceTilesAsText 21 25
]
|> intersperse boxSpacer
|> flow down
in
collage windowWidth windowHeight
<| map toForm
[ container ((widthOf bingoTiles) + boxSpacing * 2) ((heightOf bingoTiles) + boxSpacing * 2) middle bingoTiles
|> color Color.black
|> below (container ((widthOf bingoTiles) + boxSpacing * 2) ((heightOf shuffleButton) * 2) middle shuffleButton)
]
actions : Signal.Mailbox Action
actions =
Signal.mailbox NoOp
module Main where
import Control.Bind ((=<<))
import Control.Monad.Aff (Aff(), runAff, later')
import Control.Monad.Eff (Eff())
import Control.Monad.Eff.Exception (throwException)
import Control.Monad.List.Trans as ListT
import Control.Monad.Trampoline (Trampoline, runTrampoline)
import Data.Array (slice, insertAt, head, singleton)
import Data.Functor ((<$))
import Data.Lazy (Lazy, force)
import Data.Maybe (Maybe(Just), maybe)
import Data.Tuple (Tuple, fst, snd)
import Halogen
import Halogen.HTML.Events.Indexed as E
import Halogen.HTML.Indexed as H
import Halogen.HTML.Properties.Indexed as P
import Halogen.Util (appendToBody, onLoad)
import Prelude
import Test.StrongCheck.Gen (Gen, GenState(..), runGen, shuffleArray, infinite, toLazyList)
data BingoQuery a = Shuffle a
type Numbers = Array String
type Permutations = ListT.ListT Lazy (Array String)
type BingoState = { numbers :: Maybe Numbers, permutations :: Permutations }
initialNumbers :: Array String
initialNumbers =
[ "1"
, "2"
, "3"
, "4"
, "5"
, "6"
, "7"
, "8"
, "9"
, "10"
, "11"
, "12"
, "13"
, "14"
, "15"
, "16"
, "17"
, "18"
, "19"
, "20"
, "21"
, "22"
, "23"
, "24"
]
initialGenState :: GenState
initialGenState = GenState { size: 1, seed: 454645874.0 }
initialPermutations :: Permutations
initialPermutations = toLazyList (infinite $ shuffleArray initialNumbers) initialGenState
initialState :: BingoState
initialState = { numbers: Just initialNumbers, permutations: initialPermutations }
nextPermutation :: BingoState -> BingoState
nextPermutation state =
{ numbers: force $ ListT.head $ state.permutations, permutations: ListT.drop 1 state.permutations }
insertFreeSpace :: Array String -> Maybe (Array String)
insertFreeSpace = insertAt 12 "FREE SPACE"
rows :: forall a. Array a -> Array (Array a)
rows xs = [slice 0 5 xs, slice 5 10 xs, slice 10 15 xs, slice 15 20 xs, slice 20 25 xs]
ui :: forall g. (Functor g) => Component BingoState BingoQuery g
ui = component render eval
where
render :: BingoState -> ComponentHTML BingoQuery
render state =
H.div_
[ maybe renderGenError renderTableIfThereAreEnoughNumbers $ state.numbers
, H.button [ E.onClick $ E.input_ Shuffle ] [ H.text "Shuffle" ]
]
renderTableIfThereAreEnoughNumbers :: Array String -> ComponentHTML BingoQuery
renderTableIfThereAreEnoughNumbers =
maybe renderNotEnoughNumbersError renderTable <<< insertFreeSpace
renderNotEnoughNumbersError :: ComponentHTML BingoQuery
renderNotEnoughNumbersError = H.text "Needed at least 12 numbers"
renderGenError :: ComponentHTML BingoQuery
renderGenError = H.text "Generator didn't produce a sample"
renderTable :: Array String -> ComponentHTML BingoQuery
renderTable = H.table_ <<< map renderRow <<< rows
renderRow :: Array String -> ComponentHTML BingoQuery
renderRow = H.tr_ <<< map renderCell
renderCell :: String -> ComponentHTML BingoQuery
renderCell = H.td_ <<< singleton <<< H.text
eval :: Natural BingoQuery (ComponentDSL BingoState BingoQuery g)
eval (Shuffle next) = next <$ modify nextPermutation
main :: Eff (HalogenEffects ()) Unit
main = runAff throwException (const (pure unit)) initialiseAndAppendNode
where
initialiseAndAppendNode =
onLoad <<< appendToBody <<< _.node =<< (runUI ui $ nextPermutation initialState)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment