Skip to content

Instantly share code, notes, and snippets.

@amaierhofer
Created June 13, 2017 07:24
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save amaierhofer/30c0ff604013b2f1d8b8457421054ef3 to your computer and use it in GitHub Desktop.
Save amaierhofer/30c0ff604013b2f1d8b8457421054ef3 to your computer and use it in GitHub Desktop.
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