REALbasic University Resources:

RBU: Glossary Defines common REALbasic programming terms
  Archives Previously published columns
Translations: Dutch Courtesy of Floris van Sandwijk
  Japanese Courtesy of Kazuo Ishizuka
  Chinese Courtesy of Dong Li
  RBU Translation Guide Information on Translating RBU into other languages
Books: Matt's Book (2nd Edition!) Ideal for experienced programmers
  Erick's Book Best for beginning programmers
Websites: Mother Ship The publisher of REALbasic
  RB Webring Links to hundreds of REALbasic websites
  RESExcellence Another REALbasic programming column
  REALbasic Developer Magazine The premiere source for REALbasic instruction.

REALbasic University is Sponsored by

Make your Mac do what YOU want it to. Create games, utilities, cool Mac OS X tricks. Download REALbasic now and create your own software.


Print This Article

REALbasic University: Column 047

RBU Pyramid XX: Bug Fixing

Sorry about the erratic updates lately, folks. Applelinks' move to new servers last week had some DNS issues that made the site inaccessible to many, so I decided to postpone the column. I've also been very busy with something you'll all be excited about -- but I'll have announcement about that in an upcoming column.

For now, it's back to RBU Pyramid!

Kill Those Nasty Bugs

No program is completely polished until all the known bugs are caught. It's difficult to eradicate every bug, but your goal as a programmer should be to fix every bug you know about. For example, it's quite possible there's a rare bug that is only noticeable in certain situations -- you might have never encountered the situation and therefore never seen the bug.

That happens to be the case in RBU Pyramid. Fortunately, one of our sharp-eyed readers, George Bate, happened to discover a bad end-of-game bug and alerted me to it. Here's his letter and explanation.

Dear Marc,

Quite by chance, as a result of playing the game when I should have been of studying the code, I have happened on a small error in Pyramid. I noticed that the program goes wrong when a pyramid is cleared after the last card has been dealt from the deck. Actually you only see it if the interim score is also a "top ten". After the last match, the end of game procedure is entered and the top score dialogue is displayed. When this is dismissed, the applause is heard and the game continues normally.

The problem starts in updateCards. This calls findAvailableMoves if there are no cards left in the deck and it is the second time through. In findAvailableMoves the same condition leads to gGameOver being set true. That is only overruled if there is a highlighted card; which in this case there isn't. updateCards then calls endOfGame which calls showScores if the current score is in the top ten. Finally, updateCards detects the cleared pyramid, adjusts the score accordingly and continues the game.

The error can presumably be corrected by modifying the condition:

  
not gFirstTimeThrough and ubound(gTheDeck) = 0

A further check is needed to see if the pyramid has been cleared before assuming the game is over.

George Bate

Good catch, George. Let's see about fixing that, shall we?

The difficult part of this fix isn't the fix itself -- it's testing to make sure that your fix actually worked! It's tricky to get a pyramid that you'll win with an empty Deck.

George gave me his solution, which was to check to see if the top card is visible. That's an excellent method. The logic goes like this: if there are no cards left in the Deck and there are no more valid moves, there are only two possibilities: either the game is over or the pyramid has just been cleared. If there's still a card at the top of the pyramid, however, we know that the pyramid wasn't cleared and that the game is actually over.

So go to the updateCards routine and find the block of code that looks like this and replace with this new code:

  
// See if there are any moves left
if not gFirstTimeThrough and uBound(gTheDeck) = 0 then
findAvailableMoves
if gGameOver and cardCanvas(1).visible then
endOfGame
end if
end if

The only change is the middle if-then statement, where we check to see if gGameOver is true. This time we also check to see if the first cardCanvas is visible: if it is, we know the game is truly over, so we pass control on to the endOfGame routine.

I finally did manage to hit this condition during a game and it passed, so I think the bug's fixed.

Initializing the Scores

George also reminded me that we needed to initialize the scores to zero in our newGame method: I had this in my original version of the program but forgot to transfer it to over to the tutorial version.

In Detail

If you're wondering how I've been writing this tutorial, I had the complete game finished, and I basically rewrote it, piece by piece, like you're doing each week, by copying code from the original version and pasting it into the tutorial version.

Unfortunately, REALbasic does not let you open two projects at the same time, making this method of working difficult. My solution?

I run two copies of REALbasic simultaneously -- right now it's version 4 and 3.5.2 -- with a copy of the game project in each version. Then I can quickly switch between the finished game and the in-progress game and add the code for that week's lesson. It works great!

