A Universe in One Line of Code with 10 PRINT
makeartwithpython.com
makeartwithpython.com
I learned to program using BASIC when I was between 5 and 10 years old. My dad was a software engineer at DEC, and he had a kit computer in our basement that he let us play with once in a while. My first program was a number-guessing game, and it went something like this:
10 NUMBER = 23
20 PRINT "I'm thinking of a number."
30 INPUT "Make a guess: " $GUESS
40 IF GUESS < NUMBER THEN
50 PRINT "Too low!"
60 GOTO 30
70 ELSEIF GUESS > NUMBER THEN
80 PRINT "Too high!"
90 GOTO 30
100 PRINT "You got it!"
That was pretty straightforward, because everything about it was linear except for the GOTOs. You can think about each step with very little planning, and when you need a break in program flow you can use a GOTO. It also means when teaching, you can introduce each new concept as the need arises: store a value, print a statement, prompt for input, check a condition, and go back to a previous statement.Let's see the same program in Python:
number = 23
print("I'm thinking of a number.")
guess = 0
while guess != number:
guess = input("Make a guess: ")
guess = int(guess)
if guess < number:
print("Too low!")
elif guess > number:
print("Too high!")
else:
print("You got it!")
The overall program logic isn't much different when you read the code. But to write this code, you need to know a number of things in advance. You need to know how you're going to approach the problem. You need to know what a loop is, and specifically what a while loop is. Conceptually, especially for a young person, a while loop is much more abstract than a GOTO statement.I am happy that people get to learn a language like Python as their first language today. They can do much more interesting things, in a shorter amount of time, than they could in basic 40 years ago. But the very first programs people write seem fundamentally different, and that's not a bad thing.
Also one thing I remember from back when was that if there was a problem I did not understand (I was mostly learning by modifying example programs that I somehow got) the only way to solve them was to try more things and harder. I did not have any books and internet was a luxury. Eventually I have understood more of the code and got better.
Nowadays it is way easier to remain ignorant and still be productive.
The thing is BASIC does have other constructs. GOSUB[0] has been around in the 70s and that worked like a GOTO except when a RETURN statement was hit the code would then jump back to the point where it GOSUB'ed - effectively behaving as a rudimentary subroutine (hence the name, "GoSub").
There was also the FN[2][3] statement that allowed you to write basic functions, albeit you were limited to 1 liners. But by the 80s some dialects of BASIC were supporting more complex procedures[3][4].
[0] http://www.hoist-point.com/applesoft_basic_tutorial.htm#stri...
[1] http://www.cpcwiki.eu/index.php/Locomotive_BASIC#FN
[2] https://www.c64-wiki.com/wiki/FN
[3] http://www.bbcbasic.co.uk/bbcwin/tutorial/chapter16.html
[4] http://www.bbcbasic.co.uk/bbcwin/tutorial/chapter17.html
10 FOR I=1 TO 10
20 PRINT I
30 NEXT IBASIC certainly had it's faults but it was a reasonably complete language. However it can't have helped the number of different dialects there were in the 70s and 80s. Each brand of consumer electronics seemed to ship with a subtly different list of statements which meant I think lead people to often stick with the core basics of, erm, BASIC.
I just went and looked up Dartmouth BASIC (this is the original thing). The list of keywords shows FOR, but not WHILE. This is the manual for v2, as early as I could find (1964) - it also has FOR already, but not WHILE:
http://www.bitsavers.org/pdf/dartmouth/BASIC_Oct64.pdf
(Side note: it's kinda amusing that the document uses a slash-through to distinguish the letter O from zero, except it does it in a way that is reverse of what is common today: it's the letter that has a slash through it!)
I do wonder if it was a true loop, though, or whether it was implemented more along the lines of GOSUB/RETURN (i.e. whether FOR basically set up a dynamic label, and NEXT did an indirect jump). If so, then it should be possible to have two FOR statements with the same variable, both "matched" to a single NEXT, if GOTO is used to reach it from one of the loops.
Thanks for the PDF, that will make really interesting lunch time reading :)
> I do wonder if it was a true loop, though, or whether it was implemented more along the lines of GOSUB/RETURN (i.e. whether FOR basically set up a dynamic label, and NEXT did an indirect jump). If so, then it should be possible to have two FOR statements with the same variable, both "matched" to a single NEXT, if GOTO is used to reach it from one of the loops.
I could be wrong here but I think most high level structures (switch, for loop, etc) end up getting translated to GOTOs at the machine level anyway. So you might be right with regards to the BASIC interpreter.
As for whether you could have fun with that, Dartmouth is before my time but I remember Locomotive BASIC (Amstrad CPC 464, so we're talking early 80s) used to do run time checks to ensure you weren't abusing NEXT inside nested FORs.
How much is down to the language and how much is that to do with the capability of computers then vs now. A fair few dialects of BASIC could manipulate the hardware directly but the capability of those machines were always quite limited. However with the hacker community going strong for retro hardware, you can now get all sorts of cool mods, like the WiFi addon I have on my Amstrad CPC 464 which allows me to even make HTTP GET requests, from BASIC. Ok I'm still limited by 64K of memory but that just forces you to think more creatively.
I'm not denying that Python teaches better programming principles than the early dialects of BASIC, but again how much of that is down to age given than newer dialects of BASIC can be structured very much like a typical imperative program. ie no requirement for line numbers, proper functions (though some dialects of BASIC have supported this since the 80s), etc.
Sometimes though, I think the simplicity of your environment can count for a lot. I remember reading an interview with a dance DJ turned producer who commented that he regretted buying lots of hardware when he was starting out as it slowed his learning curving (too much tech to learn in one go) and thus he would have been better off buying one synth, mastering that, then moving onto another synth. I wonder if programming can be like that for youngsters these days. I wonder if they get crippled by the vastness of the options out there and all the layers of the stack involved in even the simplest of "Hello world!" programs. Yet BASIC on the micro computers of the 70s and 80s offered a simple, no hassle, interface to that allowed our young imaginations to fly. I think we were very lucky to grow up in that era.
I think what we have lost from the old 8-bit computers and their BASIC, was the ability to instantly have something show on the screen, like the classic Sinclair Spectrum spider web:
10 FOR n=0 TO 31
20 PLOT n*11,0
30 DRAW -n*11, 175-n*8
40 NEXT n
(or something like that) That stuff got me hooked.JavaScript also has this in common with BASIC: the language bigots hate it. :-)
Otoh, I bet most js bigots only know one language very good, guess what it’s name is.
There's a Javascript emulator here: http://jsspeccy.zxdemo.org/
128K mode is easier because it lets you type the keywords instead of pressing single key shortcuts. For the punctuation, type:
ctrl-l for =
ctrl-b for *
ctrl-alt-n for ,
ctrl-j for -
I'm lucky enough to still have the book for my first computer, my second encounter with BASIC. I tweeted some photos of it last year https://twitter.com/steveklabnik/status/721471207883808769
When I moved on to QBASIC, which had a wonderful DO..LOOP construct which generalized loops with positive and negative pre- or post-conditions, also allowing for no conditions at all, it was really awesome and just felt "right".
I also remember how befuddled I was by GOSUB/RETURN, having seen it after learning DEF FN and SUB. The idea that you could just fall through the "end" of a procedure by not returning from it seemed nuts.
IF ... THEN <x> ELSE <y>
accepts either a single statement or a line number to go to as <x> and <y>. So, in dialects where ENDIF doesn’t exist at all, it was actually a bit harder than just writing or thinking WHILE-style. When I moved from ZX spectrum-clone basic to gwbasic and tbasic/qbasic, it felt much easier. (I was also more advanced by the time, so can’t tell how it changed my view on the old style.)Edit: formatting
number = 4
say("I'm thinking of a number")
while True:
try:
word = audio_input("Make a guess: ").lower()
except RuntimeError:
say("Try again")
else:
say(word)
guess = word2num.get(word)
if guess is None:
say("Not a number")
elif guess < number:
say("Too low!")
elif guess > number:
say("Too high!")
else:
say("You got it!")
break
Complete code example https://gist.github.com/3b3f26602e31198d399c643c8053b8b6But I don't think either of these is more intuitive. When you learnt how a GOTO works, you probably learnt immediately afterwards how to use it to loop. This loop is a 'pattern', one which you would have to grok as a single idea before you could write a program like the above. This is similar to learning about a while loop, and then knowing you would need one when you wrote the Python script.
Would I recommend this platform to someone else? Only if they needed to learn to program while pretending to do math homework.
Now i never use any goto statements ever
This is my version
python3 -c "while 1: print(chr(int(9585.5 + __import__('random').random())), end='')" var str = ''
for (var i = 0; i < 1000; i++) {
Math.random() > 0.5 ? str += '/' : str += '\\'
}
console.log(str)It's kind for those who weren't there at the time (and is a logical thing to do)
Where are those 30 years gone :) ?
I think that it was pretty common for students to trade programs around, so most of the teachers knew that the calculators were programmable.
It seems oddly familiar, don't you think?
Thinks change so much!
If you are a masochist, it's entirely possible to write a full application as a HTA. I once had to do this when I needed to explore an organisations Active Directory, but they wouldn't install the AD Admin tools on my Laptop, or give me local admin. (For those that know AD, it was a 'Users & Computers' clone written entirely in HTA. I used Notepad as my IDE)
http://www.learningclojure.com/2010/09/graphics-like-its-197...
The author talks about how its harder to get graphics on the screen without installing prerequisite software than it used to be, and goes on to sing the praises of the ZX Spectrum and its concise and well written manual. He finishes the article with some fun coding experiments writing Clojure code in the spirit of the ZX.
xterm -t -e 'curl https://raw.githubusercontent.com/joejulian/xterm/master/tektests/usmap.tek ; sleep 10'In most people's world (where "most" is probably > 95%), their computer comes with those things already installed.
javascript:for(i=4051;i--;)document.write(i%~80?'&#'+(9585+new Date%i%2):'<p style=margin:-.5em>')
And there's sixel if you're using a terminal like mlterm.
"10 PRINT[1] is a new book which explores the magical power of a single line of code."
[1] https://www.amazon.com/10-PRINT-CHR-205-5-RND-ebook/dp/B00C9...
> The Software Studies book series, published by the MIT Press, aims to publish the best new work in a critical and experimental field that is at once culturally and technically literate, reflecting the reality of today’s software culture.
The entire 324 page book can be downloaded as a pdf at the above link.
from random import choice; print(''.join(choice(('╲', '╱')) for i in range(10000)))perl -C -e '$|++;X:print chr(9585.5+rand());goto X'
php -r "TEN: print(chr(47 + 45 * rand(0,1))); goto TEN;"Try a different terminal maybe? Works in xterm, xfce, etc, for me. Also, I have LANG set to en_US.UTF-8
The ".5" is because Perl's chr() will convert it to an int by chopping off the decimal portion. rand() returns a floating point number between 0 and 1. That's very similar to what the BASIC program is doing.
s=╱╲;while :;do printf ${s:RANDOM%2:1};sleep 0.001;done
import sys, random
while True:
sys.stdout.write(random.choice([u'\u2571', u'\u2572']))
sys.stdout.flush()
They're called box drawing characters, it's how ncurses applications draw their GUI. import sys, random
while True :
print( random.choice( [ chr( 92 ) , chr( 47 ) ] ) , end = "" )
sys.stdout.flush( )(parent):
╱╱╱╲╲╲╲╲╲╱╲╲╲╱╱╱╲╱╱╱╲╲╲
╱╱╲╲╲╲╲╲╲╲╱╱╲╲╲╱╲╲╱╱╲╲╲
vs (yours) \\\/\\\//\//\\///\\\\////\
/\////\\\//\\\/\/\/\\\\/
Also in the terminal there isn't the space between the lines for the parent either (that's just HN formatting), but there is a space when using \ or / import random
while True:
print(random.choice("╱╲"), end="")
How's that for readability?python3 -c 'while True: print(__import__("random").choice("╱╲"), end="")'
(loop (princ (char "╱╲" (random 2))))
... or, if you use alexandria (a popular set of utilities) (loop (princ (random-elt "╱╲")))You might enjoy this browser-based graphical variation. Many people also made Processing-based variations, too:
btw, you can get pretty close to the spirit of the code with just a few more lines of Javascript:
var col = 0;
setInterval(() => {
document.body.innerHTML += String.fromCharCode(47+(Math.round(Math.random())*45)) + (++col % 80 ? "" : "<br/>")
}, 10);
or if you want to code golf you can get it into 1 line: var col = 0, fn = setInterval(() => {document.body.innerHTML += String.fromCharCode(47+(Math.round(Math.random())*45)) + (++col % 80 ? "" : "<br/>")}, 10);
You can see it in action here:https://jsfiddle.net/x5mz341z/
(edited to point to new jsfiddle with css body rules)
https://jsfiddle.net/tj1zwqat/
interesting that it renders perfect ( for me ) in chrome, and a bit of an offset in firefox
js
setInterval(() => {document.body.innerHTML += (Math.random() > 0.5 ? "╲":"╱")}, 1)
css
body { line-height:1em; max-width: 500px; word-wrap:break-word;}
document.body.innerHTML += (Math.random() > 0.5 ? "\\":"/")
LINE (x1, y1) - (x2, y2), 2, BF
[B]ox, [F]ill otherForm.LINE (x1, y1) - (x2, y2), 2, BF
Curiously, LINE was an actual method, with regular parameters; it's just that it had this irregular syntax when calling it. VB had a bunch of special-cased rules for some identifiers like that, for back-compat reasons. Pretty much anything you could do in QB, you could do in VB - CIRCLE, PSET etc.https://msdn.microsoft.com/en-us/library/aa230480(v=vs.60).a...
This all got ditched in VB.NET in favor of a regular syntax.
But my favorite part in old BASICs was that the function MID$() - which in other languages is usually known as "substring" or "substr" - was special-cased so that it could be used as target of assignment. So:
s$ = "foobar"
PRINT MID$(s$, 2, 3) ' oba
MID$(s$, 1, 2) = "uu"
PRINT s$ ' fuubar
And yes, VB has this, too. In fact, even VB.NET does (although it did away with $).I wish I could see that again. Not sure if it was in one of the old acorn electron mags.
It seemed like magic, did some pretty low level stuff.
{def U1 {lambda {:n} {if {< :n 0} then else {if {< {floor {* {random} 2}} 1} then ╲ else ╱} {U1 {- :n 1}}}}} {U1 1000}
draws the labyrinthic universe in any web browser. See http://lambdaway.free.fr/workshop/?view=U1 {map {lambda {_} {if {< {floor {* {random} 2}} 1} then ╲ else ╱}} {serie 1 2000}} 10 REM drawing a galaxy
20 FOR r=1 to 50
30 CIRCLE 150, 100, r
40 NEXT r
50 ...
10 REM playing a song
20 BEEP 1,0: BEEP 1,2: BEEP .5,3: BEEP.5,2: BEEP 1,0
30 ...10 PRINT "";
20 PRINT " ";
30 GOTO 10
If you choose the right # of *'s and ' 's you get a series of diagonals moving across the screen (since the semicolons prevented the carriage return). For me, the impression of travelling along a bridge was completely captivating, and I was hooked on programming ever since.
load 'viewmat'
'rgb'viewmat--. |. (~:_1&|.)^:(<@#) (2^8){.1"Hey guys here's a short program demonstrating something"
....
"with python"
!!!!
Line numbers are used in console BASIC to reference and edit a line, it normally doesn't have a screen editor.
But this certainly doesn't fit in one line by my standard. It linebreaks in my standard tiling setup. `from random import choice; print(''.join(choice(('\\', '/')) for i in range 10000)))`
While ($True) { Write-Host (Get-Random '\','/') -NoNewLine } while true; print(rand(Bool) ? '╲' : '╱'); end for _ <- 1..1000, do: IO.write Enum.random(["\u2571","\u2572"]); IO.write "\n"