ejolson
Posts: 6586
Joined: Tue Mar 18, 2014 11:47 am

A Birthday Present for Fido

Sun Oct 11, 2020 2:54 am

The SuperPET was an educational computer developed in cooperation with the University of Waterloo as an inexpensive environment for students to learn programming and released as a product by Commodore in 1981. Thirty one years later the Raspberry Pi was released in cooperation with Cambridge University as an inexpensive computer for education. This project aims to explore the similarities and differences of the design, implementation and impact these two computers had on education, the hobbyist, the maker community and the industry in their historical contexts.

For reference this project continues the topics touched on in

viewtopic.php?p=1738151#p1738151

viewtopic.php?p=1714665#p1714665

as well as the computer literacy topics in

viewtopic.php?t=227343

viewtopic.php?t=240287

viewtopic.php?f=31&t=257317

While it may seem tangential, this thread begins with a discussion of what has historically been classified as the first computer-based adventure game. Hunt the Wumpus was written by Gregory Yob in 1972 at the People's Computer Center in Menlo Park California and published by People's Computer Company in a no-advertising-allowed newsletter targeted at teachers and students in primary and secondary schools.

While it would be interesting to compare the PCC newsletter to the MagPi, this publication also shared much in common with Raspberry Pi Learn and the associated Hello World magazine. Such a comparison, however, is outside the scope of the present project. Here we focus directly on the software and hardware of the SuperPET and the Pi.

As memories of how the SuperPET actually worked have been clouded by time and functional 31-year-old hardware is difficult to find, we begin with an exploration of the SuperPET through emulation. Fortunately, the Versatile Commodore Emulator VICE fully emulates the original hardware--including the 6702 secure boot environment--and also runs on the Raspberry Pi. To understand how all this works, the current task is to port the Unix version of Hunt the Wumpus written by Ken Thompson around 1973 to Waterloo microPascal running on the SuperPET as if it were 1982.

In beginning, the recent talk

https://uwaterloo.ca/watitis/university ... w-trenches

indicates that others have also been reflecting on how past computer literacy efforts connect to the present and provide a context for understanding the future.
Last edited by ejolson on Sun Oct 11, 2020 4:58 pm, edited 2 times in total.

ejolson
Posts: 6586
Joined: Tue Mar 18, 2014 11:47 am

Re: A Birthday Present for Fido

Sun Oct 11, 2020 3:04 am

To get started, here is a screen dump of my microPascal port of Ken's version of Hunt the Wumpus running on the SuperPET using VICE with a Pi 4B

Image
Image
Image
Image
Image
Image
Image
Image
Image

just in time for Fido's birthday.

Subsequent posts will describe what needed to be done to compile and install the latest version of VICE on the Pi and compare this with what needed to be done to port Ken's C code to microPascal for the SuperPET. After that a version of Hunt the Wumpus written in Rust for the Raspberry Pi will form the basis to further understand the relative contexts of these two historically significant educational computing platforms.

Then, moving to a modern setting--unless the thread is closed by then--I plan to consider the development of an artificial intelligence that uses a convolutional neural network to learn how to reliably win while playing Hunt the Wumpus. The question here is whether today's Raspberry Pi constitutes a sufficient platform for a student to learn about such computer-science techniques.

As in the other threads, help, feedback and additions to these topics would be very much appreciated.

lurk101
Posts: 379
Joined: Mon Jan 27, 2020 2:35 pm
Location: Cumming, GA (US)

Re: A Birthday Present for Fido

Sun Oct 11, 2020 5:19 am

Promising launch! Though impressive for their age, it was disappointing to discover that these are language interpreters, not compilers. No way to explore the unique features of the make believe 6809! There's an assembler and linker provided, so maybe...

hippy
Posts: 9135
Joined: Fri Sep 09, 2011 10:34 pm
Location: UK

Re: A Birthday Present for Fido

Sun Oct 11, 2020 12:26 pm

ejolson wrote:
Sun Oct 11, 2020 3:04 am
Subsequent posts will describe what needed to be done to compile and install the latest version of VICE on the Pi and compare this with what needed to be done to port Ken's C code to microPascal for the SuperPET. After that a version of Hunt the Wumpus written in Rust for the Raspberry Pi will form the basis to further understand the relative contexts of these two historically significant educational computing platforms.
Why Rust and not Python or C ? Why not just use Ken's original C code ?

lurk101
Posts: 379
Joined: Mon Jan 27, 2020 2:35 pm
Location: Cumming, GA (US)

Re: A Birthday Present for Fido

Sun Oct 11, 2020 3:33 pm

hippy wrote:
Sun Oct 11, 2020 12:26 pm
Why Rust and not Python or C ? Why not just use Ken's original C code ?
There are a number of choices here including Rust.

hippy
Posts: 9135
Joined: Fri Sep 09, 2011 10:34 pm
Location: UK

Re: A Birthday Present for Fido

Sun Oct 11, 2020 4:07 pm

lurk101 wrote:
Sun Oct 11, 2020 3:33 pm
hippy wrote:
Sun Oct 11, 2020 12:26 pm
Why Rust and not Python or C ? Why not just use Ken's original C code ?
There are a number of choices here including Rust.
Sure; I was wondering why Rust ?

ejolson
Posts: 6586
Joined: Tue Mar 18, 2014 11:47 am

Re: A Birthday Present for Fido

Sun Oct 11, 2020 4:26 pm

lurk101 wrote:
Sun Oct 11, 2020 5:19 am
Promising launch! Though impressive for their age, it was disappointing to discover that these are language interpreters, not compilers. No way to explore the unique features of the make believe 6809! There's an assembler and linker provided, so maybe...
The tradition of interpreted languages for teaching beginners seems to have continued with Scratch and Python. It's not clear to me whether the reason for this is a perceived inability to tell hot dogs from food or the development costs.

In the case of the emulated SuperPET, simply pressing alt-W turns on warp mode and the program then runs at the speed as if it had been compiled for the original hardware. Indeed, warp mode is almost a necessity when generating the random graphs used for the maze in the Pascal version of Hunt the Wumpus. If anyone knows how to turn off the auto repeat to prevent the keyboard from creating 7 to 14 characters with each key press, that would make warp even more useful.

The fact that microPascal, microBasic, microFortran, microAPL and microCOBOL were interpreted seems a distinguishing trait of a toy designed for teaching rather than a real computer for business use. Though Commodore marketed the SuperPET as the MicroMainframe 9000, they did not provide the development tools, for example OS/9 and a C compiler, needed to allow the SuperPET to be easily used as a real computer.

The distinction between a real computer and a toy to teach children programming continues to this day: I find it interesting the original concept of the Raspberry Pi was a teaching device limited to running only MicroPython.

https://micropython.org/download/

Had this happened, the Pi would have been not only closer to the SuperPET in implementation but likely popularity as well. After age 5 children get tired of pretending with lumps of plastic and want something that really works. The same is likely true of hobbyists, makers and even most software professionals.

Although Python and Scratch continue in the tradition of interactive programming languages with slow performance that were designed for teaching, the vendor supported software included with the Pi, while only 32-bit, is a full distribution of Linux with all the same compilers and development tools used from embedded to the cloud. The difference is significant though the similarities are many.
Last edited by ejolson on Sun Oct 11, 2020 5:11 pm, edited 1 time in total.

ejolson
Posts: 6586
Joined: Tue Mar 18, 2014 11:47 am

Re: A Birthday Present for Fido

Sun Oct 11, 2020 4:47 pm

