5
\$\begingroup\$

Does anyone have any suggestions as to how I could shorten this program?

_=map(ord,raw_input());_=[10if y==88else 0if y==45else 58-_[x-1]if y==47else y-48for x,y in enumerate(_)];t=0;exec('t+=sum(_[:2+(_[0]+_[1]>=10)]);_=_[2-(_[0]==10):];'*10);print t

Run it online

I'd like to write a bowling calculator. I'm fairly proficient in python so I chose that language.

The trick is that the input string is made up of frames not split by white-space and the spares, strikes, and misses are marked by /, X, and - respectively. (note that there are no 0 in the code, - is used instead)

Here is an example line X7/9-X-88/-6XXX81, this line would have a score of 167.

As you can see, most of my characters are spent on mapping these special characters to their actual bowl scores. I do this by taking their ord values and then replacing the X and - with 10 and 0 respectively. I then find the / rolls and set their value as ten minus the previous roll.

After this, I calculate the final game score by looping over 10 frames (the size of a line of bowling), chunking the line array based on if the frame was a strike spare or normal roll, and then reducing the array going forward.

I feel like this is the best way to perform this challenge as a golf challenge, but I'm not sure if I'm reducing it enough.

\$\endgroup\$
17
  • \$\begingroup\$ @user202729 The TIO link, as well as the use of print strongly suggests it is not. \$\endgroup\$
    – Wheat Wizard
    Jul 12, 2018 at 3:20
  • 1
    \$\begingroup\$ To be clear a spare (/) can never come after a gutter (-)? Your current code doesn't seem to handle this. \$\endgroup\$
    – Wheat Wizard
    Jul 12, 2018 at 4:05
  • 3
    \$\begingroup\$ I'm getting a score of 176 for your example. Could you maybe write out the bowling scoring rules to make sure we're all on the same page about how they are calculated? \$\endgroup\$
    – xnor
    Jul 12, 2018 at 4:27
  • 1
    \$\begingroup\$ OK, I was missing that if the game goes into extra frames, those frames are not scored. Are we guaranteed that the game as listed lasts exactly ten frames and those extra rolls are listed exactly if the the game goes into those extra frames? \$\endgroup\$
    – xnor
    Jul 12, 2018 at 5:03
  • 1
    \$\begingroup\$ Closely related. \$\endgroup\$
    – pajonk
    Jul 12, 2018 at 9:47

5 Answers 5

5
\$\begingroup\$

1.

The portion 2+(_[0]+_[1]>=10) could be rewritten as 2+(_[0]+_[1]>9) to save two bytes. In a similar vein 2-(_[0]==10) can be rewritten as 2-(_[0]>9), since 10 is the only possible value greater than 9.

a=map(ord,raw_input());a=[10if y==88else 0if y==45else 58-a[x-1]if y==47else y-48for x,y in enumerate(a)]
t=0;exec('t+=sum(a[:2+(a[0]+a[1]>9)]);a=a[2-(a[0]>9):];'*10);print t

Try it online!

(I also renamed _ to a, because even though it's code-golf we don't have to make it unreadable)

2.

You can also use a dictionary instead of conditionals to saves some bytes.

a=raw_input();a=[{y:ord(y)-48,'X':10,'-':0,'/':58-ord(a[x-1])}[y]for x,y in enumerate(a)]
t=0;exec('t+=sum(a[:2+(a[0]+a[1]>9)]);a=a[2-(a[0]>9):];'*10);print t

Try it online!

3.

Don't enumerate. enumerate tends to be too long for it's own good. Here you want to get the last character, which can better be acheived with a zip.

a=raw_input();a=[{y:ord(y)-48,'X':10,'-':0,'/':58-ord(x)}[y]for x,y in zip('-'+a,a)]
t=0;exec('t+=sum(a[:2+(a[0]+a[1]>9)]);a=a[2-(a[0]>9):];'*10);print t

Try it online!


If you combine this with @user202729's two tips you get 149 bytes.

a=raw_input();a=[[10,0,58-ord(x),ord(y)-48]['X-/'.find(y)]for x,y in zip('-'+a,a)]
t=0;exec't+=sum(a[:2+(a[0]+a[1]>9)]);a=a[2-(a[0]>9):];'*10;print t

