31
\$\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\$
  • 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 Jan 5 at 14:46
  • 8
    \$\begingroup\$ Should all possible outputs have nonzero probability? Or should they all be equally likely? \$\endgroup\$ – Luis Mendo Jan 5 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 Jan 6 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 Jan 6 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 Jan 6 at 21:15

23 Answers 23

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\}\$.

| improve this answer | |
\$\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(...) -> (*...,))

| improve this answer | |
\$\endgroup\$
  • 3
    \$\begingroup\$ You can save a few bytes using a justified string format and using tuple unpacking: tio.run/##K6gsycjPM/7/… \$\endgroup\$ – xnor Jan 5 at 5:51
  • \$\begingroup\$ Very nice, thanks. That tuple() call was really bothering me so this makes me happy. \$\endgroup\$ – Seb Jan 5 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.

| improve this answer | |
\$\endgroup\$
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.

| improve this answer | |
\$\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.

| improve this answer | |
\$\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!

| improve this answer | |
\$\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!

| improve this answer | |
\$\endgroup\$
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!

| improve this answer | |
\$\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
| improve this answer | |
\$\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.

| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ Boring is good, it just means the language is well suited to the task :) \$\endgroup\$ – Joe Jan 9 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})
| improve this answer | |
\$\endgroup\$
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!

| improve this answer | |
\$\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
| improve this answer | |
\$\endgroup\$
  • \$\begingroup\$ Can you give any explanation of how this works? \$\endgroup\$ – nihilazo Jan 4 at 21:55
  • \$\begingroup\$ @nihilazo done, please see above \$\endgroup\$ – Nick Kennedy Jan 4 at 22:17
  • 1
    \$\begingroup\$ 7ẊŻs4j8‘;€⁶ snap! (except I halved) \$\endgroup\$ – Jonathan Allan Jan 5 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.

| improve this answer | |
\$\endgroup\$
  • 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 Jan 6 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\$ – Kevin Cruijssen Jan 6 at 12:29
2
\$\begingroup\$

Dyalog APL Extended, 38 bytes

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

Try it online!

| improve this answer | |
\$\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!

| improve this answer | |
\$\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.

| improve this answer | |
\$\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.
| improve this answer | |
\$\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⌽
| improve this answer | |
\$\endgroup\$
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!

| improve this answer | |
\$\endgroup\$
  • 1
    \$\begingroup\$ The challenge says “you cannot just produce a single randomly-chosen rack every time”. \$\endgroup\$ – manatwork Jan 9 at 13:29
  • \$\begingroup\$ @manatwork, sorry, i have corrected. \$\endgroup\$ – морфиновые облака Jan 9 at 13:40
  • 1
    \$\begingroup\$ Splitting the code in 2 PHP fragments would help save 2 characters: Try it online! \$\endgroup\$ – manatwork Jan 9 at 13:51
  • \$\begingroup\$ @manatwork, hmm, an interesting idea, thank you :) \$\endgroup\$ – морфиновые облака Jan 9 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!

| improve this answer | |
\$\endgroup\$
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
| improve this answer | |
\$\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!

| improve this answer | |
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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