I already posted about Jim Menick's BASIC text adventure game "Space Derelict." That program was an example program from his book BASIC Adventure and Strategy Game Design (1984). Jason Scott mentions that there was a version of the book for the Apple, but I worked from a scan of an edition for the TRS-80 Model IV computer. I have now cleaned up the scan of his example listing for the "strategy game" component of the book, which is a variation of Draw Poker. The "house rules" for that game are:
- Each player starts with $100
- $1 Ante
- Minimum of (pair of) Jacks for opening
- 3 raise limit
- $5 bet limit
- Marvin
- Gerry
- Walter
- Betsy
- Remove all spaces between commands and variables.
- Eliminate single command lines and pack multiple commands on lines as much as possible.
- Change as many multi-character variables as possible to single letter variables.
- Scan the code for redundant/duplicated code and then shift that to a subroutine.
- Shrink and streamline messaging, which had to be done anyway to shrink a 80 X 24 game screen down to a 32 X 16 screen of the TRS-80 MC-10.
- Omit <>0 references in IF statements. The variable by itself, if a non zero, can simply trigger the if.
I got a Full House! |
- There was an error in the routine that examines 2 pair hands to find the highest card. Instead of looking at the cards in the hand and comparing which was higher, Menick compared the pointer array variable values to the positions of the first cards for each pair. Those values needed to be put into another array holding the values of the cards themselves to obtain their actual values as cards. Pointers within, pointers within pointers! He only went 2 levels deep when he needed 3-- easy to do in such a massive program with massive lines of these multiply embedded pointers. This happens in my program around line 2901 which is a little subroutine I carved out so that it could also be used by the "Untranslated Player hand" routine latter in the program. I also unpacked some of the "pointer within pointer" analysis to a subroutine to make the lines shorter, since those kinds of lookups happen a lot and have the same general format.
- The card replacement routine was really messed up. If you didn't pick correctly and then simply hit N at the "Replace" prompts, it would realize that you hadn't selected enough cards to replace and take you back to start at the top of your card list again. But it already had replaced the cards you selected and wouldn't turn them back to what they originally were. It also would not re-wind the cards drawn counter! This meant that if you repeated hitting "N" and kept "restarting" your selection you could effectively eat through the entire remainder of the deck and blow the lid of the array holding those cards! That would cause a catastrophic "out of bounds" error. So I revamped and stored your (up to) three selections, and then only change them after you have selected the cards you truly want. So if you make errors, just hit N until it restarts the process as many times as you need.
- There are places where INPUT is used, such as for "Bet?" and ""How many Cards? selections. But a numeric variable is used for input, which means if you happen to enter a letter, you get a new line and the message "Redo" and then a newline with a new prompt, which would cause the screen to scroll. Needless to say this really messed up the screen real-estate. So I switched the input to use a string variable and then assign it using VAL to the original numeric variable. So if you enter anything that is not a number it just assumes it's a zero.
- Minor issue with the game ending. If you had exactly $1 left at the end it would let you ante up and enter the game with $0. Then it would keep telling you you were trying to make an illegal bet in an endless loop. I added logic to terminate the game if you had 0 after your ante. I also made it so that if you get the pot up to over 480 it reports your "winnings" and thanks you for playing and ends the game. The other players are made to drop out if they have less than $5 each so if you have $500-16 there could be no players left for you to play with. I don't know how the game would handle only you entering into the actual game play without other players. Menick doesn't seem to have considered this eventuality happening. I guess he just thought his A.I. players were that good! Anyway, the game now has a (hopefully) functioning win routine.
- This one is not really a bug, but it allowed me to push the memory needs down enough that I had enough space to put back in some of the more elaborate game messages. Menick used three string arrays for presenting the cards: One for holding the card's value, one for the suit and one for these two strings combined for the whole card. E.g. 7 , H and 7H. This was done for each of the cards. These arrays were for 40 items each: 5 cards for each of the five players, and then up to 3 cards for each player's replacement cards. Each time these were changed Menick had to update each of three string arrays: Value, Suit and Combined Value/Suit. The combined Value/Suit would used when a card needed to be printed on screen. But since they were always changed together it seemed redundant to manage a separate "combined" string. I just stripped it out and simply printed the Suit and Value strings together in the few places needed. The removal of an entire 40 item string array really freed up space, and broke the back of my memory woes. Menick didn't need to worry about such things since he was programming on a TRS-80 Model 4, which I believe had 128K (and at least 48K for BASIC), and not a measly 20K like the MC-10. Such luxury.
- At the beginning of the subroutine that checks for a straight flush in lines 2630-2647 a variable IN(X) is set to 0. Then a check is made after a search is made in the initial part of the routine for a straight to see if the value of IN(X)=1. If it is then the subroutine is exited and the program moves on. If not, then the values for aces in the hands (14) are shifted to 1s and the whole routine is run again. I can only surmise that this is to check for a straight in the order A1234. If straight is found for either the first search or second a branch is made to elsewhere. But if the second search occurs, no change is made to the value of IN(X) to indicate that the "second check" has occurred but no straight of the A1234 type found. So the routine just keeps being run in an endless loop. So I added IN(X)=1 after the first check for a straight is made, so that the branch out on S(X)=1 occurs after the second check.
- In testing the winning and losing subroutines of the game it became apparent that Menick must not have ever got to that point in his own play testing (or perhaps only on the Apple version). I created the win and lose conditions by breaking out of the program during the Ante screen, and then changing the banks of each player stored in R(X) and then entering CONT. I was able to figure our that there was something wrong with the dropping out part of the program for "busted" players. A variable P(X) had to be set to 1 after the "busted" message gets printed, but wasn't. That variable then triggers the awareness that the player is not able to Ante and is also used to prevent printing the player onscreen and skip them for dealing duties, etc.
- There was a problem with the routine that reexamines your hand after you make card replacements. Menick didn't re-initialize the variables storing your hand values before re-starting the hand analysis subroutine (starting at line 130). So if you had a hand with 2 pairs, and then selected new cards that left you with no recognized hand type (pair, 2 pairs, 3 of kind etc.) that routine wouldn't find anything to report about those recognized hands, so it wouldn't change the reporting variable, effectively leaving your hand with the type of hand identified from before making your replacements. So if you started with a pair, and then replaced one of those cards in a way that left you with zilch, it would report that you still had a pair. It's possible that this might be a result of something that I changed, but I tried very early version of my edits of the game (I keep a succession) , and the problem was there, so I think it's original. Menick simply didn't test like I did by entering crazy entries again and again with the emulator cranked to the max speed. It would be very rare (except for Betsy who seems to be a little rash) for players to smash up their own existing hands and then stay around (bluff) to the brutal end just to lose and see their hand described. They'd normally fold. So it's likely Menick wouldn't have noticed this bug. But by playing many games on high speed I noticed this first in my own hands (I was just replacing cards at random and sticking around to the end to make sure I could see all the hands to check them). Then I noticed it happen in one of Betsy's hands too.
- I changed the annotation of how hands are reported. It now takes the form of "Full House 6's", "Four T's" or "Three A's" etc. The subroutine at 32000 finds the value of the highest card for 4-of-a-kind and full house hands. At line 3410 and 3440 I added a S$ before the GOSUB32000-- The S$ stores the apostrophe and S characters printed after the card number of the highest card in the hand. This annotation was missing from the determination of four-of-a-kind hands (except for one instance). The 32000 subroutine (now relocated to 85) would not work unless this annotation was present like it was for Full House hands. I adjusted the 32000 routine to deal with the apostrophe and streamlined its operation.