E-Scribe : a programmer’s blog

About Me

PBX I'm Paul Bissex. I build web applications using open source software, especially Django. Started my career doing graphic design for newspapers and magazines in the '90s. Then wrote tech commentary and reviews for Wired, Salon, Chicago Tribune, and others you never heard of. Then I built operations software at a photography school. Then I helped big media serve 40 million pages a day. Then I worked on a translation services API doing millions of dollars of business. Now I'm building the core platform of a global startup accelerator. Feel free to email me.

Book

I co-wrote "Python Web Development with Django". It was the first book to cover the long-awaited Django 1.0. Published by Addison-Wesley and still in print!

Colophon

Built using Django, served with gunicorn and nginx. The database is SQLite. Hosted on a FreeBSD VPS at Johncompanies.com. Comment-spam protection by Akismet.

Elsewhere

Pile o'Tags

Stuff I Use

Bitbucket, Debian Linux, Django, Emacs, FreeBSD, Git, jQuery, LaunchBar, macOS, Markdown, Mercurial, Python, S3, SQLite, Sublime Text, xmonad

Spam Report

At least 236610 pieces of comment spam killed since 2008, mostly via Akismet.

BASIC Computer Games

book cover In 1981, I was 13 years old and teaching myself BASIC on my TRS-80 Model III from official Radio Shack manuals -- accurate, comprehensive, and terminally bland.

Into that gray scene came the book Basic Computer Games: Microcomputer Edition (edited by David Ahl of Creative Computing magazine). It changed my life.

I can't remember now where it came from. Neither my parents, nor my friends, nor my teachers knew much about the home computer scene. It's possible that I found out about it in Creative Computing magazine and ordered it by mail, or "borrowed" it from somewhere and forgot to return it.

The book, subtitled "101 great games to play on your home computer," was 8-bit-nerd heaven. Pages and pages of program listings in tiny, all-caps, dot matrix type, with brief introductory paragraphs. Plus, funny illustrations of strangely plausible robots. Don't underestimate the appeal of the robots.

I spent many, many hours typing in programs from that book. Many of the games inspired variants of my own. For better or worse I became a prolific teenage BASIC programmer. I played some with Z-80 assembly and Logo and Pascal too, but the evidence of the book told me that BASIC was where the fun was at.

Fast forward to 2005. My copy of the book has long since disappeared. David Ahl has more or less retired from the computer scene, but he still has a website where he is clearing out old copies of his books and magazines. But no more copies of Basic Computer Games -- my hopes are dashed. I email him anyway to ask about something else; he writes back and says that he does have one copy left. Cheap, too.

It arrived last week. The illustrations alone take me right back to those 8-bit days. The notes on the individual programs mean more to me now, as I can see where they connect with various threads of computing history (the original, pre-microcomputer edition of the book came out in 1973). The coding style is, well, mostly horrible by modern standards, but there's an inspiring bright spark of fun and creativity there. And many of these programs are classics.

290 PRINT "TURN NO.";T;"WHAT IS YOUR GUESS";
300 INPUT M,N
310 FOR I=1 TO 4
320 IF P(I,1)=-1 THEN 400
330 IF P(I,1)<>M THEN 380
340 IF P(I,2)<>M THEN 380
350 P(I,1)=-1
360 PRINT "YOU HAVE FOUND MUGWUMP";I
370 GOTO 400
380 D=SQR((P(I,1)-M)^2+(P(I,2)-N)^2)
390 PRINT "YOU ARE";(INT(D*10))/10;"UNITS FROM MUGWUMP";I
400 NEXT I

Looking at certain pages gives me an almost physical pang. I suspect the severity of each pang is directly proportional to the number of hours I spent typing, debugging, and possibly even playing the game in question.

In a way, this book was my first open-source experience. It's not at all clear what the licensing terms of the programs are, beyond the obvious fact that the whole book is copyrighted by Creative Computing. But redistribution was not my concern then. I wanted to see real, working code that did something. Something real. And what's more real than a game of Mugwump?

My copy of the book is inscribed in the front by Ahl: "Hi Paul -- Learn from the past, live for the future." That's a deal.

Update: I re-wrote one of these old games in a handful of other languages, and readers have contributed versions as well.

Wednesday, November 23rd, 2005
+ + +
9 comments

Comment from Jim Storch , 8 weeks later

I remember checking out of copy of that book from the libray and typing games into an Apple II, making minor adjustments so Applebasic would be happy.

Sometimes, I still think of Eliza when chatting on an IM client: "Come come, elucidate your thoughts."

Or those darn wild berbers, hidden in the sand...

Comment from regeya , 8 weeks later

I have Volume 2 on my bookshelf, but not the first. I also wasted far too much time typing from that. :-)

Comment from José Santos , 13 weeks later

Hi Paul!

I also had a BASIC book, back in the days, don't remember the title anymore. Those were fun times. I hope to eventually locate it.

I've found "BASIC Computer Games" online (scanned), as well as volume 2 and lots of others. I suppose you knew about it, cause its the first hit on "basic computer games" on Google. Anyway here it is: http://www.atariarchives.org/basicgames/ , great site.

Now i'm going to have fun writing some of those good old games in Lisp. Sweet! :)

Comment from Greg Brondo , 14 months later

I've got the same book (around the same time). It's on my bookshelf at the office.

It's Priceless!

Comment from M.L. , 3 years later

I had a similar book in german discovering how to program a little text adventure. Don't remember the name. But it was the starting point to program bigger programs and games - not just number guess. ;-)

Comment from George Beker , 4 years later

I am indeed the guy who did the original "BekerBot" drawings - and went on to run a major PR/media firm (all Fortune 50 clients). I am now putzing with a new version of the vintage book and some new (archly cynical) drawings. Want to stay in touch? Also check me out at http://www.bekers.org

Comment from george beker , 4 years later

This is Beker again - now there is a robot site - http://www.bekerbots.com

Comment from Phil Conrod , 4 years later

Like you Paul, the first computer program I wrote was from the classic programming book edited by David H. Ahl on an HP 1000. Call me a little nostalgic but I also tracked down David H. Ahl to thank him for inspiring me to become a computer programmer when I was a kid. Then I did something really crazy. I asked David if I could have his permission to republish his classic programming book in a modern computer programming language so a whole new generation of kids could also be inspired by his classic book. He said yes and David then volunteered to write an updated 2010 introduction to the new book. George Beker, who illustrated several of the original books, also came out of retirement and added a few more Robot illustrations to a new Special Illustrated edition of the new 2010 book. You can now relive all those classic games like Super Star Trek, MUGWUP and Lunar LEM Rocket. The 2010 Small Basic Edition includes almost all the classic BASIC games that inspired a generation of programmers but now in Microsoft Small Basic for a new generation of kids. It was published as an E-Book so you can easily cut and paste the Small Basic Source Code right into your Small Basic Compiler. You can find out more about this new edition at http://computerscienceforkids.com/SmallBasicComputerGames.aspx. You can also find out more about Microsoft's new Small Basic Development environment for Kids at http://www.smallbasic.com.

Comment from mr small genius , 6 years later

well im a genius at small basic ive got a cool thing il show you now... GraphicsWindow.Hide()

gw = 800

gh = 600

GraphicsWindow.CanResize = "False"

GraphicsWindow.Top = (Desktop.Height-gh)/2

GraphicsWindow.Left = (Desktop.Width-gw)/2

GraphicsWindow.Title = "Bouncing balls with realistic collision physics"

GraphicsWindow.Width = gw

GraphicsWindow.Height = gh

GraphicsWindow.BackgroundColor = "LightBlue"

'Reduce gw for options

gw = gw-200

GraphicsWindow.MouseDown = OnMouseDown

Start:

' Gravity, friction and attraction to mouse

grav = 0.0 ' 0 for none

fric = 0 ' 0 for none

follow = 0 'attract to mouse

attract = 0 'attract balls to each other

dt = 1 'timestep (speed)

shape = 0 '0:ball,1 square

elastic = 1 '1 fully elastic collisions

Colour = "Yellow"

'Initialise some balls

radius = 20

diam = 2*radius

nball = Math.Floor(gw/diam)

istart = "True"

reset()

ireset = "False"

istart = "False"

iend = "False"

iselect = "False"

ioptions = "False"

'Show window - an MS comment

GraphicsWindow.Show()

'Main loop

While ("True")

If (ioptions) Then

options()

ioptions = "False"

EndIf

energy = 0.0

isCollision = "False"

If (iselect) Then

For i = 1 To nball

 x = Xpos[i]

 y = Ypos[i]

 dist = (xm-x)*(xm-x)+(ym-y)*(ym-y)

 If (dist < radius*radius) Then

   u = 0

   v = 0

   Xvel[i] = u

   Yvel[i] = v

 EndIf

EndFor

iselect = "False"

EndIf

For i = 1 To nball

update()

move()

u = Xvel[i]

v = Yvel[i]

energy = energy+(uu+vv)

EndFor

energy = dtdtenergy

energy = Math.Floor(energy)

GraphicsWindow.BrushColor = "LightBlue"

GraphicsWindow.FillRectangle(gw+15,560,190,20)

GraphicsWindow.BrushColor = "Black"

GraphicsWindow.DrawText(gw+65,560,"Energy "+energy)

If (ireset) Then

reset()

ireset = "False"

EndIf

If (istart) Then

Goto Start

EndIf

If (iend) Then

Program.End()

EndIf

' If (isCollision) Then

' Sound.PlayClick()

' EndIf

Program.Delay(10)

EndWhile

'Update ball positions

Sub update

u = Xvel[i]

v = Yvel[i]

u = Math.Min(100,Math.Max(u,-100))

v = Math.Min(100,Math.Max(v,-100))

x = Xpos[i]+dt*u

y = Ypos[i]+dt*v

bounce()

gravity()

collision()

attraction()

Xpos[i] = x

Ypos[i] = y

EndSub

'Check for edge bounces

Sub bounce

If (x < radius) Then

Xvel[i] = -Xvel[i]

x = radius

EndIf

If (x > gw-radius) Then

Xvel[i] = -Xvel[i]

x = gw-radius

EndIf

If (y < radius) Then

Yvel[i] = -Yvel[i]

y = radius

EndIf

If (y > gh-radius) Then

Yvel[i] = -Yvel[i]

y = gh-radius

EndIf

EndSub

'Check for collisions

Sub collision

'Only check each pair once

For j = i+1 To nball

xi = x

yi = y

xj = Xpos[j]

yj = Ypos[j]

dx = xi-xj

dy = yi-yj

dist = Math.SquareRoot(dxdx+dydy)

If (dist < diam) Then

 isCollision = "True"

 'Get ball vectors

 ui = Xvel[i]

 vi = Yvel[i]

 uj = Xvel[j]

 vj = Yvel[j]

 'Move backwards (forwards if dt < 0) in time until balls are just touching

 CoefA = (ui-uj)*(ui-uj)+(vi-vj)*(vi-vj)

 CoefB = 2*((ui-uj)*(xi-xj)+(vi-vj)*(yi-yj))

 CoefC = (xi-xj)*(xi-xj)+(yi-yj)*(yi-yj)-diam*diam

 If (CoefA = 0) Then

   t = -CoefC/CoefB

 Else

   If (dt >= 0) Then

     t = (-CoefB-Math.SquareRoot(CoefB*CoefB-4*CoefA*CoefC))/(2*CoefA)

   Else

     t = (-CoefB+Math.SquareRoot(CoefB*CoefB-4*CoefA*CoefC))/(2*CoefA)

   EndIf

 EndIF

 xi = xi+t*ui

 yi = yi+t*vi

 xj = xj+t*uj

 yj = yj+t*vj

 'Centre of momentum coordinates

 mx = (ui+uj)/2

 my = (vi+vj)/2

 ui = ui-mx

 vi = vi-my

 uj = uj-mx

 vj = vj-my

 'New centre to centre line

 dx = xi-xj

 dy = yi-yj

 dist = Math.SquareRoot(dx*dx+dy*dy)

 dx = dx/dist

 dy = dy/dist

 'Reflect balls velocity vectors in centre to centre line

 OB = -(dx*ui+dy*vi)

 ui = ui+2*OB*dx

 vi = vi+2*OB*dy

 OB = -(dx*uj+dy*vj)

 uj = uj+2*OB*dx

 vj = vj+2*OB*dy

 'Back to moving coordinates with elastic velocity change

 e = Math.SquareRoot(elastic)

 ui = e*(ui+mx)

 vi = e*(vi+my)

 uj = e*(uj+mx)

 vj = e*(vj+my)

 'Move to new bounced position

 xi = xi-t*ui

 yi = yi-t*vi

 xj = xj-t*uj

 yj = yj-t*vj

 'Set velocities

 Xvel[i] = ui

 Yvel[i] = vi

 Xvel[j] = uj

 Yvel[j] = vj

 'Set position

 Xpos[j] = xj

 Ypos[j] = yj

 x = xi

 y = yi

EndIf

EndFor

EndSub

'Gravity and friction and follow mouse

Sub gravity

xm = GraphicsWindow.MouseX-x

ym = GraphicsWindow.MouseY-y

dist = xmxm+ymym

dist = Math.Max(dist,radius*radius)

'dist = dist*Math.SquareRoot(dist)

u = Xvel[i]

v = Yvel[i]

fricscale = (1-fric/Math.SquareRoot(1+uu+vv))

Xvel[i] = followxm/dist+fricscaleu

Yvel[i] = followym/dist+fricscalev+grav

EndSub

'Attract-repell balls to each other

Sub attraction

If (attract <> 0) Then

For j = i+1 To nball

 xm = Xpos[j]-x

 ym = Ypos[j]-y

 dist = xm*xm+ym*ym

 dist = Math.Max(dist,radius*radius)

 'dist = dist*Math.SquareRoot(dist)

 Xvel[i] = attract*xm/dist+Xvel[i]

 Yvel[i] = attract*ym/dist+Yvel[i]

 Xvel[j] = attract*xm/dist+Xvel[j]

 Yvel[j] = -attract*ym/dist+Yvel[j]

EndFor

EndIf

EndSub

'Move ball

Sub move

ball = balls[i]

Shapes.Move(ball,x-radius,y-radius)

EndSub

'Update options display

Sub options

GraphicsWindow.PenColor = "Black"

GraphicsWindow.DrawLine(gw,0,gw,gh)

GraphicsWindow.BrushColor = "LightBlue"

GraphicsWindow.FillRectangle(gw+10,10,190,gh-20)

For i = 0 To 5

GraphicsWindow.DrawLine(gw+10,100i+10,gw+190,100i+10)

EndFor

GraphicsWindow.DrawLine(gw+100,10,gw+100,510)

GraphicsWindow.BrushColor = "Black"

GraphicsWindow.DrawBoundText(gw+15,20,70,"Gravity")

GraphicsWindow.DrawBoundText(gw+15,40,70,grav)

GraphicsWindow.DrawBoundText(gw+15,120,70,"Friction")

GraphicsWindow.DrawBoundText(gw+15,140,70,fric)

GraphicsWindow.DrawBoundText(gw+15,220,70,"Follow")

GraphicsWindow.DrawBoundText(gw+15,240,70,follow)

GraphicsWindow.DrawBoundText(gw+15,320,70,"Size")

GraphicsWindow.DrawBoundText(gw+15,340,70,radius)

GraphicsWindow.DrawBoundText(gw+15,420,70,"Count")

GraphicsWindow.DrawBoundText(gw+15,440,70,nball)

GraphicsWindow.DrawBoundText(gw+15,520,170,"Click coloured options or a ball to stop it")

GraphicsWindow.BrushColor = "Red"

GraphicsWindow.DrawBoundText(gw+15,580,50,"RESET")

GraphicsWindow.DrawBoundText(gw+115,580,50,"QUIT")

GraphicsWindow.DrawBoundText(gw+15,60,70,"More")

GraphicsWindow.DrawBoundText(gw+15,160,70,"More")

GraphicsWindow.DrawBoundText(gw+15,260,70,"More")

GraphicsWindow.DrawBoundText(gw+15,360,70,"More")

GraphicsWindow.DrawBoundText(gw+15,460,70,"More")

GraphicsWindow.BrushColor = "Blue"

GraphicsWindow.DrawBoundText(gw+15,80,70,"Less")

GraphicsWindow.DrawBoundText(gw+15,180,70,"Less")

GraphicsWindow.DrawBoundText(gw+15,280,70,"Less")

GraphicsWindow.DrawBoundText(gw+15,380,70,"Less")

GraphicsWindow.DrawBoundText(gw+15,480,70,"Less")

GraphicsWindow.BrushColor = "Black"

GraphicsWindow.DrawBoundText(gw+115,20,70,"Speed")

GraphicsWindow.DrawBoundText(gw+115,40,70,dt)

GraphicsWindow.DrawBoundText(gw+115,120,70,"Attraction")

GraphicsWindow.DrawBoundText(gw+115,140,70,attract)

GraphicsWindow.DrawBoundText(gw+115,220,70,"Elastic")

GraphicsWindow.DrawBoundText(gw+115,240,70,elastic)

GraphicsWindow.DrawBoundText(gw+115,320,70,"Colour")

GraphicsWindow.BrushColor = "Red"

GraphicsWindow.DrawBoundText(gw+115,60,70,"More")

GraphicsWindow.DrawBoundText(gw+115,160,70,"More")

GraphicsWindow.DrawBoundText(gw+115,260,70,"More")

GraphicsWindow.BrushColor = "Blue"

GraphicsWindow.DrawBoundText(gw+115,80,70,"Less")

GraphicsWindow.DrawBoundText(gw+115,180,70,"Less")

GraphicsWindow.DrawBoundText(gw+115,280,70,"Less")

