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 the MC-10 Javascript emulator.  Just select the program name from the Cassette menu and then type RUN in the main (green) screen of the emulator:


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.

No comments:

Post a Comment