Make it rain characters

31

2

Inspired by this chat mini-challenge.

Given a string as input (ASCII printable characters only), output the string with the letters "raining" down. Each letter must be a random number of lines downward (random between 0 and the length of the string, each having non-zero probability), and only one character per column. All possible outputs must again have a non-zero probability of occurring.

That's maybe a little confusing, so here's an example (taken from that CMC):

Hello World

          d
H
       o
  llo

         l
      W
 e
        r

Note how the H is one space down, the d is zero down, and the llo all happen to line up. The r is the farthest down, at 9, but is still less than the string length away from the top. This is just one example, there are dozens of other possibilities for input Hello World.

Other examples could be:

test

t
 e
  s
   t


PP&CG

  & G
 P

P  C

  • Input and output can be given by any convenient method.
  • The input is guaranteed non-empty (i.e., you'll never receive "" as input).
  • You can print it to STDOUT or return it as a function result.
  • Either a full program or a function are acceptable.
  • Any amount of extraneous whitespace is acceptable, so long as the characters line up appropriately (e.g., feel free to pad as a rectangle).
  • Standard loopholes are forbidden.
  • This is so all usual golfing rules apply, and the shortest code (in bytes) wins.

AdmBorkBork

Posted 2019-04-12T13:45:47.773

Reputation: 41 581

2Thought this was going to involve animation when I read the title. Have we had an animated version of this? – Shaggy – 2019-04-12T22:22:03.170

@Shaggy Not that I've seen or been able to find. – AdmBorkBork – 2019-04-13T00:52:49.063

"Any amount of extraneous whitespace is acceptable" - does that include a leading line of whitespace? – Jonathan Allan – 2019-04-13T19:52:22.533

I know we have had one based on the Matrix code, but good luck finding it with those 2 keywords! Do you mind if I Sandbox the idea? – Shaggy – 2019-04-13T19:57:29.877

What is the maximum input size answers need to implement? I'm seeing many people use random functions that use "pseudo-random" in the backendground, and certain input words are larger than the size of the seed used in those generators, and they will fail the " All possible outputs must again have a non-zero probability of occurring." constraint you have specified – Ferrybig – 2019-04-13T20:33:38.727

@JonathanAllan Leading whitespace is fine. – AdmBorkBork – 2019-04-15T12:32:29.360

@Shaggy Go for it! – AdmBorkBork – 2019-04-15T12:32:39.270

@Ferrybig One of the standards of the site is you can assume your language doesn't have limitations (e.g., an unlimited-memory computer), in order to implement your designed algorithm. Same should apply here with regard to the size of the PRNG (e.g., 64-bit), so long as the algorithm presented isn't based on that limitation. So far, I've not seen anything wrong with any of the submissions. – AdmBorkBork – 2019-04-15T12:36:28.770

Answers

5

R, 104 bytes

function(s){m=matrix(" ",l<-nchar(s),l)
m[cbind(1:l,sample(l,l,T))]=el(strsplit(s,""))
write(m,1,l,,"")}

Try it online!

Input as a string; writes to stdout.

Giuseppe

Posted 2019-04-12T13:45:47.773

Reputation: 21 077

You can save a byte by using scan(,'') and nesting a bunch of calls but honestly I vastly prefer the function approach, this other one is hideous for minimal gain. Might spark some ideas, though. Try it online!

– CriminallyVulgar – 2019-04-12T14:32:25.000

I think sample(l,,T) suffices instead of sample(l,l,T) (-1 byte). – Robin Ryder – 2019-04-12T15:24:29.400

4

JavaScript (ES6), 72 bytes

Takes input as a list of characters. Returns a matrix of characters.

a=>a.map((_,y)=>a.map((c,x)=>Math.random()<.5|!a[y+1]?(a[x]=' ',c):' '))

Try it online!

Arnauld

Posted 2019-04-12T13:45:47.773

Reputation: 111 334

I think you could save one byte by not negating the ternary expression. – orthoplex – 2019-04-12T14:12:20.580

2@orthoplex That wouldn't work because (0|'A') === (0|undefined) and the remaining letters would not be guaranteed anymore to appear on the last row. (So, basically, it's just like |!a[y+1] was removed altogether.) – Arnauld – 2019-04-12T14:15:27.110

Google says that Math.random() returns a number in [0, 1), so couldn't Math.random()<.5 become Math.random()>0? – nedla2004 – 2019-04-13T02:30:41.657

@nedla2004 In theory, yes. In practice, I think it's very likely that the implementation(s) of the PRNG can't possibly return exactly $0$ (let alone close enough $0$'s so that all configurations actually have a chance to occur). Because we define a language by its implementation, I personally think it's therefore invalid. – Arnauld – 2019-04-13T10:34:17.510

For what platform did you make your javascript for? Since the challenge has a uniqueness requirement, and does not specify a max input, this really matters, as most platforms are using a math.random() implementation that has an internal state, and thus cannot generate unique output. – Ferrybig – 2019-04-13T23:31:32.227

@Arnauld That makes sense, I agree that defining the language by what it does makes this dubious at best. – nedla2004 – 2019-04-14T01:42:50.060

@Arnauld Not just in theory. It is possible to get 0. Extremely small chance, but still positive. I wrote a post about it: https://dev.to/targumon/how-does-math-random-work-1526

– targumon – 2020-01-31T00:37:43.903

4

Pyth - 9 bytes

Outputs list of lines.

.tm+*;OlQ

 .t                       Transpose, padding with spaces
  m      (Q implicit)     Map over input
   +    (d implicit)      Concatenate to loop var
    *                     String repeat
     ;                    This refers to the var replaced by loop var, which is d=" "
     O                    Random number less than
      lQ                  Length of input

Try it online.

Maltysen

Posted 2019-04-12T13:45:47.773

Reputation: 25 023

4

Japt, 8 bytes

-1 byte from @Shaggy

y_iUÊö ç

y_iUÊö ç        Full Program. Implicit input U
y_              transpose and map each row in U (Call it X)
  i             Insert at the beginning of X:
       ç        " " repeated ↓ many times
   UÊö          random integer in [0, length of U] 
                implicit transpose back and output

Try it online!

Luis felipe De jesus Munoz

Posted 2019-04-12T13:45:47.773

Reputation: 9 639

18 bytes – Shaggy – 2019-04-12T14:33:51.463

lol @Shaggy I got the same answer a while ago, i was just adding an explanation. Thanks anyway c: – Luis felipe De jesus Munoz – 2019-04-12T14:44:08.570

4

J, 30 19 bytes

|:@,.]{.~"+_2-#?@##

Try it online!

Conor O'Brien

Posted 2019-04-12T13:45:47.773

Reputation: 36 228

10|:]{.~"+_1-#?# for 15 bytes – Galen Ivanov – 2019-04-13T09:12:25.917

@GalenIvanov I love this idea but since the dyad deal takes without repetition, the range of outputs won't span the full range of possibilities. eg, it won't be possible for 2 letters to have randomly dropped to the same height. – Jonah – 2019-04-14T16:52:02.100

@Conor, You can do 0|:]{.~"+_2-#?@## for 17 bytes without changing the behavior of your answer. – Jonah – 2019-04-14T17:08:08.040

1@Jonah Yes, right. I realized that and had another 17-byte solution. – Galen Ivanov – 2019-04-14T18:09:08.677

0|:]({.~_2-?)"0# for 16 bytes – Jonah – 2020-01-31T04:52:51.457

3

APL (Dyalog Unicode), 16 bytesSBCS

Anonymous tacit prefix function

⍉∘↑⊢↑¨⍨∘-∘?≢⍴1+≢

 length of string

1+ one added to that

≢⍴ "length" copies of that

∘? random integers in range 1…those, and then…

∘- negate, and then…

⊢↑¨⍨ take than many elements from each character, padding on the left with spaces

∘↑ mix list of strings into matrix, padding with spaces on the right

 transpose

Try it online!

Adám

Posted 2019-04-12T13:45:47.773

Reputation: 37 779

2

Ruby, 59 57 55 bytes

->a{a.map{|c|s=[' ']*z=a.size;s[rand z]=c;s}.transpose}

Try it online!

Inputs 1D, outputs 2D array of characters.

Kirill L.

Posted 2019-04-12T13:45:47.773

Reputation: 6 693

2

Japt, 8 bytes

yÈùUÊö Ä

Try it

yÈùUÊö Ä     :Implicit input of string U
y            :Transpose
 È           :Pass each column through the following function and transpose back
  ù          :  Left pad with spaces to length
   UÊ        :    Length of U
     ö       :    Random number in the range [0,UÊ)
       Ä     :    Plus 1

Shaggy

Posted 2019-04-12T13:45:47.773

Reputation: 24 623

2

Jelly, 10 bytes

³LŻX⁶x;)z⁶

