Beefy Boxes and Bandwidth Generously Provided by pair Networks
No such thing as a small change
 
PerlMonks  

(Golf) Let's go bowling

by virtualsue (Vicar)
on Aug 08, 2001 at 19:09 UTC ( [id://103079]=perlmeditation: print w/replies, xml ) Need Help??

[Update: The winners have been posted on my home node, along with a simple demo program which contains their code.]

Tenpin bowling, to be precise (there are many bowling games with a variety of scoring rules). This was partly inspired by a chatterbox discussion many weeks ago. For those who have never had the pleasure, tenpin bowling is a game which consists of 10 frames, in each of which you are given two tries to knock down 10 pins.* If you succeed on the first try, it's a strike. If you succeed with the second try, it's a spare, otherwise you blew it. :)

Scoring per frame works as follows:
Strike ('X'): 10 + the # of pins knocked down on the next 2 balls
Spare ('/'): 10 + the # of pins knocked down on the next ball
Else: The sum of the pins knocked down in two tries

The maximum score in any one frame is 30. The minimum is 0. A game consisting of all strikes (AKA a 'perfect game') is 300. The 10th frame is "special". If a strike is thrown on the first ball, then the player gets two more throws (to satisfy the scoring rule above). Likewise, if a spare is achieved on the second ball of the 10th frame, then the player gets one more throw. This means that a game may consist of as few as 11 balls thrown (9 strikes in first 9 frames, then no strike or spare in the 10th) to a maximum of 21. So, given these rules, construct some code to score a game.

You can assume that you will get an array containing the pinfall for 10 frames, e.g.

my @b = ('5', '/', '6', '3', 'X', 'X', '7', '0', '4', '3', 'X', '4', '/', '8', '1', '3', '/', '6');
(Update: The above data represents a game of 146)

Or, you can handle getting the pinfall data any way you want. I don't care, so long as the code calculates scores correctly.

Thanks to chipmunk and tilly for corrections/suggestions


*It also consists of nasty rented shoes and ugly shirts, the horror of which is only partially offset by the availability of bad beer

Replies are listed 'Best First'.
Re: (Golf) Let's go bowling
by dragonchild (Archbishop) on Aug 08, 2001 at 21:51 UTC
    Coming in at a whopping 223(!) characters, a first attempt:
    for(0..$#_){if($_[$_]=~/X/){$t+=10+($_[$_+1]=~/X/?10:$_[$_+1])+($_[$_+ +2]=~/X/?10:($_[$_+2]=~/\//?10-$_[$_+1]:$_[$_+2]))}if($_[$_+1]=~/\//){ +$t+=10+($_[$_+2]=~/X/?10:$_[$_+2]);splice@_,$_+1,1;last if!defined$_[ +$_+2]}else{$t+=$_[$_]}}$t
    There has to be a better way... I thought about switching the if-elsif-else to BLOCK,next if(EXPR);, but I couldn't figure a way to get rid of the splice in the elsif block. *shrugs*

    Update: for(0..$#_){if($_[$_]=~/X/){$t+=10+($_[$_+1]=~/X/?10:$_[$_+1])+($_[$_+2]=~/X/?10:($_[$_+2]=~/\//?10-$_[$_+1]:$_[$_+2]));last if!defined$_[$_+3]}if($_[$_+1]=~/\//){$t+=10+($_[$_+2]=~/X/?10:$_[$_+2]);splice@_,$_+1,1;last if!defined$_[$_+2]}else{$t+=$_[$_]}}$t

    That fixes the perfect score problem. Comes in at 242.

    Update2: With some ideas from chipmunk, I'm down to 208. (I didn't want to steal his concept, but improve my own. *grins*)

    @b=@_;sub X{pop=~X?10:pop}while(($_=shift@b)+1){$t+=10+X(($b[0])x2)+X( +($b[1]=~/\//?10-$b[0]:$b[1]),$b[1])and(defined$b[2]||last)if/X/;if($b +[0]=~m!/!){$t+=10+X(($b[1])x2)and(defined$b[2]||last)}else{$t+=$_}}$t

    ------
    /me wants to be the brightest bulb in the chandelier!

    Vote paco for President!

      This sub returns 330 for score(('X')x12) :o) Apparently mine is broken to, although I thought it conformed to the rules as stated. :-(

      cheers

      tachyon

      s&&rsenoyhcatreve&&&s&n.+t&"$'$`$\"$\&"&ee&&y&srve&&d&&print

Re: (Golf) Let's go bowling
by chipmunk (Parson) on Aug 08, 2001 at 23:15 UTC
    This is a great golf challenge!

    Here's my best solution:

    sub score { my$s;sub S{$_[0]=~X?10:$_[0]}$s+=($l=shift)=~X?10+($_[1]eq'/'?10:S($_[ +0])+S$_[1]):($r=shift)eq'/'?10+S$_[0]:$l+$r for 1..10;$s }
    126 characters. (Or 125, if $r is replaced by a punctuation variable, since a space could then be eliminated.)

    During a Chatterbox discussion, tye suggested using a regex to parse the score. This sounded promising, but unfortunately I ended up with a longer solution.

    sub score { $"=my$s;sub S{$_[0]=~X?10:$_[0]}$S="@_";$S=~/(X)(?=(.)(.))|(.\/)(?=(.) +)|(.)(.)/g,$s+=$1?10+($3eq'/'?10:S($2)+S($3)):$4?10+S($5):$6+$7for 1. +.10;$s }
    145 characters.

      I think you misunderstood what I was thinking. This routine requires a single string instead of a list of single-character scalars and weighs in at just 103 strokes!

      sub bowl { $_=pop; s#(\d)([\d/](?=(.)))|X(?=(..))|.#$3?"X$3":$4?"X$4":$1.$2#ge; s#\d/#/#g; map{(y./X.aa.)x hex}split// }

      Or, you can handle getting the pinfall data any way you want. I don't care, so long as the code calculates scores correctly.
      ...so I don't consider this cheating. (: You can replace theh first statement with $_=join'',@_; if you disagree and I'm still only at 109. This is a fairly long thread so I'm almost certain I missed something, but I didn't see any scores that low.

      Update: I can golf that down a bit if you don't mind me using more undef strings which would generate warnings if such were enabled (the original only does that for the bonus balls). Also, I golfed the tr down by one stroke:

      sub bowl { $_=pop; s#(\d)(\d|/(?=(.)))|X(?=(..))|.#$3.$4?"X$3$4":$1.$2#ge; s#\d/#/#g; map{(y./X.a.)x hex}split// }
      which gets me to 98!

      Oh, and I did cheat in that you have to call this in a scalar context or you get what looks like a binary number. I'll take a 2-stroke penalty ("0+") for that if required.

      Update: Darn, I just noticed that a late bug fix (for /X\d\d/ in the tenth frame) got left out but that fix breaks things worse. An alternate bug fix hurts a little more:

      sub bowl { $_=pop; s#(\d)(\d(?=.)|/(?=(.)))|X(?=(..))|.#$3.$4?"X$3$4":$1.$2#ge; s#\d/#/#g; map{(y./X.a.)x hex}split// }
      which puts me back up at 103.

              - tye (but my friends call me "Tye")

        I got down to 98:

        sub bowl { $_=pop; s#(\d)(\d|(/))(?=(.))|X(?=(..))|.#$3.$5?"X$4$5":$1.$2#ge; s#./#X#g; map{(y.X.a.)x hex}split// }

                - tye (but my friends call me "Tye")
      This fails on
      X 0 / X 0 / X 0 / X 0 / X 0 / X -> 200, should be 210
      Update following comments below : I stand by the 210 score here. Hitting a spare in the 10th frame means you get one extra ball.
      In this case it is a strike (meaning here that you score 10 for the pincount but don't get any further extra balls.)

      This is the same principle that allows a score of 300 for a perfect game, counting the 11th and 12th shots as purely pincount of 10 each.
      --
      Brovnik

        chipmunk has the right answer here. Here is how to score that example:
        Recorded FrameScore Total -------- ---------- ----- X 20 20 0 / 20 40 X 20 60 0 / 20 80 X 20 100 0 / 20 120 X 20 140 0 / 20 160 X 20 180 0 / 20 200 X (NOT PART OF ANY FRAME) ----- 200
        Does that make sense now?
        X 0 / X 0 / X 0 / X 0 / X 0 / X -> 200, should be 210
        Update following comments below : I stand by the 210 score here. Hitting a spare in the 10th frame means you get one extra ball.

        No, it's 200. tilly's slightly unusual exegesis of the 10th frame above may have thrown you off. As you yourself say, the tenth frame is scored exactly like the previous 9 - the spare gives 10 + the next ball (X) -> 10 + 10 = 20, just as in the other 9 frames. Each frame in the game scores 20, and 20 * 10 = 200. Perhaps seeing it in a more bowling-alley-like style will help:

        1 2 3 4 5 6 7 8 9 10 X 0/ X 0/ X 0/ X 0/ X 0/X 20 40 60 80 100 120 140 160 180 200
        Okay, let's try this out, on a perfect game. (The extra balls for the strike in the tenth frame are in the 'eleventh' and 'twelfth' frames, respectively.)
        +-----------------------------------------------+ Frame | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |10 |11 |12 | +-----------------------------------------------+ Tally | X| X | X | X | X | X | X | X | X | X | X | X | +-----------------------------------------------+ Score | 30| 60| 90|120|150|180|210|240|270|300|310|320| +-----------------------------------------------+
        The extra balls should be counted only for their bonuses in the previous frames. They do not get counted on their own. Thus, the correct score for a game of X 0 / X 0 / X 0 / X 0 / X 0 / X is 200, not 210.
Re: (Golf) Let's go bowling
by nardo (Friar) on Aug 08, 2001 at 21:58 UTC
    Doing it with substitutions
    sub bowlscore { my $t; $_=join(',',@_); while(s/X,X,X/30,X,X/){} s!X,X,(\d)!20+$1.",X,$1"!eg; s!X,(\d),(\d)!10+$1+$2.",$1,$2"!eg; s!X,\d,/!20,1,/!g; s!\d,/,(\d)(\b)!10+$1.",$1$2"!eg; s!\d,/(?=,\d\d)!20!g; s!(\d\d)(,\d){1,2}$!$1!; map($t+=$_,split(/,/)); $t; }
    223 chars not counting the unnecessary whitespace for the body of the function. It's not the lowest bytecount here, but at least it's lower than my bowling highscore.

    Update: This fixes a bug pointed out by dragonchild
    sub bowlscore { my $t; $_=join(',',@_); while(s/X,X,X/30,X,X/){} s!X,X,(\d)!20+$1.",X,$1"!eg; s!X,(\d),(\d)!10+$1+$2.",$1,$2"!eg; s!X,\d,/!20,0,/!g; s!/,X!20!; s!\d,/,(\d)(\b)!10+$1.",$1$2"!eg; s!\d,/(?=,\d\d)!20!g; s!(\d\d)(,\d){1,2}$!$1!; map($t+=$_,split(/,/)); $t; }
    10 chars added.

    And another correction:
    s!\d,/,(\d)(\b)!10+$1.",$1$2"!eg;
    should be
    while(s!\d,/,(\d)(\b)!10+$1.",$1$2"!eg){}
    for multiple spare frames in a row to work.
      I can cut you down to 163 characters by doing standard golfings ($_=join',',@_;, for example). However, you get 189 if given ('X')x12. (I'm not doing much better, myself. *winces*

      ------
      /me wants to be the brightest bulb in the chandelier!

      Vote paco for President!

        you get 189 if given ('X')x12

        I think you copied my code wrong
        print &bowlscore(('X')x12), "\n";
        prints 300 on my machine.
      You still have a bug in the 3rd case, where you go strike, 0-spare, strike, 0-spare, etc. That's also equal to 200, not 210.

      ------
      /me wants to be the brightest bulb in the chandelier!

      Vote paco for President!

Re: (Golf) Let's go bowling
by tachyon (Chancellor) on Aug 08, 2001 at 21:54 UTC

    I don't know if my logic is wrong but I get a score of 162. I havn't bothered to obfuscate it and print out a score sheet to show the frame by frame results.

    my @b = ('5', '/', '6', '3', 'X', 'X', '7', '0', '4', '3', 'X', '4', '/', '8', '1', '3', '/', '6'); print "Frame tog ball score total\n"; print score(@b),"\n\n"; print "Frame tog ball score total\n"; print score(('X')x12); sub score { $i=0;$f=1;$t=1;$score=0; for (@_) { last if $f>10; if(/X/) { $s=10+n($_[$i+1])+n($_[$i+2]); $t=1 } elsif(/\//) { $s=10+n($_[$i+1]); $t=1 } else { $s=$_; $t^=1 # flip the toggle with XOR } $score+=$s; printf "%2d %d %s %2d %3d\n",$f, $t, $_, $s, $scor +e; $t&&$f++; # count the frames $i++; } sub n{$v=pop;$v=~/[X\/]/?10:$v} return $score } __END__ Frame tog ball score total 1 0 5 5 5 1 1 / 16 21 2 0 6 6 27 2 1 3 3 30 3 1 X 27 57 4 1 X 17 74 5 0 7 7 81 5 1 0 0 81 6 0 4 4 85 6 1 3 3 88 7 1 X 24 112 8 0 4 4 116 8 1 / 18 134 9 0 8 8 142 9 1 1 1 143 10 0 3 3 146 10 1 / 16 162 162 Frame tog ball score total 1 1 X 30 30 2 1 X 30 60 3 1 X 30 90 4 1 X 30 120 5 1 X 30 150 6 1 X 30 180 7 1 X 30 210 8 1 X 30 240 9 1 X 30 270 10 1 X 30 300 300

    Did I miss something?

    cheers

    tachyon

    s&&rsenoyhcatreve&&&s&n.+t&"$'$`$\"$\&"&ee&&y&srve&&d&&print

      One reason you're getting a higher score is because you aren't dealing with spares correctly. Look at the score after the first frame, it's 21 which is impossible for a spare frame. When a frame is a spare, you count the first ball twice because the ten pins that you assign a '/' includes the pins knocked down with the first ball which you already counted (i.e. I knock down 5 pins then another 5 pins for a spare -- you count 5 pins and another 10 pins for the spare which is wrong).

        So what is the score after the first frame? Do you ignore the 5 and say it is 10+6=16?

        cheers

        tachyon

        s&&rsenoyhcatreve&&&s&n.+t&"$'$`$\"$\&"&ee&&y&srve&&d&&print

Re: (Golf) Let's go bowling
by abstracts (Hermit) on Aug 08, 2001 at 22:20 UTC
    Hello

    Here is my solution (176 chars if data is joined in a single string in $_). Do you have a test suite?

    $_ = join '', @b; s#(.)/(.)$#$1.(10-$1).$2#e;s# ?#a#g; s#XaXaXa$#10a10a10a#;s#Xa(\da\da)$#10a$1#; 1while s#Xa(\d+)a(\d+)#10a$1a$2a$1a$2#|| s#(\d+)a/a(\d+)#"$1a".(10-$1)."a$2a$2"#e; $s+=$_ for/\d+/g; print "$_: $s\n";
    Hope it's correct.

    Aziz,,,

    update:removed an extra $1 at the end of line 2 and added a missing 'a'. Score: 176 from 177

    Major Update:

    That code was wrong. Here is an update that works for the test cases: (score 131)

    update:wrote it in sub form: score 144

    print score(join'',qw(5 / 6 3 X X 7 0 4 3 X 4 / 8 1 3 / 6)),"->146\n"; print score(join'',qw(X X X X X X X X X X X X)), "->300\n"; print score(join'',qw(0 / X 0 / X 0 / X 0 / X 0 / X 0 /)),"->200\n"; print score(join'',qw(X 0 / X 0 / X 0 / X 0 / X 0 / X 0)),"->200\n"; sub score={ # 1 2 3 4 5 6 #234567890123456789012345678901234567890123456789012345678901234567890 ($_,$s)=@_;1while s#(.)/(.)#$1.($1<1?'x':10-$1)."\L$2y".$2#e||s#X(\d\d +)#x$1$1#||s#X(..)#"x\L$1z".$1#e||s#(y.|z..)$##||s#x#55#;$s+=$_ for/\d +/g;$s ##### }
    CreditTo dragonchild for providing the test cases

      Your solution returns the following results:
      qw(5 / 6 3 X X 7 0 4 3 X 4 / 8 1 3 / 6) 146 qw(X X X X X X X X X X X X) 300 qw(0 / X 0 / X 0 / X 0 / X 0 / X 0 /) 0 (should be 200) qw(X 0 / X 0 / X 0 / X 0 / X 0 / X 0) 0 (should be 210)
      And, in subroutine form, I count 192. (My counts were off cause my window wasn't 80 characters wide. Fixed now. *blushes*)

      Update:

      qw(0 / X 0 / X 0 / X 0 / X 0 / X 0 /) 0 (should be 200) qw(X 0 / X 0 / X 0 / X 0 / X 0 / X 0) 172 (should be 200)

      ------
      /me wants to be the brightest bulb in the chandelier!

      Vote paco for President!

Re: (Golf) Let's go bowling
by tachyon (Chancellor) on Aug 08, 2001 at 23:48 UTC

    218, runs test suite :-) Thanks to dragonchild and nardo for filling me in on the real rules, not the rules as I saw them.

    print score(qw(5 / 6 3 X X 7 0 4 3 X 4 / 8 1 3 / 6)),"--> 146\n"; print score(qw(X X X X X X X X X X X X)), "--> 300\n"; print score(qw(0 / X 0 / X 0 / X 0 / X 0 / X 0 /)),"--> 200\n"; print score(qw(X 0 / X 0 / X 0 / X 0 / X 0 / X 0)),"--> 200\n"; sub score { $i=-1;$f=$t=1;$b=0;for(@_){$i++;$o=$_[$i+1];$d=$_[$i+2]; last if$f>10;if(/X/){$s=($d eq'/')?20:10+n($o)+n($d);} elsif(/\//){$s=10+n($o);$t=1}else{$s+=$_;$t^=1;$t||next} $b+=$s;$f++;$s=0;}sub n{$v=pop;$v=~/[X\/]/?10:$v}$b }

    cheers

    tachyon

    s&&rsenoyhcatreve&&&s&n.+t&"$'$`$\"$\&"&ee&&y&srve&&d&&print

Re (tilly) 1: (Golf) Let's go bowling
by tilly (Archbishop) on Aug 09, 2001 at 00:00 UTC
    I would strongly appreciate someone laying out the calculation in detail for the above coming out with an answer of 146. The following chart that counts bottom to top for obvious reasons gives me 164.
    ball score total 5 5 164 / 16 159 6 6 143 3 3 137 X 27 134 X 17 107 7 7 90 0 0 83 4 4 83 3 3 79 X 20 76 4 4 56 / 18 52 8 8 34 1 1 26 3 3 25 / 16 22 6 6 6
    If that is correct, then the following code is correct. If it is wrong, then I misunderstand the problem and don't have a solution at 103 characters.
    sub hole { # 1 2 3 4 5 6 +7 8 9 10 #234567890123456789012345678901234567890123456789012345678901234567890 +1234567890123456789012345678901012345 $i=$s=0;for(@_=(0,0,reverse@_)){$s+=$_[$i-1]+$_[$i-(/X/?2:-1)],$_=/X/? +10:10-$_[$i+1]if/\D/;$s+=$_;$i++}$s }
      There are two mistakes in your solution. First, the bonus ball(s), i.e. the extra ball for a spare or extra two balls for a strike in the tenth frame, are only counted once. You've counted the 6 at the end twice.

      Second, when a spare is scored, that frame scores exactly 10 plus the next ball. You're scoring 10 for the spare, plus the first ball, plus the next ball. For example, for 3 / 6, you counted the three twice, once by itself and once as part of the spare.

      Here's how this would actually be scored, going bottom to top:

      ball score total 5 - 146 / 16 146 6 6 130 3 3 124 X 27 121 X 17 94 7 7 77 0 0 70 4 4 70 3 3 66 X 20 63 4 - 43 / 18 43 8 8 25 1 1 17 3 - 16 / 16 16 6 -

      Here is the frame by frame score (ignore the toggle :-)

      Frame tog ball score total 0 5 1 1 / 16 16 0 6 2 1 3 9 25 3 1 X 27 52 4 1 X 17 69 0 7 5 1 0 7 76 0 4 6 1 3 7 83 7 1 X 20 103 0 4 8 1 / 18 121 0 8 9 1 1 9 130 0 3 10 1 / 16 146 6

      cheers

      tachyon

      s&&rsenoyhcatreve&&&s&n.+t&"$'$`$\"$\&"&ee&&y&srve&&d&&print

        D'oh. That brings my idea of working in reverse up to 133.
        sub hole { $i=$s=0;$s-=$_[-1]if"@_[-2,-1]"=~m!/|X!;map{$s+=$_[$i-1],$_=/X/?($s+=$ +_[$i-2],10):10-$_[$i+1]if/\D/;$s+=$_;$i++}@_=(0,0,reverse@_);$s }
Re: (Golf) Let's go bowling
by rchiav (Deacon) on Aug 09, 2001 at 01:41 UTC
    Well this is my first golf, and I was pretty proud of it until I saw tilly's post. But then again, when doesn't he have the lowest score? :)

    Anyway.. I'm sure this can be improved..

    I'm not sure on the counting rules, but if it's everything inside the sub, not counting whitespace, it's 150

    #!/usr/bin/perl $|=1; my @b = ('5', '/', '6', '3', 'X', 'X', '7', '0', '4', '3', 'X', '4', ' +/', '8', '1', '3', '/', '6'); $r = a(@b); print "score is $r\n"; sub a { map {s/(\/|X)/10/} @_ ; while ($_ = shift) { if (/^.$/) { $t += ($_[0] == 10 ? $_[1] + shift : $_ + shift) }else{ $t+= ($_[0] < 10 && $_[1] == 10 ? 20 : 10 + $_[0] + $_[1]) } last if ++$f ==10 } $t }
    update: original was missing the pound symbol on the first line.. nothing to do with the sub.. just mised it in the copy-n-paste. Also, put the exact array from the example in case someone thought it was giving the wrong answer

    update II The sub inluding nesisairy whitespace is now 150..
    update III removed a space I didn't think I could and removed the closing } from the count... 148

    sub a { map{s#(/|X)#10#}@_;while($_=shift){if(/10/){$t+=($_[0]<10&&$_[1]==10?2 +0:10+$_[0]+$_[1])}else{$t+=($_[0]==10?$_[1]+shift:$_+shift)}last if++ +$f==10}$t }
    update III same principle, just trimming it down..removed while loop and if statements.. 131
    sub a { map{s#(/|X)#10#}@_;for(1..10){$_=shift;/10/?{$t+=($_[0]<10&&$_[1]==10? +20:10+$_[0]+$_[1])}:{$t+=($_[0]==10?$_[1]+shift:$_+shift)}}$t }
    yet anotherupdate IV: Got it down to 123. But as Tilly pointed out, it doesn't look like this runs on 5.005_03.. so I don't know what it's worth then. I have since tested this and it works on AIX with perl 5.005_03
    sub a { map{s#[/X]#10#}@_;for(0..9){$_=shift;/10/?$t+=$_[0]<10&&$_[1]==10?20:1 +0+$_[0]+$_[1]:($t+=$_[0]==10?$_[1]+shift:$_+shift)}$t }
    -Rich
      I'm still choking on the map {} @_; ... I've got use 5.6.0; as my first line, so I know I'm not running 5.00x. But, if I change the map to:
      @b=@_; map {s#[X/]#10#} @b; @_=@b;
      Then, it works just fine. The error I'm getting is "Cannot modify a read-only variable." What's the about?

      ------
      /me wants to be the brightest bulb in the chandelier!

      Vote paco for President!

        Hello

        I'm using 5.6.2 and got that same error. I fixed it by doing:

        @b=@_;@_=@b; # then preceed as usual
        I bet the problem is that you are calling the function as
        score(qw/X 6 5 7 9 ...../);
        instead of:
        @arr = qw/X 4 5 6 .../; score(@arr);
        Map is modifying the content of the list (using s#...#10#). Passing a list (constant) to the sub makes @_ elements aliases to the values passed. This is just like saying
        "helo" =~ s/l/ll/;
        which does not make sense.

        Did I make myself clear? Does it make sense?

        Aziz,,,

      I'd hope it could be improved. It doesn't work too well. As is, it doesn't run. By changing the map{}@_; to @b=@_;map{}@b; and all the references to $_[] to $b[], it runs, but gives the wrong answers. It looks neat, though!

      ------
      /me wants to be the brightest bulb in the chandelier!

      Vote paco for President!

        define "doesn't work"? it ran with many test cases for me..

        update: downloaded my exact code posted here and ran it with the following games..

        my @b = qw(X X X X X X X X X X 7 2); [rchiav@aps:/home/rchiav]$ score score is 286 my @b = qw(X X X X X X X X X X X X); [rchiav@aps:/home/rchiav]$ score score is 300 my @b = qw(X 9 / X 9 / X 9 / X 9 / X 9 / X); [rchiav@aps:/home/rchiav]$ score score is 200 and the actual example given in the description.. my @b = qw(5 / 6 3 X X 7 0 4 3 X 4 / 8 1 3 / 6); [rchiav@aps:/home/rchiav]$ score score is 146
        If you could, please explain to me what's "not working".
Re: (Golf) Let's go bowling
by dfog (Scribe) on Aug 09, 2001 at 01:27 UTC
    Been a long time since I've had time to try one of these. My solution seems to be 168 characters, if I counted them properly.

    sub score { $i=$z=0;@_=map{(/\d/)?$_:10}@_;while($i++<10){$x=shift;if($x<10){$y=sh +ift;$z+=($y==10)?10+$_[0]:$x+$y;}else{$z+=($_[0]==10)?20+$_[1]:($_[1] +==10)?20:10+$_[0]+$_[1];}}$z; }
    Dave
Re: (Golf) Let's go bowling
by petral (Curate) on Aug 13, 2001 at 22:01 UTC
    Just for the record, here's a fairly straightforward one treating the score as a string.  It replaces '/' with the appropriate count and dupes the following char (unless it's an extra ball), replacing X with x to avoid processing the X twice.  Then it dupes the 2 chars following each X (again, except for extras), and tallies.  It passes all of the above tests and comes in at 96 chars:
    sub score { $_=pop; # 7 s#(.)/(?=(.).|.?$)#$1.($1?10-$1:'x').lc$2#ge; # 45 s/X(?=(..).)/X$1/g; # 19 y/Xx/9/+eval join'+',/./g # 25 } # ___ # 96
    Only one problem, it fails for a strike in the 9th frame and no extra balls in the 10th!  /X\d\d$/ matches either. (Also, there's no agreement on whether the args passed in have to match the orginal @b example.) Patching for those makes it 123 :-( :
    sub fullscore { $_=join'',@_; # 13 s/^(X|\d.){10}$/$&0/; # 21 s#(.)/(?=(.).|.?$)#$1.($1?10-$1:'x').lc$2#ge; # 45 s/X(?=(..).)/X$1/g; # 19 y/Xx/9/+eval join'+',/./g # 25 } # ___ # 123


      p

Re: (Golf) Let's go bowling
by dga (Hermit) on Aug 09, 2001 at 01:12 UTC

    This one works, takes command line arguments like X X X X X X X X X X X X and runs the sample game if no args. There is some bounds checking but no other error checking.

    It uses both strict and warnings. It does not validate that you entered sensible data. Nonsense data is scored as 0.

    X can be either upper or lower but / has to be /

    #!/usr/bin/perl use strict; use warnings; our @game; if(@ARGV>11 && @ARGV<22) { @game=@ARGV; } elsif(@ARGV) { print "Not possible to score this game\n"; exit; } else { print "No game specified using sample game\n"; @game=('5','/','6','3','X','X','7','0','4','3','X','4','/','8' +,'1','3','/','6'); } bowlscore(@game); sub bowlscore { my(@pins)=@_; my $f=0; my $sb; undef($sb); my $ef=0; my $pb=0; my $score=0; while(@pins) { my $ball=shift(@pins); if($ball eq 'X' || $ball eq 'x') { $f++; $score += 10; $score += points($pins[0]); $score += points($pins[1],$pins[0]); $ef=1; undef($sb); } elsif($ball eq '/') { $f++; $score += points($pins[0]); $score+= (10 - $sb); $ef=1; undef($sb); } else { $ball =~ s/[^0-9]//g; $ball ||= 0; die "$ball Invalid frame!\n", if((defined($sb) + && $sb+$ball>9) || $ball>9) ; $score+=$ball; if(defined($sb)) { $f++; $ef=1; undef($sb); } else { $sb=$ball; } } print "$ball "; print "@pins " if($f==10); print "F$f $score\n" if($ef); $ef=0; last if($f==10); } } sub points { my($pins, $pp)=@_; $pp = 0 unless($pins eq '/'); return 10-$pp if($pins eq 'X' || $pins eq '/' || $pins eq 'x +'); return $pins; }

    Update: Notes that number of strokes is important as well as correct function. (still a novice at the monastery so have much to learn).

      Per my update above this one is less than 1/3 the other and still works if you use use strict; (and warnings)

      { my(@g)=@_; my(@c)=@g; my($t, $s, $m, $p, $f, $h); foreach (@c) { s/[Xx]/10/; s/\//10-$p/e; $p=$_; } while(@g) { $s=shift(@c); $_=shift(@g); /X/ && ($t += $s + $c[0] + $c[1], $f++, $h=0, $f>9?last:next) +; /\// && ( $t += $s + $c[0], $f++, $h=0, $f>9?last:next); $h=$h?($f++,0):1 $t+=$s; $h++; } $t; }

      Dropping the my lines will of course stop strict from letting it run and will erase the callers copy of all the arguments to the function. But it is more than 10% smaller...

      Update: Failed case 6 of virtualsue's test suite now works correctly.

        Rewriting your code in Golf-ese, just to give you an example, could yield (without really taking into account all the shortcuts) in 166 characters ...
        map{s/X/10/;s#/#10-$p#e;$p=$_}@c=@_;$t=$f=$h=0;while(@_){$s=shift@c;$_ +=shift;/[X\/]/&&($t+=$s+$c[0]+(/X/?$c[1]:0),$h=0,++$f>9?last:next);$f +++,$h=0 if$h;$t+=$s;$h++}$t
        Golf code isn't meant to be pretty or even readable. It's meant to be compact. :)

        ------
        /me wants to be the brightest bulb in the chandelier!

        Vote paco for President!

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlmeditation [id://103079]
Approved by root
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others admiring the Monastery: (3)
As of 2024-03-19 02:45 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found