DEV Community

Cover image for Writing A Word Memory Game In Elm - Part 5: More Randomness And More Game
Mickey
Mickey

Posted on • Edited on

Writing A Word Memory Game In Elm - Part 5: More Randomness And More Game

This is part 5 of the series "Writing A Word Memory Game In Elm", find:


Playing with the same sentence all the time is pretty boring. The actual game needs to have many sentences to choose from. For example:

sentences : List String
sentences =
    [ "Two wrongs don't make a right."
    , "The pen is mightier than the sword."
    , "When in Rome, do as the Romans."
    , "Keep your friends close and your enemies closer."
    , "You can't make an omelet without breaking a few eggs."
    , "The grass is always greener on the other side of the hill."
    , "You can lead a horse to water, but you can't make him drink."
    , "If you want something done right, you have to do it yourself."
    ]
Enter fullscreen mode Exit fullscreen mode

We can simplify the sentences by removing all the punctuation:

sentences : List String
sentences =
    [ "Two wrongs don't make a right"
    , "The pen is mightier than the sword"
    , "When in Rome do as the Romans"
    , "Keep your friends close and your enemies closer"
    , "You can't make an omelet without breaking a few eggs"
    , "The grass is always greener on the other side of the hill"
    , "You can lead a horse to water but you can't make him drink"
    , "If you want something done right you have to do it yourself"
    ]
Enter fullscreen mode Exit fullscreen mode

To choose a sentence at random we can use elm/Random library to generate a random number from 0 to the length of our sentences list and take a sentence at this index (need to convert the List into an Array to use an index).

Or we can use elm-community/random-extra/Random-Extra#sample/ that will choose an element uniformly at random. We already have Random-Extra so let's use it:

import Random.Extra as Random

-- omitted

chooseSentence : List String -> Cmd Msg
chooseSentence sentences_ =
    Random.sample sentences_
        |> Random.map (Maybe.withDefault "")
        |> Random.generate SentenceChosen
Enter fullscreen mode Exit fullscreen mode

In SentenceChosen handler we can call chooseWords command and continue as it was before.

type Msg
    = WordChanged Int String
    | SentenceChosen String
    | WordsChosen (List ( Int, Int ))
    | NewGame
Enter fullscreen mode Exit fullscreen mode

We need also to introduce a new state Initialising for modeling the game start:

type GameState
    = Initializing
    | Started StateStarted
    | Playing StatePlaying
    | Win StateWin
Enter fullscreen mode Exit fullscreen mode

Now let's implement the SentenceChosen branch of update function:

update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
    case msg of

-- omitted

        SentenceChosen sentenceString ->
            case model of
                Initializing ->
                    let
                        sentence : Words
                        sentence =
                            generateSentence sentenceString
                    in
                    ( Started (StateStarted sentenceString), chooseWords sentence )

                Started _ ->
                    ( model, Cmd.none )

                Playing _ ->
                    ( model, Cmd.none )

                Win _ ->
                    ( model, Cmd.none )
Enter fullscreen mode Exit fullscreen mode

We practically copied the code from the init function. And init itself becomes very short:

init : () -> ( Model, Cmd Msg )
init _ =
    ( Initializing, chooseSentence sentences )
Enter fullscreen mode Exit fullscreen mode

We set the current state of the game to Initializing and invoke chooseSentence command by passing it the list of sentences.

The compiler informs us about non exhaustive cases for GameState in the update function, so let's address that:

update : Msg -> Model -> ( Model, Cmd Msg )
update msg model =
    case msg of
        WordChanged index wordString ->
            let
                model_ =
                    case model of
                        Started { sentence } ->

-- omitted

                        Initializing ->
                            model

                        Started _ ->
                            model

                        Win _ ->
                            model
            in
            ( model_, Cmd.none )

-- omitted

        WordsChosen sortIndexes ->
            let
                model_ =
                    case model of
                        Started { currentSentence } ->
                            let
                                sentence : Words
                                sentence =
                                    generateSentence currentSentence

-- omitted

                            Playing (StatePlaying sentence_)

                        Initializing ->
                            model

                        Playing _ ->
                            model

                        Win _ ->
                            model
            in
            ( model_, Cmd.none )
Enter fullscreen mode Exit fullscreen mode

We made a couple of changes here:

  1. The first branch was Playing, now it's Started and we added a calculation of sentence inside the let block.
  2. The 3rd branch was Started, now it's Playing.

Thus the flow is more correct:

  1. Initialiazing - choose random sentence
  2. Started - choose random words
  3. Playing
  4. Win

Now the view function. Let's add a viewInitilizing view:

viewInitializing : Html msg
viewInitializing =
    p []
        [ text "Initializing..." ]
Enter fullscreen mode Exit fullscreen mode

and call it from view function:

view : Model -> Html Msg
view model =
    let
        currentView =
            case model of
                Initializing ->
                    viewInitializing

                Playing { sentence } ->
                    viewGame sentence

                Started { currentSentence } ->
                    viewStarted currentSentence

                Win { currentSentence } ->
                    viewWin currentSentence
    in
    main_ [ class "section" ]
        [ div [ class "container" ]
            [ viewTitle
            , div [ class "box" ]
                [ currentView ]
            ]
        ]
Enter fullscreen mode Exit fullscreen mode

The game compiles and now each time a new game is starting - it picks a random sentence!

The current progress is saved in the repo under a Tag v7.0 https://github.com/mickeyvip/words-memory-game/tree/v7.0.

Stay tuned for Part 6 where we will add a difficulty level and a countdown for the sentence preview.

Top comments (0)