In the last two articles, we solved two exercises, one based on comparisons, and the other one on closures. Our third and final exercise from the book will be on the topic of prototypes.
We picked these exercises up from Kyle Simpson’s You Don’t Know JS book series, and as I already mentioned those books might be the ultimate resource for mastering JavaScript, and I highly recommend everyone to go through all of the books in the series.
Prototypes Exercise
Define a slot machine with three reels that can individually spin()
, and then display()
the current contents of all the reels.
The basic behavior of a single reel is defined in the reel
object below. But the slot machine needs individual reels—objects that delegate to reel
, and which each have a position
property.
A reel only knows how to display()
its current slot symbol, but a slot machine typically shows three symbols per reel: the current slot (position
), one slot above (position - 1
), and one slot below (position + 1
). So displaying the slot machine should end up displaying a 3 x 3 grid of slot symbols.
function randMax(max) { return Math.trunc(1E9 * Math.random()) % max; } var reel = { symbols: [ "♠", "♥", "♦", "♣", "☺", "★", "☾", "☀" ], spin() { if (this.position == null) { this.position = randMax( this.symbols.length - 1 ); } this.position = ( this.position + 100 + randMax(100) ) % this.symbols.length; }, display() { if (this.position == null) { this.position = randMax( this.symbols.length - 1 ); } return this.symbols[this.position]; } }; var slotMachine = { reels: [ // this slot machine needs 3 separate reels // hint: Object.create(..) ], spin() { this.reels.forEach(function spinReel(reel){ reel.spin(); }); }, display() { // TODO } }; slotMachine.spin(); slotMachine.display(); // ☾ | ☀ | ★ // ☀ | ♠ | ☾ // ♠ | ♥ | ☀ slotMachine.spin(); slotMachine.display(); // ♦ | ♠ | ♣ // ♣ | ♥ | ☺ // ☺ | ♦ | ★
Hints:
- Use the
%
modulo operator for wrappingposition
as you access symbols circularly around a reel. - Use
Object.create(..)
to create an object and prototype-link it to another object. Once linked, delegation allows the objects to sharethis
context during method invocation. - Instead of modifying the reel object directly to show each of the three positions, you can use another temporary object (
Object.create(..)
again) with its ownposition
, to delegate from.
Analysis of the Problem
We need to fill in the display()
method and the contents of the reels
array.
The reels
array needs three individual reels. We can create them by using Object.create(...)
. This method will create a new object using the parameter as the prototype for that new object. The return value of the method is the newly created object.
We will create three reels using Object.create(...)
inside the reels
array. We will then have access to three elements, each of which is actually an instance of the reel
object, that has the symbols
array, the spin()
and display()
methods!
reels: [ Object.create(reel), Object.create(reel), Object.create(reel) ],
Now we need some logic for displaying the reels. Below we create a reels array that will store our slot machine state. We iterate through a for loop three times, and then we create the line by mapping the individual reels.
Inside the mapping function, we create a slot and then we calculate the position of the slot by adding the current length, the position subtracted by one and the current line position. The result of this will be modulo by the length of the symbols, in order to take into account the circular notion of the slot machine.
Then we call the display function.
display() { let lines = []; for (let linePos = -1; linePos <= 1; linePos++) { let line = this.reels.map((reel) => { let slot = Object.create(reel); slot.position = (reel.symbols.length + reel.position + linePos) % reel.symbols.length; return reel.display.call(slot); }); lines.push(line.join(" | ")); } return lines.join("\n"); }, };
Our final solution is in the snippet below;
function randMax(max) { return Math.trunc(1e9 * Math.random()) % max; } var reel = { symbols: ["♠", "♥", "♦", "♣", "☺", "★", "☾", "☀"], spin() { if (this.position == null) { this.position = randMax(this.symbols.length - 1); } this.position = (this.position + 100 + randMax(100)) % this.symbols.length; }, display() { if (this.position == null) { this.position = randMax(this.symbols.length - 1); } return this.symbols[this.position]; }, }; var slotMachine = { reels: [Object.create(reel), Object.create(reel), Object.create(reel)], spin() { this.reels.forEach(function spinReel(reel) { reel.spin(); }); }, display() { let lines = []; for (let linePos = -1; linePos <= 1; linePos++) { let line = this.reels.map((reel) => { let slot = Object.create(reel); slot.position = (reel.symbols.length + reel.position + linePos) % reel.symbols.length; return reel.display.call(slot); }); lines.push(line.join(" | ")); } return lines.join("\n"); }, }; slotMachine.spin(); slotMachine.display(); // ☾ | ☀ | ★ // ☀ | ♠ | ☾ // ♠ | ♥ | ☀
Final Words
Hope you had fun solving this exercise with me!
For more articles please click below, or check the blog.
- Largest Palindrome Product
- Largest Prime Factor
- Even Fibonacci Numbers
- Multiples of 3 or 5
- How to find the missing number in a given integer array of 1 to 100?
- Understanding Javascript prototypes
- Difference between Promise.all() and Promise.race()
- JavaScript generators
- Using this and arguments
- Arrow functions in JavaScript