hippy wrote:
Sun Oct 11, 2020 4:07 pm
lurk101 wrote:
Sun Oct 11, 2020 3:33 pm
hippy wrote:
Sun Oct 11, 2020 12:26 pm
Why Rust and not Python or C ? Why not just use Ken's original C code ?
There are a number of choices here including Rust.
Sure; I was wondering why Rust ?
When the SuperPET was deployed, the strong typing, structured programming and single-entry single-exit constraints of Pascal were innovations in software development thought to be significant enough to include in any introductory course on computer science. Today we have done away with the fashion of hiding data in objects and moved to the strongly enforced data ownership model of Rust. This is the latest innovation in language design. Therefore, programming in Rust on the Pi is the closest thing to Pascal on the SuperPET when it comes to teaching computer science in historical context, at least in my opinion.

Thanks for the input! Further discussion is very welcome.

lurk101
Posts: 379
Joined: Mon Jan 27, 2020 2:35 pm
Location: Cumming, GA (US)

Re: A Birthday Present for Fido

Sun Oct 11, 2020 5:22 pm

ejolson wrote:
Sun Oct 11, 2020 4:47 pm
Today we have done away with the fashion of hiding data in objects and moved to the strongly enforced data ownership model of Rust.
The structured programming fad predated the OO fad and were distinct sea changes.
Therefore, programming in Rust on the Pi is the closest thing to Pascal on the SuperPET when it comes to teaching computer science in historical context, at least in my opinion.
Indeed, I hadn't thought of it that way.

Idle musing. How many topographically unique graphs or caves can be formed with 20 vertices, each connected to exactly 3 neighbors, without overlapping edges, in three dimensional space?

ejolson
Posts: 6586
Joined: Tue Mar 18, 2014 11:47 am

Re: A Birthday Present for Fido

Sun Oct 11, 2020 7:03 pm

lurk101 wrote:
Sun Oct 11, 2020 5:22 pm
ejolson wrote:
Sun Oct 11, 2020 4:47 pm
Today we have done away with the fashion of hiding data in objects and moved to the strongly enforced data ownership model of Rust.
The structured programming fad predated the OO fad and were distinct sea changes.
Therefore, programming in Rust on the Pi is the closest thing to Pascal on the SuperPET when it comes to teaching computer science in historical context, at least in my opinion.
Indeed, I hadn't thought of it that way.

Idle musing. How many topographically unique graphs or caves can be formed with 20 vertices, each connected to exactly 3 neighbors, without overlapping edges, in a three dimensional space?
An answer to how many random mazes are possible was partially discussed and referenced in

viewtopic.php?p=1714376#p1714376

From a deep-learning AI point of view, randomly generated mazes seem much more challenging than a torus or the original dodecahedron--there is much more hidden state to be reconstructed.

It is not clear how a deep learning neural network would cope with that directly. Humans tend to look at the scroll-back buffer or draw a map, so an AI should probably have access to some sort of record of past moves in order to make the next. Note that the use of Hunt the Wumpus for AI student projects was also touched on in the above-linked thread.

I would emphasize that Ken's random graphs, the fact that you can smell the Wumpus from two vertices away and the greater tendency of the Wumpus to not eat you you make the Unix version of Hunt the Wumpus noticeably different than the BASIC program by Gregory Yob. Although the original game has been translated into many languages, the Unix variant has not. Even if the Unix version had been widely translated, it would still be necessary to write more programs to understand the suitability of each educational computer for teaching and learning. This is because with programming, as with as most other measurable skills, the way forward is to learn by doing.

ejolson
Posts: 6586
Joined: Tue Mar 18, 2014 11:47 am

Re: A Birthday Present for Fido

Sun Oct 11, 2020 7:54 pm

I just finished conferencing with Fido on Zoom about the origins of Hunt the Wumpus. It seems, even through banned from the computer, the former dog developer may have got wind of my coming birthday surprise.

At any rate, some sources say the actual code for Hunt the Wumpus was written in 1972 while Gregory was a student at the University of Massachusetts, not at the People's Computer Company but inspired by games developed by others there. At the same time, artwork created by Gregory on the daisy-wheel printer in Menlo Park appears in PCC volume 2 number 5. Fido claims that Gregory was actually in both places at the same time and might be here right now as well.

I found a BASIC Hunt the Wumpus with random graphs for the PDP-8 at

http://www.pdp8.net/pdp8cgi/os8_html/WU ... ,0;plain=1

The game dynamics do not seem to be equivalent to the Unix C code--the player has a lamp that can be turned on and off among other differences. These additions suggest Ken's version didn't derive from the PDP-8 but possibly other way around.

Note that Ken's Unix version had 22 goto statements while the PDP-8 version has 88 GOTO statements. Although Pascal allows goto statements, in order to make a proper birthday present for the super pet, I played the role of a student writing a program for an assignment where 1 percent was removed for each goto.

The results were

Code: Select all

         goto count  score  grade
microPascal     0     100     A
K&R C          22      78     C+
PDP-8 Basic    88      12     F
On the deep-learning front there appears to have been an implementation of a Wumpus expert system developed at the MIT Artificial Intelligence Laboratory in 1976 by James Stansfield, Brian Carr and Ira Goldstein.

https://files.eric.ed.gov/fulltext/ED207585.pdf

The actual code is missing, but enough details may be present to write a similar program using Rust for the Pi if not microPascal for the SuperPET.
Last edited by ejolson on Mon Oct 12, 2020 4:34 pm, edited 2 times in total.

ejolson
Posts: 6586
Joined: Tue Mar 18, 2014 11:47 am

Re: A Birthday Present for Fido

Mon Oct 12, 2020 12:26 am

While a bit off topic for this thread, I couldn't resist trying the PDP-8 version of Wumpus. Since the PDP-8 was surplussed long ago and I didn't want to figure out how to spin DECtape on SIMH, I decided it'd be easier to translate the code to BBC BASIC. The main changes were rewriting lines of the form

IF condition GOTO statement

to read as

IF condition THEN statement

and then changing all RND(0) calls to RND(1). I also removed the RANDOMIZE statement and replaced it by M=RND(-ABS(M)) to restore the apparent functionality that had been lost for setting the random seed. After this, I made some formatting changes based on differences in how semicolons were interpreted between BASICs and then changed the MAP command to output a suitable format for graphviz as discussed at

viewtopic.php?p=1719586#p1719586

I've played a couple games now. It seems the graph generator sometimes gets stuck in an infinite loop, so if after entering a random seed it hangs, then press <ESC> and run the program again with a different seed. I'm not sure if this is an error with my modifications to make it work on BBC BASIC or whether the bug was in the original program. If anyone wants to check the original running under OS8 on the SIMH PDP-8 emulator, that would be appreciated. Since BBC BASIC and SIMH run on the Raspberry Pi but the not SuperPET, it would also be interesting to know the changes needed to run the program under Waterloo microBASIC.

For reference, the BBC BASIC code is

