32
\$\begingroup\$

Introduction

Nine-Ball is a popular pool game played casually and as a competitive sport.

There is one of each numbered ball, with the numbers 1 to 9. The game starts with all 9 of the balls in a diamond shaped rack, like so:

  1
 2 3
4 9 6
 5 7
  8

The 1 ball is always at the front of the diamond, and the 9 ball is always in the middle, with all the other balls placed randomly (the "X"s in this diagram):

  1
 X X
X 9 X
 X X
  X

Challenge

Your challenge, given no input, is to generate and output random 9-ball pool racks in the text format of the examples. Extra surrounding whitespace is allowed.

Output may be through any convenient method, standard loopholes apply. A new rack MUST be generated on each run, you cannot just produce a single randomly-chosen rack every time. Extra surrounding whitespace is allowed.

Example Outputs

  1
 2 3
4 9 5
 6 7
  8
  1
 8 3
2 9 7
 4 6
  5
  1
 2 8
3 9 7
 5 4
  6

Least bytes per language wins.

\$\endgroup\$
7
  • 3
    \$\begingroup\$ Since there are 7! combinations, strictly you don't mean "A new rack MUST be generated on each run", merely "you cannot just produce a single randomly-chosen rack every time." \$\endgroup\$
    – smci
    Commented Jan 5, 2020 at 14:46
  • 8
    \$\begingroup\$ Should all possible outputs have nonzero probability? Or should they all be equally likely? \$\endgroup\$
    – Luis Mendo
    Commented Jan 5, 2020 at 17:26
  • 3
    \$\begingroup\$ @nihilazo It does matter. You need to specify what you allow. Otherwise people can for example pick between two possible outputs at random. Right now you only say it can't be just one output \$\endgroup\$
    – Luis Mendo
    Commented Jan 6, 2020 at 17:23
  • 1
    \$\begingroup\$ Okay let's require that the code be capable of generating all of the 7! possible results. (I don't see how requiring them to be equiprobable makes things harder, but someone might think of something) \$\endgroup\$
    – smci
    Commented Jan 6, 2020 at 19:18
  • 1
    \$\begingroup\$ I've been playing nine-ball casually for decades, and I've never seen anyone rack it in any order but the "right" one (your first example output). So while I now know they don't have be done that way, I doubt I'm going to throw them in any other order now :) \$\endgroup\$
    – Geobits
    Commented Jan 6, 2020 at 21:15

24 Answers 24

12
\$\begingroup\$

R, 61 bytes

