I'm doing the Elm Guide exercise from Random Section and got stuck in step 5, where I have to flip around the dices before they settle on a final Value. I'm trying to do this calling the Cmd, that updates the view with the new values of the dices, 10 times with just one click of the button "Roll!" and put some kind of sleep function before each iteraction. For what I've read, Elm does not have a for, loop or while statement. The only way to loop is through recursion, but I'm not being able to adapt my code for it. My code below, does anyone have a suggestion?
import Browser
import Html exposing (..)
import Html.Events exposing (..)
import Svg exposing (..)
import Svg.Attributes as SvgAttr exposing (..)
import Random
-- MAIN
main =
Browser.element
{ init = init
, update = update
, subscriptions = subscriptions
, view = view
}
-- MODEL
type alias Model =
{ dieFace : Int
, dieFace2 : Int
}
init : () -> (Model, Cmd Msg)
init _ =
( Model 1 1
, Cmd.none
)
-- UPDATE
type Msg
= Roll
| NewFace (Int, Int)
update : Msg -> Model -> (Model, Cmd Msg)
update msg model =
case msg of
Roll ->
( model
, Random.generate NewFace randomPair
)
NewFace (newFace, newFace2) ->
( Model newFace newFace2
, Cmd.none
)
weightedRoll : Random.Generator Int
weightedRoll =
Random.weighted
(1, 1)
[ (10, 2)
, (10, 3)
, (10, 4)
, (20, 5)
, (40, 6)
]
randomPair : Random.Generator (Int, Int)
randomPair =
Random.pair (Random.int 1 6) (Random.int 1 6)
-- SUBSCRIPTIONS
subscriptions : Model -> Sub Msg
subscriptions model =
Sub.none
-- VIEW
view : Model -> Html Msg
view model =
div []
[ h1 [] [ Html.text (String.fromInt model.dieFace) ]
, svg
[ width "120"
, height "120"
, viewBox "0 0 120 120"
]
(List.append
[ rect
[ x "10"
, y "10"
, width "100"
, height "100"
, rx "15"
, ry "15"
]
[]
]
(dicesSVG model.dieFace)
)
, div []
[ h1 [] [Html.text (String.fromInt model.dieFace2) ]
, svg
[ width "120"
, height "120"
, viewBox "0 0 120 120"
]
(List.append
[ rect
[ x "10"
, y "10"
, width "100"
, height "100"
, rx "15"
, ry "15"
]
[]
]
(dicesSVG model.dieFace2)
)]
, button [onClick Roll] [Html.text "Roll!"]
]
dicesSVG : Int -> List (Svg Msg)
dicesSVG number =
case number of
1 ->
[ circle [cx "60", cy "60", r "10", fill "white" ] [] ]
2 ->
[ circle [ cx "35", cy "35", r "10", fill "white" ] []
, circle [ cx "85", cy "85", r "10", fill "white" ] []
]
3 ->
[ circle [ cx "35", cy "35", r "10", fill "white" ] []
, circle [ cx "60", cy "60", r "10", fill "white" ] []
, circle [ cx "85", cy "85", r "10", fill "white" ] []
]
4 ->
[ circle [ cx "35", cy "35", r "10", fill "white" ] []
, circle [ cx "85", cy "35", r "10", fill "white" ] []
, circle [ cx "35", cy "85", r "10", fill "white" ] []
, circle [ cx "85", cy "85", r "10", fill "white" ] []
]
5 ->
[ circle [ cx "35", cy "35", r "10", fill "white" ] []
, circle [ cx "85", cy "35", r "10", fill "white" ] []
, circle [ cx "60", cy "60", r "10", fill "white" ] []
, circle [ cx "35", cy "85", r "10", fill "white" ] []
, circle [ cx "85", cy "85", r "10", fill "white" ] []
]
6 ->
[ circle [ cx "35", cy "35", r "10", fill "white" ] []
, circle [ cx "85", cy "35", r "10", fill "white" ] []
, circle [ cx "35", cy "60", r "10", fill "white" ] []
, circle [ cx "85", cy "60", r "10", fill "white" ] []
, circle [ cx "35", cy "85", r "10", fill "white" ] []
, circle [ cx "85", cy "85", r "10", fill "white" ] []
]
_ ->
[]
CodePudding user response:
The trick is mostly in asking the runtime to send you messages.
The first thing you could try is changing
NewFace (newFace, newFace2) ->
( Model newFace newFace2
, Cmd.none
)
to
NewFace (newFace, newFace2) ->
( Model newFace newFace2
, Random.generate NewFace randomPair
)
This has the problem of never stopping, making your program spin around forever...
So you will need to track some stopping condition. Perhaps add a field to your model that tracks how many times it has rolled already? Then switching between Random.generate
and Cmd.none
based on that field.
Finally if you want a time delay between the rolls, then
import Task
wait : Float -> msg -> Cmd msg
wait delay msgToCallWhenDone =
Task.perform (always msg) (Process.sleep delay)
will give you a Cmd
that will call whatever msg you give it after delay
milliseconds. As a hint, you might want to introduce another msg for this one.