Misha Moroshko
Misha Moroshko

Reputation: 171351

How to get a random element in a given List in Elm?

I'm building a sliding puzzle game:

enter image description here

From this initial position, I'd like to shuffle the puzzle, say 100 moves.

I'd like the puzzle to be shuffled differently each time.

This probably means that I need to call Random.initialSeed with a different number every time, which means that I need to use something like the current time.

Unfortunately, I couldn't find how to get the current time.

In every given state, I have a list of possible moves. For example, in the initial position above: [MoveRight, MoveDown]

So, basically, my question is: how to get a random element in a given List?

share-elm example would be appreciated.

Upvotes: 1

Views: 1419

Answers (3)

Mark Bolusmjak
Mark Bolusmjak

Reputation: 24409

I have created a variant of Elm's StartApp sample, which includes the Time for every event, only when an event occurs.

https://gist.github.com/z5h/41ca436679591b6c3e51

Your update function will now look like:

update : action -> Time -> model -> (model, Effects action)

Upvotes: 0

mgold
mgold

Reputation: 6366

This is a revision of pdamoc's answer. It follows The Elm Architecture more closely, but more importantly, it saves the random seed from press to press. If you keep using an initial seed, you won't get randomness. I got only even or odd numbers (got evens, refreshed, got odds) with the other version. It's important to call Random.initialSeed only once in your program, usually in the definition of the initial model.

There is also a library (which I wrote) that handles random operations on Arrays.

import Html exposing (..) 
import Html.Events exposing (onClick) 
import Random exposing (initialSeed, int, generate, Seed)

type alias Model = 
  { list : List Int , currentElement : Maybe Int, seed : Seed}

init : Model 
init = {list = [1..10], currentElement = Nothing, seed = initialSeed 42 } 

type Action = NoOp | Click

actions : Signal.Mailbox Action
actions = Signal.mailbox NoOp

update : Action -> Model -> Model
update action model =
  case action of
    NoOp -> model
    Click ->
      let gen = Random.int 0 (List.length model.list - 1)
          -- since we know the list is constant we don't have to
          -- redefine the generator every time, but we will anyway
          (i, seed) = Random.generate gen model.seed
          elem = List.drop i model.list |> List.head
      in {model| currentElement <- elem, seed <- seed}

model : Signal Model
model = Signal.foldp update init actions.signal

view : Signal.Address Action -> Model -> Html
view address model = 
  let 
    current = model.currentElement
              |> Maybe.map toString
              |> Maybe.withDefault "NONE"
  in 
    div [] 
      [ text  <| toString model.list
      , br [] []
      , text  <| "Current Element: " ++ current
      , br [] []
      , button [onClick address Click ] [text "Click for Random Element"]
      ]

main = Signal.map (view actions.address) model

Upvotes: 7

grumpyjames
grumpyjames

Reputation: 366

Rough answer (not near a compiler right now):

If you want to vary the seed based on the current time, add Time.every to your signal graph:

http://package.elm-lang.org/packages/elm-lang/core/2.1.0/Time#every

Keep the last seen timestamp in your model, then, when you need to shuffle, use it to seed your random. Generate an integer in the range [1, length list], and then pick the element of that index.

Hope that's enough of a hint, please yell if it isn't and I will elaborate when I get chance.

Upvotes: 2

Related Questions