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.
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.
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.
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.
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.
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"):
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.