0417 (Friday, Week 2)

Q&A

Questions, comments, complaints?

What should you use ports for?

Things that are not available from Elm, e.g. the local storage or web audio APIs.

Where does localStorage come from?

localStorage is a property of the window object that is always available in JavaScript running in the browser. When a variable isn't found in the current scope in JavaScript, it looks to see if it's a property on the window object (which is basically just all the globals). So, localStorage is just shorthand for window.localStorage.

How to pick names for ports?

The More Random Elm notes showed one approach, defining one incoming and one outgoing port per JavaScript functionality to wrap.

Alternatively, you might want to have one incoming port and one outgoing port per module that can handle all the messages you need (rather than one for each different functionality you want to implement). The code on the JavaScript side would then have its own "update" function, analogous to update on the Elm side. You might enjoy this talk on The Importance of Ports.

Exercises

We'll work in randomly assigned Zoom breakout rooms for ~60 minutes. Work individually and discuss questions and issues that come up.

Mouse Game

Did you play typing tutor games as a kid to hone your keyboard skills? Do you regularly test yourself to maintain your form? (After all, we are typists first — h/t Norman Ramsey.) But have you ever played clicking games to hone your mouse skills? If not, here is your chance to address a deficiency in your computer education. Aim for the center of the red target.



Now you'll implement this (really lame) game as an excuse to get a bit more practice with web programming in Elm, to help as you work on HW2 and as you brainstorm project ideas in the coming weeks.

We'll break this down into a series of milestones, filling in the holes in MouseGame.elm and MouseGame.html (look for the TODOs). Try to keep things clicking — compiling and running — as you make incremental progress towards the final version. (A couple bugs in the starter code were fixed and uploaded during class.)

0: Application Architecture

Look at how much of the application design can be discerned just from the type signatures!

type alias Model =
  { windowWidth : Int
  , windowHeight : Int
  , nextTarget : Maybe Point
  , lastClick : Maybe { point : Point, distanceToLastTarget : Float }
  , showCenter : Bool
  , targetRadius : Int
  }

type alias Flags =
  { windowWidth : Int
  , windowHeight : Int
  }

type Msg
  = Resize Int Int
  | MouseDown Point
  | RandomPoint Point
  | ToggleShowCenter
  | SetTargetRadius Int

To start:

% elm init
% elm install mdgriffith/elm-ui
% elm install elm/random
% elm install elm/json
% elm install elm/svg
% elm make MouseGames.elm --output=MouseGames.js

Then open MouseGames.html to run the game.

1: Tracking the Window Size

Edit MouseGames.html to pass the window size as flags from JavaScript to Elm during initialization.

(The rest of the edits, below, will be in MouseGames.elm. Remember to recompile to MouseGames.js every time.)

Subscribe to Browser.Events.onResize to create Resize messages, and handle them in update.

2: Random Target Points

Subscribe to Browser.Events.onMouseDown to create MouseDown messages. For now, have these messages carry dummy Point values; we will worry about the actual click locations later.

Define generatorPoint to generate random Points within the bounds of the current window. These random points will be used as the click targets in the game. (Hint: Random.map*)

In init, generate a random point.

In update, generate a random point every time the mouse is clicked.

When a random point is generated, store it in the Model as the nextTarget.

3: Draw Target

In view, use the elm/svg package to draw the target, a red circle.

4: Computing Distance From Click to Target

Go back to the onMouseDown subscription where MouseDown messages are created, and let's actually track the locations of clicks. Decode the "clientX" and "clientY" fields from the browser JSON event value. (Hint: Decode.map* and Decode.field)

In update for MouseDown, calculate the distance from this click to the previous target, and store this distance in the Model in lastClick. (This field also stores the location of the click, though our view function won't do anything with it.)

In view, display the distance as a string.

5: Button and Slider for Game Options

Use the elm-ui package to define an Input.button for toggling whether or not to show the center of the target (see showCenter and ToggleShowCenter). Fill in maybeWhiteCircle if you haven't already.

Define an Input.slider for adjusting the radius of the target (see targetRadius and SetTargetRadius). Draw the redCircle accordingly.

6: Keep Mousing Around These Libraries!

Before We Meet Again

  • (No lecture notes or videos before Monday's class)
  • Finish your Mouse Game
  • Work on HW2
  • Prepare any topics for Q&A next time