Try it online!

      )    | For each input character
³L         | Length of original input
  Ż        | 0..length
   X       | Random number from that list
    ⁶x     | That number of spaces
       ;   | Concatenate to the character
        z⁶ | Finally transpose with space as filler

Nick Kennedy

Posted 2019-04-12T13:45:47.773

Reputation: 11 829

We can output a leading line of spaces, so 9 bytes (although I feel like there may be an 8...)

– Jonathan Allan – 2019-04-16T21:14:48.973

@JonathanAllan wouldn’t that be equivalent to the range 0..(length - 1)? The question specifies between 0 and the string length. Or am I missing something? – Nick Kennedy – 2019-04-16T21:18:04.997

Oh yes, I forgot about the inclusive-ness - when I asked three days ago about leading whitespace I'm pretty sure I had a 9, and I think it wasnt what I suggested above... hmm – Jonathan Allan – 2019-04-16T21:39:32.133

2

PHP, 88 bytes

for($o='';$i<$l=strlen($argn);$o[$i+$l*rand(0,$l)]=$argn[$i++]);echo chunk_split($o,$l);

Try it online!

Or 94 bytes using PHP's cryptographic random integers function.

for($o='';$i<$l=strlen($argn);$o[$i+$l*random_int(0,$l)]=$argn[$i++]);echo chunk_split($o,$l);

Try it online!

Input from STDIN, output to STDOUT. Run as:

$ echo Hello World|php -nF rain.php
   l  W    
  l        

 e      r d

H      o   

         l 


    o 

-1 byte (empty string instead of space) and +1 byte (err on side of rules) thx to @ASCII-only!

640KB

Posted 2019-04-12T13:45:47.773

Reputation: 7 149

wonder if this is allowed, since you don't include the <?php that the ?> closes. also seems like it's fine if $o is the empty string – ASCII-only – 2019-04-12T23:41:51.207

@ASCII-only, you're right, empty string will also work (with a little more complaining). I'm not sure the ruling on using closing and re-opening tags, I'll update it though to stay on the up and up. Thx! – 640KB – 2019-04-12T23:54:43.537

Note that the rules for this challenge say "All possible outputs must again have a non-zero probability of occurring", this is not possible with the PHP rand function, as you can have an input text that requires more random than the size of the internal seed rand uses, so technically your answer fails to satisfy this condition in all situations – Ferrybig – 2019-04-13T20:33:59.947

@Ferrybig I agree that the legacy PHP/libc rand is not useful for much, however all supported/production versions of PHP (7.1+) use Mersenne Twister RND (mt_rand) internally for a random number generation. Is your concern that this is not sufficiently random for this challenge? – 640KB – 2019-04-13T23:08:59.413

mt_rand also uses an pseudo random number system internally, and also has the limitations. Assuming that PHP is compiled with 64 bit numbers (and that the seed, used for rand or mt_rand accepts this full range) would generate unique outputs for words upto the length of 13 characters or shorter. Pretty limiting if you ask me – Ferrybig – 2019-04-13T23:26:58.377

