Generate a Nine-Ball Pool rack

30

3

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.

nihilazo

Posted 2020-01-04T15:22:29.457

Reputation: 477

3Since 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." – smci – 2020-01-05T14:46:29.243

8Should all possible outputs have nonzero probability? Or should they all be equally likely? – Luis Mendo – 2020-01-05T17:26:18.107

@smci yeah, that was what I meant. – nihilazo – 2020-01-06T16:54:48.747

@LuisMendo Doesn't really matter? Not sure honestly – nihilazo – 2020-01-06T16:55:22.830

3

@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

– Luis Mendo – 2020-01-06T17:23:23.053

1Okay 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) – smci – 2020-01-06T19:18:51.950

1I'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 :) – Geobits – 2020-01-06T21:15:41.220

Answers

12

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

Robin Ryder

Posted 2020-01-04T15:22:29.457

Reputation: 6 625

11

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(...) -> (*...,))

Seb

Posted 2020-01-04T15:22:29.457

Reputation: 291

3

You can save a few bytes using a justified string format and using tuple unpacking: https://tio.run/##K6gsycjPM/7/P60oP1ehKDEvBUhl5hbkF5VocRUUZeaVaCgpKBjG5CmopgBRTB6QsgQz4ALGKUqqGlrFibkFOakaQBPSUzWMdCw1dcw1dTQ1//8HAA

– xnor – 2020-01-05T05:51:51.293

Very nice, thanks. That tuple() call was really bothering me so this makes me happy. – Seb – 2020-01-05T12:30:13.353

10

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.

David G.

Posted 2020-01-04T15:22:29.457

Reputation: 541

6

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.

Arnauld

Posted 2020-01-04T15:22:29.457

Reputation: 111 334

6

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.

Neil

Posted 2020-01-04T15:22:29.457

Reputation: 95 035

6

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!

Seb

Posted 2020-01-04T15:22:29.457

Reputation: 291

5

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!

Quinn

Posted 2020-01-04T15:22:29.457

Reputation: 1 153

5

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!

IMP1

Posted 2020-01-04T15:22:29.457

Reputation: 510

52 bytes using printf formatting – Value Ink – 2020-01-08T23:47:56.440

4

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

Jonathan Allan

Posted 2020-01-04T15:22:29.457

Reputation: 67 804

4

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.

Jo King

Posted 2020-01-04T15:22:29.457

Reputation: 38 234

Boring is good, it just means the language is well suited to the task :) – Joe – 2020-01-09T15:43:36.383

4

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})

I.T Delinquent

Posted 2020-01-04T15:22:29.457

Reputation: 165

260 bytes by optimizing whitespace – Veskah – 2020-01-06T14:49:07.573

3

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

Nick Kennedy

Posted 2020-01-04T15:22:29.457

Reputation: 11 829

Can you give any explanation of how this works? – nihilazo – 2020-01-04T21:55:11.250

@nihilazo done, please see above – Nick Kennedy – 2020-01-04T22:17:26.753

17ẊŻs4j8‘;€⁶ snap! (except I halved) – Jonathan Allan – 2020-01-05T00:07:45.367

3

Perl 5, 62 bytes

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

Try it online!

Denis Ibaev

Posted 2020-01-04T15:22:29.457

Reputation: 876

3

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.

Kevin Cruijssen

Posted 2020-01-04T15:22:29.457

Reputation: 67 575

1

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! (:

– Grimmy – 2020-01-06T12:26:43.473

@Grimmy Ah, of course. Duplicating the directions and using a single length of 2. Thanks! And happy New Year to you as well! :) – Kevin Cruijssen – 2020-01-06T12:29:49.817

2

Dyalog APL Extended, 38 bytes

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

Try it online!

dzaima

Posted 2020-01-04T15:22:29.457

Reputation: 19 048

2

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!

Galen Ivanov

Posted 2020-01-04T15:22:29.457

Reputation: 13 815

2

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.

Joe

Posted 2020-01-04T15:22:29.457

Reputation: 283

1

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.

Lyxal

Posted 2020-01-04T15:22:29.457

Reputation: 5 253

1

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!

rtpax

Posted 2020-01-04T15:22:29.457

Reputation: 411

152 bytes – ceilingcat – 2020-02-16T03:57:39.277

1

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⌽

Popov

Posted 2020-01-04T15:22:29.457

Reputation: 101

1

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!

девочка с глазами ребёнка

Posted 2020-01-04T15:22:29.457

Reputation: 111

1The challenge says “you cannot just produce a single randomly-chosen rack every time”. – manatwork – 2020-01-09T13:29:27.273

@manatwork, sorry, i have corrected. – девочка с глазами ребёнка – 2020-01-09T13:40:34.653

1

Splitting the code in 2 PHP fragments would help save 2 characters: Try it online!

– manatwork – 2020-01-09T13:51:02.083

@manatwork, hmm, an interesting idea, thank you :) – девочка с глазами ребёнка – 2020-01-09T13:56:28.687

0

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

Kevin Cruijssen

Posted 2020-01-04T15:22:29.457

Reputation: 67 575

0

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!

S.S. Anne

Posted 2020-01-04T15:22:29.457

Reputation: 1 161

131 bytes – ceilingcat – 2020-02-16T04:28:54.000