MIND IF I DO A J

Inspired by both The Dude and Ron Jeffries who was working on this problem as an exercise in learning J.

I’m pretty new to J, so please do contact me if you notice a mistake. Thanks to Tracy Harms for spotting a few mistakes.

There is a known bug computing scores with `0 10`

spares, so don’t take this code as gospel.

There are 10 frames, of 2 balls. If a player knocks down N pins in the frame, they are awarded N points. If a player knocks down all the pins in 2 balls (a spare) they are awarded bonus points equivalent to the score of their next ball. If a player knocks down all the pins with the first ball of a frame (a strike) they are awarded bonus points equivalent to the next two balls. Check out Wikipedia if this riveting, gripping scoring structure has you yearning for more.

Let’s store our rolls in an array:

```
rolls=: 4 5 5 5 10 2 3 4 5 4 5 4 5 10 5 5 4 5
```

A strike is listed as a frame with a single ball, there’s no padding in our input.

Our general array-centric approach to this problem will be:

- Find each ball that starts a frame
- Score each ball as if it were the start of a frame
- Take the scored balls that actually start frames and sum them

This is a pretty simple task, and if there were no strikes in the game it would be even simpler! For a game in which each frame has two balls per frame, every other ball starts a frame: balls `0 2 4 6 8 10 12 14 16 18`

. Unfortunately, sometimes the bowlers get strikes, and that throws our ‘every other ball’ technique off.

To start, let’s index all the balls and find all the strikes.

```
i.#rolls NB. indexes of all the rolls
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
10=rolls NB. where we have a strike
0 0 0 0 1 0 0 0 0 0 0 0 0 1 0 0 0 0
```

Using these two arrays we can pad our data, enabling us to take every other item from an array of roll indexes. A simple way to do this is with # (Copy).

```
1+10=rolls NB. 1 of every ball, 2 of every strike ball
1 1 1 1 2 1 1 1 1 1 1 1 1 2 1 1 1 1
(1+10=rolls)#i.#rolls NB. do our copies
0 1 2 3 4 4 5 6 7 8 9 10 11 12 13 13 14 15 16 17
```

Cool, now we have our input padded out and we can take every other item to get the balls that start each frame.

fx=:13 : ‘(2*i.<.2%~(20<.+/1+10=y)){(1+10=y)#i.#y’

```
2*i.2%~+/1+10=rolls NB. even items up to array length
0 2 4 6 8 10 12 14 16 18
2*i.2%~(20<.+/1+10=rolls) NB. even items up to array length MAX of 20
0 2 4 6 8 10 12 14 16 18
(2*i.2%~(20<.+/1+10=rolls)) { (1+10=rolls)#i.#rolls NB. take the indexes we need
0 2 4 5 7 9 11 13 14 16
```

Let’s write a function to do this next time:

```
fx=: 13 : '(2*i.2%~(20<.+/1+10=y)){(1+10=y)#i.#y'
```

Every ball that starts a frame has the score of itself, plus the next ball and maybe plus a third ball. If you roll a `2 3`

, your score for the first ball is 5 (2 balls + 0 bonus balls). If you roll a `10 2 3`

your score for the first ball is 15 (1 strike + 2 bonus balls). If you roll a `7 3 2`

, your score for the first ball is 12 (2 ball spare + 1 bonus ball). The third ball is included only if the sum of the first two balls is 10 or greater (either a strike + 2 balls, or a spare + 1 ball).

So to sum the first ball, add rolls to rolls shifted left by 1 position:

```
1|.!.0 rolls NB. shift and pad with 0
5 5 5 10 2 3 4 5 4 5 4 5 10 5 5 4 5 0
rolls+1|.!.0 rolls NB. add rolls to shifted by 1 rolls
9 10 10 15 12 5 7 9 9 9 9 9 15 15 10 9 9 5
```

Then find the third ball where the sum of the first two is 10 or greater:

```
9<rolls+1|.!.0 rolls NB. where 10 or greater
0 1 1 1 1 0 0 0 0 0 0 0 1 1 1 0 0 0
(2|.!.0 rolls)*9<rolls+1|.!.0 rolls NB. where 10 or greater times shifted by 2
0 5 10 2 3 0 0 0 0 0 0 0 5 5 4 0 0 0
```

And add the first two to the potential third ball:

```
(rolls + 1|.!.0 rolls)+(2|.!.0 rolls)*9<rolls + 1|.!.0 rolls
9 15 20 17 15 5 7 9 9 9 9 9 20 20 14 9 9 5
```

Write a function for this:

```
sf=: 13 : '(y+1|.!.0 y)+(2|.!.0 y)*9<y+1|.!.0 y'
```

Take the frame starting balls from our potential scores, sum them and we have the final score.

```
+/(fx rolls){sf rolls NB. our final score!
119
```

And as a function (throwing in a little bit of tacit programming):

```
fx=: 13 : '(2*i.2%~(20<.+/1+10=y)){(1+10=y)#i.#y'
sf=: 13 : '(y+1|.!.0 y)+(2|.!.0 y)*9<y+1|.!.0 y'
sc=: [:+/fx{sf
sc rolls
119
```

And that’s how you score a bowling game.