1

Charcoal, 10 9 bytes

↑Eθ◧ι⊕‽Lθ

Try it online! Link is to verbose version of code. Edit: Saved 1 byte thanks to @ASCII-only. Explanation:

  θ         Input string
 E          Map over characters
        θ   Input string
       L    Length
      ‽     Random value
     ⊕      Incremented
    ι       Current character
   ◧        Padded to length
↑           Print rotated

As ASCII-only points out, you can move the letters randomly up instead of down for the same effect (except that there might be extra white space at the bottom rather than the top). Printing an array of characters upwards is equivalent to printing a string normally, so the padding then just offsets each character vertically by a random amount.

Neil

Posted 2019-04-12T13:45:47.773

Reputation: 95 035

wonder if printing up instead would work? – ASCII-only – 2019-04-12T23:40:14.893

1

05AB1E (legacy), 9 bytes

εIgÝΩú}ζ»

Input as a string or list of characters (either is fine).

Try it online.

Much slower 9-bytes alternative:

gDÝsãΩúζ»

Input as a list of characters.

Try it online.

Both use the legacy version of 05AB1E, since the new version requires an explicit €S before the ζ..

Explanation:

ε       # Map each character in the (implicit) input to:
 Ig     #  Take the length of the input
   Ý    #  Create a list in the range [0, input-length]
    Ω   #  Pop and push a random integer from this list
     ú  #  Pad the current character with that many leading spaces
}ζ      # After the map: zip/transpose; swapping rows/columns (with space as default filler)
  »     # Then join all strings by newlines (and it output implicitly as result)

g          # Get the length of the (implicit) input-list
 D         # Duplicate this length
  Ý        # Create a list in the range [0, input-length]
   sã      # Take the cartesian product of this list that many times
     Ω     # Pop and push a random list from this list of lists of integers
      ú    # Pad the characters in the (implicit) input-list with that many spaces
       ζ   # Zip/transpose; swapping rows/columns (with space as default filler)
        »  # Then join all strings by newlines (and it output implicitly as result)

Kevin Cruijssen

Posted 2019-04-12T13:45:47.773

Reputation: 67 575

Was thinking along the lines of gD¸s∍ÝδΩ but it's longer... and ooo... this doesn't even work in the new 05AB1E ;). – Magic Octopus Urn – 2019-04-18T14:53:32.127

1

C (gcc), 131 125 bytes

-6 bytes thanks to ceilingcat

f(char*s){int l=strlen(s),R[l],i=l,j;for(srand(&l);i--;)R[i]=rand()%l;for(;++i<l*l;printf("\n%c"+!!j,i/l^R[j=i%l]?32:s[j]));}

Try it online!

gastropner

Posted 2019-04-12T13:45:47.773

Reputation: 3 264

@ceilingcat Cheers! – gastropner – 2020-01-27T20:39:58.857

1

Julia, 69 bytes

f(s)=(n=length(s);z=fill(' ',n,n);for i=1:n z[rand(1:n),i]=s[i]end;z)

This defines a function f that accepts a String or Vector{Char} and returns a Matrix{Char}.

Ungolfed:

function f(s)
    n = length(s)
    z = fill(' ', n, n)  # an n×n matrix of spaces
    for i = 1:n
        # set a random entry in the ith column to the ith character in s
        z[rand(1:n),i] = s[i] 
    end
    z
end

Example:

julia> f("test")
4×4 Array{Char,2}:
 't'  ' '  ' '  ' '
 ' '  ' '  ' '  ' '
 ' '  'e'  ' '  't'
 ' '  ' '  's'  ' '

This could surely be better; my golfing skills are pretty rusty.

Try it online!

Alex A.

Posted 2019-04-12T13:45:47.773

Reputation: 23 761

1

Perl 5 -F, 50 49 bytes

-1 by @DomHastings

map$;[rand@F][$i++]=$_,@F;say map$_||' ',@$_ for@

Try it online!

