Matt Stewart
Easiest to run via leiningen or load core.clj
in the repl.
the scorecard is represented by a 2d vector containing 12 frames [[-1 -1] [-1 -1] ...]
.
The raw data structure uses 12 frames as it simplifies handling the 2 fill frames should a spare or strike occur on the 10th frame.
Frame type representations:
[10 -1]
both rolls must be 0 - 10 inclusive and sum to 10
[7 3]
both rolls must be 0 - 10 inclusive and sum to less than 10
[1 3]
No rolls yet for this frame
[-1 -1]
If a spare is rolled on the 10th frame, 1 more roll needs to be stored in the 11th frame. The vector must have its second roll fixed at zero, eg:
[5 0]
transforms the raw scorecard into a calculated result. The game is complete when the total is not -1 as it implies every frame has sucessfully resolved its computed score. Should the game be in a pending state, all frames that can resolve their scores will do so, however the total will still be -1 until all frames have resolved scores.
where CalculatedScorecardResult
is a map -> { :calculated-scorecard [{:score Int :symbol String :rolls [Int]}] :total Int }
Example:
(calculate-scorecard [[10 -1] [10 -1] [10 -1] [10 -1] [10 -1] [10 -1] [10 -1] [10 -1] [10 -1] [10 -1] [10 -1] [10 -1]])
=>
{
:calculated-scorecard [
{:score 30, :symbol "X", :rolls [10 10 10]}
{:score 30, :symbol "X", :rolls [10 10 10]}
{:score 30, :symbol "X", :rolls [10 10 10]}
{:score 30, :symbol "X", :rolls [10 10 10]}
{:score 30, :symbol "X", :rolls [10 10 10]}
{:score 30, :symbol "X", :rolls [10 10 10]}
{:score 30, :symbol "X", :rolls [10 10 10]}
{:score 30, :symbol "X", :rolls [10 10 10]}
{:score 30, :symbol "X", :rolls [10 10 10]}
{:score 30, :symbol "X X X", :rolls [10 10 10]}
],
:total 300
}
Simply just returns the result of total != -1
Updates a frames score by taking in the scorecard, index position of the frame and the new frame and returns back the updated scorecard.
For example, to update the scorecards first frame with a strike
(score-frame [[-1 -1] [-1 -1] [-1 -1] [-1 -1] [-1 -1] [-1 -1] [-1 -1] [-1 -1] [-1 -1] [-1 -1] [-1 -1] [-1 -1]] 0 [10 -1])
=>
[[10 -1] [-1 -1] [-1 -1] [-1 -1] [-1 -1] [-1 -1] [-1 -1] [-1 -1] [-1 -1] [-1 -1] [-1 -1] [-1 -1]]
clojure spec is used to validate at the edges only. This simplifies internal helper functions as they can
guarantee the scoreboard vector is in a valid state. Its assumed the create-scoreboard
is
used to create the initial board, then the score-frame
receives most of the validation to ensure the scorecard
remains valid throughout the games lifetime.