Code: Select all

    1 PRINT "FOR HELP TYPE 'HELP WUMPUS' AS AN OS8 COMMAND"
    2 PRINT "ENTER A RANDOM NUMBER";
    3 INPUT M
    4 PRINT "HERE WE GO----"
    5 PRINT "DOWN"
    6 PRINT " O"
    7 PRINT "  W"
    8 PRINT "DOWN"
    9 PRINT
   10 DIM A(20),B(20),C(20),D(20)
   20 DIM Q$(20),P$(10)
   25 READ Q$
   30 M=RND(-ABS(M))
   40 FOR Z=1 TO 20
   50   A(Z)=0
   60   B(Z)=0
   70   C(Z)=0
   80   D(Z)=0
   90 NEXT Z
  100 R0=.02
  105 B0=0
  110 L0=0
  120 FOR Z=1 TO 20
  130   IF A(Z)<>0 THEN 190
  140   F=INT(20*RND(1))+1
  150   IF F=Z THEN 140
  160   IF A(F)<>0 THEN 140
  170   A(Z)=F
  180   A(F)=Z
  190 NEXT Z
  200 FOR Z=1 TO 20
  210   IF B(Z)<>0 THEN 280
  220   F=INT(20*RND(1))+1
  230   IF F=Z THEN 220
  240   IF A(Z)=F THEN 220
  250   IF B(F)<>0 THEN 220
  260   B(Z)=F
  270   B(F)=Z
  280 NEXT Z
  290 FOR X=1 TO 20
  300   IF D(X)<>0 THEN 410
  310   IF X=1 THEN 340
  320   B(Y)=X
  330   B(X)=Y
  340   Y=X
  350   D(Y)=1
  360   Y=A(Y)
  370   D(Y)=1
  380   IF B(Y)=X THEN 410
  390   Y=B(Y)
  400   GOTO 350
  410   D(X)=0
  420 NEXT X
  430 B(Y)=1
  440 B(1)=Y
  450 FOR Z=1 TO 20
  460   IF C(Z)<>0 THEN 540
  470   F=INT(20*RND(1))+1
  480   IF F=Z THEN 470
  490   IF F=A(Z) THEN 470
  500   IF F=B(Z) THEN 470
  510   IF C(F)<>0 THEN 470
  520   C(Z)=F
  530   C(F)=Z
  540 NEXT Z
  550 FOR X=1000 TO 10000 STEP 9000
  560   FOR Z=1 TO 3
  570     F=INT(20*RND(1))+1
  580     IF D(F)>=X THEN 570
  590     D(F)=D(F)+X
  600     GOSUB 2130
  610   NEXT Z
  620 NEXT X
  630 F=INT(20*RND(1))+1
  640 X=100000
  650 D(F)=D(F)+X
  660 GOSUB 2130
  670 W=F
  680 F=A(W)
  690 GOSUB 2130
  700 F=B(W)
  710 GOSUB 2130
  720 F=C(W)
  730 GOSUB 2130
  740 H=INT(20*RND(1))+1
  750 IF D(H)>=1000 THEN 740
  760 FOR S=5 TO 1 STEP -1
  770   IF L0=0 THEN 777
  772   GOSUB 1740
  774   PRINT "I SEE THAT TUNNELS A B AND C LEAD TO ROOMS ";
  776   PRINT "";A(H);" ";B(H);" AND ";C(H);" RESPECTIVELY"
  777   IF RND(1)>.04 THEN 779
  778   GOSUB 2191
  779   J=D(H)
  780   IF J>=1000 THEN 1240
  790   GOTO 1090
  800   IF J=0 THEN 890
  810   IF J=INT(J/10)*10 THEN 830
  820   PRINT "I HEAR SQUEAKING, ";
  830   J=INT(J/10)
  840   IF J=INT(J/10)*10 THEN 860
  850   PRINT "I FEEL A DRAFT, ";
  860   IF J<10 THEN 880
  870   PRINT "I SMELL A WUMPUS ";
  880   PRINT
  890   PRINT "YOU ARE IN ROOM ";H;" MOVE THROUGH TUNNEL";
  895   H1=H
  900   INPUT P$
  905   IF P$="Q" THEN 2310
  910   IF P$<>"A" THEN 940
  920   H=A(H)
  930   GOTO 770
  940   IF P$<>"B" THEN 970
  950   H=B(H)
  960   GOTO 770
  970   IF P$<>"C" THEN 1000
  980   H=C(H)
  990   GOTO 770
 1000   IF P$="SHOOT" THEN 1470
 1010   IF P$<>"MAP" THEN 1040
 1020   GOSUB 2200
 1030   GOTO 779
 1040   IF P$<>"LIGHTS ON" THEN 1070
 1050   L0=1
 1060   GOTO 770
 1070   IF P$<>"LIGHTS OFF" THEN 770
 1075   L0=0
 1080   GOTO 770
 1090   IF RND(1)>R0 THEN 800
 1100   PRINT "YOU TRIPPED ON A ";Q$;" IN ROOM ";H;" ";
 1110   F=INT(20*RND(1))+1
 1120   IF F=A(H) THEN 1150
 1130   IF F=B(H) THEN 1150
 1140   IF F<>C(H) THEN 1110
 1145   IF F=H THEN 1110
 1150   PRINT "AND HAVE TUMBLED INTO ROOM ";F
 1160   IF R0<.2 THEN 1190
 1165   IF RND(1)<.5 THEN 1190
 1170   PRINT "YOU ARE GETTING CLUMSIER BY THE MINUTE"
 1180   PRINT "YOU SEEM TO BE STUMBLING AROUND ";R0*100;"% OF THE TIME"
 1190   R0=R0+.02
 1200   H=F
 1210   IF D(H)>=100000 THEN 1430
 1220   GOSUB 1740
 1230   GOTO 770
 1240   J=INT(J/1000)
 1250   IF J>=10 THEN 1300
 1260   PRINT "THERE IS A SUPER-BAT IN ROOM ";H;".  FLAP--FLAP OUCH!!"
 1270   H=INT(20*RND(1))+1
 1280   IF D(H)>999 THEN 1270
 1283   IF H=H1 THEN 1270
 1290   GOTO 770
 1300   IF J>=100 THEN 1430
 1310   PRINT "YOU FELL INTO THE BOTTOMLESS PIT IN ROOM ";H
 1320   IF J-10<>0 THEN 2050
 1330   IF D(H)<>INT(D(H)/10)*10 THEN 2070
 1340   PRINT "PLAY AGAIN (YES OR NO)";
 1350   RESTORE
 1360   INPUT P$
 1370   PRINT
 1380   IF P$="YES" THEN 40
 1390   IF P$<>"MAP" THEN 1420
 1400   GOSUB 2200
 1410   GOTO 1340
 1420   STOP
 1430   PRINT "YOU HAVE BEEN DEVOURED BY THE WUMPUS IN ROOM ";H
 1440   GOTO 1340
 1450   PRINT "YOU SHOT YOURSELF"
 1460   GOTO 1340
 1470   PRINT "THE ARROW ZINGS IN ";
 1480   K=H
 1490   K1=H
 1500   FOR R=1 TO 5
 1510     PRINT "TO ROOM";
 1520     INPUT M
 1530     IF M=A(K) THEN 1600
 1540     IF M=B(K) THEN 1600
 1550     IF M=C(K) THEN 1600
 1560     IF M=0 THEN 1700
 1570     R=6
 1580     M=INT(20*RND(1))+1
 1590     GOTO 1530
 1600     IF R<=5 THEN 1626
 1620     PRINT "THE ARROW WENT ASTRAY"
 1624     GOTO 1630
 1626     IF M=K1 THEN 1570
 1630     IF M=H THEN 1450
 1640     IF D(M)<100000 THEN 1670
 1650     PRINT "YOU HAVE SLAIN THE WUMPUS"
 1655     S=S-1
 1660     GOTO 1340
 1670     IF INT(D(M)/1000)=INT(D(M)/10000)*10 THEN 1678
 1671     PRINT "YOU PICKED OFF AN INNOCENT SUPER-BAT"
 1672     X=-1000
 1673     F=M
 1674     GOSUB 2130
 1675     D(M)=D(M)-1000
 1676     B0=B0+1
 1677     GOTO 1700
 1678     K1=K
 1680     K=M
 1690   NEXT R
 1700   GOSUB 1740
 1710 NEXT S
 1720 PRINT "YOU HAVE EXHAUSTED YOUR SUPPLY OF ARROWS -- THE SHOW'S OVER"
 1730 GOTO 1340
 1740 IF RND(1)>.5 THEN 2000
 1750 PRINT "ROAR**THUD**THUD**RRUUMMBBLLEE**THWUMP**ZZZZZZZZZZZ"
 1760 X=-100000
 1770 F=W
 1780 GOSUB 2130
 1790 F=A(W)
 1800 GOSUB 2130
 1810 F=B(W)
 1820 GOSUB 2130
 1830 F=C(W)
 1840 GOSUB 2130
 1850 D(W)=D(W)-100000
 1860 F=INT(20*RND(1))+1
 1870 IF F=A(W) THEN 1900
 1880 IF F=B(W) THEN 1900
 1890 IF F<>C(W) THEN 1860
 1900 X=100000
 1910 D(F)=D(F)+X
 1920 W=F
 1930 GOSUB 2130
 1940 F=A(W)
 1950 GOSUB 2130
 1960 F=B(W)
 1970 GOSUB 2130
 1980 F=C(W)
 1990 GOSUB 2130
 2000 READ Q$
 2010 IF Q$<>"END" THEN 2040
 2020 RESTORE
 2030 GOTO 2000
 2040 RETURN
 2050 PRINT "YOU HAVE BEEN SNATCHED UP BY A SUPER-BAT"
 2060 GOTO 1270
 2070 PRINT "THERE IS A BAT IN AN ADJACENT ROOM -- HE'S FLYING THIS WAY!"
 2080 IF RND(1)>.25 THEN 2110
 2090 PRINT "YOU STRUCK YOUR HEAD ON A ";Q$;" GOOD-BYYEEE"
 2100 GOTO 1340
 2110 PRINT "PHEWWW!!!  ";
 2120 GOTO 2050
 2130 F1=A(F)
 2140 D(F1)=D(F1)+X/1000
 2150 F1=B(F)
 2160 D(F1)=D(F1)+X/1000
 2170 F1=C(F)
 2180 D(F1)=D(F1)+X/1000
 2190 RETURN
 2191 IF B0<0 THEN 2199
 2192 B0=B0-1
 2193 F=INT(20*RND(1))+1
 2194 IF INT(D(F)/1000)<>INT(D(F)/10000)*10 THEN 2193
 2195 X=1000
 2196 D(F)=D(F)+X
 2197 GOSUB 2130
 2198 PRINT "ANOTHER SUPER-BAT HAS BEEN BORN.  LOOKS LIKE A STRONG ONE!"
 2199 RETURN
 2200 Z=1
 2205 PRINT "graph maze {"
 2210 PRINT "  overlap=false; splines=true; sep=1.0"
 2215 IF Z>A(Z) THEN 2225
 2220 PRINT "  ";Z;" -- ";A(Z)
 2225 IF Z>B(Z) THEN 2235
 2230 PRINT "  ";Z;" -- ";B(Z)
 2235 IF Z>C(Z) THEN 2245
 2240 PRINT "  ";Z;" -- ";C(Z)
 2245 Z=Z+1
 2250 IF Z<=20 THEN 2215
 2255 PRINT "}"
 2280 RETURN
 2290 DATA "CORPSE","DEAD BAT","BROKEN ARROW","STALAGMITE","PEBBLE"
 2291 DATA "WUMPUS CLAW","LEDGE","LUNCH BAG","DOG"
 2300 DATA "END"
 2310 END
The game play is noticeably more chaotic than the Unix game as well as the original BASIC games by Gregory Yob including Wumpus 1, Wumpus 2 and Wumpus 3. I'll include a transcript in the next post as an example.
Last edited by ejolson on Mon Oct 12, 2020 4:38 pm, edited 2 times in total.

ejolson
Posts: 6586
Joined: Tue Mar 18, 2014 11:47 am

Re: A Birthday Present for Fido

Mon Oct 12, 2020 1:08 am

Here is a sample game of PDP-8 Hunt the Wumpus played using BBC BASIC.

Code: Select all

$ ./bbcbasic 
BBC BASIC for Linux Console v0.28
(C) Copyright R. T. Russell, 2020
>LOAD "WUMP.BAS"
>RUN
FOR HELP TYPE 'HELP WUMPUS' AS AN OS8 COMMAND
ENTER A RANDOM NUMBER? 3141
HERE WE GO----
DOWN
 O
  W
DOWN

I HEAR SQUEAKING, 
YOU ARE IN ROOM 16 MOVE THROUGH TUNNEL? A
I HEAR SQUEAKING, I FEEL A DRAFT, 
YOU ARE IN ROOM 14 MOVE THROUGH TUNNEL? A
I HEAR SQUEAKING, 
YOU ARE IN ROOM 16 MOVE THROUGH TUNNEL? B
YOU ARE IN ROOM 15 MOVE THROUGH TUNNEL? B
I HEAR SQUEAKING, 
YOU ARE IN ROOM 16 MOVE THROUGH TUNNEL? B
YOU ARE IN ROOM 15 MOVE THROUGH TUNNEL? A
I FEEL A DRAFT, 
YOU ARE IN ROOM 11 MOVE THROUGH TUNNEL? A
YOU ARE IN ROOM 15 MOVE THROUGH TUNNEL? C
I HEAR SQUEAKING, 
YOU ARE IN ROOM 20 MOVE THROUGH TUNNEL? LIGHTS ON
I SEE THAT TUNNELS A B AND C LEAD TO ROOMS 13 3 AND 15 RESPECTIVELY
I HEAR SQUEAKING, 
YOU ARE IN ROOM 20 MOVE THROUGH TUNNEL? LIGHTS OFF
I HEAR SQUEAKING, 
YOU ARE IN ROOM 20 MOVE THROUGH TUNNEL? B
I SMELL A WUMPUS 
YOU ARE IN ROOM 3 MOVE THROUGH TUNNEL? SHOOT
THE ARROW ZINGS IN TO ROOM? 1
TO ROOM? 6
YOU HAVE SLAIN THE WUMPUS
PLAY AGAIN (YES OR NO)? NO


STOP at line 1420
While playing the game I marked up a graph previously generated by the MAP command in a barely legible way using xournal with a mouse and then converted it to PNG format using the PrtScr button.

Image

It appears the random graph is generated in such a way that the edges may be labeled A, B and C and that each vertex in the graph has exactly one edge of each type connecting to it. My understanding is the random graphs created by Ken's algorithm do not necessarily have this property. On the other hand, from what I can tell, it is possible the graphs made by the PDP-8 program do not all admit a Hamiltonian path that visits each vertex only once. Which makes for a better game is difficult to determine.

For reference, the instructions for the game are available through the OS8 help facility and read

Code: Select all

Note: This is a basic game, see BASIC instructions.

YOU ARE A FAMOUS HUNTER DESCENDING DOWN INTO THE CAVES OF DARKNESS,
LAIR OF THE INFAMOUS MAN-EATING WUMPUS.  YOU ARE EQUIPPED WITH FIVE
BENT ARROWS, AND ALL YOUR SENSES.  THERE ARE TWENTY CAVES CONNECTED
BY TUNNELS, AND THERE ARE TWO OTHER KINDS OF HAZARDS:

        A) PITS, WHICH ARE BOTTOMLESS, AND USUALLY FATAL TO FALL
        INTO.  THERE ARE THREE SUCH PITS IN THE NETWORK.

        B) SUPER-BATS, WHICH IF YOU STUMBLE INTO THEIR ROOM WILL
        PICK YOU UP AND DROP YOU IN SOME RANDOM ROOM IN THE NETWORK.
        YOU MAY SHOOT SUPER-BATS, THERE IS ONE IN EACH OF THREE OR
        FOUR ROOMS WITHIN THE NETWORK.  THE SUPER-BATS GENERALLY STAY
        IN THEIR OWN ROOMS, EXCEPT WHEN DISPOSING OF INTRUDERS OR
        SCAVENGING FOR FOOD IN THE PITS.

IF YOU BLUNDER INTO THE SAME ROOM AS THE WUMPUS, YOU LOSE....
THE NORMALLY SLEEPING WUMPUS DOES NOT MOVE (HAVING GORGED HIMSELF UPON
A PREVIOUS HUNTER).  HOWEVER SEVERAL THINGS CAN WAKE HIM UP:

        1) WALKING INTO HIS ROOM,
        2) SHOOTING AN ARROW ANYWHERE IN THE NETWORK,
        3) TRIPPING OVER DEBRIS (CLUMSINESS),
        4) TURNING ON THE LIGHTS, IN ORDER TO SEE WHERE YOU ARE
        HEADED.

IF HE WAKES UP THERE'S A POSSIBILITY HE WILL MOVE, HOWEVER, HE'S TOO
LAZY TO MOVE MORE THAN ONE ROOM BETWEEN SNOOZES.  THE WUMPUS IS TOO
BIG TO BE PICKED UP BY SUPER-BATS AND HAS SUCKER FEET, SO HE DOESN'T
FALL INTO THE PITS.

YOU CAN SMELL THE WUMPUS FROM ONE OR TWO ROOMS AWAY.  YOU WILL
TREMBLE WITH FEAR WHEN HE MOVES ABOUT.  YOU CAN HEAR SUPER-BATS FROM
ONE ROOM AWAY, AND FEEL DRAFTS (FROM BOTTOMLESS PITS) FROM ONE ROOM
AWAY (AND TASTE THE FEAR...).

TO SHOOT AN ARROW TYPE "SHOOT" INSTEAD OF A MOVE, AND THEN
SPECIFY WHICH ROOMS THE ARROW SHOULD PASS THROUGH.  YOU ARE STRONG
ENOUGH TO SHOOT IT THROUGH AS MANY AS FIVE ROOMS.  BENT ARROWS HAVE
NO PROBLEM ROUNDING CORNERS OF LESS THAN 98.6 DEGREES.  IF YOU
SPECIFY AN IMPOSSIBLE PATH THE ARROW WILL RICOCHET OFF THE WALLS OF
THE ROOM, LOSING SPEED, AND WILL EVENTUALLY COME TO REST IN ONE OF
THE ADJOINING ROOMS.  THE PATH MAY BE TERMINATED BY SPECIFYING ROOM 0.

EACH ROOM IS CONNECTED TO THREE OTHER ROOMS BY THREE TUNNELS A, B
AND C.  YOU MUST ALWAYS MOVE BETWEEN ROOMS BY SPECIFYING WHICH
TUNNEL YOU WISH TO EXPLORE.  YOU CAN ALWAYS RETRACE YOUR FOOT STEPS
BY MOVING BACK USING THE SAME TUNNEL DESIGNATOR.

IF YOU WISH TO SEE WHICH ROOMS ARE AT THE ENDS OF THE TUNNELS YOU
MAY TYPE "LIGHTS ON" INSTEAD OF A MOVE.  THIS MAY BE AN UNHEALTHY
LUXURY HOWEVER BECAUSE THE LIGHT GIVES THE WUMPUS INSOMNIA.  TO
EXTINGUISH THE LIGHTS SIMPLY TYPE "LIGHTS OFF".

                GOOD LUCK HUNTING!!
Note the instructions do not mention the MAP command, which was clearly a way to cheat. At any rate, I modified the command to make the graphical output and to remove the exact location of the Wumpus, pits and bats. This is still cheating a bit, because I knew ahead of time how the rooms were linked in order to shoot my arrow for the last turn. Without the map, it would have required additional exploration to finish the game.
Last edited by ejolson on Mon Oct 12, 2020 4:39 pm, edited 1 time in total.

ejolson
Posts: 6586
Joined: Tue Mar 18, 2014 11:47 am

Re: A Birthday Present for Fido

Mon Oct 12, 2020 6:30 am

I've been studying the BASIC code which makes the random graphs and came across an interesting algorithm between lines 290 through 440. This loop appears to guarantee that taking alternatively A and then B tunnels will complete a Hamiltonian circuit that visits all the vertices once and ends up back where you started. Alternating A and C in general does not visit all the nodes nor does alternatively choosing tunnels C and B. At any rate, it would appear the random graphs generated by the BASIC code for the PDP-8 may form a subset of those generated by the Unix C code.

My guess is that sometimes the random selection of edges leads to a partial maze which cannot be completed to a regular valence-three graph. At that point the BASIC code gets stuck in an infinite loop. On the other hand, when the Unix program detects such a problem it simply tries again from the beginning. Does anyone want to exterminate a 48-year-old bug from the past?

From a machine learning point of view, I think it would be preferable to choose from among the tunnels A, B and C for each move rather than selecting the destination by room number. Of course both are possible in the PDP-8 version since the lamp can be turned on and off.

Being able to turn on the lights also allowed me to win one game where I was trapped behind two pits. In that case I turned on the lights and then moved back and forth in the limited part of the maze I had access to until the Wumpus crossed over the pits using those sucker feet, at which point I deployed an arrow.
Last edited by ejolson on Tue Oct 13, 2020 2:55 pm, edited 1 time in total.

ejolson
Posts: 6586
Joined: Tue Mar 18, 2014 11:47 am

Re: A Birthday Present for Fido

Tue Oct 13, 2020 7:31 am

It occured to me that any computer used for teaching must also support legacy programming languages, not only because the old has value, but because textbooks and other educational materials are updated more slower than technology and not all teachers want or have the time to keep up with every new innovation. In 1982 this meant that the SuperPET needed to run BASIC programs while today the Pi needs to run Python.

Before comparing the legacy capabilities of these two computing platforms, it seems reasonable to present the finished form of Hunt the Wumpus written in Waterloo microPascal with a student-level understanding of all the latest advances in programming technology circa 1982.

Code: Select all

(*
 *  wumpus.pas
 *
 *  a translation from the c code that appears in the 7th edition
 *  of unix on the pdp-11 that attempts to be dynamically accurate
 *  by implementing the exact same random number generator.  note
 *  the c code was written by ken thompson and apparently predates
 *  the 6th edition of unix by a number of years.
 *
 *  all this was inspired by the original basic adventure games
 *
 *    gregory yob, hunt the wumpus, posted by magnus olsson
 *    on usenet in 1972, pc-blue collection, simtel20.
 *
 *    gregory yob, hunt the wumpus, people's computer company,
 *    vol 2, no 2, 1973, page 22.
 *
 *    gregory yob, hunt the wumpus, creative computing, vol 1,
 *    no 5, 1975, pages 51-54.
 *
 *)

program main(input,output);

const nroom=20; ntunn=3; nbat=3; npit=3;
type int5=array[0..4] of integer;
    prompt=packed array[1..25] of char;
    things=(empty,epit,ebat);
var r7a,r7c,r7x:int5;
    rms:array[1..nroom] of array[1..ntunn] of integer;
    pits:array[1..npit] of integer;
    bats:array[1..nbat] of integer;
    arrow,loc,wloc:integer;
    tchar:char;
    blank:prompt;

procedure r7init;
begin
    r7a[0]:=109; r7a[1]:=28; r7a[2]:=25; r7a[3]:=14; r7a[4]:=4;
    r7c[0]:=57; r7c[1]:=96; r7c[2]:=0; r7c[3]:=0; r7c[4]:=0;
    r7x[0]:=1; r7x[1]:=0; r7x[2]:=0; r7x[3]:=0; r7x[4]:=0;
    blank:='                         ';
end;

procedure r7mul(var r:int5;a,b:int5);
var i,j,c,p:integer;
begin
    c:=0;
    for i:=0 to 4 do begin
        r[i]:=c; c:=0;
        for j:=0 to i do begin
            p:=r[i]+a[i-j]*b[j];
            r[i]:=p mod 128;
            c:=c+(p div 128)
        end
    end;
    r[4]:=r[4] mod 16
