Saturday, 18 February 2023

"Gin Rummy" by S. Silverman (1980)

This one is a bit of a mystery.  I went looking for some early source of a Gin Rummy game.  I wanted to add this popular card game to my list of early attempts at BASIC AI opponent programming.  I first stumbled across a reference to a game for the Atari called Gin Rummy 3.0.  by Manhattan Software.  Some sites, such as the Giant List of Classic Game Programmers, designated the programmer as S. Silverman.

I have found converting programs from Atari BASIC difficult in the past. The emulators are complex and the BASIC is somewhat different from Microsoft versions of BASIC.  So I went looking for something by Silverman or Manhattan Software for the TRS-80.  This lead me to some BASIC source for Gin Rummy 2.0, which I was able to convert fairly easily to TRS-80 Micro Color BASIC.  The TRS-80 Model I/III has a 64X16 character screen so conversion mainly involves taking PRINT@ screen location values and dividing them by 2. That gives you the rough equivalent for the PRINT@ value for the MC-10's 32X16 screen. Then all you have to do is condense the length of messages by about a half to get everything to fit on a MC-10 screen.

Here is the TRS-80 Model I/III main screen:

Here is the TRS-80 MC-10 main screen:

You can see how the prompts have been shrunk.  Also, the representation of the cards was switched from full text descriptions to two characters.  The face cards become letters, as does the ten and Ace, which become "T" and "A" respectively.  Otherwise the values are simply a number. The suits also just become single characters.  I separate the two with the standard suit colour of red or black character to help identify the suit and to separate the card value from the suit more clearly.  This is a much shorter way to identify the cards, which really helped with the confined screen space of the MC-10.

The real problem came with the program itself. The program contains a large number of unclosed FOR/NEXT loops.  Silverman uses multiple such loops to do searches of the hands, and then simply jumps out of them when some sought after value is found. The program then just continues execution by GOTOing to some location after the FOR/NEXT routine.  However, in one place (600-740) the author actual goes from in the midst of two nested FOR loops, to a subroutine and then GOTOs back to the NEXT of the calling loop.  It does this in two different FOR/NEXT routines.  To continue those routines it uses an IF to GOTO back to the NEXTS of the different calling routines. Or if a search item is found it simply continues with execution after those routines and the subroutine. All these crisscrossing FOR/NEXTs get the Micro Color BASIC interpreter confused.  Especially if it comes across a NEXT that doesn't have a specific variable attached to it. So occasionally I would get a NEXT without FOR error, which indicated that the interpreter was putting the program back into the midst of the inner loop of the nested FOR/NEXTs, but without initializing the outer FOR, perhaps because that loop and its variable had been reinitialized elsewhere in the code's meanderings.  It was a real mess.

So I tried to add proper exits to some FOR/NEXTs in a few places where I though this would help. I also entered in a few dummy loops at the start of the offending section of code. This is a way to ensure that all the FOR/NEXTs get reset before they are utilized again. Finally I put the appropriate variable on every NEXT command in the program, so there is no possibility for a NEXT to trigger a return to an unclosed loop. By doing all this I was able to eliminate the error and I was able to remove an ON/ERROR kluge command that Silverman had obviously used to recover from the error.

590 ON ERROR GOTO 600
600 RESUME 610

There was also a simple typo in a FOR/NEXT loop:

3360 FORX=(C+1)TOO111

I changed this to 3360 FORX=(C+1)TO11, as 11 was common search value.  O111 would just be interpreted as an undeclared variable and simply return a zero.  So the loop would only be run once.