GraphicsWindow.BrushColor = "Red"

GraphicsWindow.DrawBoundText(gw+115,340,70,"Red")

GraphicsWindow.BrushColor = "Blue"

GraphicsWindow.DrawBoundText(gw+115,360,70,"Blue")

GraphicsWindow.BrushColor = "Yellow"

GraphicsWindow.DrawBoundText(gw+115,380,70,"Yellow")

GraphicsWindow.BrushColor = "Black"

GraphicsWindow.DrawBoundText(gw+115,420,70,"Shape")

GraphicsWindow.BrushColor = "Red"

GraphicsWindow.DrawBoundText(gw+115,440,70,"Circle")

GraphicsWindow.DrawBoundText(gw+115,460,70,"Square")

EndSub

'Change settings

Sub OnMouseDown

xm = GraphicsWindow.MouseX

ym = GraphicsWindow.MouseY

'Left column settings

If (xm > gw+15 And xm < gw+85) Then

If (ym > 60 And ym < 75) Then

 grav = grav+0.01

EndIf

If (ym > 80 And ym < 95) Then

 grav = grav-0.01

EndIf

If (ym > 160 And ym < 175) Then

 fric = fric+0.001

EndIf

If (ym > 180 And ym < 195) Then

 fric = fric-0.001

EndIf

If (ym > 260 And ym < 275) Then

 follow = follow+1

EndIf

If (ym > 280 And ym < 295) Then

 follow = follow-1

EndIf

If (ym > 360 And ym < 375) Then

 radius = radius+1

 diam = 2*radius

 ireset = "True"

EndIf

If (ym > 380 And ym < 395) Then

 radius = radius-1

 radius = Math.Max(1,radius)

 diam = 2*radius

 ireset = "True"

EndIf

If (ym > 460 And ym < 475) Then

 nball = nball+1

 ireset = "True"

EndIf

If (ym > 480 And ym < 495) Then

 nball = nball-1

 nball = Math.Max(1,nball)

 ireset = "True"

EndIf

If (ym > 580 And ym < 595) Then

 istart = "True"

EndIf

EndIf

'Right column settings

If (xm > gw+115 And xm < gw+185) Then

If (ym > 60 And ym < 75) Then

 dt = dt+0.1

EndIf

If (ym > 80 And ym < 95) Then

 dt = dt-0.1

EndIf

If (ym > 160 And ym < 175) Then

 attract = attract+1

EndIf

If (ym > 180 And ym < 195) Then

 attract = attract-1

EndIf

If (ym > 260 And ym < 275) Then

 elastic = elastic+0.01

EndIf

If (ym > 280 And ym < 295) Then

 elastic = elastic-0.01

EndIf

If (ym > 340 And ym < 355) Then

 Colour = "Red"

 ireset = "True"

EndIf

If (ym > 360 And ym < 375) Then

 Colour = "Blue"

 ireset = "True"

EndIf

If (ym > 380 And ym < 395) Then

 Colour = "Yellow"

 ireset = "True"

EndIf

If (ym > 440 And ym < 455) Then

 Shape = 0

 ireset = "True"

EndIf

If (ym > 460 And ym < 475) Then

 Shape = 1

 ireset = "True"

EndIf

If (ym > 580 And ym < 595) Then

 iend = "True"

EndIf

EndIf

'Select a ball

If (xm < gw) Then

iselect = "True"

EndIf

ioptions = "True"

EndSub

'Reset new balls

Sub reset

mball = Array.GetItemCount(balls)

For i = 1 To mball

balls[i] = ""

If (istart Or i > nball) Then

 Xpos[i] = ""

 Ypos[i] = ""

 Xvel[i] = ""

 Yvel[i] = ""

EndIf

EndFor

GraphicsWindow.Clear()

options()

GraphicsWindow.BrushColor = Colour

For i = 1 To nball

If (shape = 0) Then

 ball = Shapes.AddEllipse(diam,diam)

EndIf

If (shape = 1) Then

 ball = Shapes.AddRectangle(diam,diam)

EndIf

balls[i] = ball

If (istart Or i > mball) Then

 x = Math.GetRandomNumber(gw)

 y = Math.GetRandomNumber(gh)

 u = Math.GetRandomNumber(500)/100-3

 v = Math.GetRandomNumber(500)/100-3

 Xpos[i] = x

 Ypos[i] = y

 Xvel[i] = u

 Yvel[i] = v

EndIf

EndFor

EndSub

this does work please leave a comment!!!

Comments are closed for this post. But I welcome questions/comments via email or Twitter.