23

I picked up a copy of the book 10 PRINT CHR$(205.5+RND(1)); : GOTO 10

This book discusses the art produced by the single line of Commodore 64 BASIC:

10 PRINT CHR$(205.5+RND(1)); : GOTO 10

This just repeatedly prints randomly character 205 or 206 to the screen from the PETSCII set:

I'm not sure why the original uses the characters 205 and 206 instead of the identical 109 and 110. Also, I prefer to add a clear at the beginning. This is what I usually type into the C64:

1?CHR$(147)
2?CHR$(109.5+RND(1));:GOTO2
RUN

You can try this all for yourself in an emulator, such as this one using Flash or JavaScript:


I decided it would be amusing to write a bash line to do something similar.

I currently have:

clear; while :; do [ $(($RANDOM%2)) -eq 0 ] && (printf "\\") || (printf "/"); done;

Two questions:

  1. Any suggestions for making this more concise?
  2. Any suggestions for a better output character? The forward and backward slash are not nearly as beautiful since their points don't line up. The characters used from PETSCII are special characters, not slashes. I didn't see anything in ASCII that could work as well, but maybe you can suggest a way to pull in a character from UTF-8 or something else?

Best ANSWERS So Far

Shortest for bash (40 characters):

yes 'c=(╱ ╲);printf ${c[RANDOM%2]}'|bash

Here is a short one for zsh (53 characters):

c=(╱ ╲);clear;while :;do printf ${c[RANDOM%2+1]};done

Here is an alias I like to put in my .bashrc or .profile

alias art='c=(╱ ╲);while :;do printf "%s" ${c[RANDOM%2]};done'

Funny comparing this to the shortest I can do for C64 BASIC (23 characters):

1?C_(109.5+R_(1));:G_1

The underscores are shift+H, shift+N, and shift+O respectively. I can't paste the character here since they are specific to PETSCII. Also, the C64 output looks prettier ;)

You can read about the C64 BASIC abbreviations here:

spex
  • 1,110
  • 10
  • 21
  • 1
    You might be also interested in [Random maze generator](http://www.perlmonks.org/?node_id=843144). It removes all unaccessible rooms larger than 1 after generating the maze. A [screencast](http://www.youtube.com/watch?v=HqyeksHSoRo) is available, too. – choroba Nov 28 '12 at 21:39
  • @choroba: That's really great, thank you! I will certainly be playing with that. – spex Nov 29 '12 at 00:50
  • 2
    One of your ancillary questions: "why 205 and 206 instead of 109 and 110?" The book addresses this in depth. Quick answer is that that is how the program was published at the time (circa 1982). The authors speculate on page 226: "A likely explanation can be found in the way the Commodore 64 responds to PRINT ASC("X")..." "[a user] could type PRINT ASC("/") and the computer would respond with "206." Not really your main question but couldn't resist answering :) – Steve Koch Apr 20 '13 at 04:56
  • If I understand the BASIC abbreviations correctly, the `R_` in your shortest version should be `RN`, not `RA` (for `RND`). – nneonneo Jun 23 '16 at 17:42

8 Answers8

6

After looking at some UTF stuff:

2571 BOX DRAWINGS LIGHT DIAGONAL UPPER RIGHT TO LOWER LEFT
2572 BOX DRAWINGS LIGHT DIAGONAL UPPER LEFT TO LOWER RIGHT

(╱‬ and ╲) seem best.

f="╱╲";while :;do print -n ${f[(RANDOM % 2) + 1]};done

also works in zsh (thanks Clint on OFTC for giving me bits of that)

paultag
  • 103
  • 5
  • Those definitely look better `clear; while :; do [ $(($RANDOM%2)) == 0 ] && (printf "╱") || (printf "╲"); done;` Thanks! Still open to other symbol ideas and making the code more concise. I'm also wondering now if there is a better way to print those UTF-8 characters... – spex Nov 28 '12 at 18:42
  • 1
    Your example only works in zsh. Bash doesn't have the print command. – spex Nov 28 '12 at 20:22
6

How about this?

# The characters you want to use
chars=( $'\xe2\x95\xb1' $'\xe2\x95\xb2' )
# Precompute the size of the array chars
nchars=${#chars[@]}
# clear screen
clear
# The loop that prints it:
while :; do
    printf -- "${chars[RANDOM%nchars]}"
done

As a one-liner with shorter variable names to make it more concise:

c=($'\xe2\x95\xb1' $'\xe2\x95\xb2'); n=${#c[@]}; clear; while :; do printf -- "${c[RANDOM%n]}"; done

You can get rid of the loop if you know in advance how many characters to print (here 80*24=1920)

c=($'\xe2\x95\xb1' $'\xe2\x95\xb2'); n=${#c[@]}; clear; printf "%s" "${c[RANDOM%n]"{1..1920}"}"

Or, if you want to include the characters directly instead of their code:

c=(╱‬ ╲); n=${#c[@]}; clear; while :; do printf "${c[RANDOM%n]}"; done

Finally, with the size of the array c precomputed and removing unnecessary spaces and quotes (and I can't get shorter than this):

c=(╱‬ ╲);clear;while :;do printf ${c[RANDOM%2]};done

Number of bytes used for this line:

$ wc -c <<< 'c=(╱‬ ╲);clear;while :;do printf ${c[RANDOM%2]};done'
59

Edit. A funny way using the command yes:

clear;yes 'c=(╱ ╲);printf ${c[RANDOM%2]}'|bash

It uses 50 bytes:

$ wc -c <<< "clear;yes 'c=(╱ ╲);printf \${c[RANDOM%2]}'|bash"
51

or 46 characters:

$ wc -m <<< "clear;yes 'c=(╱ ╲);printf \${c[RANDOM%2]}'|bash"
47
gniourf_gniourf
  • 44,650
  • 9
  • 93
  • 104
  • Tons of great information, thank you. That said, your final single-line script is not filling the screen. Any thoughts? It seems it might be an issue with directly including the characters. `c=($'\xe2\x95\xb1' $'\xe2\x95\xb2');clear;while :;do printf ${c[RANDOM%2]};done` works fine. – spex Nov 28 '12 at 19:18
  • @spex Sorry, I have no idea why it doesn't work when including the characters directly! maybe it has something to do with the input method of your terminal... Try to pass the string through `od`: `od -t x1 <<< 'c=(╱‬ ╲);clear;while :;do printf ${c[RANDOM%2]};done'` to see what your terminal actually sends... the first line of the output should be `63 3d 28 e2 95 b1 e2 80 ac 20 e2 95 b2 29 3b 63`. – gniourf_gniourf Nov 28 '12 at 19:27
  • Did so, got the same od output `63 3d 28 e2 95 b1 e2 80 ac 20 e2 95 b2 29 3b 63` – spex Nov 28 '12 at 19:37
  • @spex oh, then you'll have to stick with entering the utf-8 code with the `$'...'` construct instead of inserting the character directly, sorry about that. (but it works fine here on Debian with bash 4.2.24). – gniourf_gniourf Nov 28 '12 at 19:54
  • Yeah, it was a copy paste issue, couldn't pinpoint it, but just manually reconstructed the line to get it to work `c=(╱ ╲);while :;do printf "%s" ${c[RANDOM%2]};done` Thanks! – spex Nov 28 '12 at 19:56
4

Here is my 39 character command line solution I just posted to @climagic:

grep -ao "[/\\]" /dev/urandom|tr -d \\n

In bash, you can remove the double quotes around the [/\] match expression and make it even shorter than the C64 solution, but I've included them for good measure and cross shell compatibility. If there was a 1 character option to grep to make grep trim newlines, then you could make this 27 characters.

I know this doesn't use the Unicode characters so maybe it doesn't count. It is possible to grep for the Unicode characters in /dev/urandom, but that will take a long time because that sequence comes up less often and if you pipe it the command pipeline will probably "stick" for quite a while before producing anything due to line buffering.

deltaray
  • 382
  • 3
  • 16
3

Bash supports Unicode now, so we don't need to use UTF-8 character sequences such as $'\xe2\x95\xb1'.

This is my most-correct version: it loops, prints either / or \ based on a random number as others do.

for((;;x=RANDOM%2+2571)){ printf "\U$x";}
41

My previous best was:

while :;do printf "\U257"$((RANDOM%2+1));done
45

And this one 'cheats' using embedded Unicode (I think for obviousness, maintainability, and simplicity, this is my favourite).

Z=╱╲;for((;;)){ printf ${Z:RANDOM&1:1};}
40

My previous best was:

while Z=╱╲;do printf ${Z:RANDOM&1:1};done
41

And here are some more.

while :;do ((RANDOM&1))&&printf "\U2571"||printf "\U2572";done
while printf -v X "\\\U%d" $((2571+RANDOM%2));do printf $X;done
while :;do printf -v X "\\\U%d" $((2571+RANDOM%2));printf $X;done
while printf -v X '\\U%d' $((2571+RANDOM%2));do printf $X;done
c=('\U2571' '\U2572');while :;do printf ${c[RANDOM&1]};done
X="\U257";while :;do printf $X$((RANDOM%2+1));done

Now, this one runs until we get a stack overflow (not another one!) since bash does not seem to support tail-call elimination yet.

f(){ printf "\U257"$((RANDOM%2+1));f;};f
40

And this is my attempt to implement a crude form of tail-process elimination. But when you have had enough and press ctrl-c, your terminal will vanish.

f(){ printf "\U257"$((RANDOM%2+1));exec bash -c f;};export -f f;f

UPDATE:

And a few more.

X=(╱ ╲);echo -e "\b${X[RANDOM&1]"{1..1000}"}" 46
X=("\U2571" "\U2572");echo -e "\b${X[RANDOM&1]"{1..1000}"}" 60
X=(╱ ╲);while :;do echo -n ${X[RANDOM&1]};done 46
Z=╱╲;while :;do echo -n ${Z:RANDOM&1:1};done 44
philcolbourn
  • 4,042
  • 3
  • 28
  • 33
  • It's interesting to note the speed differences betweeen your 41 character solution `while Z=╱╲;do printf ${Z:RANDOM&1:1};done` and this 40 character solution `yes 'c=(╱ ╲);printf ${c[RANDOM%2]}'|bash` – spex Jan 21 '14 at 19:30
  • I think using 'yes' is cheating a little as it is not bash. But we learn from these challenges. – philcolbourn Jan 30 '14 at 01:53
2

Sorry for necroposting, but here's bash version in 38 characters.

yes 'printf \\u$[2571+RANDOM%2]'|bash

using for instead of yes inflates this to 40 characters:

for((;;)){ printf \\u$[2571+RANDOM%2];}
lierdakil
  • 548
  • 2
  • 9
  • Nice contribution! Unfortunately, to get that to work for the default Mac OS X version of Bash, it would have to be `yes 'printf \\xe2\\x95\\xb$[1+RANDOM%2]'|bash` which is 45 characters. – spex Mar 24 '15 at 03:15
  • I tried this on 4.3. I believe first version to support `\u` escapes in printf was 4.1. – lierdakil May 10 '15 at 05:33
2

109 chr for Python 3 Which was the smallest I could get it.

#!/usr/bin/python3
import random
while True:
    if random.randrange(2)==1:print('\u2572',end='') 
    else:print('\u2571',end='')
Baby Birb
  • 1
  • 2
1
#!/usr/bin/python3
import random
import sys

while True:
    if random.randrange(2)==1:sys.stdout.write("\u2571")
    else:sys.stdout.write("\u2572")
    sys.stdout.flush()
1

Here's a version for Batch which fits in 127 characters:

cmd /v:on /c "for /l %a in (0,0,0) do @set /a "a=!random!%2" >nul & if "!a!"=="0" (set /p ".=/" <nul) else (set /p ".=\" <nul)"