end;

procedure r7add(var r:int5;a,b:int5);
var i,c,p:integer;
begin
    c:=0;
    for i:=0 to 4 do begin
        p:=a[i]+b[i]+c;
        r[i]:=p mod 128;
        c:=p div 128
    end
end;

procedure r7seed(x:integer);
begin
    if(x>=0) then begin
        r7x[0]:=x mod 128;
        x:=x div 128;
        r7x[1]:=x mod 128;
        x:=x div 128;
        r7x[2]:=x mod 4;
        r7x[3]:=0; r7x[4]:=0
    end else begin
        x:=x+16384+16384;
        r7x[0]:=x mod 128;
        x:=x div 128;
        r7x[1]:=x mod 128;
        x:=(x div 128)+2;
        r7x[2]:=x mod 4;
        r7x[3]:=0; r7x[4]:=0
    end
end;

function r7rand:integer;
begin
    r7mul(r7x,r7x,r7a);
    r7add(r7x,r7x,r7c);
    r7rand:=(r7x[2] div 4)+r7x[3]*32+(r7x[4] mod 8)*4096
end;

function rnum(n:integer):integer;
begin
    rnum:=trunc((r7rand/32768.0)*n)+1
end;

procedure rmsprint;
var i,j:integer;
begin
    for i:=1 to nroom do begin
        write(i:2,':');
        for j:=1 to ntunn do begin
            write(' ',rms[i][j]:2)
        end;
        writeln
    end
end;

procedure rmsgraph;
var i,j:integer;
begin
    writeln('graph maze {');
    writeln('    overlap=false');
    writeln('    splines=true');
    writeln('    sep=1.0');
    for i:=1 to nroom do begin
        for j:=1 to ntunn do begin
            if i<rms[i][j] then begin
                writeln('    ',i:1,' -- ',rms[i][j]:1)
            end
        end
    end;
    writeln('}')
end;

function getchar:char;
var c:char;
begin
    if eof then begin
        getchar:=chr(0)
    end else if eoln then begin
        readln;
        getchar:=chr(10)
    end else begin
        read(c);
        getchar:=c
    end
end;

function snarf(l:integer;p:prompt):char;
var c:char;
    i:integer;
    snarfed:boolean;
begin
    for i:=1 to l do write(p[i]);
    i:=0;
    snarfed:=false;
    repeat
        i:=i+1;
        c:=getchar;
        if i>l then snarfed:=true
        else if c<>p[i] then snarfed:=true
    until snarfed;
    while c=' ' do begin
        c:=getchar
    end;
    snarf:=c
end;

function rin(l:integer;p:prompt):integer;
var c:char;
    n:integer;
begin
    c:=snarf(l,p);
    n:=0;
    while (c<>chr(10)) and (c<>' ') do begin
        if (ord(c)<ord('0')) or (ord(c)>ord('9')) then begin
            while c<>chr(10) do begin
                c:=getchar
            end;
            n:=0;
            c:=' '
        end else begin
            n:=n*10+ord(c)-ord('0');
            c:=getchar
        end
    end;
    rin:=n
end;

function rline(l:integer;p:prompt):char;
var c,r:char;
begin
    c:=snarf(l,p);
    r:=c;
    while (c<>chr(10)) and (c<>' ') do begin
        c:=getchar
    end;
    tchar:=c;
    rline:=r
end;

function tunnel(i:integer):integer;
var c,j,n,r:integer;
begin
    r:=0; c:=20;
    repeat
        n:=rnum(nroom);
        while (n=i) and (c>1) do begin
            c:=c-1;
            n:=rnum(nroom)
        end;
        for j:=ntunn downto 1 do begin
            if rms[n][j]=0 then r:=j
        end
    until r>0;
    rms[n][r]:=i;
    tunnel:=n
end;

procedure connect;
var i,j,k:integer;
begin
    k:=1; i:=2;
    while i<=nroom do begin
        j:=rnum(nroom);
        if (j<>k) and (rms[j][1]=0) and (rms[j][2]=0) then begin
            rms[j][2]:=k; rms[k][1]:=j;
            k:=j; i:=i+1
        end
    end
end;

function finish:boolean;
var i,j,k:integer;
    r:boolean;
begin
    i:=1; j:=1; r:=true;
    repeat
        if rms[i][j]=0 then begin
            rms[i][j]:=tunnel(i)
        end;
        if rms[i][j]=i then begin
            r:=false
        end else begin
            for k:=1 to j-1 do begin
                if rms[i][j]=rms[i][k] then begin
                    r:=false
                end
            end
        end;
        if j<ntunn then begin
            j:=j+1
        end else begin
            j:=1; i:=i+1;
        end
    until (not r) or (i>nroom);
    finish:=r
end;

(*
 *  initialize the room connections
 *)

procedure doinit;
var i,j,k,n:integer;
begin
    repeat
        for i:=1 to nroom do begin
            for j:=1 to ntunn do begin
                rms[i][j]:=0
            end
        end;
        connect
    until finish;
    for i:=1 to nroom do begin
        for j:=ntunn downto 2 do begin
            for k:=j-1 downto 1 do begin
                if rms[i][j]<rms[i][k] then begin
                    n:=rms[i][j];
                    rms[i][j]:=rms[i][k];
                    rms[i][k]:=n
                end
            end
        end
    end
end;

function occupied(n:integer):things;
var i:integer;
begin
    occupied:=empty;
    for i:=1 to npit do begin
        if pits[i]=n then occupied:=epit
    end;
    for i:=1 to nbat do begin
        if bats[i]=n then occupied:=ebat
    end
end;

(*
 *  put in player, wumpus
 *  pits and bats
 *)

procedure dosetup;
var i,n:integer;
begin
    for i:=1 to npit do pits[i]:=0;
    for i:=1 to nbat do bats[i]:=0;
    arrow:=5;
    i:=1;
    while i<=npit do begin
        n:=rnum(nroom);
        if occupied(n)=empty then begin
            pits[i]:=n;
            i:=i+1
        end
    end;
    i:=1;
    while i<=nbat do begin
        n:=rnum(nroom);
        if occupied(n)=empty then begin
            bats[i]:=n;
            i:=i+1
        end
    end;
    wloc:=rnum(nroom);
    i:=1;
    while i<=1 do begin
        n:=rnum(nroom);
        if (occupied(n)=empty) and (wloc<>n) then begin
            loc:=n;
            i:=i+1
        end
    end
end;

procedure mwump;
var i:integer;
begin
    i:=rnum(ntunn+1);
    if i<=ntunn then wloc:=rms[wloc][i];
end;

procedure goroom(i:integer);
begin
    loc:=i;
    if i=wloc then mwump
end;

function doagain:boolean;
var i,j,k,p:integer;
    c:char;
    bump:boolean;
begin
    doagain:=true;
    repeat
        bump:=true;
        c:=rline(20,'Move or shoot (m-s)      ');
        if c='m' then begin
            if tchar=chr(10) then begin
                i:=rin(12,'which room?              ')
            end else begin
                i:=rin(0,blank)
            end;
            for j:=1 to ntunn do begin
                if i=rms[loc][j] then bump:=false
            end;
            if bump then begin
                writeln('you hit the wall')
            end else begin
                goroom(i)
            end
        end else if c='s' then begin
            if tchar=chr(10) then begin
                writeln('Give list of rooms terminated by 0')
            end;
            p:=loc; i:=1; 
            j:=rin(0,blank); 
            while (j>0) and (i<=5) do begin
                repeat
                for k:=1 to ntunn do begin
                        if j=rms[p][k] then begin
                            p:=j; bump:=false
                        end
                    end;
                    if bump then j:=rnum(nroom);
                until not bump;
                if j=loc then begin
                    writeln('You shot yourself');
                    j:=0; doagain:=false
                end else if j=wloc then begin
                    writeln('You slew the wumpus');
                    j:=0; doagain:=false
                end else begin
                    i:=i+1; 
                    j:=rin(0,blank);
                    bump:=true
                end
            end;
            bump:=false; arrow:=arrow-1;
            if arrow=0 then begin
                writeln('That was you last shot');
                doagain:=false
            end
        end else if c='g' then begin
            rmsgraph
        end
    until not bump
