REVERSI
ON YOUR DESKTOP
A GEM TUTORIAL AND A GREAT GAME
by Christophe Bonnet
This desk accessory version of Reversi (or Othello) is the perfect executive toy for those lengthy spreadsheet sessions. French programmer Christophe Bonnet uses this classic computer game to demonstrate how to create a desk accessory.
Although the 68000 processor in the Atari ST is fully capable of running several programs simultaneously, the current version of GEM does not support such multi-tasking. It does, however, support multi-programs" When a program is designed as a desk accessory and properly installed, it can share Desktop space with up to six other GEM programs. During operation of any one of these programs, the others cease foreground activity Thus, desk accessories do not provide true multi-tasking, though they can be a convenience.
Desk accessories, such as the familiar Control Panel, are programs that have been designed and compiled specifically to be accessories. When placed on the 'boot' disk, GEM recognizes these files by their .ACC extender and loads them into a safe place in RAM. Titles for each accessory will appear in the "Desk" drop-down panel on the Menu Bar and will therefore be accessible from within any GEM program displaying the Menu Bar The titles are created by the accessory programs and are not necessarily the same as the program filenames. More than one desk accessory may be included within a program file, but GEM supports a total of six desk accessories, no matter what the number of program files.
Many desk accessories are available through the public domain, such as calculators, clocks, and slide puzzles. Commercial desk accessories are also beginning to appear The advantages as well as the limitations of a program designed as a desk accessory suggest certain creative directions. In this case, I chose the ubiquitous computer game, Reversi. It has appeared in various forms on practically every computer from mainframe to micro. As an ST desk accessory, it provides a refreshing spot of strategic entertainment during long hours at the computer.
REVERSI RULES
Reversi is a popular programming challenge. The rules of the game are very simple, but developing a good strategy takes skill. Reversi is found on many computers because the game mechanics are easy to understand and the graphics requirements minimal. (The tokens on mainframe versions are usually made up of X's and O's.)
In our version of Reversi, the human player always takes the black token and the ST computer the white. Each side alternately places one token of its own color on an unoccupied square, so that the following rule is respected:
The token must be placed so that an imaginary straight line, drawn from that token to any other token of matching color will cut through one or more tokens of the opposing color (See Figure 1.)
Following a move, all opposing "sandwiched" tokens flip to the active player's color So, if black's token was placed on the bottom "?" in Figure 1, the board will change as in Figure 2.
With each move, you must take at least one opposite-color token between two of yours or "pass" for that move. The game continues with each side alternately placing tokens and capturing pieces until all squares are occupied, or no legal move is possible. The winner is the player with the most tokens of his or her color
USING THE REVERSE DESK ACCESSORY
The Reversi desk accessory file is found within the REVERSI.STQ folder on your START disk. Since it is a desk accessory, copy the file REVERSI.ACC to a boot disk, then turn on your ST with this disk in the main drive. Now, click on the REVERSI label that should appear in the Desk drop-down menu, and the Reversi window will appear in the middle of your screen with two tokens for each side preset in the middle of the board.
As with most desk accessories, you may click in the title area and drag the Reversi window anywhere on the Desktop. Though you may not size the window, you may close it by clicking on the upper-left corner box. Unless you reboot, Reversi will remember your last position and you may continue your game as you left off. The current score is on the right side of the window.
You are black and you move first. Point the mouse cursor where you want to place your token and click the left button. If your choice is legal, the computer will place your token and flip the appropriate opposite tokens. It will then take its own turn. Continue in this way until all squares are filled or there are no more moves. After the winner is declared, you may play again by clicking anywhere within the window. If you can't make a legal move, resign your turn by pressing any key on your ST
THE PROGRAM
If you plan on compiling Reversi from the source code on the START disk, please see the Disk Instructions page in this issue for details regarding the files related to this article. The program was written with Alcyon C from the Atari Developer's Toolkit. Other C's may require some modification.
A small part of Reversi code is taken from ACCSKEL.C, which stands for "accessory skeleton," and may be found in the Developer's Toolkit. Those parts are indicated both in the listing and in the following analysis. ACCSKEL.C is exactly what it sounds like: a skeleton structure to make your own code into a desk accessory.
Looking at REVERSI.C on your START disk (or on a printout if you insist on doing it the easy way), you can see that the only external file needed is GEMDEFS.H. The first cluster of #define statements makes the code easier to read, and the second group takes care of the only XBIOS calls we use, thereby eliminating the need to #include OSBIND.H. Following this are a number of global variables including several from the original ACCSKEL.C.
The second declaration group contains variables used by the game subroutines. The array b_grid[][] will contain the values of each game square; positive values represent good squares and negative values bad ones. (A few quick games will quickly demonstrate why some squares are "bad' .) These values will change during the course of a game but b_init[][] is the same array holding the starting values for the squares before the first move occurs. c_grid[][] is an array of codes showing the contents of each square: l=empty square, 2=player, 3=computer, and 0=border (not a valid square).
Notice, among the other variables, phase and adv. These are used in some procedures to tell the computer which side is playing. A value of two indicates human and three computer; phase represents the current player and adv represents the opponent during a move.
Two standard GEM routines, open_ vwork() and open_window(), open the virtual screen workstation and open a window of specific size and attributes for the Reversi playfield. Both routines are from the ACCSKEL.C source code.
Following these two procedures is the main() entry point of the program. This routine is called by the GEM shell immediately before displaying the Desktop. In this section is appl_init() which should be the first function in any GEM program. The menu_ register() function places the desk accessory's menu item string, "Reversi," on the Desk menu. Also in main() are screen() and init(), initialization calls for the game itself, which will be explained later in this article. Following this is multi(), which contains our "psuedo-multitasking" routine.
THE ROUTINES
The portion of the program which enables the desk accessory to interact with any GEM application is multi(). The AES function evnt_multi() continually checks for GEM calls until you turn off the power or press the reset button. The purpose of evnt__multi() is to wait for specified events chosen by the programmer. When the event occurs, the operating system interrupts the current application and begins the appropriate program-or desk accessory. The specified GEM events for Reversi are MU_MESAG, MU_BUTTON, MU_KEYBD, MU_Ml and MU_M2.
MU_MESAG is for specific window events (sizing, moving, closing, etc.), MU_BUTTON is the mouse button state (down or up), MU_KEYBD represents keyboard input, and MU_Ml and MU_M2 are mouse position events. Mouse position events occur when the mouse cursor enters or leaves a specific rectangle, in this case, when the cursor exits or enters the Reversi window. With this information, we can change the cursor form to a pointed hand if it is in the window.
Throughout all of multi(), the program dispatches the various calls in order to execute various tasks (window moving, cursor modification, accessory call by Menu bar, etc.). The multi() function is normally found in all GEM programs using the Menu Bar.
board ()
Following a window updating message from GEM, or when you open the Reversi window, board() is called upon to draw the window (game board, tokens, score, etc.). Most of the functions used in board() are from GEM VDI (line drawing, ellipses). The drawing parameter variables are adjusted depending upon resolution so the game will work in any mode.
set_clip()
ACCSKEL.C provides both set_clip() and do_redraw(). The set_cLip() routine uses the size parameters of a given rectangle to "clip" any screen graphics which are drawn beyond this rectangle; do_redraw redraws any rectangle portions which have been "clipped" from the Reversi window by other windows. For example, when you drag a window on top of the Reversi one, then remove it, parts of the Reversi window must be redrawn. This function finds the parts and processes the subsequent drawing.
pIayer()
During the player's turn, player() is called. It first converts the mouse X and Y coordinates (captured by evnt_multi()) into grid square coordinates ranging from zero to eight. If the cursor is really pointing on a square, player subroutine sub__1() is called.
atari()
The atari() procedure contains the core game strategy for Reversi. The program first scans the c_grid array for empty squares, then checks their playability with verify(), which returns either zero (not a valid move), or the number of opposing tokens which would be flipped by this move. Then atari() assigns a specific value to this square, depending on number of returned tokens and the strategic position of the square:
Value=(b_grid[x][y] * 3)+ flip
Because the number of returned tokens is more important near the end of the game, this value is affected by the course of the game. (This is a personal choice. You can modify the scoring to fit your own strategy.)
The computer then chooses the best square value and plays it: change(v,w). If there is no playable square, it resigns for this turn: (point= = -99).
display()
To update the score on the right side of the window, display() uses VDI functions. Further down in the listing, rectangle() draws a filled rectangle of a chosen color, using the VDI v_bar() function. Here, it is used to erase the previous score before writing a new one. Much of display() is devoted to converting the score numbers to strings for the VDI v_gtext function.
turn ()
One complete game turn includes both the human and the computer and is handled by turn(), which is called from the multi() procedure whenever the player presses a key or clicks in the window. First, turn() checks the end flag to see if the game is over If so, init() is called to redraw the game board and reset the squares to their starting values. Otherwise, player() is activated. (See above.)
If the player's move is valid, atari() determines the computer's decision. At the same time, the program checks for an end-of-game situation (all squares played: score[2]+score[3]=64, or both sides could not play anywhere: pass= =2).
Lastly, turn() uses the VDI function, wind_set() to add the player prompt: "Your move...\0".
sub_ 1() and sub_2()
The sub_1() function checks if the player's chosen square is empty before going on to sub_2(), which uses verify() (see below) to get a count on the number of opposing tokens before flipping their colors.
verify()
The verify() function scans all eight directions from a specified square for valid, opposing tokens. The variable for the number of opposing tokens in one direction is called flap, while flop (the number returned by verify()), is the total number of opposing tokens found in all eight directions.
change()
The change() procedure is the most complex one in Reversi. It draws a token on the board window, then updates the necessary variables to reflect the changes.
After activating a clipping rectangle within the window, and choosing some pleasing values for the drawing mode, the VDI v_ellipse() function draws the played token on the screen.
As mentioned, all graphics within Reversi are designed to look the same regardless of resolution. To correct for vertical deformation in medium resolution, the -x adjustment values are doubled when in this mode. Similarly, all high-resolution drawing values are adjusted to be twice as big as low ones to create the same size board in all three modes.
Next, all directions from the played square are scanned for opposing tokens. The tokens are placed into the variables (c_grid[][]=phase ) and onto the screen (v_ellipse). For each returned token, the score of both sides is modified accordingly in score[phase] + + and score [adv] - -.
Finally, wind_update is recalled to signal the end of window modification.
Modify()
After a few games, you will notice that different squares have different strategic value during the course of play; in fact, the overall value of each square closely depends upon the status of its adjacent squares. If we want an acceptably challenging game, we must adjust the values of the squares appropriately That is the purpose of modify(), which is called after each move. Only some squares are examined, to save memory space (and because I didn't want to spend months on this problem). I urge you to customize this routine for your own use. The best game results will come from improving this portion of the program.
stop ()
As indicated by its name, stop() is called when no more moves can occur A brief diagnostic message appears in the window using the wind_set().
init()
The init() function initializes all the b_grid[][] array variables (importance of each square) with those in b_init[][]. Also, the c_grid[][] array (content of the squares) is initialized with a one (empty) in each square, a zero (border) in all peripheral rows and a two (player) and three (computer) in central squares.
wait()
The delay loop wait() is necessary. Without it, the computer responds too fast for the player to see where the token was played.
screen()
During desk accessory initialization, screen() is called once by main(). It checks the current screen resolution, with getrez(), then allocates the appropriate values to the drawing variables used for window, board, tokens and text drawing.
Looking for a desk accessory? There are plenty available, and the supply is growing. Here is just a partial list of ST desk accessories which may be found commercially, or free on CompuServe or the Atari BBS.
Michtron leads the pack in desk accessories which include:
ALT-Alt-key macros make your life easier Up to 36 programmable combinations. ($29.95)
Calendar-A complete appointment and reminder calendar, including alarm clock. ($29.95)
Cornerman -This monster multi-function desk accessory includes calculator, notepad, clock, phone dialer and directory, and a game thrown in for fun. ($49.95)
Michtron, 576 S. Telegraph, Pontiac, Ml 48053, (313) 334-5700.
CIRCLE 218 ON READER SERVICE CARD
Batteries Included's Thunder! ($39.95) is a real-time spelling checker See Mark Skapinker's article in this issue for more on this program. Batteries Included, 30 Mural Street, Richmond Hill, Ontario L4B 1B5, Canada, (416) 881-9941.
CIRCLE 219 ON READER SERVICE CARD
Apex Resources is distributing a British product called Rhythm ($34.95), which is a desk accessory calculator in the format of a small spreadsheet. Apex Resources, 17 St. Mary's Court. Brookline, MA 02146, (617)232-9686.
CIRCLE 220 ON READER SERVICE CARD
The Catalog will soon release Crystal ($24.95), an accessory that lets you access all GEM Desktop commands (including some never implemented by Atari) while within other GEM programs. The Catalog, 524 Second Street, San Francisco, CA 94107, (800) 443-0100 ext. 133.
CIRCLE 230 ON READER SERVICE CARD
COMPUSERVE ACCESSORIES
Get on line, type GO ATARI16 then browse through the data libraries. We found most of these in DL3, though some are also from DL0 and DL1.
PUZZLE.ACC-A tile-slide puzzle.
BICAL2.ACC-A three-resolution calculator.
BLUE.ACC-Tums your Desktop blue without needing the Control Panel.
CALC.ACC-Another calculator; take your pick.
CALEND.ACC- How about a desk calendar?
CRABS.ACC- Nasty little critters from Alex Leavins.
DIRPRI.ACC-Prints disk directory in condensed mode on Gemini lox.
DMPNEC.ACC-NEC printer scrccn dump written in Personal Pascal.
FIXPNL.ACC-A fixed Control Panel that maintains date and time on reset.
RAM.ACC-Tells you how much free RAM is left.
SCRSAV.ACC -Turns off your screen if you leave your CRT
MITES.ACC-April fool's day joke. Similar to CRABS.ACC.
RAMDSK.ACC-This is just one of many RAMdisks in the libraries.
ATARI BBS-(408) 745-5308 will put you online with Atari. Go to SIG #8 for ST goodies. Among the more than 200 programs to download are the following accessories (many accessories listed above are also in the Atari BBS):
CLOCIKA.ACC-A graphic analog clock for your Desk.
DIGICLOK.ACC-Or, how about a digital clock?
97KRAM.ACX- Like CompuServe. there are a lot of RAMdisks here.
SNAPSHOT.ACX- Saves GEM screen in DEGAS format.
EMULATOR.ACC- Updated VT52 emulator
CLI.ACC-Command Line Interpreter
PALLET.ACC-Adjust your color palette without the Control Panel.