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;
}
- 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, andspend(amount)takes them away and returnstrue, or returnsfalseand changes nothing when the player doesn't have enough. The count lives in a variable insidemakeTokenCounter, never in a property of the object. - Finish
makeTicketMachine(). It returns one function that adds a win to the machine's total and returns the total so far. - Finish
once(action). It returns a new function that callsactionthe first time, with the arguments it was given, and returns its result. Every later call returns that same first result, andactionnever 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.
spendhas 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). onceremembers two things. Theoncein the lesson keeps one flag. Yours also needs to keep whatactionreturned 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
- Closure
- Factory Function
- Encapsulation
- Default Parameter