import Browser
import Html exposing (Html)
import Html.Attributes exposing (style)
import Html.Events exposing (onClick)
import Svg
import Svg.Attributes as Svg
import Bitwise
-- Lights
type Lights = Lights Int
black : Lights
black = Lights 0
white : Lights
white = Lights 0xff
toggle : Lights -> Int -> Lights
toggle (Lights bits) light = Lights <| Bitwise.xor bits <| lights_mask light
lights_mask : Int -> Int
lights_mask light =
-- rotate mask over 8 bits: shift and fold it once
let m = Bitwise.shiftLeftBy (Bitwise.and light 0x7) 0x93
in Bitwise.xor (Bitwise.and m 0xff) (Bitwise.shiftRightZfBy 8 m)
get : Lights -> Int -> Bool
get (Lights bits) light = 0 /= (Bitwise.and bits <| Bitwise.shiftLeftBy (Bitwise.and light 0x7) 1)
get_all : Lights -> List (Int, Bool)
get_all lights = List.map (\n -> (n, get lights n)) [0,1,2,3,4,5,6,7]
-- MAIN
main =
Browser.element
{ init = init
, subscriptions = always Sub.none
, view = view
, update = update
}
-- MODEL
type alias Model =
{ lights : Lights
}
init : () -> (Model, Cmd Msg)
init _ =
( { lights = black }
, Cmd.none
)
-- UPDATE
type Msg
= Toggle Int
| Reset
update : Msg -> Model -> (Model, Cmd Msg)
update msg model =
case msg of
Toggle light ->
( { model | lights = toggle model.lights light }
, Cmd.none
)
Reset ->
( { model | lights = black }
, Cmd.none
)
-- VIEW
svg_center_text : String -> String -> String -> Svg.Svg msg
svg_center_text x y text =
Svg.text_
[ Svg.x x, Svg.y y, Svg.dominantBaseline "middle", Svg.textAnchor "middle" ]
[ Svg.text text ]
button : Float -> Float -> Float -> Float -> String -> msg -> Svg.Svg msg
button x y width height text msg =
Svg.g []
[ Svg.rect
[ Svg.x <| String.fromFloat x
, Svg.y <| String.fromFloat y
, Svg.width <| String.fromFloat width
, Svg.height <| String.fromFloat height
, Svg.fill "#808080", Svg.stroke "black", Svg.strokeWidth "1px"
] []
, svg_center_text (String.fromFloat <| x + width/2) (String.fromFloat <| y + height/2) text
, Svg.rect
[ Svg.x <| String.fromFloat x
, Svg.y <| String.fromFloat y
, Svg.width <| String.fromFloat width
, Svg.height <| String.fromFloat height
, Svg.fill "#00000000", Svg.stroke "none"
, onClick msg, style "cursor" "pointer"
] []
]
view : Model -> Html Msg
view model =
Svg.svg
[ Svg.viewBox "0 0 400 400"
, Svg.width "400"
, Svg.height "400"
]
[ Svg.g [] <| List.map viewLight (get_all model.lights)
, svg_center_text "50%" "50%" <| if model.lights == white then "Done!" else "Turn all lights on"
, button 300 340 80 30 "Reset" Reset
]
viewLight : (Int, Bool) -> Svg.Svg Msg
viewLight (index, on) =
let
t = 2 * pi * (toFloat index / 8)
x = String.fromFloat <| 200 + 120 * cos t
y = String.fromFloat <| 200 + 120 * sin t
color = if on then "#ff0000" else "#000000"
in
Svg.circle [ Svg.cx x, Svg.cy y, Svg.r "20", Svg.fill color, onClick (Toggle index), style "cursor" "pointer" ] []