Hacker News new | past | comments | ask | show | jobs | submit login
A single line of Basic sends readers into a labyrinth (slate.com)
153 points by munishdayal on Dec 1, 2012 | hide | past | favorite | 89 comments



A version that works in bash:

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

Source and credit: http://stackoverflow.com/a/13612327


Beautiful: http://i.imgur.com/tsFR9.png

I can't believe I've programmed for 15 years without knowing about this.


PowerShell for those on Windows:

for (;;) { write-host -n (get-random ╱,╲) }

Obviously this requires a font supporting those characters. MS Mincho seems to serve well: http://www.microsoft.com/typography/fonts/font.aspx?FMID=206


If you don't want to install a font, you can use slashes. Works pretty well.

    for (;;) { write-host -n (get-random /,\) }
It's kind of nice with _ and | too :

    for (;;) { write-host -n (get-random '_','|') }


You can also just use for(){...}. No need for the semicolons there.


After reading the article, I was really hoping the comments would be full of ports, glad to see this thread! Here's a version for the Inferno shell, because I couldn't resist:

wm/sh -c 'load std mpexpr;while{}{unicode -t ${expr 2571 1 rand +}}'


The one-line shell command python version ended up being surprisingly painful:

    echo "# -*- coding: utf-8 -*-\\nfrom random import choice\\nwhile(True):\\n  print(choice(\"╱╲\"), end='')" | python3
or

    python3 -c 'exec("from random import choice\nwhile(True):\n  print(choice(\"╱╲\"), end='"''"')")'
(Oh, the fun of deeply nested quote escaping!)

The exec/echo thing being required since we need multiple lines, because while ';' is the statement separator python barfs on "import foo; while (True): pass". Bug or spec?


I did it with

    python3 -c "`echo -e 'import time\nwhile True:\n print(u"\u2571\u2572"[int(time.time() * 100)%2],end="")\n time.sleep(0.001)'`"
The neat thing about it is that the randomness comes from the inexactness of time.sleep


You can write it like this:

    python -c "while 1:import sys,random;sys.stdout.write(random.choice('\/'))"
and it's shorter too !


It is a spec. Python in general prefers a statement over an expression, so the while statement does not have an expression form.


Python has two types of statements; "simple" statement and "compound" statements. Compound statements are those, like the if- and while-statements, which require multiple lines.

Only simple statements may be separated by a ';', not compound statements.

This is a bit different than what you wrote. For example, "x=4" is a statement, and not an expression, but "x=4;y=5;z=6" is a valid Python line because assignment is a simple statement, and multiple simple statements may be separated by a ';'.


Ruby:

echo 'loop do print ["\u2571","\u2572"].sample end'|ruby


This makes it slower, so it's more pleasant to watch:

echo 'loop do print ["\u2571","\u2572"].sample ; sleep 0.001 end'|ruby


Why piping to ruby? You have the -e operator for that:

    ruby -e 'loop do print ["\u2571","\u2572"].sample ; sleep 0.001 end'


I can't resist a golf!

    ruby -e 'print %W{\u2571 \u2572}.sample while sleep 1e-3'
(Updated: Made 3 chars shorter.)

And a shorter, but cheats, version:

    yes|ruby -pe'$_=%W{\u2571 \u2572}.sample;sleep 1e-3'
My favorite overall balance of readability and shortness though is:

    ruby -e 'print %w{╱ ╲}.sample while sleep 0.01'


Amazing! I always forget about the `something while other_thing` syntax, thank you for reminding me of it :)


I tried to like it. I bumped up the font size to 36 points. I slowed down the loop to .01 seconds. Unfortunately, it isn't remotely the same.

The C64 had magic. I miss my old C64.


Try this: http://www.secretgeometry.com/apps/cathode/

Lots of fun. Use it to make old stuff like this feel more authentic, or just run your builds in it and tell your housemates you're haxxing teh gibson.

TBH though the reason it doesn't feel the same is ultimately because you're not the person you were 20-30 years ago, whoever that was. Nothing to do with the code or its execution environment.


While it was far better (and thanks for the pointer to Cathode!), I figured out what the problems are. First (and probably most important), the lines being output don't touch each other, so there are horizontal gaps across the entire screen. Second, the glyphs are much thinner, reducing the feeling of "wall".


What a totally clever app. And extremely well done. Thanks for sharing! Brought joy to this not-so-old-but-old-enough child of the 80s :)


A slight modification to make it generate mostly solvable mazes: `yes 'c=(┻ ┫);printf ${c[RANDOM%2]}'|bash`


choosing from

  || __     # (2 pipes, one space, 2 bars)
seems to work pretty well, too


Perl 6, using Rakudo:

perl6 -e 'loop { print <9585 9586>.roll.chr }'

In fact, perl6 -e 'loop { print <╱ ╲>.roll }' will work too if your terminal is set up properly.


Interactive JavaScript version: http://js.do/samples/labyrinth


Hmm.. thats not a oneliner. Which is what makes the Basic version interesting in the first place. Also it differs in that it does not run continously.

I shortened it a bit:

http://js.do/code/154

Still not as elegant as the Basic version.

This is one of the big downsides of how javascript is implemented in the browsers these days. There is no simple way to redraw the screen. You have to turn your whole program into a state machine and work with timouts just to display something.


> ...not as elegant as the BASIC version

Beautiful statement. So much unintentional irony, and at the same time so very true in this context.


I won't claim this is elegant, but here's the same thing in one line:

for(i=1;i<300;i++) document.write(i % 30 == 0 ? "<br>" : (Math.random()>0.5) ? '\u2571' : '\u2572');


(function draw(i){ return function () { document.write(i%30==0 ? '<br>' : Math.random()>0.5?'\u2571':'\u2572'); setTimeout(draw(i+1), 100) } }(1)())

A throttled run-forever version.

edit, slightly neater:

(function draw(i){ setTimeout(function (){ document.write(i%30==0 ? '<br>' : Math.random()>0.5 ? '\u2571':'\u2572'); draw(i+1); }, 100) }(1))


Here's a JavaScript version without the linebreaks (it uses zero-width spaces instead): http://tinkerbin.com/uEvgxZ6g

JS: for(i=0;i<1000;i++) document.write(['\u2571\u200B', '\u2572\u200B'][(Math.floor((Math.random()*2)))])

CSS: body {line-height: 1}


Watch it character-by-character in bash:

while true; do c=(╱ ╲);printf ${c[RANDOM%2]}; sleep .0001; done


Not for me. "syntax error near unexpected token ';' == no printf


It worked for me when I copied it, not when I tried retyping it. I think the fancy unicode ╱ ╲ matters vs. plain ascii \/


ascii works for me if I escape the \, ie: / \\


Was it maybe trouble with '\' being treated as an escape character?


Another fun example of a computer generating mezmerizingly complex art from simple code is that as a kid, I wrote a Langton's Ant implementation in QBASIC on a DOS machine (don't remember the version). IIRC this was using Graphics Mode 1 (320 x 200 pixel framebuffer), and I used PSET and PRESET to draw white and black pixels. This so far sounds ordinary but:

The cool thing was that I forgot to write the bounds check to make sure the ant stayed inside the framebuffer! So the ant would happily trundle off-screen, encounter arbitrary bits that were in that memory, and would often eventually return back on the screen from a different location. Or sometimes (probably due to encountering zeros offscreen) it would run a lap around the edge of the screen and then veer back inward.

Langton's Ant: http://en.wikipedia.org/wiki/Langtons_ant


I had the same bug (no pun intended), but in JavaScript. It does produce some interesting effects after making a few laps.

http://chengsun.github.com/walk.html

I still am a "kid", and it amuses me that I'm making the same stuff on modern platforms that have already been done on DOS machines by kids like me.


Awesome!! Glad you were able to reproduce the effect! :D


Reminds me of this blog post about a Tron game that didn't check memory bounds either: http://blog.danielwellman.com/2008/10/real-life-tron-on-an-a...


Just to be clear...it doesn't generate a labyrinth. It generates something that looks like a labyrinth, but isn't one. It's just random forward and backward slashes.


The device you're on doesn't generate text. It looks like text, but it's really just red, green and blue dots.


I presume OP means that it may not have a 'solution'. i.e. i can't really navigate it like a 'proper' labyrinth.


> The device you're on doesn't generate text. It looks like text, but it's really just red, green and blue dots.

Printers don't generate text. It looks like text, but it's really just ink on paper.

The point is that real labyrinths have properties these pseudo-labyrinths don't. To be specific:

> In colloquial English, labyrinth is generally synonymous with maze, but many contemporary scholars observe a distinction between the two: maze refers to a complex branching (multicursal) puzzle with choices of path and direction; while a single-path (unicursal) labyrinth has only a single, non-branching path, which leads to the center. A labyrinth in this sense has an unambiguous route to the center and back and is not designed to be difficult to navigate.

http://en.wikipedia.org/wiki/Labyrinth


"In colloquial English, labyrinth is generally synonymous with maze" means that yes, this is a labyrinth because "labyrinth" is synonymous with "maze". "Many contemporary scholars observe a distinction between the two" means "most people don't observe any distinction between the two."


This is an interesting long-running debate in procedural generation more generally. There are a lot of approaches, hybrids between them, and orthogonal decisions, but two ends of one axis of what I'd call "procedural faithfulness" are:

1. Attempt to faithfully simulate an underlying process. An example: procedurally generate canyons by simulating water flow and erosion. Dwarf Fortress tries to take this one to its logical conclusion, doing things like simulating thousands of years of history in order to decide where to place things.

2. Produce an algorithm that mimics a desired result without necessarily using a process even close to what produced the original. Often this involves attempting to capture patterns in the original without attempting to capture why those particular patterns arose. Many grammar-based methods take this approach, such as the classic "shape grammars" used in architecture, as do data-mining approaches.

This book looks at something closer to #2 from the perspective of procedural faithfulness. But it's a variant that was particularly popular in early algorithmic art: pick an algorithm that has interesting outputs, tweak it, and see what you can do with the results. So in a sense it's closer to #1 in that its macro-properties are emergent, rather than being specifically optimized for (it's not doing things like solving for path reachability).

If generating a city, for example, you could attempt any of these approaches. You could build a little artificial agent society where you simulate agents going to work, having families, buying houses, etc., and their actions produce a city. Or, you could play with hand-coded city-generation algorithms that produce interesting patterns, and go with one. Or, you could choose specific targets or constraints (maybe culled from databases of real city geometry) and use constraint-solving or genetic algorithms or generative machine-learning algorithms to produce the desired results. Different pros and cons.

It's something I've been thinking a bit about lately, because I've been trying to understand the landscape of systems that claim to do "automated game design" [1]. Much of the work takes a simulation-based approach: tries to come up with a theory of what makes a game "balanced" or "fun", and optionally also simulates a broader theory of game design, playtesting, and revision. But another approach is to view it as crafting generative spaces of game variants, which is more of a theory of how game structure can vary than a theory of why it varies, and gives a different (not clearly better or worse) angle on the problem.

[1] http://www.kmjn.org/notes/generating_mechanics_bibliography....


Here's one with some contiguous areas highlighted, showing that it's made of interwoven islands: http://i.imgur.com/BX0ok.png?1


It may be somewhat interesting to analyze the properties of randomly-generated labyrinths of this type.

For instance, some spaces are clearly enclosed. i.e., there is a finite border outside which you cannot escape. Some paths seem to run on for a very long time. Are all paths enclosed? If so, given a random point on a randomly generated maze, what is the average area of an enclosure?


I don't think a "corridor" ever forks, so it's not really a labyrinth where there are decisions to be made about which way to go, it's just a bunch of disjoint long, twisty hallways.


Add a space every now an then and it becomes a 'real' maze:

    #include <stdio.h>
    main() { for(;;) {printf(rand()%2 ? "╱":"╲"); if(rand()%100<1) putchar(' ');} }


Mathematicians have given this some thought. The relevant keyword seems to be "Truchet tilings", though there's a curved variation that's more widely known. Here's a very accessible overview with some lovely generalizations of this pattern: http://mypages.iit.edu/~krawczyk/rjkisama11.pdf


Can this book not be available in digital form? Amazon and MIT Press only list the hardcover edition.

http://mitpress.mit.edu/books/10-print-chr2055rnd1-goto-10-0


The article links to the book's (free) download page: http://10print.org


Thanks, I wonder who is the idiot who downvotes an honest question.

Still, I would pay $9.99 for mobi or epub. PDF is inconvenient for the Kindle and the iPad. Converting with Calibre is a chore, and rarely yields good results.


I find GoodReader excellent for pdfs on the iPad. It's not only a great reader but also allows annotation etc plus makes getting pdfs into and out of your iPad fairly painless


I'm downloading it now. Would like to donate by buying but UK Amazon site lists it as out of stock.


I discovered this on my Commodore PET probably about 1980 and presented it at a users' group meeting (in the Los Angeles area). I have no way to prove this right now, but I swear that this is true.


The article seems to be a a lot of hyperbole without much substance.

The code itself is simple. CHR$(205.5 + RND(1)) becomes CHR$(205) or CHR$(206) depending on the random number. 205 generates a \ , and 206 generates a /.


   10 PRINT MID$("/\",1.5+RND(1),1);:GOTO 10
Run it on another old-school computer, like an Apple II, and you won’t get the same transfixing result, for details that have to do with the Commodore 64’s character set, called PETSCII.


Is it the same as this C snippet?

  main() { while (1) printf("%c", rand()%2 ? '/' : '\\'); }
Edit: Golfed my original snippet down, then put back original after mmphosis's identical translation.


Small variant:

void main() { while (1) printf("%c", "/\\"[rand()%2] ); }


yes.

  #include <stdio.h>
  main() { for(;;) printf(rand()%2 ? "/" : "\\"); }


i think the key point is that / and \ do not span their character cells from corner to corner.


I quite thought the same thing. Ok, it's fun, but writing a lengthy article like this for that ... Wait, a book ?


Reading this right now. I also made an SVG version at http://romulusetrem.us/10print/


If you have a machine with a UTF-8 terminal and a recent-ish Perl (tested on Perl 5.10 + Mac OS X 10.6), you can play along at home by running:

    perl -e '$ |= 1; binmode(STDOUT, ":utf8"); while (1) { print chr(9585.5 + rand()); select(undef, undef, undef, 0.01); }'


While interesting, it isn't the same. I'm not sure what, exactly, the difference is, but I don't get the feeling of "labyrinth". Instead, I get the feeling of a bunch of similar glyphs.


Try changing the 0.01 at the end to 0.001 or lower.


This was the most beautiful one for me: http://cl.ly/image/3y0I2J2q421E


That gives me a gentle optical illusion - there's a sort of 3d effect going on and I can't tell which way is up or out or in or down.


That's cool! If you don't have a C64, you can input a similar line of code at https://clubcompy.com/?shell

10 PRINT TOCHAR 13.5 + RANDOM; GOTO 10 RUN

I prefer the hopelessly lost maze variant! :>

10 PRINT TOCHAR 222.5 + RANDOM; GOTO 10 RUN


I've always had a soft spot for mazes. Seeing how a recursive function could solve a maze was what got me hooked on programmng to begin with. At the time it was mind-blowing. It was the first time that software seemed like magic.


Highly recommend picking up this book. As a former local colleague of Nick Montfort, I can say it's going to be a great read.


I recently read Racing the Beam (Monfort, Bogost), a survey of the defining technical details of the Atari 2600. I've read a number of gaming history books, and RtB was by far the most interesting. It might be better described as a hacker history of the platform, focusing on the limitations of the system and the hacks around them that caused Atari VCS games to have a personality distinct from its contemporaries. Also highly recommended.

[http://www.nickm.com/vcs/]

[http://www.platformstudies.com/]


Racket version (should work in most Scheme implementation too actually):

    (let loop () (display (list-ref '("╱" "╲") (random 2))) (loop))
Use `racket -e <expr>` tu run it directly, for Guile it should work with `guile -c <expr>` but it seems that the unicode charaters make it crash (however it works if you copy the line of code in Guile's REPL rather than on the command line with -c).



JavaScript (in a browser):

var r = 0; function go(){setTimeout('document.write(r++ % 80 ? Math.round(Math.random()) ? "\\\\" : "/" : "<br>"); go()', 100)}; go();

This was actually a lot more difficult for me to write than I thought it would be. `document.write` blocks and also a browser doesn't wrap by default, hence the r variable.

This will, of course, nuke any page you happen to run it on. :)

P.S. Why is code formatted so badly on HN these days? Until that's fixed I'll just use normal formatting.


The node version is also hard to write. I tried `process.stdout.write` but it always prints a newline, at least from the repl.


13 bytes version in 6502 machine code:

7C 00 20 D2 FF A1 85 29 01 E9 92 D0 F5

(Save this as a .prg file and run it in your favourite C64 emulator or on the real thing itself. Please note that the first two bytes are actually required by .prg file format to indicate load address. The actual code is 11 bytes).

For more information:

http://pouet.net/prod.php?which=60810


Python version:

  python -c 'import random; print "".join(random.choice(r"/\\") for i in range(80*24))'


Same thing, but with corner-to-corner unicode glyphs:

    python -c 'import random; print "".join(random.choice([u"\u2571",u"\u2572"]) for i in range(80*24))'


python -c "print ''.join(__import__('random').choice(u'\u2571\u2572') for i in range(80*24))"


If you were like me back in the 80s on a PET you did that and then moved up to more complex randomness with other graphic symbols like the four corner curves or bends. I think they had that line in one issue of Creative Computing with a bunch of other character graphic effect snippets.


You kids and your fancy one liners:

import java.util.Random;

public class Maze {

public static void main(String[] args) {

		String[] blah = new String[] { "╱", "╲" };

		Random rng = new Random();

		for (;;) {
			System.out.print(blah[rng.nextInt(2)]);

		}
	}
}


I remember learning this trick, not sure where I learned it from but this particular snippet had a huge impact on me. I spent hours and hours playing with variations on this.



Same fascination with this, I think, as with the simple rules of Conway's. S'pose there are other such "a-mazing" simple revelations still floating in the void?


I spent many long hours playing with this on a Vic-20 back in the 80s. Commodore BASIC was so weird that you pretty much had to hack it.



PHP:

while (1) echo rand (0,1) ? "╱" : "╲";


You can execute it on the command line like this:

php -r 'while (1) echo rand (0,1) ? "╱" : "╲";'




Join us for AI Startup School this June 16-17 in San Francisco!

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: