Bot-Man-Toe/elm/Games/TicTacToe.elm

202 lines
5.9 KiB
Elm

module Games.TicTacToe exposing (..)
{-| This module exposes a library for the simple game of tic-tac-toe.
-}
import Color
import Element exposing (Element)
import Json.Decode as D
import Layout
import Pixels exposing (Pixels)
import Quantity exposing (Quantity)
import Svg
import Svg.Attributes
import Theme
-- MODEL
type Field
= X
| O
| Empty
type alias TicTacToe =
{ field_1 : Field
, field_2 : Field
, field_3 : Field
, field_4 : Field
, field_5 : Field
, field_6 : Field
, field_7 : Field
, field_8 : Field
, field_9 : Field
}
decoder : D.Decoder TicTacToe
decoder =
D.map3
(\( a, b, c ) ( d, e, f ) ( g, h, i ) ->
TicTacToe a b c d e f g h i
)
(D.map3 (\a b c -> ( a, b, c )) fieldDecoder fieldDecoder fieldDecoder)
(D.map3 (\a b c -> ( a, b, c )) fieldDecoder fieldDecoder fieldDecoder)
(D.map3 (\a b c -> ( a, b, c )) fieldDecoder fieldDecoder fieldDecoder)
empty : TicTacToe
empty =
{ field_1 = Empty
, field_2 = Empty
, field_3 = Empty
, field_4 = Empty
, field_5 = Empty
, field_6 = Empty
, field_7 = Empty
, field_8 = Empty
, field_9 = Empty
}
fieldDecoder : D.Decoder Field
fieldDecoder =
D.andThen
(\s ->
case s of
"X" ->
D.succeed X
"O" ->
D.succeed O
"" ->
D.succeed Empty
_ ->
D.fail "Unknown field type"
)
D.string
-- VIEW
view :
{ flavor : Theme.Flavor
, game : TicTacToe
, height : Quantity Int Pixels
, width : Quantity Int Pixels
}
-> Element msg
view data =
Layout.svg
{ aspectRatio = 1 / 1
, height = Pixels.inPixels data.height
, width = Pixels.inPixels data.width
, viewMinX = 0
, viewMaxX = 300
, viewMinY = 0
, viewMaxY = 300
, svg =
Svg.g
[ Svg.Attributes.strokeLinecap "round"
]
[ svgField { field = data.game.field_1, flavor = data.flavor, offsetX = 0, offsetY = 0 }
, svgField { field = data.game.field_2, flavor = data.flavor, offsetX = 1, offsetY = 0 }
, svgField { field = data.game.field_3, flavor = data.flavor, offsetX = 2, offsetY = 0 }
, svgField { field = data.game.field_4, flavor = data.flavor, offsetX = 0, offsetY = 1 }
, svgField { field = data.game.field_5, flavor = data.flavor, offsetX = 1, offsetY = 1 }
, svgField { field = data.game.field_6, flavor = data.flavor, offsetX = 2, offsetY = 1 }
, svgField { field = data.game.field_7, flavor = data.flavor, offsetX = 0, offsetY = 2 }
, svgField { field = data.game.field_8, flavor = data.flavor, offsetX = 1, offsetY = 2 }
, svgField { field = data.game.field_9, flavor = data.flavor, offsetX = 2, offsetY = 2 }
, Svg.g
[ Svg.Attributes.fill <| Color.toCssString <| Theme.text data.flavor
, Svg.Attributes.strokeWidth "7.5"
]
[ Svg.line
[ Svg.Attributes.x1 "100"
, Svg.Attributes.x1 "100"
, Svg.Attributes.y1 "20"
, Svg.Attributes.y2 "280"
]
[]
, Svg.line
[ Svg.Attributes.x1 "200"
, Svg.Attributes.x1 "200"
, Svg.Attributes.y1 "20"
, Svg.Attributes.y2 "280"
]
[]
, Svg.line
[ Svg.Attributes.x1 "20"
, Svg.Attributes.x1 "280"
, Svg.Attributes.y1 "100"
, Svg.Attributes.y2 "100"
]
[]
, Svg.line
[ Svg.Attributes.x1 "20"
, Svg.Attributes.x1 "280"
, Svg.Attributes.y1 "200"
, Svg.Attributes.y2 "200"
]
[]
]
]
}
svgField :
{ field : Field
, flavor : Theme.Flavor
, offsetX : Int
, offsetY : Int
}
-> Svg.Svg svg
svgField data =
let
radius =
35
in
Svg.g
[ Svg.Attributes.fill <| Color.toCssString <| Theme.subtext0 data.flavor
, Svg.Attributes.strokeWidth "10"
]
[ case data.field of
Empty ->
Svg.g [] []
O ->
Svg.circle
[ Svg.Attributes.cx (String.fromInt (50 + 100 * data.offsetX))
, Svg.Attributes.cy (String.fromInt (50 + 100 * data.offsetY))
, Svg.Attributes.r (String.fromInt radius)
]
[]
X ->
Svg.g
[]
[ Svg.line
[ Svg.Attributes.x1 (String.fromInt (50 - radius))
, Svg.Attributes.x2 (String.fromInt (50 + radius))
, Svg.Attributes.y1 (String.fromInt (50 - radius))
, Svg.Attributes.y2 (String.fromInt (50 + radius))
]
[]
, Svg.line
[ Svg.Attributes.x1 (String.fromInt (50 - radius))
, Svg.Attributes.x2 (String.fromInt (50 + radius))
, Svg.Attributes.y1 (String.fromInt (50 + radius))
, Svg.Attributes.y2 (String.fromInt (50 - radius))
]
[]
]
]