This is part 1 of the series "Writing A Word Memory Game In Elm", find:
- Part 1: Setting Up an Elm Application With Parcel
- Part 2: Modeling And Building a Basic Word Memory Game
- Part 3: Rethinking the Model
- Part 4: Spicing Things Up With Randomness
- Part 5 - More Randomness And More Game
Preface
One of the most effective ways to learn is by doing. I was looking for a project to improve my Elm skills and a friend gave me an excellent idea -- to write a word memory game.
Note: this series of posts reflects my personal learning experience, any constructive feedback is welcome.
The Memory Game
There is a sentence which the player can read. Several words are then taken out and the player must reconstruct the sentence from memory.
For example, the sentence is:
If you want something done right, you have to do it yourself.
And with several words removed:
If --- want something ---- right, you ---- to do it yourself."
The game is pretty simple, but there are many ways it can be be extended:
- The missing words can be presented to the user in-place (as a dropdown), or near the sentence as a list
- Each time the player guesses the correct word, they can be informed straight away or find out the result only when all the words are used
- At the beginning of the game the sentence is visible to the player for several seconds only
- Etc
Starting the Project
To make the development cycle easier I chose to use Parcel bundler, which supports Elm out of the box since v1.10.0
and HMR
since v1.11.0
.
Let's create a project folder, for example word-memory-game
, cd
into it and initialise the NPM project:
$ npm init -y
The -y
option will accept all defaults and create package.json
file.
Let's install the Parcel bundler locally (it can also be installed globally):
$ npm i -D parcel-bundler
Following Parcel's Getting Started let's add index.html
:
<!doctype html>
<html>
<body>
<script src="./index.js"></script>
</body>
</html>
and index.js
:
console.log('Words Memory Game started');
Also let's add the following lines to the scripts
section of package.json
:
"scripts": {
"dev": "parcel index.html",
"build": "parcel build index.html"
}
In order to start the development server let's run:
$ npm run dev
and navigate to http://localhost:1234
In order to host our Elm application in a page let's add
<div id="app"></div>
to index.html
:
<!-- index.html -->
<!doctype html>
<html>
<body>
<div id="app"></div>
<script src="./index.js"></script>
</body>
</html>
Let's create src
folder and add Main.elm
to it:
module Main exposing (main)
import Html
main =
Html.text "Words Memory Game"
and change index.js
to:
import { Elm } from "./src/Main.elm";
Elm.Main.init({
node: document.getElementById("app")
});
After we save all the files, Parcel will automatically initialise an Elm project (including creating elm.json
file), install needed dependencies and reload. We can also reload the page manually to clear all the previous errors from the console.
Now let's make our Elm skeleton a real Elm application by adding Msg
, Model
, view
and update
, as well as updating main
:
module Main exposing (main)
import Browser
import Html exposing (Html)
type alias Model =
{ sentence : String
}
initialModel : Model
initialModel =
{ sentence = "The pen is mightier than the sword" }
type Msg
= NoOp
update : Msg -> Model -> Model
update msg model =
case msg of
NoOp ->
model
view : Model -> Html msg
view model =
Html.text model.sentence
main : Program () Model Msg
main =
Browser.sandbox
{ init = initialModel
, update = update
, view = view
}
Great! We have our basic "Hello, World!".
I personally like my applications to look nice, so let's add the popular Bulma CSS framework in our index.html
(or we can install it locally and import it) and a meta
tag that Bulma recommends:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Words Memory Game</title>
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.7.2/css/bulma.min.css"
/>
</head>
<body>
<div id="app" class="section"></div>
<script src="./index.js"></script>
</body>
</html>
Now in Main.elm
let's update our view
function to show a title and a sentence in a nice box:
view : Model -> Html msg
view model =
main_ [ class "section" ]
[ div [ class "container" ]
[ viewTitle
, div [ class "box" ]
[ p
[ class "has-text-centered" ]
[ text model.sentence ]
]
]
]
viewTitle : Html msg
viewTitle =
h1 [ class "title has-text-centered" ]
[ text "Words Memory Game" ]
Don't forget to import Html
and Html.Attributes
functions:
import Html exposing (Html, div, h1, main_, p, text)
import Html.Attributes exposing (class)
Now our application looks nicer:
Another touch that I want to add to our game is the ability to show pretty icons. Therefore let's add a link to Font Awesome web font in our index.html
:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Words Memory Game</title>
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/bulma/0.7.2/css/bulma.min.css"
/>
<link
rel="stylesheet"
href="https://use.fontawesome.com/releases/v5.7.1/css/regular.css"
integrity="sha384-IG162Tfx2WTn//TRUi9ahZHsz47lNKzYOp0b6Vv8qltVlPkub2yj9TVwzNck6GEF"
crossorigin="anonymous"
/>
<link
rel="stylesheet"
href="https://use.fontawesome.com/releases/v5.7.1/css/fontawesome.css"
integrity="sha384-4aon80D8rXCGx9ayDt85LbyUHeMWd3UiBaWliBlJ53yzm9hqN21A+o1pqoyK04h+"
crossorigin="anonymous"
/>
</head>
<body>
<div id="app" class="section"></div>
<script src="./index.js"></script>
</body>
</html>
Now we are all set up for the next steps, read about them in the upcoming posts.
The current progress is saved in the repo under a Tag v0.0
: https://github.com/mickeyvip/words-memory-game/tree/v0.0.
Top comments (9)
I understand that you are in the process of learning Elm. You do not need to use Parcel, there is a command "elm init" that sets up an initial directory. Also there is various other options, like elm-app that creates a service worker. You should also be familiar with elm-live, which is nice when trying things out.
An alternative to HTML and CSS is elm-ui, but if you have a CSS framework that you know, of course start there.
Hi Per.
Thank you for your comment.
Yes I am learning Elm and I am aware of the
elm init
,create-elm-app
(if this is what you meant byelm-app
) andelm-live
.Here I wanted a very lightweight setup that will give me a nice development experience.
elm init
would mean manually compiling each time.elm-live
- manually refreshing the page.create-elm-app
- setting up a lot of things that I don't need.So I decided to go for
parcel-bundler
- zero config, automatically setting up the Elm project and giving me a development server + hot module reload.Regarding
elm-ui
- I am planning to dig into it deeper, but for this small project I wanted to be focused on more Elm core things. I am thinking of eventually converting this project to useelm-ui
, but not just now.I went with the parcel as well. But I have an empty page. If I have an error in the compiler the parcel tells me about it. So that means it's working. But the page is still empty.
A note on elm-live. You don't have to manually update the page it is automatic.
Hi, Anton.
Sorry to hear the Parcel didn't work for you.
It is hard to understand what is wrong without seeing your code. Can you share it so I can take a look?
Also, you're right about the elm-live. My mistake.
Actually, it works. I figured what the problem was. The problem was with my Russian government blocking the Elm. Parcel wanted to download some packages. I turned on vpn and it worked.
I knew there was something strange about it. Because the code didn't have a mistake. And when I for example purposefully introduce a mistake the parcel/elm would show it to me in the browser.
It's great you figured that out!
It's working with elm-live though:
Unfortunately the link seems to be broken.
Seems to have been a dev.to issue.
The screenshot basically shows that it runs with elm-live.