cat(paste0(c("  1
 ","","
","9 ","
 ","","
  "),sample(2:8)))

Try it online!

Prints alternately the proper whitespace/newline (with 1 and 9 included) and elements of a random permutation of \$\{2,\ldots,8\}\$.

\$\endgroup\$
11
\$\begingroup\$

Python 3, 91 87 bytes

from random import*
print("  1\n %d %d\n%d 9 %d\n %d %d\n%3d"%(*sample(range(2,9),7),))

Try it online!

-4 bytes thanks to xnor (final \n %d -> \n%3d and tuple(...) -> (*...,))

\$\endgroup\$
2
  • 3
    \$\begingroup\$ You can save a few bytes using a justified string format and using tuple unpacking: tio.run/##K6gsycjPM/7/… \$\endgroup\$
    – xnor
    Commented Jan 5, 2020 at 5:51
  • \$\begingroup\$ Very nice, thanks. That tuple() call was really bothering me so this makes me happy. \$\endgroup\$
    – Seb
    Commented Jan 5, 2020 at 12:30
10
\$\begingroup\$

Bash or any Bourne Shell, with modern coreutils, 54 52 bytes

printf '  1
 %s %s
%s 9 %s
 %s %s
  %s' `shuf -i2-8`

Try it online!

-2 bytes thanks to @dzaima (use shuf -i option for an implicit seq)

Originally:

printf '  1
 %s %s
%s 9 %s
 %s %s
  %s' `seq 2 8|shuf`

Try it online!

I saved one byte by not including a newline at the end of the source, and another by not including one at the end of the output.

Explanation:

seq 2 8 outputs 2 3 4 5 6 7 8 on separate lines.

|shuf randomly reorders them. (This is what needs modern coreutils.)

printf <formatstring> <args> does a C-like printf of the arguments with the format string.

\$\endgroup\$
0
6
\$\begingroup\$

JavaScript (ES6), 89 bytes

_=>`  1
 X X
X 9 X
 X X
  X`.replace(/X/g,g=_=>m^(m|=1<<(i=Math.random()*9|0))?i:g(),m=3)

Try it online!

How?

We replace each "X" in the template with a random integer \$0\le i<9\$ such that the \$i\$-th bit in the bitmask \$m\$ is not already set, and we simultaneously update \$m\$.

We start with \$m=3_{10}=000000011_2\$ so that \$0\$ and \$1\$ are never chosen.

\$\endgroup\$
6
\$\begingroup\$

Charcoal, 27 25 bytes

F⁷⊞υ‽⁻E⁷⁺²κυ↖⪪⁺⪫⪪⪫υω⁴9¦1³

Try it online! Link is to verbose version of code. Explanation:

F⁷

Loop 7 times.

⊞υ‽⁻E⁷⁺²κυ

Create a list of the integers from 2 to 8, perform set difference with the list of integers collected so far, select a random integer, and push it to the empty list. This shuffles the integers from 2 to 8.

⁺⪫⪪⪫υω⁴9¦1

Concatenate the integers, insert a 9 after the fourth, and append a 1. Edit: Saved 2 bytes by using @JonathanAllan's trick to insert the 9.

↖⪪...³

Split the string into a 3×3 grid and print it diagonally thus achieving the desired output.

\$\endgroup\$
6
\$\begingroup\$

Python 3, 128 bytes

from random import*
l=[1]+sample(range(2,9),7)
l.insert(4,9)
for i in[1,2,3,2,1]:print(f"{' '.join(map(str,l[:i])):^5}");l=l[i:]

Try it online!

Unfortunately not even close to the straightforward solution, but I spent so much time on this that I still wanted to share it.

Ungolfed:

from random import *

# Generate random sampling and ensure 1st element is 1
l = [1] + sample(range(2, 9), 7)
# Ensure 5th element is 9
l.insert(4, 9)

for i in [1, 2, 3, 2, 1]: # Numbers of digits per row
    # Take i numbers from the beginning of l, join them with spaces
    # Then center the resulting string in a field of length 5 and print
    print(f"{' '.join(map(str,l[:i])):^5}")
    # Drop the just printed numbers
    l = l[i:]

It can be shortened but then it no longer makes use of the nifty :^5 format specifier:

Python 3, 112 bytes

from random import*
l=[1]+sample(range(2,9),7)
l.insert(4,9)
for i in[1,2,3,2,1]:print(' '*(3-i),*l[:i]);l=l[i:]

Try it online!

A funny one without insert which bounces the 9 into the middle by slicing with a step size of 2:

Python 3, 115 bytes

from random import*
l=[1]+sample(range(2,9),7)+[9]
for i in[2,4,6,4,2]:print(' '*(3-i//2),*l[:i:2]);l=l[i:]+l[1::2]

Try it online!

\$\endgroup\$
5
\$\begingroup\$

Ruby, 66 52 bytes

$><<"  1
 %d %d
%d 9 %d
 %d %d
  %d"%[*2..8].shuffle

Try it online!

Also quite a boring answer, similar to the Perl and Bash solutions.

Saved 14 bytes thanks to Value Ink!

\$\endgroup\$
1
5
\$\begingroup\$

Kotlin, 102 95 93 92 bytes

{List(7){it+2}.shuffled().fold("  1\n XX\nX9 X\n XX\n  X"){s,c->s.replaceFirst("X", "$c ")}}

Try it online!

\$\endgroup\$
4
\$\begingroup\$

Jelly, 24 bytes

7ẊŻŒHj8‘;€⁶⁽¦ỵD¤œṖz⁶ZṚ€Y

A full program which prints the result.

Try it online!

How?

7ẊŻŒHj8‘;€⁶⁽¦ỵD¤œṖz⁶ZṚ€Y - Main Link: no arguments
7                        - seven
 Ẋ                       - shuffle (implicit range [1..7])
  Ż                      - prepend a zero
   ŒH                    - split in half
     j8                  - join with an eight
       ‘                 - increment (vectorises)
        ;€               - concatenate each with:
          ⁶              -   a space character
                œṖ       - partition before indices:
               ¤         -   nilad followed by link(s) as a nilad:
           ⁽¦ỵ           -     2479
              D          -     digits -> [2,4,7,9]
                  z⁶     - transpose with space filler
                    Z    - transpose
                     Ṛ€  - reverse each
                       Y - join with newline characters
                         - implicit, smashing print
\$\endgroup\$
4
\$\begingroup\$

Perl 6, 51 bytes

printf '  1
 %s %s
%s 9 %s
 %s %s
  %s',pick *,2..8

Try it online!

I'm not really happy with how boring this solution is.

\$\endgroup\$
1
  • \$\begingroup\$ Boring is good, it just means the language is well suited to the task :) \$\endgroup\$
    – Joe
    Commented Jan 9, 2020 at 15:43
4
\$\begingroup\$

PowerShell, 60 bytes

'  1
 {0} {1}
{2} 9 {3}
 {4} {5}
  {6}'-f(2..8|Sort{Random})

Try it online!

-17 bytes thank you @Veskah for cleaning up whitespaces

Originally:

'
   1
 {0}  {1}
{2}  9  {3}
 {4}  {5}
   {6}
' -f (2..8 | Sort {Get-Random})
\$\endgroup\$
1
3
\$\begingroup\$

Perl 5, 62 bytes

printf"  1
 %d %d
%d 9 %d
 %d %d
  %d",sort{int(rand 3)-1}2..8

Try it online!

\$\endgroup\$
3
\$\begingroup\$

Jelly, 28 26 bytes

4>þ`ṚŒḄYṣ1µ7ẊŻs4j8‘;€⁶ż@o⁶

Try it online!

A full program that takes no arguments and outputs a pool rack.

Explanation

4>þ`                       | Outer table using 1,2,3,4 on both sides and > as the the function [0,1,1,1],[0,0,1,1],[0,0,0,1],[0,0,0,0]
    Ṛ                      | Reverse [0,0,0,0],[0,0,0,1],[0,0,1,1],[0,1,1,1]
     ŒḄ                    | Bounce [0,0,0,0],[0,0,0,1],[0,0,1,1],[0,1,1,1],[0,0,1,1],[0,0,0,1],[0,0,0,0]
       Y                   | Join with newlines
        ṣ1                 | Split at 1s
          µ                | Start a new monadic chain
           7Ẋ              | Shuffle list of 1..7
             Ż             | Prepend with zero
              s4           | Split into pieces of length 4
                j8         | Join with 8
                  ‘        | Increment by 1
                   ;€⁶     | Append space to each
                      ż@   | Zip with list generated above
                        o⁶ | Replace 0s with spaces
\$\endgroup\$
3
  • \$\begingroup\$ Can you give any explanation of how this works? \$\endgroup\$
    – nihilazo
    Commented Jan 4, 2020 at 21:55
  • \$\begingroup\$ @nihilazo done, please see above \$\endgroup\$ Commented Jan 4, 2020 at 22:17
  • 1
    \$\begingroup\$ 7ẊŻs4j8‘;€⁶ snap! (except I halved) \$\endgroup\$ Commented Jan 5, 2020 at 0:07
3
\$\begingroup\$

05AB1E, 18 16 bytes

Y8L.r1†9ª•26Ià•Λ

-2 bytes thanks to @Grimmy by using a fixed length of 2 with some directions duplicated.

Try it online.

Explanation:

Y            # Push 2
8L           # Push a list in the range [1,8]
  .r         # Randomly shuffle it
    1†       # Filter the 1 to the front
      9ª     # Append a trailing 9 (i.e. [1,3,4,8,5,2,6,7,9])
•26Ià•       # Push compressed integer 33557713
Λ            # And use the Canvas builtin with those three values

See this 05AB1E tip of mine (section How to compress large integers?) to understand why •26Ià• is 33557713.

Canvas explanation:

The Canvas builtin takes three arguments:
1. The lengths of the lines to 'draw', which in this case is the fixed length of 2.
2. The string to 'draw', which in this case is the list [1,#,#,#,#,#,#,#,9] (where the # are random digits in the range [2,8]).
3. The directions, which in this case is 33557713. These will be the directions [↘,↘,↙,↙,↖,↖,↗,↘].

With these arguments, it will draw as follows (let's take the random list [1,3,4,8,5,2,6,7,9] as example here):

2 characters (1,3) in direction 3 (↘):
1
 3

2-1 characters (4) in direction 3 (↘):
1
 3
  4

2-1 characters (8) in direction 5 (↙):
1
 3
  4
 8

2-1 characters (5) in direction 5 (↙):
1
 3
  4
 8
5

2-1 characters (2) in direction 7 (↖):
 1
  3
   4
2 8
 5

2-1 characters (6) in direction 7 (↖):
  1
   3
6   4
 2 8
  5

2-1 characters (7) in direction 1 (↗):
  1
 7 3
6   4
 2 8
  5

2-1 characters (9) in direction 3 (↘):
  1
 7 3
6 9 4
 2 8
  5

NOTE: Since we use a fixed length of 2, the directions are leading in drawing the Canvas. So the trailing 3 in the directions 33557713 is mandatory for outputting the 9 here, whereas my previous 18-byter used the leading 3 with implicit wraparound to output the 9, since the lengths-list were leading for drawing instead.

See this 05AB1E tip of mine for a more in-depth explanation of the Canvas builtin.

\$\endgroup\$
2
  • 1
    \$\begingroup\$ That is much shorter than what I had using » and .c. You can still save 2 bytes with Y8L.r1†9ª•26Ià•Λ. Happy new year! (: \$\endgroup\$
    – Grimmy
    Commented Jan 6, 2020 at 12:26
  • \$\begingroup\$ @Grimmy Ah, of course. Duplicating the directions and using a single length of 2. Thanks! And happy New Year to you as well! :) \$\endgroup\$ Commented Jan 6, 2020 at 12:29
2
\$\begingroup\$

Dyalog APL Extended, 38 bytes

{5 5⍴(~2⊤29010619)⍀∊⍕¨∊,∘9¨@4⊢1,1+?⍨7}

Try it online!

\$\endgroup\$
2
\$\begingroup\$

Red, 90 bytes

t: random[2 3 4 5 6 7 8]foreach c{  1
 ; ;
; 9 ;
 ; ;
  ;}[prin either c >#":"[take t][c]]

Try it online!

\$\endgroup\$
2
\$\begingroup\$

SAS, 160 Bytes

data;array j[9](1:9);do i=1to 1e4;l=int(2+7*ranuni(0));m=int(2+7*ranuni(0));n=j[l];j[l]=j[m];j[m]=n;end;put+2j[1]/+1j[2]j[3]/j[4]j[9]j[5]/+1j[6]j[7]/+2j[8];run;

Ungolfed:

data _null_;
  array j[9] (1:9);       **Define and populate an array; 
  do i = 1 to 1e4;        **Iterate ten thousand times;
    l=int(2+7*ranuni(0)); **make a random int;
    m=int(2+7*ranuni(0)); **make another random int;
    n=j[l];               **save num in first slot;
    j[l]=j[m];            **swap second to first slot;
    j[m]=n;               **swap original first to second slot;
  end;
  put +2j[1]/+1j[2]j[3]/j[4]j[9]j[5]/+1j[6]j[7]/+2j[8];
                          **outputs the string, / is linebreak, +# is shift over, always adds one space;
run;

If I wanted to do it a bit "better" but longer, I would use rand("integer",2,8);, as that is a better random number distribution, but 1 char longer each line...

I'm pretty sure PROC IML would be much smaller, as that would be more similar to the R/Python implementation. Base SAS has tools to sort rows randomly, but not columns/variables. I figure doing 10,000 random swaps is sufficiently randomized here (probably as random as any of the other implementations, anyway); could easily do 1 billion random swaps for no more characters, but 10k seems enough.

\$\endgroup\$
1
\$\begingroup\$

Keg, 62 bytes

2345678(~z%|&(~z%|')&^)  ,,1.
, ,. ,.
,. ,9. ,.
, ,. ,.
,  ,,.

Try it online!

Remove the z%' s for -4 bytes but a really long execution time.

This:

  • Pushes the numbers 2-8 onto the stack.
  • Shuffles everything in a very interesting way.
  • Prints out the required shape.
\$\endgroup\$
1
\$\begingroup\$

APL, 37 chars/bytes

2⌽t⌽↑⍕¨((t←1 2 3 2 1)/⍳5)⊆1,5⌽9,1+?⍨7

permutation of numbers from 2 to 8

1+?⍨7

add leading 9, shift 5 positions to the left, add leading 1

1,5⌽9,

replicate numbers from 1 to 5, 1-2-3-2-1 times respectively

(t←1 2 3 2 1)/⍳5

make 5 nested vectors of length 1-2-3-2-1 respectively

((t←1 2 3 2 1)/⍳5)⊆

transform numerical arrays into char vectors

⍕¨

nested vectors into matrix

shift each line 1-2-3-2-1 positions to the left respectively, and then 2 more for every line

2⌽t⌽
\$\endgroup\$
1
1
\$\begingroup\$

PHP, 83 81 bytes

83 bytes

<?=($l=range(2,8))&&shuffle($l)?vsprintf("  1\n %d%2d\n%d 9 %d\n%2d%2d\n%3d",$l):0;

Try it online!


81 bytes

Splitting code into 2 fragments to save 2 characters. Thanks for the comment @manatwork.

<?$l=range(2,8);shuffle($l)?><?=vsprintf("  1\n %d%2d\n%d 9 %d\n%2d%2d\n%3d",$l);

Try it online!

\$\endgroup\$
4
  • 1
    \$\begingroup\$ The challenge says “you cannot just produce a single randomly-chosen rack every time”. \$\endgroup\$
    – manatwork
    Commented Jan 9, 2020 at 13:29
  • \$\begingroup\$ @manatwork, sorry, i have corrected. \$\endgroup\$
    – wnull
    Commented Jan 9, 2020 at 13:40
  • 1
    \$\begingroup\$ Splitting the code in 2 PHP fragments would help save 2 characters: Try it online! \$\endgroup\$
    – manatwork
    Commented Jan 9, 2020 at 13:51
  • \$\begingroup\$ @manatwork, hmm, an interesting idea, thank you :) \$\endgroup\$
    – wnull
    Commented Jan 9, 2020 at 13:56
1
\$\begingroup\$

C (GCC) 161 158 bytes

-3 bytes from ceilingcat

#define B,b[i++]
b[]=L"";main(a,t,i){srand(time(0));for(i=b;--i;b[a]=t)*b=b[a=rand(t=*b)%7];printf("  1\n %d %d\n%d 9 %d\n %d %d\n  %d"B B B B B B B);}

L"" has non-printing characters that I can't get to show in SE is equivalent to {2,3,4,5,6,7,8}

this code makes the assumption that the address of b is at least 14 in order to guarantee all results are possible

Try it online!

\$\endgroup\$
1
1
\$\begingroup\$

Vyxal, 42 32 bytes

`  1
 % %
% 9 %
 % %
  %`7ɾ1+Ṗ℅%

Try it Online!

-10 thanks to lyxal.

\$\endgroup\$
2
  • \$\begingroup\$ -10 bytes \$\endgroup\$
    – lyxal
    Commented May 22, 2021 at 3:30
  • \$\begingroup\$ @lyxal Nice! Replacing! \$\endgroup\$
    – emanresu A
    Commented May 22, 2021 at 3:40
0
\$\begingroup\$

Java 10, 140 139 bytes

v->{var r="  1\n x x\nx 9 x\n x x\n  x";for(int d=9,m=3;m<511;r=r.replaceFirst("x",d+""))for(;m==(m|=1<<(d*=Math.random()));d=9);return r;}

Port of @Arnauld's JavaScript answer, so make sure to upvote him as well!

Try it online.

Explanation:

v->{                // Method with empty unused parameter and String return-type
  var r="  1\n x x\nx 9 x\n x x\n  x";
                    //  Result-String, starting at:
                    //  "  1
                    //    x x
                    //   x 9 x
                    //    x x
                    //     x"
  for(int d=9,      //  Temp integer for the random digit, starting at 9
          m=3;      //  Bit-mask `m`, starting at 3 (binary 000000011)
          m<511     //  Loop as long as `m` is not 511 yet (binary 111111111):
          ;         //    After every iteration:
           r=r.replaceFirst("x",d+""))
                    //     Replace the first "x" with digit `d`
    for(;m==        //   Inner loop as long as `m` is unchanged after
            (m|=    //   `m` is bitwise-ORed with:
                1<< //    2 to the power
                   (d*=Math.random()));
                    //    a random digit in the range [0,9)
      d=9);         //     After every iteration: reset `d` to 9 again
  return r;}        //  And finally return the result-String
\$\endgroup\$
0
\$\begingroup\$

C (gcc), 139 bytes

char a[7],c;r(){for(srand(time(0));c=rand()%7,a[c]++;);c+=2;}f(){printf("  1\n %d %d\n%d 9 %d\n %d %d\n  %d",r(),r(),r(),r(),r(),r(),r());}

Generates a random number 0-6, tests if that number has been generated before, and if not, prints it.

Try it online!

\$\endgroup\$
1

Your Answer

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

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