This is a useful tip for everyone, if you've got pieces of code you want from one project into another one -- you don't have to keep opening and closing projects, just open each in their own copy of RB. (I suspect they must be different versions, but I haven't really tested that. I just did it this way because I happened to have both installed and it was easier that way.)

Open globalsModule and find the newGame routine. Insert this at the beginning:

  
gPyramids = 0
gScore = 0
gFirstTimeThrough = true
gGameStarted = false
gGameOver = false

This just makes sure to reset the score to zero at the start of a game -- otherwise a person's score would just increase from the last game!

Adding a Deck Label

During the process of fixing the end-of-game bug, I noticed a small detail that got overlooked in our polishing lessons. I forgot to add a Deck label feature! (This was something I had in my original version of the program.) A Deck label is just a small StaticText label beneath the Deck that tells the user how many cards are left in the Deck.

So drag a StaticText onto gameWindow and give it the following settings:

Now we just tell to display the number of cards left. Go to the drawCard routine. At the very bottom of the routine you should fine three end if statements, like this:

  
end if
end if
end if

Before the last one we want to insert a bit of code. It should look like this:

  
end if
end if
if index = 29 then
DeckLabel.text = str(uBound(gTheDeck))
end if
end if

This just simply checks to see if we're drawing the Deck card (number 29), and if we are, to write the number of cards left in the Deck -- the uBound of gTheDeck -- into DeckLabel. Simple!

Mac OS X Symbol Font

One of the most obvious bugs in RBU Pyramid occurs in the Mac OS X version. When I originally wrote RBU Pyramid last spring and tested in Mac OS X 10.0, it worked fine. But in the fall Apple released 10.1 and something changed: the Symbol font no longer draws properly!

Here's what RBU Pyramid looks like under Mac OS X (10.1 or better):

Yikes! That is ugly!

I don't know if the change Apple made is a bug or intentional, but it sure is strange. There's nothing wrong with our code -- it's just that Apple made it so the card symbols won't display.

To test this, I copied the card symbols to BBEdit with the font set to Monaco. Here's what that looks like:

When I change the font to Symbol, we have our card shapes!

But here's the strange thing: if I copy that same text and paste it into TextEdit, it looks like this:

Isn't that bizarre? It's the same letter, the same font, but what is displayed is different in the two programs. My theory is that it has something to do with Carbon versus Cocoa, as BBEdit 6.5 is a Carbon app and TextEdit is a Cocoa app. But then REALbasic programs are Carbon -- so shouldn't they display correctly?

(If you want to learn more about the difference between Carbon and Cocoa, Geoff Perlman, CEO of REAL Software, has written an excellent white paper on the subject.)

But the bottom line is that regardless of why this happening, we must come up with a workaround: our use of the Symbol font in OS X isn't going to work.

So how do we fix this? It's not too difficult. Instead of using a font, we'll just draw pictures. Pictures are more difficult to work with than fonts, as you'll see in a moment.

The first thing we need are a bunch of card graphics -- I've created an archive of them here (they're also available as part of this week's project archive). We need four graphic files, one for each suit: a spade, diamond, club, and heart.

However, we soon will run into another problem (part of the reason I went with fonts in the first place). When a card is selected, it is highlighted by reversing the colors. In the case of a red card, the red text shows up on black selected card, so we don't need to do anything special there. But for the black text cards, we change the text to gray so it's visible. But how do we do that with a graphic?

The answer is to create more graphics: in this case, a special version of each of the black cards, a club and a spade reversed. So in total we'll have six graphic files. After putting them inside our "Linked Graphics" folder, we drag them all into our project file. You'll see they each import with the name of heart, spade, club, etc.

Now find your drawCard routine in gameWindow. This is the part we must modify. Midway through the routine you should find code that looks like this:

  
g.textFont = "Symbol"
g.textSize = 30
g.bold = false
g.drawString theSymbol, 28, g.textHeight - 4

Replace that text with this:

  
g.textSize = 30
g.bold = false
g.textFont = "Symbol"
#if targetCarbon then
select case theSymbol
case chr(169) // Hearts
g.drawPicture heart, 23, 5
case chr(170) // Spades
if reverse then
g.drawPicture spadereverse, 23, 5
else
g.drawPicture spade, 23, 5
end if
case chr(168) // Diamonds
g.drawPicture diamond, 23, 5
case chr(167) // Clubs
if reverse then
g.drawPicture clubreverse, 23, 5
else
g.drawPicture club, 23, 5
end if
end select
#else
g.drawString theSymbol, 28, g.textHeight - 4
#endif

Wow, that looks a lot more complicated, right? Actually, it's not. We keep our standard Symbol font drawing method for most versions of RBU Pyramid, so the majority of our routine stays the same. All we're really doing here is intercepting the drawString command -- before we use it, we check to see what platform we're compiling for. If our target is is Carbon (Mac OS X), then we substitute pictures instead of the font.

In Detail

This is known as conditional compiling. It's a very common and powerful technique. Obviously there are important differences between Mac OS and Windows, between Mac OS and Mac OS X, and even PPC versus 68K (although starting with REALbasic 4, RB doesn't support compiling for 68K Macs any more). Rather than have to write completely separate projects for each platform, REALbasic lets you -- on the fly -- substitute the correct code for the platform.

You could do something like this:

  
#if targetCarbon then
msgBox "This is a Carbon app."
#else
msgBox "This is not a Carbon app."
#endif

If you compiled Mac OS and Mac OS X versions of your program, they each would display a different message when you ran them. Note: this won't work in the IDE, only in the compiled application, because the IDE will always reflect the state of the environment where it is running. i.e. if you're running the IDE under Mac OS X, targetCarbon will always be true in the IDE.

There are a bunch of different environmental variables you can use to test what kind of application is being created: Target68K, TargetWin32, etc. Read more about them in RB's online documentation.

So how does our little picture-substitution thing work? Well, we already have a variable, theSymbol, which contains the kind of card we are drawing. So we can just check that to see what it is: for instance, if it's a spade, we draw the spade graphic instead of the font. Note that for the two black cards, we also check to see if reverse is true, and if it is, we draw the reversed version of the graphic.

So does it work? Well, run the program and see.

Oh no! Disaster! What in the world happened? Our Mac OS X version now looks like this:

Sure, the graphics are being substituted properly, but they aren't looking right at all. For instance, that heart is a white box not a heart shape. What can we do?

The answer is simple. We need a portion of our graphics to be transparent. That will get rid of the white box effect. Fortunately, REALbasic makes this easy to do. Within your project window, select one of the graphics. On the Properties palette, change the transparency setting from "none" to "white." Do that for the four suit graphics (not the reverse versions, or selected black cards won't display correctly) and save the project. When you run it now, it should look like this:

Yeah! That's much better.

RB4 Instructions Bug

There still are a few more things we need to fix. For instance, REALbasic changed the way editFields work: it used to be that if you changed the selection of text, RB would automatically scroll to that line to make the text visible. Starting with REALbasic 4, however, that is no longer the case. (Whether this is a bug or a feature, I don't know.)

The problem is that this change kills our Instructions Window: when the user uses the popup menu to select a topic, the editField does not scroll to display that topic.

In Detail

Keep this in mind with future upgrades to REALbasic. Any time you compile your program with a new version of RB, you should thoroughly check your program for new bugs. Test every feature and make sure it still works. Sometimes RB has been changed to work differently, and other times you depended on REALbasic acting certain way and that behavior has changed in the new version.

This is easily fixed, however. EditFields now include cool new scrolling commands (commands many in the RB community have been asking for for years). All we need to do is to manually scroll the editField to the appropriate topic. It just takes one line of code!

Open InstructionsWindow and go to the Change event of popupMenu1. Before the final end if, put in this line:

  
editField1.scrollPosition = editField1.lineNumAtCharPos(i - 1)

What does this do? First, we know from our earlier code that i - 1 is the starting character of the selected topic. (Remember, every character in an editField is numbered from first to last.) But to scroll an editField we need to know the line number the character is on, not the character number. The line number is tricky to calculate because it changes depending on the font size and width of the editField (since paragraphs will wrap to different heights depending on their width).

The answer is the new lineNumAtCharPos method: we pass this a character number and it returns the line number that character is on. Then we send that line number to the new scrollPosition method, which scrolls the editField so that line is the top line. Simple!

By the way: while this fix isn't needed in REALbasic 3.5.2, it works fine in that version (it doesn't hurt anything).

As long as we're here within InstructionsWindow, lets update our help text. I've created a new version of the rules which can grab here. Copy and paste that text into editField1.

Final Polishes

We're almost done. We need to change the version number of our program. Open globalsModule, go to the constants and find kVersion. Double-click on it and change the value to 1.0. Now when you run the program, the correct version will display in the About Box.

We'll also need to compile the various versions of the program. If you're using REALbasic 4 Professional, you can compile a version for Windows as well as Mac OS and Mac OS X. Here are the settings you'll want to use in the "Build Settings..." dialog (found on the File menu).

Here's what the program looks like running under Windows 95 (within Virtual PC, of course -- I don't believe in owning a real PC ;-).

