Last active
August 22, 2016 06:49
-
-
Save ralfw/52a24689db63b09b4b1edd3ef2b056c9 to your computer and use it in GitHub Desktop.
Tic Tac Toe in Elm
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module Main exposing(main) | |
import Html exposing (Html, div, span, br, text, table, tr, td, button) | |
import Html.Attributes exposing (style) | |
import Html.App exposing (beginnerProgram) | |
import Html.Events exposing (onClick) | |
import Array exposing (Array) | |
import List | |
import Game | |
main = | |
beginnerProgram { | |
model = Game.newGame, | |
view = view, | |
update = update | |
} | |
view model = | |
let | |
n = 3 | |
fieldIndexOf r c = n*r+c | |
fieldValueAt r c = | |
model.board |> Array.get (fieldIndexOf r c) |> Maybe.withDefault " " | |
renderBoard = | |
table [] ( | |
[0..n-1] |> List.map (\r -> | |
tr [] ( | |
[0..n-1] |> List.map (\c -> | |
td [] [(button [fieldButtonStyle, onClick (MoveTo (fieldIndexOf r c))] | |
[text (fieldValueAt r c)])] | |
) | |
) | |
) | |
) | |
in | |
div [] [ | |
renderBoard | |
,span [] [text model.msg], br [] [] | |
,button [onClick NewGame] [text "New Game"] | |
] | |
fieldButtonStyle = | |
style | |
[ ("font-size", "300%") | |
,("width", "60px") | |
,("height", "60px") | |
] | |
type Msg = | |
NewGame | |
| MoveTo Int | |
update msg model = | |
case msg of | |
NewGame -> | |
Game.newGame | |
MoveTo fieldIndex -> | |
Game.executeMove fieldIndex model |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module Game exposing(..) | |
import Array exposing(Array) | |
import Referee | |
type alias Model = { | |
board : Array String | |
,player : Int | |
,msg : String | |
,gameOver : Bool | |
} | |
newGame = {board = Array.fromList [ | |
" ", " ", " ", | |
" ", " ", " ", | |
" ", " ", " "] | |
,player = 0 | |
,msg = "It's your turn player X" | |
,gameOver = False} | |
executeMove fieldIndex model = | |
let | |
checkValidityOfMove onIsValid onIsInvalid model = | |
if model.gameOver then | |
{model | msg = "No more moves allowed! Game over."} | |
else | |
if (model.board |> Array.get fieldIndex) == Just " " then | |
onIsValid model | |
else | |
{model | msg = "Invalid move; field already taken!"} |> onIsInvalid | |
currentPlayerSymbol player = if player == 0 then "X" else "O" | |
placeToken model = | |
{model | board = model.board |> Array.set fieldIndex (currentPlayerSymbol model.player)} | |
togglePlayer model = | |
{model | player = if model.player == 0 then 1 else 0} | |
promptPlayer model = | |
{model | msg = "It's your turn, player " ++ (currentPlayerSymbol model.player)} | |
in | |
model |> checkValidityOfMove | |
(placeToken | |
>> Referee.checkGameOver | |
(\m -> m) | |
(togglePlayer >> promptPlayer)) | |
(\m -> m) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
module Referee exposing (checkGameOver) | |
import Array | |
checkGameOver onGameOver onGameOn model = | |
model |> checkWinner "X" | |
onGameOver | |
(checkWinner "O" | |
onGameOver | |
(checkTie | |
onGameOver | |
onGameOn) | |
) | |
checkWinner candidateWinnerToken onGameOver onGameOn model = | |
let | |
winningPatterns = [ | |
[0,1,2], [3,4,5], [6,7,8], | |
[0,3,6], [1,4,7], [2,5,8], | |
[0,4,8], [2,4,6] | |
] | |
isOnBoard pattern = | |
pattern |> List.all (\i -> (model.board |> Array.get i) == Just candidateWinnerToken) | |
in | |
if (winningPatterns |> List.any isOnBoard) then | |
onGameOver {model | msg = "Game over! " ++ candidateWinnerToken ++ " wins.", gameOver = True} | |
else | |
onGameOn model | |
checkTie onGameOver onGameOn model = | |
let | |
numberOfAvailableFields = model.board |> Array.filter (\f -> f == " ") |> Array.length | |
in | |
if numberOfAvailableFields == 0 then | |
onGameOver {model | msg = "Game over! It's a tie.", gameOver = True} | |
else | |
onGameOn model |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment