2 August 2021

Let's Go Bowling!

Learning something new? Try bowling with it...

I was first introduced to the "Bowling Game" kata/exercise in "Agile Principles, Patterns, and Practices in C#" (by Robert and Micah Martin), which I read more than 10 years ago. Chapter 6 of that book is a 40+ page programming episode presented in dialog between two programmers using TDD to implement an algorithm for scoring a bowling game. Robert Martin has used it to teach TDD ever since. I was drawn to the exercise as a way to begin learning new programming languages or new approaches to testing. Here are a few previous renditions:

I'm currently learning Clojure (a functional language descended from Lisp that runs on the JVM) so I took yet another stab at the "Bowling Game" today:

(ns bowling.core-spec
(:require [speclj.core :refer :all]))
(def all-pins 10)
(def frames-per-game 10)
(defn game-over [frame] (= frame frames-per-game))
(defn strike-score [rolls] (+ all-pins (nth rolls 1) (nth rolls 2)))
(defn spare-score [rolls] (+ all-pins (nth rolls 2)))
(defn frame-score [rolls] (+ (first rolls) (second rolls)))
(defn is-strike [rolls] (= all-pins (first rolls)))
(defn is-spare [rolls] (= all-pins (frame-score rolls)))
(defn bowl [rolls]
(loop [rolls rolls
frame 0
score 0]
(if (game-over frame)
score
(cond
(is-strike rolls)
(recur (rest rolls)
(inc frame)
(+ score (strike-score rolls)))
(is-spare rolls)
(recur (rest (rest rolls))
(inc frame)
(+ score (spare-score rolls)))
:else
(recur (rest (rest rolls))
(inc frame)
(+ score (frame-score rolls)))))))
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
(defn roll-many [times pins]
(take times (cycle [pins])))
(describe "gutter game"
(it "scores zero points"
(let [gutter-game (roll-many 20 0)]
(should= 0 (bowl gutter-game)))))
(describe "all ones"
(it "scores one point per throw"
(let [all-ones (roll-many 20 1)]
(should= 20 (bowl all-ones)))))
(describe "spare"
(it "counts the next roll as a bonus"
(let [game (concat [4 6 3 1] (roll-many 16 0))]
(should= (+ 4 6 3 3 1)
(bowl game)))))
(describe "strike"
(it "counts the next two rolls as a bonus"
(let [game (concat [10 3 4 1] (roll-many 15 0))]
(should= (+ 10 3 4 3 4 1)
(bowl game)))))
(describe "perfection"
(it "should score 300"
(let [game (roll-many 12 10)]
(should= 300 (bowl game)))))
view raw bowling.clj hosted with ❤ by GitHub