Skip to content

Instantly share code, notes, and snippets.

@megamaddu
Last active January 17, 2018 20:19
Show Gist options
  • Save megamaddu/202fc7a9fe456b2e069e to your computer and use it in GitHub Desktop.
Save megamaddu/202fc7a9fe456b2e069e to your computer and use it in GitHub Desktop.
module Main where
import Control.Monad.Eff
import Data.Array (replicate, (!!), updateAt)
import Data.Maybe.Unsafe (fromJust)
import Data.Nullable (toMaybe)
import Prelude
import DOM.HTML (window)
import DOM.HTML.Document (body)
import DOM.HTML.Types (htmlElementToElement)
import DOM.HTML.Window (document)
import React
import qualified React.DOM as D
import qualified React.DOM.Props as P
import Signal.Channel
import Signal
data Token = X | O | E
instance showToken :: Show Token where
show X = "X"
show O = "O"
show E = ""
classForToken :: Token -> String
classForToken X = "cell x"
classForToken O = "cell o"
classForToken E = "cell"
type Board = Array Token
get :: Int -> Int -> Board -> Token
get x y board = fromJust (board !! (3 * x + y))
set :: Int -> Int -> Token -> Board -> Board
set x y token board = fromJust (updateAt (3 * x + y) token board)
newGameState :: GameState
newGameState = {currentPlayer: X, board: replicate 9 E}
boardComponent :: Environment -> ReactClass Unit
boardComponent env = createClass $ spec unit \_ -> return (game env)
newGameButton :: Channel Action -> ReactElement
newGameButton c =
D.button [P.onClick (\_ -> send c NewGame)] [D.text "New Game"]
game :: Environment -> ReactElement
game env = D.div'
[newGameButton env.channel
, grid env
, D.text (show (env.currentPlayer) ++ "'s turn.")]
grid :: Environment -> ReactElement
grid env = D.table' (map (row env) [0,1,2])
row :: Environment -> Int -> ReactElement
row env x = D.tr' (map (cell env x) [0,1,2])
cell :: Environment -> Int -> Int -> ReactElement
cell env x y = D.td
[P.className (classForToken token)
, P.onClick (\_ -> send env.channel (Click x y))]
[D.text (show token)]
where
token = get x y env.board
-----------------------------------------------------------
type State a = { board :: Board, currentPlayer :: Token | a }
type GameState = State ()
type Environment = State (channel :: Channel Action)
mkEnv :: Channel Action -> GameState -> Environment
mkEnv channel gameState = {
board: gameState.board,
currentPlayer: gameState.currentPlayer,
channel: channel
}
data Action = NewGame | Click Int Int
nextPlayer :: Token -> Token
nextPlayer X = O
nextPlayer O = X
nextPlayer E = E
step :: Action -> GameState -> GameState
step NewGame _ = newGameState
step (Click x y) gameState =
case get x y gameState.board of
E -> gameState {
board = set x y gameState.currentPlayer gameState.board,
currentPlayer = nextPlayer gameState.currentPlayer
}
_ -> gameState
-----------------------------------------------------------
main :: forall eff. Eff (chan :: Chan, dom :: DOM.DOM | eff) Unit
main = do
body' <- getBody
channel <- channel NewGame
let actions = subscribe channel
let gameState = foldp step newGameState actions
let game = gameState ~>
mkEnv channel >>> (\env -> render (ui env) body') >>> void
runSignal game
where
ui env = D.div' [ createFactory (boardComponent env) unit ]
getBody = do
win <- window
doc <- document win
bodyElem <- fromJust <$> toMaybe <$> body doc
return $ htmlElementToElement bodyElem
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment