marc walter

Mini game 1 + 2 = 3

2017-10-08 (Last updated on 2018-09-01)

I saw a nice little example game written by Emanuele Feronato where you need to calculate sums and differences using only 1, 2 or 3.

I then decided to program it using elm.

You can use either mouse, fingers, or the keyboard to enter the correct values and try to get as high a score as possible. The highscore is saved to local storage.

If this iframe is not displayed, try the game directly.

The code is available as zip and on ellie.

Interesting sections

Keyboard interactions

To allow players to use the keyboard to play the game, I had to subscribe to key press events and decode the brower's result values with Json.Decode

subscriptions : Model -> Sub Msg
subscriptions model =
    Browser.Events.onKeyDown (Json.Decode.map KeyPress keyDecoder)


keyDecoder : Json.Decode.Decoder String
keyDecoder =
    Json.Decode.field "key" Json.Decode.string

Counting a question as wrong after three seconds

I had to add a subscription to Time.every, I used half-second intervalls. It is important for most cases to limit the subscriptions to the timeframe when they are needed.

In this example, if no question is displayed, the ticks are ignored.

subscriptions : Model -> Sub Msg
subscriptions model =
    case model.view of
        Question _ _ ->
            Time.every 500 Tick

        _ ->
            Sub.none

Countdown animations

I used CSS animations to render the countdown, but the elm runtime was too smart and the animation was only rendered once. After the initial run, elm re-used the existing buttons and thus the animation after adding to the DOM was not playing.

Because of that I changed the type of the list from a simple unordered list to a keyed unordered list. After that new buttons were added to the DOM and the animation is rendered every time.

See the functions view/1 and btn/2.

Other options could have been:

  • Render different classes onto the buttons after user interaction
  • Render the animation using css directly
  • Render the animation using a helper utility like elm-style-animation

Saving the high score

Elm does not yet have direct access to the Web Storage API and so I had to use a port and JavaScript code to persist the highest score.

port module Main exposing (..)

--...

port storeHighscore : Int -> Cmd msg

The highscore is passed from the JavaScript code to the Elm app on start using a flag.

main : Program Flags Model Msg
main =
    Browser.document
        { view = view
        , init = init
        , update = update
        , subscriptions = subscriptions
        }


type alias Flags =
    { highscore : Int
    }


init : Flags -> ( Model, Cmd Msg )
init flags =
    ( { view = Start
      , score = 0
      , highscore = flags.highscore
      , questions = []
      , level = 0
      , countdown = 0
      , counter = 0
      }
    , Cmd.none
    )

Download the source files or view and edit them on ellie.

This post was updated for Elm 0.19, the original code for Elm 0.18 is also available for Download and on ellie.