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" ] []