Bowling challenge
The final challenge for week five was to create a bowling scorecard program. Conceptually, this was the most difficult project so far and it required a lot of research and planning.
The requirements were:
- Count and sum the scores of a bowling game for one player.
- Create it as a terminal app using Ruby.
- No database backend needed.
Why is bowling scoring so tricky?
I'd never thought about how bowling scores are calculated, but when I saw this challenge I assumed it must be fairly straightforward. It isn't. A lot of the complexity comes from the fact that when you get a strike or a spare in a frame, the score for that frame isn't calculated until the next one or two balls have been bowled. That means you're often going back to previous frames and updating their scores.
To add extra complexity, the first nine frames basically work the same way (two bowls per frame, or one if you get a strike), but the tenth frame can have up to three bowls.
I started by watching some YouTube videos about bowling scoring. (As a result, my feed is now full of bowling content. Gotta love the algorithm!) I then diagrammed the first couple of frames of a game to help me understand the logic.
Designing the application
I initially thought I'd use an array of hashes to store the ball, frame and bonus scores. I started sketching that out, but it was clear that it would quickly get difficult to manage. It also didn't feel right to separate the data from the methods like that. The essential unit of the game is a frame, so it felt more logical to take a properly object-oriented approach and create instances for each frame.
I designed a class system that separated the various elements into:
Frame
which is a model class and holds the score for each frame, along with methods like.strike?
that can be called by the other classes to determine if a particular frame contains a strike.Application
which creates 10Frame
s when the application is first run, and contains arun
loop which calls theGameplay
methods.Gameplay
contains various methods to prompt for and process terminal IO, and determine whether the game is over.Scorecard
has various methods to calculate frame and game scores, and apply frame bonuses after strikes and spares.
Final product
I tried a few different ways of displaying the output, but I wasn't happy with any of them. There's surpsingly high information density in a traditional bowling scorecard, and it's hard to replicate that in a different format. So I decided to figure out how to draw a proper scorecard template in the terminal.
I started by drawing a single frame in ASCIIFlow, then broke that down into template methods. There's some slightly clunky code in there, and if I get a chance to go back and refactor, I'd look at making better use of map
to re-sort the array elements into the correct order. Overall though, I was surprised and pleased that I managed to get the formatting to work.
1. Taking the time to properly learn how bowling scoring works before I started planning the application paid dividends. If I'd started coding before I understood the logic, I know I'd have ended up needing to scrap the code and start again.
2. This is one of the first projects I've done where I've truly understood the benefits of object-oriented programming. Being able to call methods like
.frame_score
from within the frame is such a neat and efficient way of approaching things.- A couple of the methods are in the wrong classes.
.final_score
needs to move from Gameplay
into ScoreCard
, for example.- Drawing the scorecard could be refactored to make it simpler.
- Everything is unit tested, but I need to implement more integration testing (particularly for terminal IO).