Try it online!


Here's another version that is also 149 bytes:

a=raw_input();a=[['-123456789X'.find(y),58-ord(x)][y=='/']for x,y in zip('-'+a,a)]
t=0;exec't+=sum(a[:2+(a[0]+a[1]>9)]);a=a[2-(a[0]>9):];'*10;print t

Try it online!

\$\endgroup\$
3
  • \$\begingroup\$ Wow, fantastic response! And here I thought I was being clever with my use of enumerate to save some time. Thanks! \$\endgroup\$
    – BigBerger
    Jul 12, 2018 at 4:10
  • \$\begingroup\$ @Swiper-CCCVI Glad that helps. I would suggest waiting a little while before accepting an answer, there are definitely some people that are much better at python golfing than I am, and this question has only been viewed 18 times so far. \$\endgroup\$
    – Wheat Wizard
    Jul 12, 2018 at 4:13
  • \$\begingroup\$ Sure can do, I wish it was possible to accept multiple! \$\endgroup\$
    – BigBerger
    Jul 12, 2018 at 4:24
3
\$\begingroup\$

146 bytes

Based on Cat Wizard's "another version".

Basically I include / into the string so find returns 0 (falsy value), then use or to replace that 0 with the desired result.

a=raw_input();a=[('/-123456789X'.find(y)or 59-ord(x))-1for x,y in zip('-'+a,a)]
t=0;exec't+=sum(a[:2+(a[0]+a[1]>9)]);a=a[2-(a[0]>9):];'*10;print t

Try it online!

Explanation: x or y == x if x else y. So:

   '/-123456789X'.find(y)or 59-ord(x)
== '/-123456789X'.find(y) if '/-123456789X'.find(y) else 59-ord(x)
== '/-123456789X'.find(y) if '/-123456789X'.find(y) != 0 else 59-ord(x)
== '/-123456789X'.find(y) if y != '/' else 59-ord(x)
\$\endgroup\$
3
  • \$\begingroup\$ Could you elaborate on how the .find(y)or section is working? I'm having a hard time understanding how this is checking if the character is a spare or not. \$\endgroup\$
    – BigBerger
    Jul 12, 2018 at 4:40
  • \$\begingroup\$ @Swiper-CCCVI If the character is a spare '/-123456789X'.find(y) will be zero. This short circuits the or, because 0 is the same as false, so it returns the second part of the or which is the 59-ord(x). \$\endgroup\$
    – Wheat Wizard
    Jul 12, 2018 at 4:42
  • \$\begingroup\$ Gotcha, makes sense now. Thanks! And very nifty. \$\endgroup\$
    – BigBerger
    Jul 12, 2018 at 4:46
3
\$\begingroup\$

Shorten the conditional using str.find

Compare:

Mine:        [10,0,58-_[x-1],y-48]['X-/'.find(chr(y))]
Mine (char): [10,0,58-ord(a[x-1]),ord(y)-48]['X-/'.find(y)]
WW's:        {y:ord(y)-48,'X':10,'-':0,'/':58-ord(a[x-1])}[y]
Original:    10if y==88else 0if y==45else 58-_[x-1]if y==47else y-48
\$\endgroup\$
2
  • 1
    \$\begingroup\$ @CatWizard ... That's because you use characters and I use character codes... \$\endgroup\$
    – DELETE_ME
    Jul 12, 2018 at 3:50
  • \$\begingroup\$ Yeah, looks like I confused myself, if you implement this with characters rather than codes you save 2 bytes. Try yours \$\endgroup\$
    – Wheat Wizard
    Jul 12, 2018 at 3:53
2
\$\begingroup\$

Remove () around parameter of exec

exec is a command, not a function. This saves 2 bytes.

\$\endgroup\$
1
  • \$\begingroup\$ Nice catch! I did not know this. \$\endgroup\$
    – BigBerger
    Jul 12, 2018 at 4:17
1
\$\begingroup\$

Is there a reason why you need to use raw_input() and not replace it with input() and wrap the input string in quotes e.g. "X7/9-X-88/-6XXX81"? That would save 4.

\$\endgroup\$

Not the answer you're looking for? Browse other questions tagged or ask your own question.