Score a game of Bowling

25

3

Your task is to sum up and output one player's score in a game of 10-pin bowling after up to 21 rolls.

The rolls are represented as a sequence of integers in your preferred method of input. Each integer corresponds to the number of pins that were knocked down in that roll.

Scoring

After each round the number of pins knocked down in that round is counted into the final score. If a player knocks down all ten pins in the first roll of a round, this is a strike, and the round is over. Otherwise, the round lasts for one more roll. If the second roll of a round knocks down all the remaining pins, this is a spare.

For each strike there is a bonus equal to the sum of pins knocked down in the two next rolls. For each spare there is a bonus equal to the number of pins knocked down in the next roll.

The 10th and final round, the player may be granted extra rolls: In case of a strike, the player gets two more rolls to make up his strike bonus. In case of a spare, the player gets one more roll.

Examples

Input: 4 3 8 2 7 1 10 7 3 0 10 2 2 10 10 5 4
Output: 131

Input: 10 10 9 1 7 3 2 7 10 1 9 10 7 1 10 10 10
Output: 183

Rules

  • You may assumed that the input is valid.
  • As per Mego's comment I have loosened the requirements for the input/output methods to meet our current standard.
  • Answers in languages that are newer than the challenge are allowed
  • Shortest code wins!

daniero

Posted 2012-10-07T23:51:54.980

Reputation: 17 193

Do I remember correctly that bonuses don´t stack? – Titus – 2017-04-22T21:58:21.040

@Titus I'm not exactly sure what you mean, but no, bonuses don't "stack", i.e, for a strike you add the number of pins that are knocked down in the two next rolls, regardless of whether they are strikes or not. So maximum score for one strike is 30 points, and maximum for a whole game is 300. – daniero – 2017-04-23T00:39:29.413

Do distinct command line arguments qualify as space separated integers? – Titus – 2017-04-23T11:07:05.763

1@Titus sure. This is an old post - Today's consensus on acceptable input methods was not established at this point. Actually, I don't see now why today's standard shouldn't apply to this (including function parameters etc), although I'm not a fan of changing the rules of the challenge retroactively. – daniero – 2017-04-23T14:16:37.747

1@daniero The usual advice is that it's acceptable to loosen the rules to fit modern standards, so long as it doesn't drastically change the challenge. – Mego – 2017-08-24T08:41:55.963

@Mego sounds reasonable. I've updated the rules. – daniero – 2017-08-24T17:05:46.567

Do we have to check if the input is valid? Right amount of rolls, right amount of pins per round, etc? – JAD – 2017-08-24T17:07:59.103

@JarkoDubbeldam No, that was never intended. I will add an clarification – daniero – 2017-08-24T17:10:25.260

Answers

6

GolfScript, 50 41 characters

~0]-1%~0{\.9>{+1$3$}{@+.9>3$*}if++}10*p];

Another attempt in GolfScript (run it online).

An explanation of the code follows. The solution utilises the stack nature of the problem (consume rolls one after another) but therefore the input has to be reversed.

~0          # Take the input and evaluate to single numbers on the stack. Add zero.
]-1%~       # Reverse the stack (make array, reverse array, dump array)

0           # Start with a sum of zero
{           # Perform this block 10 times (once for each round)
  \         #   Take the next roll
  .9>{      #   If it is a strike
    +       #     Add the value of the roll to the sum
    1$3$    #     and duplicate top two members of the stack (i.e. next two rolls).
  }{        #   ... else ...
    @+      #     Take the next roll and add with first roll in round.
    .9>     #     Does this sum show a spare?
    3$*     #     Get next roll (potential bonus) and multiply with yes/no.
            #     Since we pushed an additional 0 in the beginning 
            #     there is a spare roll even for the last round.
  }if       #   endif
  ++        #   Add top three stack entries together
            #   (i.e. sum+2 bonus rolls for strike, sum+rolls+bonus else)
}10*        # Loop ten times

p];         # Sum is top of stack. Print sum and discard any leftover rolls.

Previous version:

~].1>.1>]zip{((.10<{@(0=@+@1>1$9><}*@}10*;]{+}.@**

Howard

Posted 2012-10-07T23:51:54.980

Reputation: 23 109

5

Python, 116 110 105 103 100 99 characters

z=map(int,raw_input().split())
s=0
exec('s+=sum(z[:2+(z[0]+z[1]>9)]);z=z[2-(z[0]>9):];'*10)

Spending 30 characters on input is irksome. Suggestions welcome.

Much thanks to Howard for improvements.

Steven Rumbalski

Posted 2012-10-07T23:51:54.980

Reputation: 1 353

You may replace 1+(z[i]!=10) with 2-(z[i]>9) to save one char. – Howard – 2012-10-10T20:44:46.033

@Howard: Excellent suggestion. I have incorporated it into my answer. It saved two characters. – Steven Rumbalski – 2012-10-10T20:51:58.367

And two more if you remove i completely (set to 0) and instead of i+=... use z=z[2-(z[0]>9)::]; – Howard – 2012-10-10T20:53:53.900

@Howard: Thanks again. Saved three characters. – Steven Rumbalski – 2012-10-10T21:02:43.827

Standards for I/O are generally more flexible now so z=input() should be fine (effectively taking a string representation of a list of ints and evaling it). However full programs should output somewhere (I think this was also the case back then too). As such I believe this may be changed to this 78 byte program

– Jonathan Allan – 2017-09-27T17:54:36.303

4

R, 101 bytes

I am not sure why this challenge was bumped, but I like it, so I'll late answer anyways.

f=function(x,s=0,c=10)`if`(c&length(x),f(x[-(0:(x[1]!=10)+1)],sum(x[1:(2+(sum(x[1:2])>9))])+s,c-1),s)

Try it online!

Ungolfed:

f <- function(throws, score = 0, count = 10){
  if(count != 0 & length(throws) != 0){
    IsStrike <- throws[1] == 10
    IsStrikeOrSpare <- sum(throws[1:2]) >= 10
    f(throws[-c(1, 2 * !IsStrike)],
      score + sum(throws[c(1:(2 + IsStrikeOrSpare))]),
      count - 1)
  } else {
    return(score)
  }
}

Recursive function. Takes x as input, which holds the scores. Initialises the scores and counts the amount of rounds thrown.

The if statement checks if 10 rounds are thrown, or if x is empty. If that is the case, the score is returned. Else the function will call itself as follows:

It removes the throws from x, by checking if it is a strike or not. If so, the first entry is removed, else the first two. (S=x[1]!=10) checks for strikes. We remove (-) index 0:S, where S is 1 if it is a strike, and 0 if not. And then we add one: -(0:(x[1]!=10)+1). We pass the shortened x to the next call.

As for the score, this is found by taking x[1:2] if it is a regular turn, and x[1:3] if it is a strike or a spare. We check if sum(x[1:2]) is bigger or equal than 10. If it is a strike, obviously this is the case. If it is a spare, then this works as well. So if this is TRUE, we add x[3] to the sum. This is then added to s.

JAD

Posted 2012-10-07T23:51:54.980

Reputation: 2 898

1

PHP, 82 bytes

for($a=$argv;$r++<10;$i+=$p<10)$s+=(9<$q=$a[++$i+1]+$p=$a[$i])*$a[$i+2]+$q;echo$s;

takes input from command line arguments; run with -nr or test it online.

breakdown

for($a=$argv;       # import arguments
    $r++<10;        # loop through rounds
    $i+=$p<10)          # 6. if no strike, increment throw count again
    $s+=(9<
        $q=$a[++$i+1]+  # 1. increment throw count  2. $q=second throw plus
        $p=$a[$i]       # 3. $p=first throw
        )*$a[$i+2]      # 4. if $q>9 (strike or spare), add third throw to sum
    +$q;                # 5. add first and second throw to sum
echo$s;             # print sum

Titus

Posted 2012-10-07T23:51:54.980

Reputation: 13 814

1

Perl 5, 65 + 2 = 67 bytes

Requires -ap flags

$\+=(($"=shift@F)<10?$"+=shift@F:10+$F[1])+$F[0]*(9<$")for 0..9}{

Try it online!

Xcali

Posted 2012-10-07T23:51:54.980

Reputation: 7 671

1

Jelly,  36  35 bytes

+\µi©⁵+Ị$ḂḤị;®×Ị¤¡-
;0Ç3ƤFṣ-m€2Fḣ⁵S

A monadic link accepting a list of integers and returning an integer.

Try it online!

How?

Calculates the score of each overlapping run of three bowls as if it were one that started at the beginning of a frame and optionally appends a strike identifier (-1), flattens this resulting list, splits it at the strike identifiers, then discards every second result from each chunk (removing the scores of those runs that did not really start with the start of a frame).

To cater for the final frame a zero is first appended to the input (to allow the 3-wise slicing to allow a frame to start on what was the penultimate bowl) and the resulting scores are truncated to the first ten (to remove the now possible bogus 11th frame) prior to summing them up.

+\µi©⁵+Ị$ḂḤị;®×Ị¤¡- - Link 1, threeBowlEvaluate: list, bowlScores
                    -               e.g. [0,4,6]   [9,1,10]   [0,4,4]  [10,7,9]
 \                  - cumulative reduce with:
+                   -   addition         [0,4,10]  [9,10,20]  [0,4,8]  [10,17,26]
  µ                 - monadic chain separation, call that "left"
     ⁵              - literal ten        10        10         10       10
   i                - first index in left 3         2 (spare)  0        1 (strike)
    ©               - (copy to register for later reuse)
        $           - last two links as a monad (f(x)):
       Ị            -   abs(x) <= 1       0         0          1        1
      +             -   add x             3         2          1        2
         Ḃ          - modulo by 2         1         0          1        0
          Ḥ         - double              2         0          2        0
           ị        - index into left (both 1-indexed and modular)
                    -            ...      4        20          4       26
                  - - literal -1         -1        -1         -1       -1
                 ¡  - repeat:
            ;       - ...action: concatenate
                ¤   - ...number of times: nilad followed by link(s) as a nilad:
             ®      -   z from register   3         2          0        1
               Ị    -   abs(z) <= 1       0         0          1        1
              ×     -   multiply          0         0          0        1 (strike)
                    - ...yielding:        4         20         4        [26,-1]

;0Ç3ƤFṣ-m€2Fḣ⁵S - Main link: list bowlValues
                -                    e.g. [4,3,8,2,7,1,10,7,3,0,10,2,2,10,10,5,4]
 0              - literal zero            0
;               - concatenate             [4,3,8,2,7,1,10,7,3,0,10,2,2,10,10,5,4,0]
   3Ƥ           - for infixes of length 3:
  Ç             -   last link (1) as a monad
                -                         [7,11,17,9,8,11,[20,-1],10,3,12,[14,-1],4,12,[25,-1],[19,-1],9]
     F          - flatten                 [7,11,17,9,8,11,20,-1,10,3,12,14,-1,4,12,25,-1,19,-1,9]
       -        - literal -1              -1
      ṣ         - split at                [[7,11,17,9,8,11,20],[10,3,12,14],[4,12,25],[19],[9]]
          2     - literal two             2
        m€      - modulo slice for €ach   [[7,17,8,20],[10,12],[4,25],[19],[9]]
           F    - flatten                 [7,17,8,20,10,12,4,25,19,9]
             ⁵  - literal ten             10
            ḣ   - head to index           [7,17,8,20,10,12,4,25,19,9] (no effect this time)
              S - sum                     131

Jonathan Allan

Posted 2012-10-07T23:51:54.980

Reputation: 67 804

1

JavaScript (ES6),  79 78 72 69  68 bytes

f=([x,y,...r],n=9)=>x+y+~~r[!n|x+y>9?0:f]+(n&&f(x>9?[y,...r]:r,n-1))

Try it online!

Commented

f = (               // f is a recursive function taking:
  [x,               //   x = result of the current roll
      y,            //   y = result of the next roll
         ...r],     //   r[] = remaining results
  n = 9             //   n = remaining number of rounds
) =>                //
  x + y +           // add x + y to the score
  ~~r[              //
    !n |            // if this is the last round
    x + y > 9 ?     // or this is either a spare or a strike:
      0             //   add the result of the next roll to the score
    :               // else:
      f             //   use a dummy index so that nothing is added
  ] + (             //
    n &&            // if n is not equal to 0:
      f(            //   add the result of a recursive call:
        x > 9 ?     //     if this is a strike:
          [y, ...r] //       re-insert y into r[]
        :           //     else:
          r,        //       pass r[] unchanged
        n - 1       //     decrement n
      )             //   end of recursive call
  )                 //

Arnauld

Posted 2012-10-07T23:51:54.980

Reputation: 111 334

Is the double-bitwise-NOT needed or is it not a bitwise NOT? – S.S. Anne – 2020-02-09T20:21:35.277

1@S.S.Anne It's just there to turn undefined into $0$ if there's nothing at the computed index. – Arnauld – 2020-02-09T20:45:23.100

1

CoffeeScript (234 215 170)

z=(a)->b=(Number i for i in a.split(' ').reverse());t=0;(r=b.pop();l=b.length;if(9<r)then(t+=r;t+=b[l-1]+b[l-2];)else(f=r+b.pop();t+=f;(t+=b[l-2])if 9<f))for i in[0..9];t

EDIT: A hefty re-write, shamelessly plagiarising Howard's great stack-based approach. I'm confident more can be stripped out for accessing the last element of an array without destroying it...

Johno

Posted 2012-10-07T23:51:54.980

Reputation: 301

1

Ruby, 252 bytes

Accepts input into an array add all elements first, then searches for spare and strike bonus

s,p,t,r=0,0,1,1
o=ARGV
o.each_with_index do |m,n|
y=m.to_i
s+=y
if r<10
p+=y
if p==10&&t==1
r,p=(r+1),0
s+=o[n+1].to_i+o[n+2].to_i
elsif p<10&&t==1
t=2
elsif p<10&&t==2
t,p,r=1,0,(r+1)
elsif p==10&&t==2
t,p,r=1,0,(r+1)
s+=o[n+1].to_i
end end end
puts s

beginnerProg

Posted 2012-10-07T23:51:54.980

Reputation: 51

0

Perl, 140?

First attempt:

#!/usr/bin/perl
# usage: ./bowling.pl [list of scores]

@A=@ARGV;{last if(9<$n++);$a=shift@A;$S+=$a;($S+=$A[0]+$A[1])&redo if($a==10);$b=shift@A;$S+=$b;($S+=$A[0])&redo if(10==$a+$b);redo}print$S

Sadly, there are certain cases where it fails. I will come and redo it later.

o_o

Posted 2012-10-07T23:51:54.980

Reputation: 251