In that case I think the issue was not that J is hard but that writing elegant code to implement bowling scoring in J is hard.

But a bowling game never needs more than 21 balls, so efficiency is not particular important, and a verbose scorer that works adequately could be:

bowlScore=:3 :0

  runningTotal=. 0 NB. running total

  prevBall=. 0 NB. previous ball's score

  bonusEnds=. '' NB. current list of bonus end indices

  frameStart=. 0 NB. index of start of this frame

  frame=. 1 NB. frame number

  for_i. i.#y do.

    ball=. i{y

    runningTotal=. runningTotal+ball*(frame<:10)++/bonusEnds>:i

    if. frame<:10 do.

      if. frameStart=i do.

        if. 10=ball do.

          bonusEnds=. bonusEnds,frameStart+2

          frameStart=. i+1

          frame=. frame+1



        if. 10=ball+prevBall do. bonusEnds=. bonusEnds,frameStart+2 end.

        frameStart=. i+1

        frame=. frame+1



    prevBall=. ball




score=: bowlScore

assert 300 -: score 12#10 NB. all strikes and strike out

assert 219 -: score 8 2 10 10 10 9 1 10 10 10 9 1 1 9

assert 186 -: score 10 10 10 6 4 10 3 7 10 9 1 8 2 0 2

assert 134 -: score 10 5 1 5 5 10 6 2 0 10 10 10 1 3 6 1

assert 85 -: score 10 1 0 6 3 10 0 6 4 2 10 6 4 1 0 1 3

assert 110 -: score 1 3 7 3 0 0 10 5 5 10 2 8 9 1 0 4 3 0

assert 44 -: score 2 0 7 0 5 0 5 1 2 0 2 0 0 0 2 6 10 1 0

assert 37 -: score 3 6 1 3 2 0 2 3 1 2 2 3 3 0 0 2 0 2 1 1 NB. all open (all blows)

assert 56 -: score 1 4 0 5 4 0 3 6 3 1 0 4 0 5 0 0 1 5 2 8 4

assert 0 -: score 20#0

assert 145 -: score 0 10 1 9 2 8 3 7 4 6 5 5 6 4 7 3 8 2 9 1 0 NB. all spares

assert 200 -: score 16$10 9 1 NB. sandwich game

assert 257 -: score 14$10 10 10 9 1 NB. turkey sandwich

And, this illustrates some of the technical difficulties facing the programmer wanting to implement this elegantly in J.

First, my implementation here has five independent kinds of state.  To implement this tacitly no operation should have more than two independent kinds of state.  So we have to figure out how to organize our data consistently and that can be difficult.

Also, I have three nested levels of “if” statements there.  In principle, “if” statements can be replaced with other operations (such as data selection or multiplication or perhaps something else that can potentially ignore some of the data), and in principle once you have figured out how to structure your data this issue should be relatively easy.  But thinking in terms of “if/then” can focus your attention away from those other kinds of solutions.

So, let’s take Henry’s example which Ron Jerrries reposted at :

NB. Index of each ball that starts a frame
framex =: 10 {. 0 {~^:a:~  _1 _1  ,~  i.@# >:@:+  10&~:
NB. Score for each ball, assuming that ball starts a frame
scoreifframe =: 3   +^:(9<])`+/@|.\   ,&0
NB. Pick the balls that actually start frames & add em up
gamescore =: [: +/ framex { scoreifframe

By the way, I should note that Henry’s implementation was not designed to deal with a game that ended early.  To fix this, you might use:  gamescore@{.~&21

In Henry’s code, framex produces a result which combines both ‘frame’ and ‘frameStart’ from my verbose version. The result itself is a list of the distinct ‘frameStart’ values and the indices are the distinct ‘frame’ values.

Meanwhile ‘runningTotal’ is dealt with by computing the score of each frame separately.  Once you have each frame’s score, it’s simple enough to add them up.

And ‘prevBall’ was always present in the original scores for each ball.  It is only used in one place which means the variable was never needed.  For example, I could have used (i-1){y instead (but of course Henry’s code deals with this issue differently).

Finally, bonusEnds winds up being replaced by the ^:(9<]) in scoreifframe.  That is, in essence, and “if” statement, but it’s testing the core issue relevant to bowling (were all of the pins knocked down in this frame?) instead of just an aspect of that issue (was it a spare? was it a strike?).  And, as an added bonus the two distinct instances of ‘bonusEnds=. bonusEnds,frameStart+2’ both fold together into one instance in Henry’s code.

That said, note that scoreifframe could have been written:

   scoreifframe=: (+ 1&|.) ([ + ] * 9<[) 2&|.

which eliminates the ^: (along with a few other changes).  And, I would be inclined to write framex this way:

   framex =: 10 {. 0 {~^:a:~  _1 _1  ,~  #\ +  10&~:

since that is grammatically simpler and accomplishes the same thing.

Bowling Terms

Frame:  a non-overlapping sequence of balls scores, either one or two balls, with a maximum possible total of 10.  Or, an overlapping sequence of two or three ball scores, which are totalled to determine the score of the frame.  The “overlapping” and “non-overlapping” concepts for frames both start at the same ball, it’s their ending ball that sometimes differs.  A bowling game consists of ten frames and any extra balls that you roll after those ten frames.

Strike: a frame where the first ball’s score was 10.  This is the only kind of frame where you use only one ball (for the non-overlapping concept of frame).

Spare: a frame where the total score of the first two balls was 10, but was not a strike.

In terms of implementation, note that these terms do not characterize orthogonal issues:  You cannot know what a frame is without dealing with whether or not you have a strike.  But you cannot know what a strike is without dealing with whether or not you are at the start of a frame.

Additionally, we have several different concepts of score:  There is the score you get from rolling a ball, which is how many pins you knock down.  There is the score you get from a frame, which is the total of the balls in that frame and sometimes some of the balls after that frame.  There is the score you get from rolling a ball which is a multiple of the number of pins you knock down (that number of pins times 1, 2 or 3) -- but bowlers usually do not talk in terms of this score.  And, of course there is the total score in a game.

In terms of the “overlapping concept of frame”, both strikes and spares are three ball frames.  Open frames (where the first two balls did not knock down all 10 pins) are two ball frames in terms of this “overlapping concept of frame”.  Note that if all frames in a game are open none of them will overlap.  So perhaps “overlapping” is not the best way to characterize that concept.

I think people normally deal with this by learning to score bowling concretely.  And then they use these terms to describe their internal model after they have created it.

Additionally, the number 10 is used both for the number of pins to be knocked down in a frame (but almost never the score of a frame) and the number of frames in a game (except that you can have extra “fill” frames after the first 10 -- they do not have a score of their own, but pins knocked down in them count towards previous frames).  This might lead to bogus identities in the mind of someone trying to understand a description of bowling.  Or, you can use two different names for these two uses of names, instead of numbers, but that can obscure the numbers and/or distract from the rest of the algorithm.   But dealing with specific game examples should help clear up any of those kinds of confusions.