It was Greg Dionne who let me know about this bug, after I asked folks on the Facebook group for the MC-10 to have a look at the program.  He used his BASIC compiler to check for undeclared variables.  I had noticed before he made his bug report that sometimes the program would miscount the melds in my hand. Occasionally, a meld of 4 items like JH,JD,JS,JC would only count three of the items (It might also have done this on a 3 item meld but I can't precisely recall if this was something I spotted early in my testing or not).  I had hoped that Greg's fix would be the fix to this problem, but after much play testing it occurred again on a 4 item meld of Jacks.  It only happens very occasionally. I've checked the code for typos I might have entered, and don't think it is a result of any change I made. I  think it might be related to the special search (See the REM about "hidden meld") for whether a card is playing a role in a meld or a run. That is to say, the program has to distinguish between if you are using a card, say one of the jack's above, as part of a run, rather than as an item in 4 meld.  It certainly can count 4 item melds, but I need to generate multiple game conditions where I can test this hypothesis, which takes a long time.  And the code is so spaghetti that it is really difficult to get a sense of what is going on. It's also possible that a rule I'm simply unaware is what is causing the problem, or some nuance in the code conversion that I have overlooked.  I'll have to play the original TRS-80 on an emulator to see if the error occurs there too.

In any case, I've created a kluge work-around.  If on the SCORE SHEET screen you type 'C' at the "Another Hand" prompt you are given a chance to type in new values for the final scores for the player and the computer that are listed. That way, if you notice the error, you will at least have an opportunity to make a manual readjustment to the scoring tally and keep playing.  I think Silverman might have been aware of these errors and worked to fix them.  You can see the program being modified to work on different computer systems, but also increasing in version number.  The TRS-80 version is labeled 2.0." The Atari version is labeled "3.0."  And there appears to be a PC version labeled "4.0."

TRS-80 Version Ad

Atari Version Ad


I hope that I might be able to figure out some day how to get my hands on the Atari source code for 3.0. Then I could look through some of the search routines for analyzing the hands for changes to the code.  I have searched but can't find any copy of the PC version.  I might also continue to search for the bug myself.  But in the mean time, I would still agree with the rhetoric of the ads that Silverman's code does play "a strong game," and thus represents an interesting part of early BASIC AI opponent programming.  Of course that is an assessment of someone who'd never played Gin Rummy before this porting exercise.  So I only have Gin Rummy 2.0 as teacher (and some reading on Wikipedia) upon which to base my assessment.

"GINRUMMY" can be played online using an MC-10 emulator(just search for the program name): https://archive.org/details/@james_gerrie

P.S.
I went back and added a few more dummy FOR/NEXT loops just before the hand checking routine.  I'm not sure if this might have fixed the problem with rare miscounting melds, but I haven't seen any for some time.  I also added colourful graphic cards that somewhat emulate the cards which can be seen on the Atari version.  I have changed the version number to 2.5 to reflect this slight updating.  I also fixed up the Score Sheet screen to format the numbers properly via right justification.  The original used the PRINT USING command, which I had to emulate by the use of STR$(num var) and RIGHT$ commands to get things properly lined up.  Here's a vid of the new look:


I also think I might have stumbled on a possible explanation of the miscounting of melds.  The computer seems to prioritize RUNs over melds in its analysis of hands, such as can be seen in my hand in the following pic of the computer achieving Gin!  I might not have recognized this in my early play  testing as I was still learning the game.  

And I think I found some other bugs in the program.  The routine that prevents you from discarding the upcard you picked up before knocking, didn't do the same variable resets before returning you to line 80 as other checks did (e.gs. knocking with more than 10, lying about your knock points).  This seemed to cause some catastrophic weirdness.  So I changed it to mimic the resets of the other check routines.  Also, you can now type -1 at the discard prompt after knocking, to back out of that routine.  I found that I could knock right at the start of the game and it would allow me to discard any card and input any knock count under 10, and then reward me with 25 points, even though it reported that my unmatched cards and the computer's were both zero.  So I made a distinct check for this condition, and then display the warning that you have knocked with more than 10 knock points. It then does the resets and returns you to your hand.

Computer Got Gin!


Me Got Gin!

P.P.S.   I did catch the game miscounting melds again.  I had 3 aces, which the computer simply ignored in its count.  My new hypothesis is that the error may have something to do with the discarding of the 11th card.  In this case I might have had 4 aces, and been forced to discard 1.  Then something in the counting of this 4-meld and then the checking for the possible use of 1 of the cards in the meld being used instead in a run goes wrong.  Somehow the re-check for the 3-meld does not get done correctly.  I think what happens normally is that melds are found first, then runs, but if knock counts are different, then 4-melds are blanked, and then checks for runs are done again.  Then the meld checks are done again.  Or something like this.  But something is screwing up in very rare circumstances, and I think what makes them rare has to do with the 11th card (the one the player is forced to discard when they knock).  Which raises another issue, I'm unsure how 11-card Gin is handled for the player.  There is a message and a flag variable for the computer getting such, and some code to display the 11th card, but there doesn't seem to be anything like this for the player.  Sadly, I think I will have to leave this one with some extant errors.  I've created a special version GINRUM25_15.TXT that prints an error message and returns you to your hand if your knock count doesn't match with the computer's check of your count.  This will give me some ability to re-try after moving cards around.  But I don't think this will help much as the checking routines probably check the cards in their original slate orders of value and suit (by way of pointers) rather than the actual cards of the player's hand.

Friday, 10 February 2023

Programs for 2023 10Liner BASIC Contest

I have created three programs for the BASIC 10-Liner contest using Micro Color BASIC for the TRS-80 Micro Color Computer aka the MC-10.

SMASHUP (for PUR-120)

This program is a simple simulation of a smashup derby. You are the green car. The aim of the game is to hit the other cars.  Unless you are willing to risk driving head-on in the hope that your move/turn is timed as the last before the two colour blocks connect, the best strategy is to try to hit the other colours from the side as they pass in front of you.  this takes good timing.  If your timing is off and you drive in front of the oncoming colour block, then it hits you and you lose a life. 3 hits and you're out.  But if you hit the other car you get a point.  There is a time limit.  Your score is the number of hits achieved within the time limit.  A high score is displayed and you can then choose to play again.

Movement Keys: AWSD

VOLCANO (for Extreme-256)

This program involves flying your helicopter over a erupting volcano to retrieve a trapped person "Y" on the other side of the volcano.  Avoid the rocks flying out of the volcano. You can only take 3 hits, then you are dead.  Land on top of the victim to collect them.  When you do so, another person will appear on the other side of the island.  You must fly back and retrieve them. Continue to collect as many people as you can.  You must be careful when landing. The ground will not stop your descent. You must land gently and not simply hit the ground relying it to stop your descent.  How many people can you save before your craft reaches its limit?  Lives are printed in the bottom right.  Your score will be printed next to your helicopter after your last hit.

Movement Keys:  WASZ

Each game can be played here: https://archive.org/details/@james_gerrie

Here are the program listings:

SMASHUP

0 DIMT,L(8),D(8),V(8),C(8),R,I,J,K(255),H,Q,V,M:V(1)=-1:V(2)=-33:V(3)=-32:V(4)=-31:V(5)=1:V(6)=33:V(7)=32:V(8)=31:GOTO7
1 SOUND100,1:S=S+1:PRINT@480,"SC"S"HI"HI;:ON-(S<HI)GOTO5:HI=S:PRINT@480,"SC"S"HI"HI;:GOTO5
2 POKE49151,64:POKEL(T),134:L=L-1:PRINT@496,"LIVES"L;:POKEL(T),137:SOUND1,1:ON-(L>.)GOTO5:GOTO6
3 Q=2:FORV=1TO350:FORT=.TO7:IFPEEK(L(T)+V(D(T)))=.THEND(T)=RND(8):GOTO5
4 POKEL(T),H:L(T)=L(T)+V(D(T)):I=PEEK(L(T)):IFI>HTHENIFT=.ORI=RTHENON-(T=.)GOTO1:GOTO2
5 POKEL(T),C(T):NEXT:D(.)=K(PEEK(Q)ANDPEEK(J)):NEXT
6 FORT=1TO10:SOUND99,1:NEXT:PRINT@496,"TRY AGAIN?";:FORI=0TO1STEP0:Q=ASC(INKEY$+CHR$(0)):I=-(Q=89ORQ=78):NEXT
7 CLS:PRINT@452,"smash up! BY JIM GERRIE":V=32:H=128:I=16:J=17023:R=143:K(65)=1:K(68)=5:K(87)=3:K(83)=7:S=0:POKE16925,0
8 POKE16926,1:M=16384:FORT=MTOM+479:POKET,.:NEXT:FORT=1TO13:?@V*T+1,"€€€€€€€€€€€€€€€€€€€€€€€€€€€€€€";:NEXT
9 FORT=0TO7:D(T)=RND(8):C(T)=R+T*I:NEXT:FORT=0TO7:L(T)=M+261+3*T:POKEL(T),C(T):NEXT:L=4:ON-(Q<>78)GOTO3:END
10 REM                                                                                             1         1         1
11 REM   1         2         3         4         5         6         7         8         9         0         1         2
12 REM789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890
13 REM smash up! JIM GERRIE
14 REM 2023 10-LINER CONTEST
15 REM USE: W
16 REM    A S D
17 REM YOU ARE THE GREEN CAR.
18 REM SMASH INTO THE OTHER
19 REM CARS, BUT DON'T LET
20 REM THEM SMASH INTO YOU.
21 REM THERE IS A TIME LIMIT.

VOLCANO
0 CLEAR500:CLS:T=0:A=28:B=12:M=16384:FORT=1TO192:S$=S$+"":NEXT:D=1:V=32:G=17023:L=V*B+A:N=L+M:Z=RND(3):J=48+4:X=60:E=3:W=2
1 C$(1)="‚‡":C$(3)="‹":D$(1)="€‰":D$(3)="†€":Z(1)=16801:Z(2)=16651:Z(3)=16742:J=J-1:POKEM+511,J:SOUND1,2:IFJ<49THEN?S:END
2 ?@.,S$"Ž•ŸšŽÑߟßҍŽÑ¿¿Ÿ¿¿„ˆÓÓÓ¿¿";
3 ?"¿Ÿ¿¿¿Òˆ×¿¿¿¿¿¿Ÿ¿¿¿¿ÓŒˆ×¿¿¿ÿÿÿÿŸÿÿÿ¿¿Û„ŒˆÓÓ׿¿ÿÿÿÿŸŸŸŸŸÿÿ¿¿¿»Ò";
4 ?"ˆ×¿¿¿¿ÿÿÿÿŸŸŸŸŸŸŸÿÿÿÿ¿»„ÓÓÓÓ׿¿¿ÿÿÿÿÿŸŸŸŸŸŸŸŸŸÿÿÿÿ¿»£¢£¡¿¿¿¿¿¿¿ÿÿÿÿÿŸŸŸŸŸŸŸŸŸŸŸÿÿÿÿ¿¿ª¯";:POKEZ(Z),89
5 FORT=1TO5:X(T)=X(T)+A(T):Y(T)=Y(T)+B(T):IFX(T)<.THENX(T)=.
6 IFX(T)<EORX(T)>XORY(T)<2ORPOINT(X(T),Y(T))<>1THENX(T)=33+RND(8):Y(T)=8+RND(E):A(T)=W-RND(E)*(1+RND(.)):B(T)=W-RND(E)*1.5
7 RESET(X(T),Y(T)):NEXT:P=PEEK(N)+PEEK(N+1)+PEEK(N+V)+PEEK(N+33):C$(W)=C$(W+D):D$(W)=D$(W+D):?@L,C$(W);:?@L+V,D$(W);
8 K=PEEK(W)ANDPEEK(G):D=(K=65ANDA>.)-(K=83ANDA<30):A=A+D:B=B+(K=87ANDB>.)-(K=90ANDB<30):L=V*B+A:N=L+M:IFP=572THEN2
9 ON-(P<>518)GOTO1:Z=RND(E):S=S+1:?@.,"SCORE"S:SOUND99,1:FORT=1TO2000:NEXT:Z=.:Z(0)=16829:ON(1ANDS)GOTO2:Z=RND(E):GOTO2
10 REM                                                                                             1         1         1        
11 REM   1         2         3         4         5         6         7         8         9         0         1         2        
12 REM78901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678
13 REM VOLCANO RESCUE
14 REM BY J.GERRIE
15 REM 10LINER CONTEST 2023
Both programs use PEEK and POKES to place the player objects on the screen and to check for collisions between objects.  Key sensing is also achieved using PEEKs for speed and for continuous input sensing rather than the single key press sensing of INKEY$.

Volcano uses a large number of PRINT statements with the graphic of the volcano to refresh the screen.  It also uses the RESET command (line 7) of Micro Color BASIC to plot the exploding debris from the volcano.  The POINT command is used to determine if a RESET space is clear.

In both programs ON-(A=B)GOTO2 constructs are used instead of IFA=BTHEN2 to allow for multiple  branches to be packed onto a single line.  For example see the end of line 9 in Smashup.


ERII (for SHAU category)

This program simply presents a memorial image of her Majesty Queen Elizbeth the Second. It uses a special screen mode of the MC-10 to get 9 colours rather than the regular 8.  One of those colours works better as a skin tone. This program is for the SHAU category.

0 CLS1:DIMC1,C2,M$,M:M=16384:GOTO2
1 C1=(PEEK(17024)AND1)*256+PEEK(17025)-1+M:FORC2=1TOLEN(M$):C3=ASC(MID$(M$,C2)):POKEC1+C2,C3-(C3AND64):NEXT:?@C1-M+C2,:RETURN
2 PRINT"¯¯Ÿ¯£    £¯¯¯¯¯­Ÿ¯¯¯¯¯¯¯¯¯¯¯®¯¯«¯¯¬¬¬¬¬¬¬­¯¯§¯";
3 PRINT"ß߯¯¯¯¯¯¯¯¯¯¯ßßߏßßßß ßß    ß߀ßߏß߀   Ï€    Ï€€ß";
4 PRINT"ß€   ÿÿ    ÿÿ€Ï€          €€       ÿ  €";
5 PRINT"€  ÿ       €€  ÿ ¿¿¿¿  €€    ¿¿¿¿  €";
6 PRINT"€  €      €€Ï      €€¯Ï €€€€";
7 PRINT"€¯¯ÏÀ";:POKE49151,64:PRINT@32*7+22,;:M$="GOD BLESS":GOSUB1:PRINT@32*8+22,"elizabeth";
8 PRINT@32*9+22,;:M$="    II   ":GOSUB1:PRINT@32*10+22,;:M$="1926-2022":GOSUB1
9 M$=INKEY$:ON-(M$<>CHR$(13))GOTO9:CLS0:SOUND1,1:CLS:END
10 REM                                                                                             1         1         1        
11 REM   1         2         3         4         5         6         7         8         9         0         1         2        
12 REM78901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678
13 REM ELIZABETH REGINA II
14 REM BY J.GERRIE
15 REM 10LINER CONTEST 2023

Tuesday, 7 February 2023

The Day Type-in Mania Died: Joseph Roehrig's Scrabble (1981)

I recently worked on typing in a program from BYTE Magazine. It was for a Scrabble game "simulation"/AI opponent.  From the article accompanying the program you could tell that the author Joseph Roehrig was very proud of his creation.  Working completely in BASIC he had managed to create an AI opponent for a very complex game.  He had also created a version specifically for the TRS-80 that loaded as one file (i.e. could work from tape), only required a 32K machine (the oncoming standard replacing the 16K and 4K base versions first produced in 1977), and included a dictionary of over 700 words.  Compromises had to be made for the sake of speed and storage.  He limited the AI opponent to using only 2 and 3 letter words. There were other compromises made in terms of search rigor, and also you could choose a FAST game that lowered these standards even further.  But it could play a basic game that certainly might challenge a kid or maybe neophyte players (it's hard for to tell as I'm a neophyte).

The year of 1981 was still firmly a year of the "type-in mania" early informal 8-bit computing period.  Lots of home coders were making programs and sharing them with the wider world.  The period of selling cassette tapes from home sold in a plastic baggies with some simple reproduced documentation folded in was still a reality, exemplified by Scott Adam's Adventure International company.  At that point his "company" was essentially him and a loose group of collaborators.  However, by 1983, the company had become a proper software company and had a location and 40 employees (by 1986 it would be bankrupt).  It was somewhere in that period that computing pivoted from a do-it-yourself hobby, to an industry overseen by organized corporate actors serving clients.  In researching about Roehrig's program in subsequent issues of BYTE to check for possible bug reports, I cam across the following letter:

We were pleased to see an article discussing the feasibility of a computer opponent for Selchow & Righter's popular Scrabble word game (see "Computer Scrabble," December 1981 BYTE, page 320). Others who are intrigued by this concept will appreciate knowing that the state of the art in microcomputer Scrabble has made a great leap forward. It is far beyond the boundaries that Mr. Roehrig tells us will not be broken by anything less than a new, superior generation of microcomputers.

"Monty plays the Scrabble Brand Crossword Game" (a computer-opponent program available on disk for the Apple II and TRS-80 Models I and III from Ritam Corporation for $39.95) demonstrates both speed and ability, within the constraints of today's microcomputers. Monty spends an average of only 4 1/2 minutes per move at the highest skill level, and yet it uses an extensive word list (over 50,000), based in part on the Official Scrabble Players Dictionary. 

As for memory, the program requires no more than 48K bytes for Apple and 32K bytes for TRS-80 versions, much of which is ,devoted to machine-language graphics, music, and other user-interface requirements. The dictionary is accessed from disk and is stored in an average of only two bytes per word (with an average length of 6 or 7 letters) by use of advanced compression techniques. In addition, Monty is capable of challenging other players' words, based on linguistic analysis, without accessing the disk.

To give Mr. Roehrig's efforts due credit, the "game's complexities" do offer a challenge I It took us several major design breakthroughs, over four man-years of programming (for three different computers), and a lot of determination to develop "Monty plays Scrabble" without conceding to "certain constraints" on word length, search, and placement.

Although his conclusion that "improved computerized Scrabble will require a faster host computer with more memory capacity" has been disproved by example, we thank Mr. Roehrig for his article. It makes our endeavor seem quite worthwhile when we learn that we've achieved the impossible!

By the way, Mr. Roehrig neglected to properly acknowledge that Scrabble is a trademark of the Selchow & Righter Company, and to disclaim, as does Ritam, any sponsorship or endorsement by Selchow & Righter.

Robert Wall
Ritam Corporation
POB 921
Fairfield, IA 52556

* Scrabble is a registered trademark of Selchow & Righter Company. We apologize for not acknowledging this in a prior article ... MH 

(BYTE: Small Systems Journal, April 1982 Vol 7 No. 4., p. 20.)

This letter perfectly exemplifies the transition. The condescension is palpable.  You can sense the hand of Ritam Corp. patting poor Mr. Roehrig on the head for a his "good effort."  But the message is clear-- real programs require many "man-years" of programing using a real language like Assembly, and not a kiddie language like BASIC.   

The fact that I can't find anything else on the Net about Mr. Roehrig's program indicates his efforts were not rewarded with sales of his own via plastic baggies and reproduced sheets of paper documentation.  Around this time people started buying disk drives in North America and purchasing their programs from stores. There would still be a brief sunset for folks like me, getting our first machines in 1984, to "do it yourself" (my MC-10 cost $79.99 just twice the price of Ritam's $39.99 Scrabble program).  But if you had more than lawnmowing money, you wouldn't have to tarry with the type-in age very much longer. The software industry was on the march.

To make the program work on MC-10 I had to switch some of the larger two-dimensional numeric array's to string arrays.  Since the program only stored 0s or 1s in these two-dimensional matrixes, using strings and string operations like MID$ to look horizontally meant each "item" only will take a single byte to store.  This is much better than the 5 bytes needed for each floating-point numeric item in a numeric array. The TRS-80 Model I/III has a DEFINT command that allows such variables to be designated as INTs, saving memory, but the MC-10 only has floating point. But by making this change  to string arrays and searching and replacing multi-character variable names (some had more than 6 character long names!) to single character variable names allowed me to get the program to work in 20K.

Hopefully all this also speeds up the searches for the computer's move. Roehrig mentions that some of them can take over 15 minutes.  It's hard to tell how speed will translate from the TRS-Senior to the MC-10.  The Senior has a higher clock speed, but the MC-10 has a later more efficient version of Microsoft BASIC.  It also uses a Motorola MC6803 rather than the Senior's Z80.  My sense is that the MC-10 is normally faster.  A sign of this is that the standard delay for displaying a brief message on screen on the TRS-Senior is FORDL=1TO1000:NEXT, whereas for the MC-10 you need FORDL=1TO2500.  But who knows.  Perhaps the string array searches and string manipulations will trigger lots of garbage collection, which can slow things down.  I did most of my testing using the 8X normal speed mode of the VMC10 emulator.

If you have the patience and interest in seeing an old early BASIC AI at work, SCRABB2 can be played online using an MC-10 emulator (Just search for "SCRABB2"):

https://archive.org/details/@james_gerrie

The TRS source ("SCRABB2TRS80.TXT") and the source for my MC-10 version can be found here:


TRS-80 Model I/III Version

The original game used a system of numbers (1-225) listed on the TRS-80 Model I/III screen, that you typed in to enter words. I switched it to using arrow keys to move a cursor and added the color markers for word and letter bonuses to allow it to fit the smaller screen of the MC-10 and to ease use for those playing it online today.  I also added some checking to prevent errors if you try to enter characters other than letters.

Thursday, 12 January 2023

"Astronave Farmer" by Mario Pettenghi (1985)



"Astronave Farmer" or "Spaceship Farmer" in English is a text adventure game I've ported to Micro Color BASIC originally programmed for the Sharp MZ80B. I also translated it from Italian. This seems to have been a well loved game. There is an article about it (in Italian) in RetroMagazine by Antonino Porcino.  It is from 1985. I'm not sure if there are earlier Italian text adventures, but this is earlier one than the other Italian adventure I have, "Atzeco Adventure," which Gary from over on the CASA Solutions Archive converted and translated when I brought it to his attention.  It was from a magazine for the VZ-200, so I thought it would be an easier job converting it to MC-10, since they both share the Motorola MC6847 video chip.  But Gary is a real fan of that system and when he heard of a program that didn't seem to be part of the software archive for the system, he immediately set to getting it in working order and well translated.  He said he found lots of spelling mistakes.  I take this mean spelling mistakes in the original Italian-- probably with the distinctly Aztec terminology.  The game was probably made by a young Italian who wasn't overly concerned with the anthropological niceties of meso-America cultures. When I converted Garry's translation to MC-10, I also added a semigraphic-4 meso-American image for the title screen.

When I did my translation of Astronave I asked folks from the Facebook group to try out the rough early version.  Greg Dionne found some bugs by running it thought his BASIC compiler--  variables the weren't declared, which was probably a byproduct of Google translate. Otherwise Google translate seems pretty good at ignoring the BASIC command parts of the text, and just translating the messages for the most part.  This wasn't the case a few years ago.  You had to snip out the text messages, otherwise Google would garble all the BASIC commands. Walter Zeick pointed out some verbs that Google had failed to translate well (that is to say in keeping with two-word parser conventions).  I also had to go through and tweak the translation in a few places. Some phrases just seem completely colloquial, like "Light up your beard hippogriff!", which I just left as is. I had to make some guesses, but I hope it's pretty faithful to the original.  There is some clever humour in this adventure and some biting sarcasm in the remarks of your "puppet" or "alter ego" acting as your hands eyes and ears in the adventure.  Having now played the game to the end, I think I can understand why it is remembered with such fondness.

**SPOILER ALERT** (Below is a complete map of the adventure.)

There are only a few instant deaths that can't be anticipated, so you will have to play this one a few times if you are going to solve it on your own.  The puzzles are not overly hard.  Modern IF players will of course complain about the arbitrariness and limited vocabulary.  But as 8-bit adventures go this one is pretty fair and reasonably coherent. It's a nice little jaunt for an hour or two.  It has a hidden message in the code that you will only be able to (easily) see by playing the game through to the end yourself.  I have altered that secret code subroutine to present the message in English  If you need some help here's the map:

The source code can be found here:

https://jggames.github.io/jgames_TextAdventures.html

ASTRONAV can be played online (at least for a little while) here:

https://archive.org/details/@james_gerrie

Thursday, 29 December 2022

"Dr. Who Adventure" by James Smith (1982)


I ported this text adventure quite some time ago, but my techniques for squeezing games into the 20K space of the MC-10 have improved a lot since then.  I now don't have to use the two-file data-loading methods that I discuss in that post.  I've learned other ways to save memory space, especially regarding the management of string space.  By renumbering lines by ones and using all my other memory saving techniques I have now got Dr. Who Adventure to fit into 20K.

I worked from a scan of this game first published in Australian magazine Micro-80, Volume 3, Issue 8.  It was not a very good scan, so it was a real challenge to get the game working.  And once I did I discovered a very challenging early 8-bit BASIC text adventure. Originally requiring the loading of two separate programs to allow it to fit in the memory of a 16K TRS-80 Model 1, I have finally succeeded in modifying it to work as a single program for the TRS-80 MC-10.  I have also made some bug fixes (many of which I can't remember). However, the most significant is probably this one:

890 IFRND(0)>.998ANDWTANDPS<>99PRINTSY"hit over the back of the head and everything goes black...You awake to find that everything you were holding is gone.":FORI=2TO16:IFO(I,1)=90THENO(I,1)=6:NEXTI:GOTO840ELSENEXTI:GOTO840

This routine involves you getting mugged and all your items NO(I,16) from I=2 to I=16 being moved to location 6. Unfortunately, Smith did not also add the code needed to deduct the "weight" of each item from your inventory. They just get re-located. Since you have a max weight of 20 (each item has its own distinct weight), once you are mugged it's possible that if you recover the items you won't be able to carry all the ones you need back to Galafry to win the game. You have to be carrying the items when you reach the throne room and "talk" with the other Timelord there. Other items also "disappear" without subtracting their weight, but most of these were game ending anyway, since they involve you giving away or eating items, etc. that you need to win. In any case, I rationalized all the dropping/giving /eating routines to subtract the weight of the lost items.

Another significant bug involves the jumping out of FOR/NEXT loops. This has the practical impact for memory saving that all NEXTs have to include the variable reference, otherwise strange results can occur in the return to the top of unclosed FOR loops. The offending code occurs in the very fancy multiple word parsing subroutine:
860 J=0:K=-1:FORI=1TOLEN(A$):X$=MID$(A$,I,1):IFX$=" "THENIFKTHENNEXTIELSEK=-1:NEXTIELSEIFNOTKTHENSW(J)=SW(J)+X$:NEXTIELSEIFJ<5THENK=0:J=J+1:SW(J)=X$:NEXTI
870 FORI=1TOJ:SW(I)=LEFT$(SW(I)+"   ",3):NEXTI:K=0:FORI=1TOJ:A1=1:AN=78:FORI1=1TO78:AI=INT((A1+AN)/2):X$=MID$(SC,AI*3-2,3):IFSW(I)>X$A1=AI+1ELSEAN=AI-1
880 IFX$<>SW(I)ANDA1<=ANNEXTI1ELSEIFX$=SW(I)V(K)=ASC(MID$(S1,AI,1)):K=K+1:IFK>2THENGOSUB1840ELSENEXTI:GOSUB1840ELSENEXTI:GOSUB1840
I unraveled all the ELSEs used in this code and then made it so that the FOR/NEXT loops exit gracefully. This allowed me to remove all the variable references attached to NEXTs.  And it also prevents the variable stack from blowing its lid, which is deadly when you're trying to save every byte.

I have also changed some of the parameters of the game that were overly frustrating. You used to be limited to a random number of Tardis rides, which ranged from 20-40. Since the planets the Tardis goes to are arbitrary whenever you "reset" it, the game can be quite arbitrary in the level of challenge it presents.  I simply set it to 40. The random location selection is challenge enough.  I also changed the "search" function to a much lower level of possibility of not revealing items hidden in rooms.  As well, I made the "Renticulator" device report the exact number of required items that you have found.  It used to report a randomized "approximate" number. I made it report the exact number of required items  because it will be tough enough for people to figure out what it does, although a hint is given in the instructions published in the magazine and included in the CASA: Solutions archive description of the game.

Smith obviously loved using randomness to make his game more challenging, but it was to an extreme extent-- a typical flaw in early BASIC text adventure games.  Many authors of the era took grotesque pleasure in making their games diabolically tricky.  But it could often be too much.  In this game there are random mazes and random events a plenty, such as the "mugging" or walking off cliffs etc., so I don't feel bad about toning down the arbitrariness.  There is a really clever game here under all the gratuitous arbitrariness, with lots of interesting NPC interactions (for an 8-bit BASIC game) and a clever sense of humour, which I want other people to try and enjoy.

'PlayDalek' is pretty hot stuff

DRWHO can be played online here: https://archive.org/details/@james_gerrie


The VMC10 Emulator with DRWHO in the JIMG subdirectory of the Cassette directory can be downloaded here:

Friday, 9 December 2022

Usborne Books "Dungeon of Doom" (1984)


By Les Howarth, Cheryl Evans and Chris Oxlade, this game was the example program at the heart of Write your Own Fantasy Games by Usborne books. It's a fairly straightforward RPG, but unlike many others of the era it lets you design endless dungeons and characters. You can create dungeons of  up to 5 levels of difficulty (I have changed this to 9 for my version). I ported the game to Micro Color BASIC for TRS-80 MC-10 from BBC source found on Github typed in by Brian C as a first step to his conversion of the game to the modern Ruby programming language. I added a simple 3D first person view using a SG4 graphic system mocked up by Erico Patricio Monteiro. He's a real wizard of semi-graphic design. I have wanted for some time to find a program that could put his 3D graphics to good use. I think this one does that nicely.
 
The most difficult part of the conversion was re-creating the save/load features for the dungeon maps and character info. I had to consolidate various array variables so I could use a single CLOAD*array,"FILENAME" command for each file. For example, the map data also needed to save the starting position and level of the map.  It took a while but I was able to do it. The other thing I had to do was consolidate three separate programs (Map Maker, Character Generator, and Game Play Module) into one. This would allow me to post the program on the Net in a simple way for people to try it without all the fuss of loading and running multiple programs. It also allowed me to allow the player to create a character, map and then simply play the game from a single menu. However, you do get presented with the options to load files if you wish and are playing on an emulator that allows file saving and loading or on real hardware. The program requires you to create or load a character and create or load a map before it will allow you to enter the play game option of the menu.

I needed the 3D option because the original program used character redefinition to create unique character graphics to represent the items on its two-dimensional map of the dungeon. However, redefining screen characters is impossible on the MC-10. Instead, I simply upscaled the 8X8 redefined characters into SG4 8X8 pixel images that get presented in front of you on your first-person view when you come into contact with them.

DDOOM
3D View Top Left, 2D Map Top Centre

Meanwhile on a simple 2D map, you can see the walls, with monsters and traps flashing briefly as you move.  Activated monsters and traps briefly flash red and then monsters move to track you in the regular wall colour (cyan). Other items flash briefly but simply in the wall colour. They all appear when they are right next to you as you move, or when you use your torches to (R) "Reveal" in a grid up X3 in the 4 directions around you. Torches are limited, and pressing "R" also activates monsters, so torches have to be used carefully.

In the course of porting the program I uncovered a few bugs and quirks that I "fixed:"
  1. In the Dungeon Maker the variable used in the (O) "Offer" routine that allows you to haggle over prices was incorrect. The amount removed from your gold supply was the secret lowest possible price used by the computer to assess your offer rather than your offer.
  2. When you bumped into a monster while another monster was already active, the monster you bumped into wouldn't activate, but simply sit there. The program would remove a small amount from your health if you kept bumping into it, but that was it.  You couldn't even initiate conflict with it.  You could only fight the active monster.  I changed the program so that when you bump into monsters they activate and attack.  Any other active monster deactivates. The primary changes occur in line 210
  3. In line 2460 the DATA statement lists some short hand names for weapons and armour.  However, it was defined in a way that seemed different from the weapons and armour list of the character definition program. These short hands are used in the combat routine to indicate weapons that get broken. So weapons would be reported as broken that your character would not even be able to own.  So I realigned the two lists.  The problem was that the Game Play module was missing the broad sword, so everything was off by one.
  4. Line 1310 contained a real game breaking error discussed by WhatHoSnorkers. The metamorphosis spell randomly allows magical users to materialize any of the graphic characters used in the game in front of them. This included the "Idol" character, which ends the game in victory (conceivably to used only on the last of the levels of your dungeon).  So you could enter the dungeon launch the spell, get an idol, and finish the game. I created a short sub list of items that does not include the idol or exit from the level.  So you can materialize chests, safe spaces, vases, monsters, entry doors and traps.  Also, if a monster appears, it wouldn't necessarily activate and attack. The program made a check that if anything else appeared, it would deactivate any active monster on the chance that the materialized item actually was created in the space of that monster (i.e. transforming it into the new item and replacing it).  So I switched the program to just deactivate any active monster, and then check the transformed space to see whether a monster was there, which would then activate it if that was so. Otherwise, you are safe. These changes occur in the line 1340 area.
  5. There was an error in my BBC source, but not the original listing in the book, whereby experience was increased by .5 for every monster killed instead of .1, which made advancement too fast. The BBC micro also used the short hand "CAN" for labelling the number of your torches. I think this was for candle. I changed it to "TOR" for torches, since that is what is used in the Character Generator module. I have made further tweaks (downward) to the EXP earned so that experience keeps better pace with moving through dungeons with about 6-8 monsters slain  providing enough to get the player to the next level.  So when making dungeons, I would recommend putting at least 6, if not 8 on each level.
  6. Around line 1480 I added a routine to give the player random weapons when chests are found and "Got" if they are a fighter class (only weapons they are entitled to carry). This occurs only for a random number of chests opened. Similarly, you can on a chance of 1 in 6 find one of the 6 magic spells if you are a magic using class. Finally, there is a random chance (1 in 9) to find 2 torches. The original game provided no way to replenish broken or used up items. And weapons get broken quite frequently. The result is that your character would just get progressively weaker the deeper you went, which goes against the general principle of designing dungeons to get tougher as you go deeper.  It also gives a greater incentive for searching for chests than the single treasure point you get for each, which only contributes to your score. Now chests also play a practical function as well.
  7. In lines 620-650 the monster seeking routine was weird. A monster could get hung up on corners because the move routine was based on decimal incremented moves. These decimal moves were done to allow for different speeds for the monsters. The highest of the 3 would track the fastest, but the result was that often a monster could only always get at you once you moved to a parallel fully open track. I changed the system to move using only 1 space for each move, but that moves were activated only a random number of times based on the monster type. So the 3rd of the monsters is still the fastest, but there is also a slight randomness to all of their movements.
  8. Line 250 of the source has an IF that checks for I$>"" which was meant to trigger a small decrement of your health/strength for movements and actions.  When you input a command to move or do something then I$ will not be equal to null (<> "").  In other words, you've used energy.  Maybe the I$>"" command works on BBC BASIC to achieve this but I doubt it would work on any Microsoft incarnation of BASIC.  But I didn't spot any alternative code listed in the original book notifying the user to change this line for the other computers accommodated in the book (C64, VIC, Sinclair Spectrum).  So I think it might simply be a typo.  The result would be that you would not expend energy when doing stuff, just when fighting, which might have been bad enough and so the missing effect probably wasn't noticed by the programmers when testing.
  9. Line 730 defined the chances for monsters to break your weapons.  However, it does so in a way that seemed to make it more likely for the lower level monsters to break stuff more easily than the higher.  It simply looks for whether a random number based on the monster codes -2 (i.e. 7-9) equals 1. That means the lower 7 monster has a higher chance for a 1 to occur (1 in 7) than the higher monster, which only has a (1 in 9) chance.  I switched it so the higher level monster has the higher chance of breaking things.  It should be consistently meaner.  It certainly does more damage and moves quicker.  This should allow for the user to populate deeper levels with more of the higher level monsters to make those levels consistently tougher.
  10. Line 1630 is where to score is calculated. It involves using your current experience F(5) as a multiplier of treasure and also adds your strength and vitality, but it also adds your agility F(3) stat, which seemed strange, as this state never changes in the game beyond what you set it to when creating your character.  I think the reference was a mistake (between F(3) and F(5)) and that the authors really intended to add your current strength, vitality and experience level in their calculation, since experience level is a stat that changes depending on your performance.
  11. In line 50 of the Game Module, which checks for whether you have hit the "F" key to fight with a monster, there is also a check to see if the monster is actually active. That check was IF DX<255.  DX is the numeric value for the horizontal difference between your X location and the monster's X location. There is also a DY, which is the difference in vertical distance.  If no monster is attacking or when a monster is killed, DX is set to 255, so this variable plays the dual role of being the flag for when a monster is active and when you are not under attack (DX=255). Clearly, the authors were making sure that when you chose to fight, that a monster was also trying to fight you. But by not actually checking the specific distances of DX and DY, you could fight monsters through walls and even at great distances, once they were activated. This seemed very strange. So I changed the check to require that the distance between you and the monster in both the X and Y directions must be under 2. In other words, you can only fight monsters if they are next to you or right on top of you (i.e. zero distance--in your square-- which is also possible).
  12. I changed the Cleric class from being able to use only a knife (just like magicians) to being able to use the mace and the gauntlet. When I played original D&D, Clerics could use maces.  And why not a gauntlet too?  After all, they are acting as God's right hand against the denizens of the deep!
  13. The program excluded the shield from the calculation of your armour strength. I think the authors probably actually intended to exclude some of the shinier items like the gold helmet and the headpiece.  I suspect that they wanted to use such items as red herrings to lure the gullible into spending their money recklessly.  However, I think they might have at one point (as noted above with the differences in item lists between modules) shuffled items to perhaps help create a better illusion of legitimacy of the red herring items and inadvertently left out the humble shield from the calculations. I changed it so that the headpiece and gold crown (gold is relatively soft, and likely to be thin if used for something as large as a helmet) don't factor in, but that the humble shield does. The shield is one of the items that non-fighter class characters have access to, such as the cleric, so it is an important item for the purposes of balance.
  14. I made Spell 1 somewhat variable in its effectiveness. It allowed you a one shot kill of a monster, which seemed excessive. Now it gives you a 1 in "Your Aura" chance of failing to work. So the higher your Aura the better your chances the spell will be effective. And if your Aura decreases (which using higher level spells can cause), your general effectiveness as a magician decreases too.
  15. At line 1760 a check is made when you try to exit the dungeon to see if you have earned enough experience to be ready for the next level. It does this by checking your current experience level and seeing if it is at least +1 the experience you had when you started the level of dungeon you are on.  However, experience is calculated by percentage increments but only ever reported as an INT number, so you could enter a level with a certain EXP, say 5.2, which would be reported as 5, and then obtain EXP 6, but exactly 6 and be told you still "NEED EXPERIENCE." I found this confusing. So I changed it so that you only need to obtain INT(starting EXP)+1. That way, when you enter a level you know that you just need to get +1 whatever you see as EXP to be able to move on to the next level.
  16. Line 890 it reads "890 IF F(3)+F(6)< RND(M)+2 THEN M$=T$(4):HT=0".  Then just after that line you subtract the damage done by the player to an attacking monster: "900 MS=MS-H:GOSUB 430".  I think the reference to variable HT (thanks Greg Dionne for bringing this to my attention) is meant to zero out the damage if the player happens to miss (i.e. when message T$(4) is printed which is "missed").  But that doesn't happen because a different variable name HT is used instead of H.  In other words, the player never misses.  I suspect that when I fix this the difficulty of the game will go up.  I'll have to do some testing to see.
  17. I changed the reference to "Sire" in the "olden timey" lingo of the character creator routine to the plainer "Sir".  I think even in the middle ages ordinary folks weren't "sires"-- just the monarch.
Finally, I made some changes to make the game work better on the MC-10. The arrow movement keys are switched to the familiar (and marked) AWSZ diamond pattern. This necessitated changing the combat key from "A" for attack to "F" for fight.  I added a help screen in the Game Play module to let the user know what the keys do.  I also added a prompt to give you the choice of leaving when moving onto the exit. This way you can continue to scrounge for items. This was just a problem for the MC-10 version, since the player can't really identify objects, including the exit, until she/he tries to move into a space.

The program is called DDOOM. The full emulator can be downloaded here. DDOOM can be found in the JimG subdirectory of the Cassette directory.


And if you want some premade dungeons, you can find a .zip file with 5 of them, and a couple of pre made characters throw in a this link: https://drive.google.com/file/d/1xs7ocowKySWy70H5Yt_wfkVfOUrL8pDy/view?usp=sharing

If you are looking for a classic RPG experience from the early 1980s 8-bit era, you should give this program a try.  It's a real nice one.


Acknowledgements

Thanks to WhathoSnorkers for his excellent set of videos on the game.  He was the one who made me aware of the game-breaking error that I discuss in #4 above.  His fixes and updates also inspired me to put in the fixes to the Chest routine, which allow you to find treasures, magic and torches.

Thanks again to Erico for his wonderful wall graphics.

Addendum

Here's me playing and winning a C64 version:


It's from a site with lots of useful information on playing the game:
Usborne Books has made its old programming how-to books available free to download: https://usborne.com/ca_en/books/computer-and-coding-books

Friday, 4 November 2022

Classic Rally-X Arcade Game Written in BASIC

I've been working on my attempt at a "re-programming" of Rally-X in BASIC for a while. But I've never quite been able to feel satisfied with it until today.  Darren Atkinson's  machine language subroutine for refreshing the screen from a large sequence of array strings was a big help.  Before it, I had been using a bunch of PRINT MID$ of the array strings, and it was just too slow to refresh the screen background in an adequate fashion.

Now it is just a barely noticeable page flipping background effect.  But the speed of game play was actually up to a point of being a little too fast for fair navigation with such a small window.  The enemy car can swoop in and really get you.  Part of the reason for that is that the key sensing routine that I was using consisted of the single check for all 4 directions:

ONK(PEEK(2)ANDPEEK(17023))GOSUB1,2,3,4

But as I have learned from Greg Dionne, these key sensing PEEKs have their limitations, and will occasional "latch" when certain double key combinations are input. When that happens, the only way to "unlatch" and be able to input a desired direction is to release all keys, hit another key other than the one you were trying to input, and then select the arrow key you wanted.  It's a fairly intermittent and rare effect, so in many game application its not a problem. The advantage of needing to do only 2 PEEKs to get a direction outweigh the occasional quirky response.  But in this game good reliable responses are really needed.  The enemy can really get close, and every keystroke counts. So I switched to the keyboard rollover peeks suggested by Greg Dionne. That table provides absolutely reliable return values for the four direction keys.  But it uses individual IF statements to determine which keys are pressed:

5 CLS
10 IF PEEK(2) AND 4 AND NOT PEEK(16952) THEN PRINT@1,"W";
20 IF PEEK(2) AND 1 AND NOT PEEK(16946) THEN PRINT@2,"A";
30 IF PEEK(2) AND 4 AND NOT PEEK(16948) THEN PRINT@3,"S";
40 IF PEEK(2) AND 1 AND NOT PEEK(16949) THEN PRINT@4,"D";
50 GOTO 5

In the past I would have recoiled over using multiple IFs and preferred instead to use a single ONK(PEEK(2)ANDPEEK(17023)GOSUB1,2,34 method.  The use of a "lookup" K() array in combination with the simple PEEK method seemed highly efficient to me. But I have learned that in Micro Color BASIC the IF runs very quickly in terms of its implementation in the interpretation of each command, whereas ON/GOSUB does not run so quickly.  Also, I was able to eliminate the A() array, which removed a variable from the variable table, which is good for memory and also a source for a slight increase in speed.  The fewer the variables that must be searched for each variable reference,  the faster things go.  And, as I noted, the game was running just a little too quickly anyway, so I could afford a slight slowdown.

I've also searched for all the "speedups" that I can in terms of the code.  I've eliminated all
IF A<>0 THEN references and replaced them with
IFATHEN references
I've replaced more ON/GOSUBs used instead of simple IFs, in speed critical areas of the code (the main loop mostly).  I was able to include some actions as a direct part of the key movement IFs, rather making jumps to subroutines.

I think all these final edits have contributed to making the game truly a playable arcade style game in BASIC.  I was inspired to make these efforts by the Racing Car game jam on Itch.io, where the challenge is simply to "have fun with our beloved retro computers," the retro programmers Facebook group https://www.facebook.com/groups/RetroProgrammersInside.

Here is a demo of game:


Unfortunately I can't do it justice simply because I'm crap when it comes to playing video games. But if you would like to give it a try it can be played in the online:


Just select RALLYX from the Cassette menu and then type RUN and hit Enter in the green main screen.  The controls are WASD, just in case you can't read the characters in the chunky Semigraphics-6 font of the MC-10. The pixel resolution of that screen is 64 X 48, and there is not provision for regular text being displayed.  So unfortunately, the letters and numbers have to be rendered in the blocky pixels.  It takes a little getting used to, but with a little time you will be recognizing the letters and numbers.  It's just one of the limitations we users of this quirky 8-bit micro are used to.

Inspired by the smooth motion of the key input of RALLYX, I also decided to modify and update my version of Steven Wozniak's "Little Brickout."  Now it also uses Greg Dionne's PEEK method, and is much more responsive. Since I was also using multiple IFs, I was able to make some improvements to the animation of the paddle that made it flicker less. I could just remove the end of the paddle that needed to be removed to move it in a specific direction instead of both ends. Here's a vid of the new version: