Conway in Minimal BASIC

Last Monday I released the 59th issue of De Programmatica Ipsum, my dear monthly magazine about code, developers, and society, and this month I talked about BASIC in all of its flavors. As part of the preparation of this issue, I dived into the world of Minimal BASIC code, the one with source code line numbers, the one that would start immediately after powering up your computer, and the one that brings endless nostalgia.

As part of my exploration, I decided to reimplement my venerable Polyglot Conway project in what is usually called “Minimal BASIC”, defined as a standard known as ECMA-55 and released in 1978.

Writing it

To make it fit in the screen space offered by most emulators, I reduced the size of the grid I used in other versions of the program from 30 by 30 to just 9 by 9, with only a few “objects” animated.

The result of my work, admittedly retro, is right here, with all the bells and whistles you expect from ECMA-55: line numbers, global variables, uppercase statements, DATA, GOTO, GOSUB, the whole package.

In line 100 we initialize the variable W (or “World”) with a matrix of data read from lines 1000 to 1090. The value 1, as you might expect, represents a location occupied by a living cell, while 0 represents an empty location.

100 REM INITIALIZATION
110 LET S = 8
120 LET D$ = "|"
130 DIM W(S, S)
140 DIM Z(S, S)
150 FOR I = 0 TO S
160 FOR J = 0 TO S
170 READ W(I, J)
180 NEXT J
190 NEXT I
200 LET G = 0

The variable D$ in line 120 contains a vertical bar, separated on its own, so that we can change its value (for example, in the Commodore 64, we change it to an uppercase B, which in PETSCII is represented as a vertical bar. Don’t ask.)

The variable S contains the maximum index of rows or columns in the grid. Since we iterate from zero, the final grid is 9 by 9.

From lines 370 to 400 we print the grid system on top and to the left of the world. In lines 420 and 430 we print an X at each living cell, and a blank space for the rest.

In line 440 we avoid printing a final vertical bar if we’re at the end of the line, to save some screen space.

We use two GOSUB statements here (lines 380 and 510), and both “procedures” are described below in this article. Line 520 contains a nice GOTO statement that makes this part of the code repeat ad nauseam.

350 REM PRINT GRID IN AN ENDLESS LOOP
360 PRINT ""
370 FOR A = 0 TO S
380 IF A = 0 THEN GOSUB 600
390 PRINT A;
400 PRINT D$;
410 FOR B = 0 TO S
420 IF W(B, A) = 0 THEN PRINT "   ";
430 IF W(B, A) = 1 THEN PRINT " X ";
440 IF B < S THEN PRINT D$;
450 NEXT B
460 PRINT ""
470 NEXT A
480 LET G = G + 1
490 PRINT ""
500 PRINT "GENERATION ", G
510 GOSUB 700
520 GOTO 350

In line 380 we GOSUB to line 600 to print the first line of the grid system, and then we return. GOSUBs are the closest thing we have to subroutines in Minimal BASIC.

Also, remember: all variables are global!

600 REM FIRST LINE WITH COORDINATES
610 FOR B = 0 TO S
620 IF B = 0 THEN PRINT "     0 ";
630 IF B > 0 THEN PRINT B;
640 IF B < S THEN PRINT D$;
650 NEXT B
660 PRINT ""
670 RETURN

From lines 700 to 900 we have a routine that makes the W variable evolve to its next state. From 710 to 760 we reset all values in W to zero; but don’t worry, we make a copy of W called Z in line 730.

Then from lines 770 to 900 we have a series of nested FOR loops. We iterate cell by cell, and for each, we count the number of living organisms around them, storing it in a variable called C.

In line 850 we subtract the value of the current cell, because we only want to count the number of living neighbors.

Finally, we apply the evolution algorithm: if the cell was alive (line 860) it stays alive only if it had 2 or 3 neighbors. Otherwise, it dies, either of boredom or of suffocation. Poor thing.

If the cell was dead (line 870) it becomes alive if it has three neighbors (I won’t comment on the practical implications of such number.) We set the values in the W global variable, and we return to line 520, which is a GOTO to line 350, to start the process all over again.

700 REM EVOLVE WORLD TO NEXT GENERATION
710 FOR I = 0 TO S
720 FOR J = 0 TO S
730 LET Z(I, J) = W(I, J)
740 LET W(I, J) = 0
750 NEXT J
760 NEXT I
770 FOR X = 0 TO S
780 FOR Y = 0 TO S
790 LET C = 0
800 FOR A = X - 1 TO X + 1
810 FOR B = Y - 1 TO Y + 1
820 IF A >= 0 AND B >= 0 AND A <= S AND B <= S THEN LET C = C + Z(A,B)
830 NEXT B
840 NEXT A
850 LET C = C - Z(X, Y)
860 IF Z(X, Y) = 1 AND (C = 2 OR C = 3) THEN LET W(X, Y) = 1
870 IF Z(X, Y) = 0 AND (C = 3) THEN LET W(X, Y) = 1
880 NEXT Y
890 NEXT X
900 RETURN

At the end of the program, we have the DATA statements used to initialize the W variable on top of the program, and a mandatory END statement at the end, even if we never really reach it, thanks to the GOTO in line 520.

It’s not hard to see the initial configuration of the grid through the ones scattered in the DATA.

1000 REM INITIALIZATION DATA
1010 DATA 0, 1, 0, 0, 0, 0, 0, 0, 0
1020 DATA 0, 1, 0, 0, 0, 0, 1, 0, 0
1030 DATA 0, 1, 0, 0, 1, 0, 1, 0, 0
1040 DATA 0, 0, 0, 0, 0, 1, 1, 0, 0
1050 DATA 0, 0, 0, 0, 0, 0, 0, 0, 0
1060 DATA 0, 0, 0, 0, 0, 0, 0, 0, 0
1070 DATA 0, 0, 1, 0, 0, 0, 0, 0, 0
1080 DATA 0, 1, 0, 1, 0, 0, 0, 0, 0
1090 DATA 0, 0, 1, 0, 0, 0, 0, 0, 0

9999 END

Et voilà :)

Running it

This code runs verbatim (and compiles with FreeBASIC) with the following interpreters and compilers, all tested on Fedora 38.

I’ve also managed to run the code verbatim on some online emulators:

380 IF A = 0 THEN GOSUB 600
390 PRINT A;
400 PRINT D$;

It was a fun experience to discover the incredible community of users online that keep the memory of BASIC alive, providing emulators of all kinds, both online and offline. I was pleased to see the fervor and the love people put in these things, and it was a fantastic trip 40 years in the past.

More Conway in BASIC

With this implementation, there are now 5 6 different dialects of BASIC in the Conway project:

The last three ones are those that look more similar to other projects. In the first three cases, I had to adapt the code to the various constraints of those BASIC dialects.

Update, 2023-08-18: Added a sixth BASIC dialect with a version for Visual Basic 1.0, thanks to WinWorld offering downloads of MS-DOS 5.0, Windows 3.1, and Visual Basic 1.0, ready to be conveniently installed and run in a VirtualBox VM.