end;

function near(t:things):boolean;
var j:integer;
begin
    near:=false;
    for j:=1 to ntunn do begin
        if occupied(rms[loc][j])=t then near:=true
    end
end;

function smell:boolean;
var i,j,k:integer;
begin
    smell:=false;
    for j:=1 to ntunn do begin
        k:=rms[loc][j];
        if wloc=k then smell:=true;
        for i:=1 to ntunn do begin
            if wloc=rms[k][i] then smell:=true
        end
    end
end;

(*
 *  main loop of the game
 *)

function status:boolean;
var alive,moved:boolean;
    i:integer;
begin
    alive:=true;
    repeat
        moved:=false;
        writeln('You are in room ',loc:1);
        if occupied(loc)=epit then begin
            writeln('You fell into a pit');
            alive:=false;
        end else if loc=wloc then begin
            writeln('You were eaten by the wumpus');
            alive:=false;
        end else if occupied(loc)=ebat then begin
            writeln('Theres a bat in your room');
            loc:=rnum(nroom);
            moved:=true;
        end
    until not moved;
    if alive then begin
        if smell then writeln('I smell a wumpus');
        if near(ebat) then writeln('Bats nearby');
        if near(epit) then writeln('I feel a draft');
        write('There are tunnels to');
        for i:=1 to ntunn do begin
            write(' ',rms[loc][i]:1)
        end;
        writeln;
    end;
    status:=alive
end;

procedure help;
begin
    writeln;
    writeln('Welcome to ''Hunt the Wumpus.''');
    writeln('The Wumpus lives in a cave of ',nroom:1,' rooms.');
    writeln('Each room has ',ntunn:1,' tunnels leading to other rooms.');
    writeln;
    writeln('Hazards:');
    writeln;
    writeln('Bottomless Pits - Some rooms have Bottomless Pits in them.');
    writeln('   If you go there, you fall into the pit and lose!');
    writeln('Super Bats - Some other rooms have super bats.');
    writeln('   If you go there, a bat will grab you and take you to');
    writeln('   somewhere else in the cave where you could');
    writeln('   fall into a pit or run into the . . .');
    writeln;
    writeln('Wumpus:');
    writeln;
    writeln('The Wumpus is not bothered by the hazards since');
    writeln('he has sucker feet and is too big for a bat to lift.');
    writeln;
    writeln('Usually he is asleep.');
    writeln('Two things wake him up:');
    writeln('   your entering his room');
    writeln('   your shooting an arrow anywhere in the cave.');
    writeln('If the wumpus wakes, he either decides to move one room or');
    writeln('stay where he was.  But if he ends up where you are,');
    writeln('he eats you up and you lose!');
    writeln;
    writeln('You:');
    writeln;
    writeln('Each turn you may either move or shoot a crooked arrow.');
    writeln;
    writeln('Moving - You can move to one of the adjoining rooms;');
    writeln('   that is, to one that has a tunnel connecting it with');
    writeln('   the room you are in.');
    writeln;
    writeln('Shooting - You have 5 arrows.  You lose when you run out.');
    writeln('   Each arrow can go from 1 to 5 rooms.');
    writeln('   You aim by telling the computer');
    writeln('   The arrow''s path is a list of room numbers');
    writeln('   telling the arrow which room to go to next.');
    writeln('   The list is terminated with a 0.');
    writeln('   The first room in the path must be connected to the');
    writeln('   room you are in.  Each succeeding room must be');
    writeln('   connected to the previous room.');
    writeln('   If there is no tunnel between two of the rooms');
    writeln('   in the arrow''s path, the arrow chooses one of the');
    writeln('   three tunnels from the room it''s in and goes its');
    writeln('   own way.');
    writeln;
    writeln('   If the arrow hits the wumpus, you win!');
    writeln('   If the arrow hits you, you lose!');
    writeln;
    writeln('Warnings:');
    writeln;
    writeln('When you are one or two rooms away from the wumpus,');
    writeln('the computer says:');
    writeln('       ''I smell a Wumpus''');
    writeln('When you are one room away from some other hazard, it says:');
    writeln('       Bat    - ''Bats nearby''');
    writeln('       Pit    - ''I feel a draft''');
    writeln;
end;

procedure doplay;
var alive,finished:boolean;
begin
    finished:=false;
    doinit;
    repeat
        dosetup;
        alive:=true;
        while alive do begin
            alive:=status;
            if alive then alive:=doagain
        end;
        if rline(20,'Another game? (y-n)      ')='y' then begin
            if rline(23,'Same room setup? (y-n)   ')<>'y' then doinit
        end else begin
            finished:=true
        end
    until finished
end;

begin
    r7init;
    if rline(20,'Instructions? (y-n)      ')='y' then help;
    if tchar=chr(10) then begin
        r7seed(rin(25,'What is the random seed? '))
    end else begin
        r7seed(rin(0,blank))
    end;
    doplay;
end.
Other than serving as a birthday present for Fido, another reason for writing the above code was to experience what it might have been like trying to learn structured programming with strong typing using the SuperPET. Please skip the following observations unless you have a high tolerance for boredom:
  • The built-in editor was easy to use and reasonably powerful.
    • It was modal like vi but the commands were different and the regular expressions employed a different syntax.
    • The keyboard mappings for page down were broken on the GTK3 version of VICE but worked using SDL.
    • SDL put the open and close parenthesis above 8 and 9 like they are on the PET keyboard, this nearly ruined my typing until I discovered the GTK3 version of VICE doesn't do this.
  • The strong typing rules of Pascal insisted that strings had to be the exact same length in order to be type compatible.
    • Strings literals were padded with spaces to be the same length when used as function and procedure arguments.
    • This trouble with Pascal strings was so annoying as to make strong typing seem like a joke--commented on, for example, in the SuperPET Gazette.
    • Turbo Pascal and Free Pascal allow strings of different lengths to be type compatible.
  • Pascal allows nesting of function definitions.
    • I remember nested functions being quite useful in the past.
    • If there had been extra credit for nesting function definitions I would surely have done so.
    • Pascal's nested function definitions seem to be called closures by other students and the pseudo-intellectual elite.
  • The SuperPET transmits an entire line visible on the screen--trailing blanks omitted--when the enter key is pressed.
    • The prompt appears as part of the input and needs to be parsed to find what the user actually typed.
    • Trying to find a way around this and failing took more time than any other part of writing the program.
    • The current code works without change in environments where the prompt is not echoed back to the input.
  • Waterloo microPascal enforces single-entry single-exit for functions and subroutines.
    • Flags had to be set for simple tasks such as smelling a wumpus, terminating the flight of an arrow and making random graphs.
    • A goto statement could have been used for multiple-exit, except for losing points for lack of structure.
    • Most languages in practical use have kept the single-entry constraint but allow multiple exit.
  • Waterloo microPascal is an interpreted language.
    • The interactive debugger was easy to use because no new syntax was needed to to examine and change variables.
    • Bounds errors and uninitialized variables were automatically caught and the system didn't crash even once.
    • It took from 5 to 10 minutes to generate a regular 3-valence graph at the beginning of the Hunt the Wumpus game.
Last edited by ejolson on Wed Oct 14, 2020 4:18 am, edited 9 times in total.

Heater
Posts: 17355
Joined: Tue Jul 17, 2012 3:02 pm