There are a number of extra polishes we could, and probably even should do, to make our program fit in better with the various operating systems. For instance, my brother, who's a programmer for the Dark Side, once told me RBU Pyramid for Windows didn't quite follow Windows' "Interface Guidelines." My initial reaction was to laugh out loud: there are interface standards under Windows?

But he is correct: if I was serious about marketing this program to Windows users, I should make sure it conforms with the standards on their platform, to the best of my ability. That means fonts, shortcut keys, etc. would all need to be adjusted to be more appropriate for Windows. (There are a host of these kinds of issues, and I'm just sophisticated enough to know the problems exist, but I haven't had enough cross-platform experience to know how to fix them.)

I also didn't test this with different versions of Windows: I only have Windows 95 and I don't relished giving Bill Gates more of my money just to test my solitaire card game on his various operating systems. (Isn't that what Windows is for -- playing solitaire?) I figure if it works under 95, it should work on the others, but I can't confirm that.

Some of the same issues exist with the Mac OS X version. For instance, Mac OS X programs have a Preferences menu option on the Application menu by default. Within a REALbasic program, the menu is there but grayed out. To be a true Mac OS X application, we should fix this, but it's a kludgy fix, requiring either a plug-in or an OS call, and I won't bother with it for RBU Pyramid.

Overall, I'm pretty pleased with the program. It's a fun game, and I learned a lot in the creating of it (and teaching about it). I hope you enjoyed it as well!

Final RBU Pyramid Application and Source Files

If you would like the complete REALbasic project file for the final RBU Pyramid 1.0 release (including all sounds, graphics, and other resources), you may download it here:

RBU Pyramid REALbasic Project File (335K)

If you're just interested in playing RBU Pyramid, here are the compiled versions of the final 1.0 release:

RBU Pyramid 68K (858K)

RBU Pyramid Classic (1,000K)

RBU Pyramid Mac OS X (1,200K)

RBU Pyramid for Windows (638K)

(Since there was interest, I decided to compile a 68K version of RBU Pyramid with REALbasic 3.5.2 which supports 68K. No warranties, expressed or implied.)

Finally, if you want to let your non-programming friends try RBU Pyramid (it is a fun game), give them this permanent URL:

http://www.applelinks.com/rbu/047/index.shtml#download

That will take them right to this section for easy downloading!

REALbasic University Quick Tip

You'll notice in the above I named the files generically. For instance, for the Classic version the filename is "rbupyramidclassic.hqx" -- there's no indication of a version number.

When I first started releasing software, I would name my files with the version number inside it, like "rbupyramidclassic1.0.hqx". The problem I discovered is that many software download sites will link directly to your download file, and if you keep changing the name, those links break.

You are therefore better off keeping the filenames generic. For instance, on my STS website, "z-write.bin" is always the name of the current release of Z-Write. Older versions, which I make available, are named with the version number. You can always change the hypertext label to refer to the appropriate version number, but by keeping the actual filename generic, others can easily link to it even when you update the program.

For REALbasic University, for instance, I'll update RBU Pyramid as needed (perhaps a 1.0.1 update), but I won't need to change the filenames at all: new downloaders will automatically receive the latest version.

Next Week

We'll have a look back at RBU Pyramid and analyze what we accomplished.

Letters

No letters this week -- I'm just too swamped and this week's column is long enough. More next week!


About the Column
REALbasic University is a weekly instructional column on programming with REALbasic and is brought to you by REALbasic Developer, the magazine for REALbasic programmers.

Each week we answer select reader questions, and we're always open to ideas for future columns. Send your questions to . (Keep your questions simple and specific. General queries like "How do I write my own web browser?" will be neglected.) Your question won't be answered immediately, but will be answered in a future column. (If you don't want your correspondence published, just be sure to indicate that when you write. Otherwise it's fair game.)

About the Author
is an author, philosopher, graphic designer, photographer, film director, soccer fanatic, and programmer (among other things). He writes for MacOpinion, runs his own software company, Stone Table Software, which sells the revolutionary Z-Write word processor, and is Publisher and Editor of REALbasic Developer. He lives in Northern California with his cats, Mischief and Mayhem, and is rapidly running out of free time.

See the REALbasic University Archives


REALbasic University contents ©2001-2004 by Marc Zeedar and REALbasic Developer. All Rights Reserved.

.

.