Question

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:

Was it helpful?

Solution

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

OTHER TIPS

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)

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.

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

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];}

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='')
#!/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()

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)"
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top