Re: A Birthday Present for Fido

Tue Oct 13, 2020 8:10 am

ejolson wrote:
Tue Oct 13, 2020 7:31 am
[*] Most languages in practical use have kept the single-entry constraint but allow multiple exit.
Interestingly the new kids on the programming language design block are intent on fixing that constraint of the structured programming idea. They have been busy adding "generators" to every possible language recently. Generators allow multiple entry points to a function depending on what state it's internals are in. Alternatively known as coroutines:

Python:https://medium.com/@chandansingh_99754/ ... 4ed9c343ae
C++ https://en.cppreference.com/w/cpp/language/coroutines
Javascript: https://developer.mozilla.org/en-US/doc ... Generators
C#: https://devblogs.microsoft.com/dotnet/i ... enerators/
Rust: https://doc.rust-lang.org/beta/unstable ... ators.html

It seems to me that for every constraint imposed by the ideals of "structured programming", that were intended to ensure one writes clear, well organized, understandable code, high level language builders have found a way to circumvent it so as to allow us to write "spaghetti".

Can't have "goto", OK we'll put exceptions in the language.
Can't have multiple entry points to a function. OK we'll put coroutines in there.

Etc, etc.
Memory in C++ is a leaky abstraction .

User avatar
jahboater
Posts: 6665
Joined: Wed Feb 04, 2015 6:38 pm
Location: Wonderful West Dorset

Re: A Birthday Present for Fido

Tue Oct 13, 2020 8:39 am

coroutines were always implemented in languages like B and C by using longjmp().

A decent compiler might be said to should implement multiple entry points (sort of!) by "partial inlining".
That is, it will inline only that part of the function which is actually used.
It only works when it can deduce the argument value somehow.
Pi4 8GB (Raspberry Pi OS 64-bit), Pi4 4GB, Pi1 Rev 1 256MB, Pi Zero

Heater
Posts: 17355
Joined: Tue Jul 17, 2012 3:02 pm

Re: A Birthday Present for Fido

Tue Oct 13, 2020 9:12 am

jahboater wrote:
Tue Oct 13, 2020 8:39 am
coroutines were always implemented in languages like B and C by using longjmp().
I guess that counts.

Otherwise known as "evil hackery": https://fanf.livejournal.com/105413.html
jahboater wrote:
Tue Oct 13, 2020 8:39 am
A decent compiler might be said to should implement multiple entry points (sort of!) by "partial inlining".
That is, it will inline only that part of the function which is actually used.
It only works when it can deduce the argument value somehow.
Indeed.

Compilers can do all kind of "evil hackery" under the hood. That's rather a different thing to the surface syntax and semantics that is the language itself.
Memory in C++ is a leaky abstraction .

User avatar
jahboater
Posts: 6665
Joined: Wed Feb 04, 2015 6:38 pm
Location: Wonderful West Dorset

Re: A Birthday Present for Fido

Tue Oct 13, 2020 9:49 am

Heater wrote:
Tue Oct 13, 2020 9:12 am
Compilers can do all kind of "evil hackery" under the hood. That's rather a different thing to the surface syntax and semantics that is the language itself.
Yes of course.
Language defined multiple entry points always count as evil in my book.
Imagine the problems the linker has.
There is also the problem of initialization being bypassed etc.

In my experience (from long ago) it was done (in assembler) in the name of efficiency.
Modern compilers can execute a jump table in a couple of instructions or so.
Pi4 8GB (Raspberry Pi OS 64-bit), Pi4 4GB, Pi1 Rev 1 256MB, Pi Zero

Heater
Posts: 17355
Joined: Tue Jul 17, 2012 3:02 pm

Re: A Birthday Present for Fido

Tue Oct 13, 2020 11:30 am

jahboater wrote:
Tue Oct 13, 2020 9:49 am
In my experience (from long ago) it was done (in assembler) in the name of efficiency.
Guilty. Jumping into the middle of functions is a natural way to save code space.
Memory in C++ is a leaky abstraction .

ejolson
Posts: 6586
Joined: Tue Mar 18, 2014 11:47 am

Re: A Birthday Present for Fido

Tue Oct 13, 2020 1:21 pm

Heater wrote:
Tue Oct 13, 2020 11:30 am
jahboater wrote:
Tue Oct 13, 2020 9:49 am
In my experience (from long ago) it was done (in assembler) in the name of efficiency.
Guilty. Jumping into the middle of functions is a natural way to save code space.
I'm not sure I understand how a generator works. Is it reentrant? Is it thread safe? What happens if the same generator is simultaneously called from two different threads? What happens if it is called again from a signal handler after having been interrupted before returning one of the generated sequence of return values?

ejolson
Posts: 6586
Joined: Tue Mar 18, 2014 11:47 am

Re: A Birthday Present for Fido

Tue Oct 13, 2020 3:12 pm

Heater wrote:
Tue Oct 13, 2020 11:30 am
jahboater wrote:
Tue Oct 13, 2020 9:49 am
In my experience (from long ago) it was done (in assembler) in the name of efficiency.
Guilty. Jumping into the middle of functions is a natural way to save code space.
I used multiple entry for the push and pop routines of the recursive Karatsuba multiplication algorithm that appears in the classic line-numbered Basic program for the big Fibonacci challenge. While that saved one line, having multiple entry points but fewer routines made it less difficult to keep the code consistent.

In Hunt the Wumpus it would have been natural to use multiple entry to play a sequence of games without changing the maze. While it wasn't too difficult to code this functionality by passing a flag back and forth, it's not clear how that improved code readability. Maybe that's the point behind the goto madness in the Wumpus code Ken wrote for Unix.

lurk101
Posts: 379
Joined: Mon Jan 27, 2020 2:35 pm
Location: Cumming, GA (US)

Re: A Birthday Present for Fido

Tue Oct 13, 2020 4:12 pm

ejolson wrote:
Tue Oct 13, 2020 7:31 am
Pascal's nested function definitions seem to be called closures by other students and the pseudo-intellectual elite.
A very limited type of closure, but yeah... maybe.

lurk101
Posts: 379
Joined: Mon Jan 27, 2020 2:35 pm
Location: Cumming, GA (US)

Re: A Birthday Present for Fido

Tue Oct 13, 2020 4:20 pm

ejolson wrote:
Tue Oct 13, 2020 3:12 pm
In Hunt the Wumpus it would have been natural to use multiple entry to play a sequence of games without changing the maze. While it wasn't too difficult to code this functionality by passing a flag back and forth, it's not clear how that improved code readability. Maybe that's the point behind the goto madness in the Wumpus code Ken wrote for Unix.
With nothing to do on a rainy Covid Monday and as an exercise in futility I tackled this very problem. The code can be de-spaghettied easily using a state machine. Arbitrary code segment reentry is done without resorting to GOTOs. No need for exotic features.

ejolson
Posts: 6586
Joined: Tue Mar 18, 2014 11:47 am

Re: A Birthday Present for Fido

Tue Oct 13, 2020 4:39 pm

lurk101 wrote:
Tue Oct 13, 2020 4:12 pm
ejolson wrote:
Tue Oct 13, 2020 7:31 am
Pascal's nested function definitions seem to be called closures by other students and the pseudo-intellectual elite.
A very limited type of closure, but yeah... maybe.
I thought if a multiple-entry subroutine was the same as a coroutine, the same as a generator and nothing other than a longjmp, then surely my turbo encabutor based quantum tunneling composable Wumpus generator could be improved using closures.

Before embarking upon a study of the legacy programming environments on purpose-built educational computing devices, I wonder what pseudo-random number algorithm did BASIC on the PDP-8 use and whether the correct way to safely calculate the same sequence of numbers in Rust is with the strong data ownership of a generating coroutine.

Return to “Other projects”