17
\$\begingroup\$

I was watching the world snooker championship and it got me wondering..

Snooker scoring

In the game of snooker there are certain rules that you must adhere too:

  • When there are red balls on the table, during your turn you must first pot a red ball
  • After potting each red ball, you must pot a colored (not red) ball (the potted colored ball is then replaced onto the table)
  • After all the red balls are up (there are 15) you can first choose a colored ball and then you start with the lowest scoring ball and work your way up to the highest scoring ball (these are not replaced)
  • Not potting at any point ends your turn.
  • Points per ball
    • Red ball: 1 point
    • Yellow ball: 2 points
    • Green ball: 3 points
    • Brown ball: 4 points
    • Blue ball: 5 points
    • Pink ball: 6 points
    • Black ball: 7 points

The question

You start with a table with all the balls still on it - 15 red and one of each of the other coloured balls - and are given the score of a player in snooker after their first turn, what are the ways that they could have achieved this score?

Input will be a score going from 1 to 147. You can choose if it is an integer or a string. Output should be the different combinations of number of times you potted each ball.

Test cases:

Input: 4
Output: 
1r 1g
2r 1y
Input: 25
Output:
4r 3b
5r 2b 1br 1y
5r 2b 2g
...
9r 8y

Rules:

  • You can choose whether you output the possibilities divided by a new line or a separator of some sort (/,;|\ or even others I'm missing)

This is codegolf, so the shortest code wins.

\$\endgroup\$
15
  • \$\begingroup\$ Can I output as list of arrays? \$\endgroup\$
    – Leaky Nun
    Apr 25, 2017 at 5:29
  • \$\begingroup\$ I'd recommend making the input score range between 1 and 147 (oh for the 155). \$\endgroup\$ Apr 25, 2017 at 6:05
  • 1
    \$\begingroup\$ Regarding an array output: number of balls ordered by score is unambiguous, so maybe "5r 3b 2g" could be output as [5,0,2,0,3,0,0] (as long as this is consistent)? \$\endgroup\$ Apr 25, 2017 at 6:10
  • 2
    \$\begingroup\$ You use b for brown and bl for blue; so bk for black? Could we use n, e and k (last letters) for these three? How about dleruna to identify all eight colours (3rd letter of each)? \$\endgroup\$ Apr 25, 2017 at 7:20
  • 1
    \$\begingroup\$ @Shaggy, If you use an indication of color like dleruna or another one, no. If you just use an array like [5 0 0 4 1 0 0], then they should be sorted from low to high. \$\endgroup\$
    – Michthan
    Apr 25, 2017 at 8:15

2 Answers 2

6
\$\begingroup\$

Jelly,  65  25 bytes

7Ḋṗ15Ė€F;ɗ€ƊÄċƇ⁸f€RŻ€IṢ€Q

A monadic Link that accepts an integer, "score", from \$[1,147]\$ and yields a list of lists - each being a potential selection of ball scores used in reaching the given score disregarding the order in which they were actually potted.

Try it with only six reds (15 replaced with 6; again, fifteen reds would take an age).

How?

7Ḋṗ15Ė€F;ɗ€ƊÄċƇ⁸f€RŻ€IṢ€Q - Link integer, S
7                         - seven
 Ḋ                        - dequeue -> coloured ball scores, [2..7]
           Ɗ              - last three links as a monad - f([2..7]):
   15                     -   fifteen
  ṗ                       -   ([2..7]) Cartesian power (15) -> all length 15 words
                                using the coloured ball scores as letters
          €               -   for each (such Word):
         ɗ                -     last three links as a dyad - f(Word, [2..7])
                                              e.g. Word = [7,...,7]
     ˀ                   -       enumerate each          [[1,7],...,[1,7]]
       F                  -       flatten                 [1,7,...,1,7]
        ;                 -       concatenate ([2..7])    [1,7,...,1,7,2,3,4,5,6,7] 
            Ä             - cumulative sums, i.e. convert each such legal way to
                              clear the table to a list of the running score
              Ƈ           - filter keep those for which this is truthy (non-zero):
               ⁸          -   chain's left argument (S)
             ċ            -   count - i.e. count of S in the running score
                  R       - range (input Score) -> [1..S]
                f€        - for each (running score list) filter keep ([1..S])
                   ݀     - prefix each with a zero
                     I    - forward differences of each (back to ball scores)
                      Ṣ€  - sort each
                        Q - deduplicate

Original \$66\$ byte entry (\$65\$ really, since the trailing G is post-formatting):

L⁼30µÐfµ7Ḋ;\¤;€Ṣ€µ€;/
ċ1<⁴µÐfµ;Ç
7Ḋœċ⁴Ḷ¤;/L€;$€;@þ2B¤;/ḟ€0ÇS⁼¥Ðf⁸G

Well, it's too slow for TIO now!
...so here is a paste of the 2636 ways to make exactly 100 produced offline.
...and here is a version that will run there with just SIX reds (maximum break = 75)

Prints a grid of numbers each line being a space separated list of ball values (e.g. three red and two green would be on a line reading 1 1 1 3 3).


For a value-grouped version that prints lines of counts along with the full names of the balls, at 102 bytes:

ŒrÑ€
Ṫ;ị“¡^³ṗ⁼¬wḌ⁼ø÷OẏK¦ẆP»Ḳ¤$K
L⁼30µÐfµ7Ḋ;\¤;€Ṣ€µ€;/
ċ1<⁴µÐfµ;Ç
7Ḋœċ⁴Ḷ¤;/L€;$€;@þ2B¤;/ḟ€0ÇS⁼¥Ðf⁸Ñ€K€Y

How?

L⁼30µÐfµ7Ḋ;\¤;€Ṣ€µ€;/ - Link 1, create post-red-ball games: list of all pre-yellow-ball-games
    µÐf               - filter keep if:
L⁼30                  -   length equals 30 (games that get on to the yellow)
       µ         µ€   - for €ach sequence leading to the yellow:
            ¤         -   nilad followed by link(s) as a nilad:
        7Ḋ            -     7 dequeued  = [2,3,4,5,6,7]
          ;\          -     ;\ cumulative reduce with concatenation  = [[2],[2,3],[2,3,4],...]
             ;€       - concatenate the sequence with €ach of those
               Ṣ€     - sort each one
                   ;/ - reduce with concatenation (flatten by one)

ċ1<⁴µÐfµ;Ç - Link 2, filter bogus entries created and append post-yellow-ball games: list of pre-yellow-ball games (along with the bogus ones with 16 reds potted)
    µÐf    - filter keep if:
ċ1         -   count ones
   ⁴       -   literal 16
  <        -   less than?
       µ   - monadic chain separation
         Ç - call the last link (1) as a monad
        ;  - concatenate

7Ḋœċ⁴Ḷ¤;/L€;$€;@þ2B¤;/ḟ€0ÇS⁼¥Ðf⁸G - Main link: score
7Ḋ                                - 7 dequeued = [2,3,4,5,6,7]
      ¤                           - nilad followed by link(s) as a nilad:
     ⁴                            -   literal 16
    Ḷ                             -   lowered range = [0,1,2,...,15]
  œċ                              - combinations with replacement (every possible colour-ball selection that goes with the red pots)
       ;/                         - reduce with concatenation (flatten by one)
            $€                    - last two links as a monad for €ach:
         L€                       -   length of €ach (number of coloured balls potted)
           ;                      -   concatenate
                   ¤              - nilad followed by link(s) as a nilad:
                 2B               -   2 in binary = [1,0]
                þ                 - outer product with:
              ;@                  -   concatenation with reversed @rguments
                    ;/            - reduce with concatenation (flatten by one)
                      ḟ€0         - filter discard zeros from €ach
                         Ç        - call the last link (2) as a monad
                             Ðf   - filter keep:
                            ¥  ⁸  -   last two links as a dyad, with score on the right
                          S⁼      -     sum(ball values) is equal to score?
                                G - format as a grid
                                  - implicit print
\$\endgroup\$
7
  • \$\begingroup\$ It works well for all the cases I have tried. Only in some cases the last code gives leading zeros. \$\endgroup\$
    – Michthan
    Apr 25, 2017 at 9:25
  • \$\begingroup\$ Ah yes they should have been filtered out... Fixed. \$\endgroup\$ Apr 25, 2017 at 9:25
  • \$\begingroup\$ Your output for the 53 one is unambiguous as I said before, but I'm still doubting if it is readable for everyone.. \$\endgroup\$
    – Michthan
    Apr 25, 2017 at 10:47
  • \$\begingroup\$ It is much better in the grid way. If there aren't any shorter answers in the next few days, I am going to accept your answer! \$\endgroup\$
    – Michthan
    Apr 25, 2017 at 11:48
  • \$\begingroup\$ Hmm. I get 2636 century break combinations. So either you or I are wrong... \$\endgroup\$
    – Arnauld
    Apr 25, 2017 at 14:01
4
\$\begingroup\$

JavaScript (ES7), 188 180 178 bytes

Returns an array of arrays (sorted from red to black).

n=>[...Array(17**6)].map((_,x)=>[2,3,4,5,6,p=7].map(v=>(k=a[++j]=x%17|0,x/=17,t+=k,p+=!!(y=y&&k),s-=k*v),y=s=n,a=[j=t=0])&&(s==15|s>=t)&s<16&s<t+2&t<9+p&&(a[0]=s,a)).filter(a=>a)

Commented

Note: This version doesn't include the last optimization on p (now initialized to 7) which makes the logic harder to understand.

n =>                              // given a target score n:
  [...Array(17**6)].map((_, x) => // for each x in [0 .. 17^6 - 1]:
    [2, 3, 4, 5, 6, 7].map(v =>   //   for each v in [2 .. 7] (yellow to black):
      ( k = a[++j] = x % 17 | 0,  //     k = a[j] = number of colored balls of value v
        x /= 17,                  //     update x to extract the next value
        t += k,                   //     update t = total number of colored balls
        p += !!(                  //     update p = number of consecutive colors that were
          y = y && k              //     potted at least once, using y = flag that is zeroed
        ),                        //     as soon as a color is not potted at all
        s -= k * v ),             //     subtract k * v from the current score s
      y = s = n,                  //     initialize y and s
      a = [j = t = p = 0]         //     initialize a, j (pointer in a), t and p
    )                             //   at this point, s is the alleged number of red balls
    &&                            //   this combination is valid if we have:
      (s == 15 | s >= t) &        //     - 15 red balls or more red balls than colored ones
      s < 16 &                    //     - no more than 15 red balls
      s < t + 2 &                 //     - at most one more red ball than colored ones
      t < 16 + p                  //     - no more than 15 + p colored balls
    &&                            //   if valid:
      (a[0] = s, a)               //     update the combination with red balls and return it
  ).filter(a => a)                // end of outer map(): filter out invalid entries

Example output

Below is the output for n = 140:

//  R   Y  G  Br Bl P  Bk 
[ [ 15, 1, 1, 1, 1, 8, 9  ],
  [ 15, 1, 1, 1, 2, 6, 10 ],
  [ 15, 1, 1, 1, 3, 4, 11 ],
  [ 15, 1, 1, 2, 1, 5, 11 ],
  [ 15, 1, 1, 1, 4, 2, 12 ],
  [ 15, 1, 1, 2, 2, 3, 12 ],
  [ 15, 1, 2, 1, 1, 4, 12 ],
  [ 15, 1, 1, 2, 3, 1, 13 ],
  [ 15, 1, 1, 3, 1, 2, 13 ],
  [ 15, 1, 2, 1, 2, 2, 13 ],
  [ 15, 2, 1, 1, 1, 3, 13 ],
  [ 15, 1, 2, 2, 1, 1, 14 ],
  [ 15, 2, 1, 1, 2, 1, 14 ],
  [ 15, 1, 1, 1, 1, 1, 15 ] ]

Demo

This is too slow for a snippet. You can try it here instead. (You may get one or two unresponsive script alerts, but it should eventually complete.)

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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