Xcali

Posted 2019-04-12T13:45:47.773

Reputation: 7 671

Nice, actually works! :P You can save a couple of bytes with map$_||$",@$_ too! – Dom Hastings – 2019-04-18T07:51:29.457

1

PowerShell, 108 102 98 bytes

-4 bytes thanks to mazzy

$a=1..($z=($y=$args|% t*y).count)|%{random $z}
1..$z|%{-join($y|%{" $_"[$a[$i++%$z]-eq+$r]});$r++}

Try it online!

Basically iterates 1..length of the string twice, once to get random line locations for each character, and a second time to actually build each line using those indices. Figuring out how to do it in one sweep is where the big byte savings are.

Veskah

Posted 2019-04-12T13:45:47.773

Reputation: 3 580

1Try it online!? – mazzy – 2019-07-17T10:07:02.377

1

AWK, 120 118 bytes

{while(i++<NF)r[i]=int(rand()*NF)+1;for(;l++<NF;j=k=0){while(k++<NF)if(r[k]==l){printf"%*c",k-j,$k;j=k}print FS}}

Try it online!

I've included the 5 bytes in the score for the necessary -F '' switch, but I had to use the BEGIN{FS=""} directive for the TIO link since it doesn't process the command-line switch properly.

rootbeersoup

Posted 2019-04-12T13:45:47.773

Reputation: 111

0

Python - 92 bytes

import random
lambda s:map(None,*[(random.randrange(len(s))*' '+c).ljust(len(s))for c in s])

Maltysen

Posted 2019-04-12T13:45:47.773

Reputation: 25 023

You have to include the import random – MilkyWay90 – 2019-04-12T22:20:51.913

@MilkyWay90 d'oh – Maltysen – 2019-04-13T11:23:19.107

1You can save 1 byte by using from random import* instead. – orthoplex – 2019-04-13T11:44:16.230

I think map(None,... doesn't work in Python 3, so you should specify Python 2 in your title. – orthoplex – 2019-04-13T11:50:10.343

0

SmileBASIC 3, 62 bytes

LINPUT T$L=LEN(T$)CLS
FOR I=0TO L-1LOCATE,RND(L+1)?T$[I];
NEXT

snail_

Posted 2019-04-12T13:45:47.773

Reputation: 1 982

0

Red, 84 bytes

func[s][foreach n random collect[repeat n length? s[keep n]][print pad/left s/:n n]]

Try it online!

Galen Ivanov

Posted 2019-04-12T13:45:47.773

Reputation: 13 815

0

K (oK), 20 bytes

Solution:

+c$(-1-c?c:#x)$++x:

Try it online!

Explanation:

+c$(-1-c?c:#x)$++x: / the solution
                 x: / store input as x
                +   / flip (enlist)
               +    / flip again (break into chars)
              $     / pad (each) character
   (         )      / do this together
           #x       / length of x
         c:         / save as c
      -c?           / choose (?) c times from c
    -1              / subtract from -1
 c$                 / pad to length of x
+                   / flip

streetster

Posted 2019-04-12T13:45:47.773

Reputation: 3 635

0

Python 3, 140 131 bytes

from random import*
def f(s):
	e=range(len(s))
	p=[choice(e)for t in s]
	for r in e:print(''.join((r-p[i]and' 'or s[i]for i in e)))

Try it online!

movatica

Posted 2019-04-12T13:45:47.773

Reputation: 635

0

Python 3, 208 bytes

import random as r;x=input();R=range(len(x));c=[r.choice(R) for i in R];y=[' '*c[i]+x[i]+' '*(len(x)-c[i]) for i in R];R=range(len(y));print('\n'.join([''.join(r) for r in [[y[i][j] for i in R] for j in R]]))

Creates a list of random choices, then makes a list of columns with blank space everywhere except at the index specified by each random choice. The columns are transposed into rows and printed out with newlines between them.

Try it online!

CyborgOctopus

Posted 2019-04-12T13:45:47.773

Reputation: 131