Exercise: Arcade Token Counters

Write the Starlight Arcade's token counters, prize-ticket machine and once-only welcome bonus, each keeping its count private in a closure, and let the tests check each one.

Journeyman · JavaScript · Updated

The Starlight Arcade is retiring its paper token cards. Every player needs a counter for their tokens that nobody else's game can touch, every prize-ticket machine needs to keep its own running total, and every new player gets a welcome bonus of 10 free tokens that must work exactly once, however many times they tap the button. Those are three closures for you to write, with no global variables anywhere, and tests check every one as you go.

Get the exercise code

In the Exercise box beside the lesson, choose Get the starter on GitHub to open its GitHub repository. Click Code, then Download ZIP, and unzip it where you keep your practice folders. Or clone it with Git.

The folder holds:

  • arcade.js: the three functions, for you to finish.
  • play.js: runs a visit to the arcade with your functions and prints what happens. Ready to use.
  • arcade.test.js: the tests that check your functions. You don't need to change it.
  • package.json: tells Node.js how to run the code and its tests. Ready to use.

Open the folder in VS Code, and open a terminal there with Terminal > New Terminal.

What to do

Every function is already declared and returns a placeholder for now. The comment above each one says what it should do, with examples:

// 2. makeTicketMachine()
//    Make a prize-ticket machine. It returns a function, addTickets(tickets), that adds the tickets a player
//    won to the machine's total and returns the new total. The total starts at 0.
//    Called with no tickets at all, it adds nothing and returns the total so far: give tickets a default value.
//    const tickets = makeTicketMachine();
//    tickets(12) -> 12
//    tickets(30) -> 42
//    tickets()   -> 42
export function makeTicketMachine() {
  return (tickets) => 0;
}
  1. Finish makeTokenCounter(start). It returns an object with three functions: check() gives the player's tokens, add(amount) adds some and returns the new count, and spend(amount) takes them away and returns true, or returns false and changes nothing when the player doesn't have enough. The count lives in a variable inside makeTokenCounter, never in a property of the object.
  2. Finish makeTicketMachine(). It returns one function that adds a win to the machine's total and returns the total so far.
  3. Finish once(action). It returns a new function that calls action the first time, with the arguments it was given, and returns its result. Every later call returns that same first result, and action never runs again. The arcade uses it for the welcome bonus.

No variables outside the functions: two players' counters, or two ticket machines, must never share a count.

Check your work

Run the tests from the terminal:

node --test

At first all 12 tests fail. That's expected: each function you finish turns its tests green. When a test fails, its message says what it expected, for example With 5 tokens, spend(8) returns false. A test named every counter keeps its own tokens that stays red usually means a count is kept outside the function, where every counter shares it.

To see your counters at work in a visit to the arcade, run:

node play.js

Hints

  • Start from a worked example. The lives counter in Closures at Work: Private State and Factories has the same shape as the token counter: one private variable, and an object of arrow functions that share it.
  • Decide before you change. spend has two outcomes. Check whether the player has enough tokens first, and only then change the count.
  • A default value fills a gap. (tickets = 0) => uses 0 when the caller leaves the argument out (see Function Expressions and Arrow Functions).
  • once remembers two things. The once in the lesson keeps one flag. Yours also needs to keep what action returned the first time, so later calls can hand it back.
  • Passing on arguments you don't know yet. A rest parameter, (...args), collects whatever the caller passes, and spread, action(...args), hands them on. Spread and Rest shows both.

When you're done

All 12 tests are green, and node play.js prints:

Ava arrives with 20 tokens.
Ava claims her welcome bonus: 30 tokens.
Ava tries to claim it again: still 30 tokens.
Ava plays Space Blaster (2 tokens): game on!
Ava has 28 tokens left.
Leo tries the claw machine (8 tokens): not enough tokens
Leo has 5 tokens left.
Prize tickets after Skee-Ball: 35
Prize tickets after Whack-a-Mole: 55
Prize tickets to spend at the prize counter: 55

Now look back at your code. Is there any variable outside your three functions that other code could use to change a player's tokens? If there is, move it inside, where only its own functions can reach it.

So what

You've built counters that guard their own data: nothing outside them can hand out free tokens or erase a player's tickets. The same pattern keeps a shopping cart's total honest, stops a payment button from charging twice, and sits inside many of the libraries you'll use.

Key terms

Related