5 min read

Week 1 - Bowling

This week, we’re heading down to the bowling alley to help keep score by implementing a score-keeping class.
Week 1 - Bowling
Photo by Daniel Álvasd / Unsplash

Thanks for subscribing, and welcome to the first issue of the revamped All Tests Pass, a weekly programming debugging puzzle newsletter. I’m your puzzlemaster, Brandon Morrison, and I’m really happy that you’ve decided to play along.

All Tests Pass aims to be an entertaining and educational weekly read for readers with some programming knowledge. Many of the puzzles we'll feature will allow us to discuss language features that you may be less familiar with. The original version of this newsletter was entirely focused on JavaScript, but thanks to advances in quick spin-up environments such as GitHub Codespaces and CodeSandbox, we'll likely explore other languages as time goes on.

Every puzzle featured here will be backed by a suite of one or more unit tests that need to pass in order to complete the puzzle. In most cases, you shouldn't need to edit the unit tests in order to complete the puzzles, just read them. If you're unfamiliar with unit testing, MDN has a good (but slightly dated) overview of how automated testing can work in the browser.

Each week, we'll feature the results of any responses I receive, along with a solution that I write. See the rules at the end of this post for more details!

This week’s puzzle: Bowling for errors

This week, we’re heading down to the bowling alley to help keep score by implementing a score-keeping class.

Creating code and tests for tracking bowling scores is a classic coding exercise in the CodeKata community. First proposed by Robert Cecil Martin, author of Clean Code and co-author of the Agile Manifesto, the bowling kata asks developers to calculate the score of a bowling match given a list of throws.

In this week's puzzle, you’ll build a class BowlingScoreTracker and finish the implementation. The class has three methods that need to stay intact in order to be a valid solution.

  • score() - takes an arbitrary number of integers. Each integer is the score of a throw. For example, to register a strike, you could call tracker.score(10), or to register knocking down 5 pins then a gutter ball, you could call tracker.score(5, 0).
  • get totalScore() - a getter that returns the total score of the game based on the previous throws.
  • get currentFrame() - a getter that returns the current frame of the game based on the number of throws.

If you would like to include more robust error handling for your solution, like preventing a client from entering more throws than legally allowed or submitting invalid scores on a single throw, feel free to do so. However, I’m mostly looking for solutions that fulfill the basic tests already listed in the program.

Some advice here, attempting to get all the tests to pass in a single go can be extremely overwhelming, especially if you haven't thought about bowling scoring in years like me. It's always better to break down the problem into smaller pieces and work from there. The tests for this puzzle are organized in a way that should encourage you to attempt to solve them one at a time in sequential order.

Deep Dive: JavaScript Classes

If you've only come to JavaScript as an engineer through frameworks, or haven't had to build larger systems from scratch, you might not be familiar with the nuances of classes in general.

A formal definition of a class is a structure that defines shared aspects for other objects derived from the class. This is not necessarily a helpful definition because of how broad it is, so let's look at a code example!

class Rectangle {
  constructor (width, height) {
    this.width = width;
    this.height = height;
  }

  area() {
    return this.width * this.height;
  }
}

// Usage
let myRectangle = new Rectangle(5, 4);
console.log(myRectangle.area()); // returns 20
    

A definition and usage example of a basic JavaScript class

In this example, Rectangle is defined as a class that accepts two inputs (width and height), and defines a helper function area() to describe the area of the rectangle. Without classes, you might write something like this to get the same functionality.

function RectangleArea(dimensions) {
  // assume dimensions is an array with two elements.
  return dimensions[0] * dimensions[1];
}

let myRectangle = [4, 5];
console.log(RectangleArea(myRectangle)); // returns 20

Calculating the area of a Rectangle using a function instead.

In the end, this gets you the same thing, but the class approach has at least two advantages over the last example...

  • 👥 Bundling: Functionality is bundled with the created object, so you don't have to remember whether or not the first or second item in your rectangle array is the width or height (if you're in a situation where that matters).
  • 📖 Legibility: Your mileage may vary, but I find the class approach easier to read and understand. Given that you'll likely spend way more time reading code than writing it, legibility is key.

You don't need class structures for everything, but classes are great for situations where you might need to make a lot of objects that have similar structures that need helper functions bundled with them. Easy examples that people usually point to are things like JavaScript's built in Date, Map, and Set classes, or creating your own Color classes where you need to transform an internal model of a color to a bunch of different formats.

References:

  • MDN: Using classes - Solid overview of classes in JavaScript, including a great overview of how they're fundamentally weirder in JavaScript than they are in other languages due to the language not making objects their own thing, but just an extension of functions. 🤯

The rules

  1. Every Wednesday I'll send out a link to a short coding challenge. Sometimes your challenge will be to debug a pre-built function. Other times you'll need to flesh out a function or a method with a predefined specification. Sometimes we might just laugh at WTF JS moments. Regardless of the specifics, the puzzles should take roughly 30 minutes or less to solve.
  2. If you decide to play along, take the code from that week's newsletter, and fork it. Your code can live anywhere you feel comfortable. Feel free to use CodeSandbox, GitHub, JSBins, or some other external delivery system. Make your modifications, and respond back to brandon at brandonmorrison dot com . Responses should arrive no later than the following Monday at midnight Eastern. Please don't reply back with your code as an email attachment — I will not read it, no matter how nice the code is inside. 
  3. When I send out a new puzzle (on Wednesdays!), I'll post a solution to the previous week's puzzle. I'll also give shoutouts to those who responded with a correct answer. Privacy is important to me, so I'll only name you in the way that you identify yourself via your public GitHub/JSBin/CodeSandbox account, unless specifically asked to do otherwise. I won't share your email address.
  4. AI/Code Generation: While you're free to use any development tools to create your response, including generative AI coding tools, please consider not using them to solve the puzzles for you. The whole point of these puzzles is to enjoy solving the problem, not to get the answer with as little effort as possible.