Created
June 13, 2017 07:24
-
-
Save amaierhofer/30c0ff604013b2f1d8b8457421054ef3 to your computer and use it in GitHub Desktop.
This file contains 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 Matrix exposing (..) | |
import Array exposing (..) | |
import Html exposing (..) | |
import Html.Events exposing (..) | |
import Html.Attributes exposing (..) | |
import Debug exposing (..) | |
-- Model | |
type Player | |
= Cross | |
| Circle | |
type alias Model = | |
{ board : Board | |
, player : Player | |
} | |
type alias Point = | |
{ x : Int, y : Int, player : Maybe Player } | |
type alias Board = | |
Matrix (Maybe Player) | |
init : Model | |
init = | |
Model defaultBoard Cross | |
defaultBoard : Board | |
defaultBoard = | |
Matrix.repeat 3 3 Nothing | |
-- |> Matrix.set 0 0 (Just Cross) | |
-- |> Matrix.set 0 1 (Just Cross) | |
-- |> Matrix.set 0 2 (Just Cross) | |
nextPlayer : Player -> Player | |
nextPlayer player = | |
List.filter (\next -> next /= player) [ Cross, Circle ] | |
|> List.head | |
|> Maybe.withDefault Cross | |
solved : List (Maybe Player) -> Bool | |
solved list = | |
let | |
head = | |
Maybe.withDefault (Nothing) (List.head list) | |
in | |
List.map (\item -> item == head && item /= Nothing) list | |
|> List.all (\item -> item == True) | |
pointWithValue : Board -> Int -> Int -> Point | |
pointWithValue board x y = | |
let | |
maybePlayer = | |
Matrix.get x y board | |
in | |
Point x y (Maybe.withDefault (Just Cross) maybePlayer) | |
findWinner : Board -> List Point | |
findWinner board = | |
let | |
range = | |
List.range 0 2 | |
candidates = | |
[ List.map (\i -> pointWithValue board 0 i) range | |
, List.map (\i -> pointWithValue board 1 i) range | |
, List.map (\i -> pointWithValue board 2 i) range | |
, List.map (\i -> pointWithValue board i 0) range | |
, List.map (\i -> pointWithValue board i 1) range | |
, List.map (\i -> pointWithValue board i 2) range | |
, List.map2 (pointWithValue board) range range | |
, List.map2 (pointWithValue board) range <| List.reverse range | |
] | |
findSolution list = | |
List.map (\point -> point.player) list | |
|> solved | |
in | |
List.filter findSolution candidates | |
|> List.head | |
|> Maybe.withDefault [] | |
-- Update | |
type Msg | |
= Click Point | |
| Reset | |
updateBoard : Point -> Model -> Board | |
updateBoard point model = | |
Matrix.set point.x point.y (Just model.player) model.board | |
clickPoint : Point -> Model -> Model | |
clickPoint point model = | |
let | |
value = | |
Maybe.withDefault Nothing | |
(Matrix.get point.x point.y model.board) | |
in | |
case Debug.log "updating value" value of | |
Nothing -> | |
{ model | |
| board = updateBoard point model | |
, player = nextPlayer model.player | |
} | |
_ -> | |
model | |
update : Msg -> Model -> Model | |
update msg model = | |
case msg of | |
Reset -> | |
init | |
Click point -> | |
clickPoint point model | |
-- View | |
squareText : Maybe Player -> String | |
squareText maybePlayer = | |
case maybePlayer of | |
Nothing -> | |
"" | |
Just player -> | |
case player of | |
Cross -> | |
"x" | |
Circle -> | |
"o" | |
drawSquare : List Point -> Int -> Int -> Maybe Player -> Html Msg | |
drawSquare solution x y value = | |
let | |
point = | |
Point x y Nothing | |
partOfSolution = | |
List.any (\point -> point.x == x && point.y == y) solution | |
backgroundColor = | |
case value of | |
Nothing -> | |
"gray" | |
Just value -> | |
if partOfSolution then | |
"green" | |
else | |
case value of | |
Cross -> | |
"orange" | |
Circle -> | |
"cyan" | |
squareStyle = | |
style | |
[ ( "display", "inline-block" ) | |
, ( "background", backgroundColor ) | |
, ( "border", "1px dashed gray" ) | |
, ( "padding", "0.2em 0.5em" ) | |
, ( "margin", "0.3em" ) | |
, ( "font-size", "3em" ) | |
, ( "width", "80px" ) | |
, ( "height", "80px" ) | |
, ( "vertical-align", "middle" ) | |
, ( "text-align", "center" ) | |
] | |
in | |
if List.isEmpty solution then | |
div [ squareStyle, onClick (Click point) ] [ text <| squareText value ] | |
else | |
div [ squareStyle ] [ text <| squareText value ] | |
matrixToDivs : Matrix (Html Msg) -> List (Html Msg) | |
matrixToDivs matrix = | |
let | |
range = | |
List.range 0 <| Matrix.height matrix | |
toDiv row = | |
Matrix.getRow row matrix | |
|> Maybe.withDefault Array.empty | |
|> Array.toList | |
|> div [] | |
in | |
List.map toDiv range | |
drawBoard : Model -> Html Msg | |
drawBoard model = | |
let | |
boardStyle = | |
style | |
[ ( "border", "1px solid black" ) | |
, ( "display", "inline-block" ) | |
, ( "padding", "1em" ) | |
, ( "margin", "1em" ) | |
] | |
in | |
Matrix.indexedMap (drawSquare <| findWinner model.board) model.board | |
|> matrixToDivs | |
|> div [ boardStyle ] | |
drawBoardControl : Model -> Html Msg | |
drawBoardControl model = | |
let | |
winner = | |
findWinner model.board | |
|> List.head | |
in | |
case winner of | |
Nothing -> | |
div [] | |
[ h1 [] [ text <| "Next player: " ++ toString model.player ] | |
, button [ onClick Reset ] [ text "Reset" ] | |
] | |
Just point -> | |
div [] | |
[ h1 [] [ text <| toString (point.player) ++ " won" ] | |
, button [ onClick Reset ] [ text "Reset" ] | |
] | |
view : Model -> Html Msg | |
view model = | |
div [] | |
[ div [ style ([ ( "display", "inline-flex" ) ]) ] | |
[ drawBoard model | |
, drawBoardControl model | |
] | |
, hr [] [] | |
, div [] [ text <| toString model ] | |
] |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment