Skip to content

Latest commit

 

History

History
64 lines (47 loc) · 2.23 KB

README.md

File metadata and controls

64 lines (47 loc) · 2.23 KB

tenpin

A few functions to score a bowling game

Usage

lein test

Approach

Our initial naive approach was to store the entire score card as the state. However, this quickly becomes complicated due the the application of bonus points (i.e. rolls count towards the current frame but must also be applied to previous frames where applicable). This had a state that looked like

; After 1 roll
{:frames [{:rolls [10] :bonus [] :pending 2}]
 :rolls [10]}

; After 2 rolls
{:frames [{:rolls [10] :bonus [1] :pending 1}
          {:rolls [1]}]
:rolls [10 1]}

After quickly scratching that, we decided to use a more functional approach of using a minimal state, and deriving everything that we could. Thus we stored just the rolls and the bonuses to be applied. Rather than in the above where bonus looks forward as a decrementing counter that is tied to the next roll, in the new approach, we simply put a place holder (i.e. the index of the roll that supplies the bonus). This means that rather than calculating bonus on each roll, we can do a single pass during scoring to inject the actual bonus values.

Thus the new state looks like:

; Initial
{:rolls   []
 :bonuses {}}

; After strike
{:rolls   [10]
 :bonuses {0 [1 2]}}
 
; After another roll of 7
{:rolls   [10 7]
 :bonuses {0 [1 2]}}

Our API is a classic reducer function of state -> roll -> next-state. We use rolls as the action rather than frames since it is a roll-centric game, that just happens to be scored by frames. i.e. it is unnatural to hack in bonus rolls into frames.

We use rolls->frames to generate frames from the rolls, which is necessary for calculating e.g.

  • whether the game is complete
  • whether to apply bonus for this roll (e.g. you would not have bonus for a strike after the 10th frame)

Finally for scoring, we initially had separate functions to calculate total and frame scores individually, but it is far nicer to generate score card as data, with the required elements just being looked up.

Enhancements

Going forward, we would like to use things like clojure.spec to firm down on the data at the boundaries of the system. There are currently some assumptions (e.g. not checking the total of a frame <= 10) which are intentionally omitted.