202 lines
5.9 KiB
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))
|
|
]
|
|
[]
|
|
]
|
|
]
|