|
Volume Number: | 2 | |||
Issue Number: | 10 | |||
Column Tag: | Assembly Language Lab |
Create a Tic Tac Toe Game!
By Mike™ Scanlin, World Traveler
While reading MacTutor and learning how to program in 68000, I decided to try and write a simple game. I wanted something that was simple enough so that I could concentrate on the coding of it rather than the logic of it. Tic Tac Toe was the obvious choice. It would let me try using menus, windows, dialogs and simple graphics. After the initial game was written, I added some other features just for the sake of making the game a little more interesting. Then I added two text manipulation effects to take some of the boredom out of the "About" dialog. The result is a program that is more complicated than the average Tic Tac Toe program, but (hopefully) simple enough so you can follow the code with relative ease and learn something from the techniques and routines used in it.
The files used are "TicTacToe.asm", a resource file "TicTacToe_rsrc.asm" that contains two icons used in the "About" dialog, two text effects routines "GrowText.asm" and "SnakeText.asm", and the link file "TicTacToe.link". I have found that building a library of common subroutines (like GrowText and SnakeText) that can be linked together and used in different programs is incredibly useful. After 5 years of 6502 programming on the Apple ][ without this feature, I have learned to love the Macintosh Linker. The Tic Tac Toe program that follows could be split up into even more subroutine files if desired.
The Program
The program starts off by initializing all routine manangers that it will be using. I should point that there is a "resume" procedure pointer passed to _InitDialogs (instead of the typical nil pointer). The consequence of this is that if a serious system error should occur, the "resume" button will not be greyed and if you click on it control will be passed to the "resume" procedure. In my case, all the resume procedure does is jump to the Finder (via _exitToShell). I find this quite helpful when developing a program because it is much quicker than rebooting the system. Of course, if the program messed up RAM in some way it could be a big mistake to jump to the Finder and continue work as if all was normal. But due to the nature of the program I was writing (wasn't dealing with disk IO), I decided to use it anyway (and had no problems). Use this with discretion and ALWAYS keep backups. If you're at all unsure (or nervous) about the possible effects of your program, then don't use this method (use "CLR.L -(SP)" in place of "PEA resume" to push a nil pointer. That will grey the "resume" button and force a restart if you get a serious system error). The next thing called is _TEInit. As Inside Macintosh says, it should be called even though you may not use TextEdit so that desk accessories and dialogs will work correctly.
Fig. 1 Our Tic Tac Toe Game!
After the basic initialization, all menus, windows and dialogs are created. The reason for creating all of the windows and dialogs in the beginning of the program is twofold. First, because it's nice having them all together in the source code for debugging purporses. Secondly, and more important, it helps to keep the heap from becoming fragmented if they are all created one after the other. This technique is probably best used for static data structures that are needed throughout the entire program (e.g. global variables). On larger programs with lots of windows and dialogs, it is probably not a good idea to make them all at once (due to RAM limitations). The remainder of the initialization and the main event loop (GetEvent) should be straightforward. It is very similar to previous programs published in MacTutor.
The Dialogs
All of the dialogs use a routine called CenterString. Its function is to calculate the offset from the left edge of a window needed to center a string in a window, given the width of the window (the windowWidth variable) and a pointer to the string. The algorithm is fairly obvious: subtract the length of the string (found from _StringWidth) from windowWidth and divide by two. This value is returned in D0 and is used as the horizontal value for the _MoveTo called just before _DrawString.
Additionally, the "About" dialog uses the two routines GrowText and SnakeText. GrowText starts out by drawing the string at a textSize of 1 point and then redraws it at 2 point, etc., up to 12 point (erasing the previous string with _eraseRect just before drawing the new string) so that it looks like the string is growing. SnakeText is rather more complicated, but works in a similar manner. It doesn't really look like a snake, but for lack of a better name that's what it is called. The best way to understand what it is doing is to watch it perform a couple of times. Basically, it is redrawing characters at different textSizes until they're all at 12 point. By studying the commented source code, you should be able to get some idea of how it works. But you don't need to understand how it works to use it within your own programs (just be sure you set up the input registers correctly). I should mention that the rectangle that you pass a pointer to must be tight fitting around the text it is to draw (i.e. no extra white space around the top,left, or bottom -- the right side isn't too important). If it isn't, the routine will still function, but it may not look too pretty on the screen (like a lot of overdrawn or partially overdrawn characters). The method I used to find the correct dimensions of the rectangle is to draw the string where I wanted it and then use _FrameRect until I found the smallest possible rectangle that would enclose all of the text. It will probably take a little trial and error to get the dimensions perfect, but it's important.
Fig. 2 With some nifty dialog 'about' boxes
The DefaultOutline subroutine is used to draw the 3 pixel wide oval around the "OK" button in the dialogs in the standard way. It is worth pointing out that anything you want in a dialog box that isn't in the dialog's item list has to be put in the dialog box before calling _ModalDialog (when you call _ModalDialog, it draws the things that are in the dialog's item list). So the oval around the "OK" button is actually drawn before the button is drawn by _ModalDialog. This is also true for any dynamic text (or special effects like GrowText or SnakeText) that you want to have in the dialogs.
Game Logic
Every time you select "New Game" from the "Game" menu, the computer randomly decides who goes first. If you're looking at a blank board after you have selected "New Game" then you go first, otherwise you'll see a square somewhere (the computer's first move). The player is always circles, the computer is always squares. The "Reverse Positions" option merely switches all circles for squares and squares for circles -- it does not change the order of turns or who is what piece. It was included as an option mainly because it was easy to code (besides, it does add a little to the interest of the program). It is only enabled when a game is in progress.
If the difficulty is set on "easy" then the computer will always move randomly (which gets boring after about 1 game). The "hard" difficulty should be called "not quite so easy, but not very difficult either" but that is a bit of a long name for a menu option. Face it, this is not a difficult game to play, there's not a whole lot you can do to make the computer (or anyone else, for that matter) play more intelligently. When set on "hard" it makes three checks for intelligent moves before it decides to move randomly: (1) checks if the computer can win in the next move, (2) checks if the computer can block a player win in the next move, (3) checks if the middle square is taken. The "cheating" options were added for fun. As the dialog says, anyone who is allowed to cheat (player or computer) is allowed to move on any square, even if someone else is already there. As far as the coding of this is concerned, all it amounts to is bypassing the usual checks for valid moves.
If there is a mouseDown event anywhere in the game window, the point is checked to see if the player clicked in one of the nine squares. The coordinates of the click are first converted into local coordinates by calling _GlobalToLocal and then checked to see if the point is in any of the nine squares by calling _PtInRect. The coordinates of the nine squares are in stored in the table BoardLocs. These coordinates are used as dimensions for drawing the squares and circles (with _FrameRect and _FrameOval) as well as checking for validity of mouseDown events.
Miscellaneous
The "Show/Hide scores" menu option was included to demonstrate how to change menu items. All that is needed is to set up the parameters on the stack and call _SetItem. Likewise, the checking and unchecking of the difficulty setting and cheat settings can be accomplished with calls to _CheckItem.
One thing I learned while writing this program (after many headaches) is that any variable declared with the "DS" (Define Storage) declarative will have to be referenced with address register A5. This is not true for variables declared with the "DC" (Define Constant) declarative. For instance, if ShowFlag is declared "ShowFlag DS 1" it will be referenced as "MOVE ShowFlag(A5),D0" but if it is declared "ShowFlag DC 1" it will be referenced as "MOVE ShowFlag,D0". This is because all storage declared with DS is put in the application globals space (pointed to by A5 when your program starts) while all storage declared with DC stays within the program code segment (addresses filled in by the Linker). If you use both types of declarations (DS and DC) in your programs, as is done in this one, it is critical to pay attention to which is which and use the correct type of reference or you will end up with random results.
As was mentioned above with respect to SnakeText and GrowText, the routines presented here can be used as part of your own programs. The RandomBounds routine (combined with its initialization of randSeed in the beginning) is a good way to get bounded random numbers into your assembly language programs. It returns a positive integer in the range 1 to D0, where you set D0 before calling it. The CenterString and DefaultOutline routines should be useful, too.
; GrowText 4 March 1986 ;-------------- ; print a text string starting at 1 point up to 12 point, ; centered in a rectangle (horizontally, not vertically). ; the string gets a little bigger each time through the loop. ; inspired by Lynn Bond, who gets a little bigger each time ; she eats a pound of M&M's. .TRAP _EraseRect $A8A3 .TRAP _TextSize$A88A .TRAP _MoveTo $A893 .TRAP _DrawString$A884 Xref GrowText,CenterString,windowWidth right EQU 6 left EQU 2 ;------------ GrowText: ;------------ ; before calling, user should push the following onto the stack: ; (in this order) ; address of string to print ; addr of rect that encloses the string ; vertical height to print string ;------------- ;save return address MOVE.L (SP)+,returnAddress(A5) ;get parameters MOVE (SP)+,vPosition(A5) MOVE.L (SP)+,rectPointer(A5) MOVE.L (SP)+,stringPointer(A5) ;save windowWidth MOVE windowWidth(A5),-(SP) ;set up windowWidth and leftEdge so we can draw some ; centered text MOVE.L rectPointer(A5),A0 MOVE right(A0),windowWidth(A5) MOVE left(A0),D0 MOVE D0,leftEdge(A5) SUB D0,windowWidth(A5) ;init textSize to 1 MOVE #1,textSize(A5) @1 MOVE.L rectPointer(A5),-(SP) ;erase old string _EraseRect MOVE textSize(A5),-(SP) _TextSize MOVE.L stringPointer(A5),A0 JSR CenterString ADD leftEdge(A5),D0 MOVE D0,-(SP) ;h MOVE vPosition(A5),-(SP) ;v _MoveTo MOVE.L stringPointer(A5),-(SP) ;draw new string _DrawString MOVE #32000,D0;delay @2 DBRA D0,@2 ADDQ #1,textSize(A5) ;inc textSize for ;next iteration CMPI #13,textSize(A5) BNE @1 ;restore windowWidth MOVE (SP)+,windowWidth(A5) MOVE.L returnAddress(A5),-(SP) RTS ; global variables textSizeDS1 vPosition DS1 rectPointer DS.L 1 stringPointer DS.L1 leftEdgeDS1 returnAddress DS.L1 ; SnakeText 22 February 1986 ;---------------- ; Do a neat special effect with a string of chars. Print each ; char in string 1 to 11 times (currently set at 6), from 2 pt ; to 12 pt so that it looks really cool. .TRAP _EraseRect $A8A3 .TRAP _TextSize$A88A .TRAP _MoveTo $A893 .TRAP _DrawChar$A883 .TRAP _CharWidth $A88D .TRAP _NewPtr $A100+30 .TRAP _DisposPtr $A000+31 Xref SnakeText topEQU 0 left EQU 2 bottom EQU 4 tblChar EQU 0 tblTextSize EQU 2 tblTop EQU 4 tblLeft EQU 6 tblBottom EQU 8 tblRightEQU 10 tblRect EQU 4 ;------------- SnakeText: ;------------- ; on entry: ; A0 points to string ; A1 points to rectangle to print string in (left edge should ; be tight) ; D0 = starting v ;-------------- MOVEM.LA2-A4/D3,-(SP) ;save some regs ;save inputs MOVE.L A0,StringPtr(A5) MOVE.L A1,StringRectPtr(A5) MOVE D0,vLoc(A5) ;init textSize to 12 point MOVE #12,-(SP) _TextSize ;set up a table (block of mem) to work with. Each char in ; string will occupy 6 words in the table. One word for each of ; the following: ; char, curSizeOfChar, top, left, bottom, right ;Each char has a rectangle defined by top, left, bottom and ; right that encloses the char. This rect is used to erase the old ; char before drawing the new (and larger) char. MOVE.L StringPtr(A5),A0 ;first byte of string is length byte. ;we need 12 bytes (6 words) for each char in string. CLR.L D0 MOVE.B (A0),D0 ;get length of string MOVE.L D0,D1 ASL #2,D0 ASL #3,D1 ADD D1,D0 ;D0=12*length(string) ;get a pointer to a block of D0 bytes of free RAM _NewPtr MOVE.L A0,A2 ;copy BlockPtr (assume no err) ;initialize some stuff MOVE.L A2,BlockPtr(A5) MOVE.L StringRectPtr(A5),A0 MOVE top(A0),TopValue(A5) MOVE left(A0),LeftValue(A5) MOVE bottom(A0),BottomValue(A5) MOVE.L StringPtr(A5),A3 CLR D3 MOVE.B (A3)+,D3 ;get length of string SUBQ #1,D3 ;build the table ; A2 = BlockPtr (pointer to current char in table) ; A3 = StringPtr (points to next char in string to work with) ; D3 = length of string (loop control variable) @2 MOVE.B (A3)+,D0 ;get a char MOVE D0,tblChar(A2) ;save char in table CLR tblTextSize(A2) ;textSize=0 to start MOVE TopValue(A5),tblTop(A2) MOVE LeftValue(A5),tblLeft(A2) MOVE BottomValue(A5),tblBottom(A2) CLR -(SP) ;space for integer result MOVE D0,-(SP) ;push char _CharWidth ;get width of char MOVE (SP)+,D0 ADD LeftValue(A5),D0 ;add char width ; to previous MOVE D0,tblRight(A2) ; LeftValue to get ; RightValue MOVE D0,LeftValue(A5) ADDA #12,A2 ;inc table pointer DBRA D3,@2 ;save ptr to last char in table SUBA #12,A2 MOVE.L A2,LastCharPtr(A5) MOVE.L BlockPtr(A5),A2 ;A2 = FrontPtr MOVE.L A2,A3 ;A3 = EndPtr ;do the drawing ;------------------- ; A4 = loop control variable @3 MOVE.L A2,A4 ;start with FrontPtr ;vary the speed by putting an even factor of 12 (i.e. 1,2,3,4,6) ; in the line below. ; 1 = slow (11 char lag) ; 2 = sorta slow (5 char lag) <-- set at 2 now ; 3 = medium (3 char lag) ; 4 = fast (2 char lag) ; * note, if you set it at 5, you'll regret it (hang system) ; 6 = really fast (1 char lag) ;Change the immediate data in the next line (@4): @4 ADDQ #2,tblTextSize(A4);inc textSize(A4) CMPA.L A3,A4 ;are we at EndPtr? BEQ @5 SUBA #12,A4 ;go to previous entry in table BRA @4 @5 PEA tblRect(A4);erase old char _EraseRect MOVE tblLeft(A4),-(SP) ;draw new char MOVE vLoc(A5),-(SP) _MoveTo MOVE tblTextSize(A4),-(SP) _TextSize MOVE tblChar(A4),-(SP) _DrawChar CMPA.L A2,A4 ;have we reached FrontPtr? BEQ @6 ADDA #12,A4 BRA @5 @6 CMP #12,2(A3);is EndPtr size=12? BNE @7 ADDA #12,A3 ;inc EndPtr @7 MOVE.L LastCharPtr(A5),A0 CMPA.L A0,A2 ;is FrontPtr at last char? BEQ @8 ADDA #12,A2 ;inc FrontPtr @8 CMP #12,tblTextSize(A0) ;is last char's ; size=12? BNE @3 MOVE.L BlockPtr(A5),A0 ;free the memory used _DisposPtr MOVEM.L(SP)+,A2-A4/D3 ;restore some regs RTS StringPtr DS.L 1 StringRectPtr DS.L1 LastCharPtr DS.L 1 BlockPtrDS.L1 TopValueDS1 BottomValue DS 1 LeftValue DS1 vLoc DS1 ; Tic Tac Toe ;=========== ; 1 March 1986 Mike™ Scanlin Include MacTraps.D Include ToolEqu.D true EQU $100 false EQU 0 randSeedEQU -126 Time EQU $20C everyEventEQU $FFFF cmdKey EQU 256 right EQU 6 left EQU 2 ;codes for board: empty EQU 0 square EQU 1 circle EQU 2 ;external routines Xref SnakeText,GrowText ;internal routine and data used by GrowText Xref CenterString,windowWidth ;------ Start: ;------ PEA -4(A5) ;init everything _InitGraf _InitFonts _InitWindows _InitMenus PEA resume ;serious system error ; resume procedure _InitDialogs _TEInit MOVE.L #everyEvent,D0 _FlushEvents _InitCursor ;make Apple menu CLR.L -(SP) ;space for hndl result MOVE #1,-(SP) ;menu id PEA AppleMenuTitle _NewMenu ;save handle, but leave it on the stack for _AppendMenu MOVE.L (SP),AppleMenuHndl(A5) PEA AppleMenuAbout ;add 'about' _AppendMenu MOVE.L AppleMenuHndl(A5),-(SP) CLR -(SP) _InsertMenu ;make Game menu CLR.L -(SP) ;space for hndl result MOVE #2,-(SP) ;menu id PEA GameMenuTitle _NewMenu MOVE.L (SP),GameMenuHndl(A5) PEA GameMenuNew;add New Game _AppendMenu MOVE.L GameMenuHndl(A5),-(SP) PEA GameMenuRev ;add Reverse Positions _AppendMenu MOVE.L GameMenuHndl(A5),-(SP) PEA GameMenuShow ;add Show Scores _AppendMenu MOVE.L GameMenuHndl(A5),-(SP) PEA GameMenuClear ;add Clear Scores _AppendMenu MOVE.L GameMenuHndl(A5),-(SP) PEA GameMenuLine ;add a line _AppendMenu MOVE.L GameMenuHndl(A5),-(SP) PEA GameMenuQuit ;add Quit _AppendMenu MOVE.L GameMenuHndl(A5),-(SP) CLR -(SP) _InsertMenu ;disable the line and Reverse Positions MOVE.L GameMenuHndl(A5),-(SP) MOVE #5,-(SP) _DisableItem MOVE.L GameMenuHndl(A5),-(SP) MOVE #2,-(SP) _DisableItem ;make difficulty menu CLR.L -(SP) ;space for hndl result MOVE #3,-(SP) ;menu id PEA DiffMenuTitle _NewMenu MOVE.L (SP),DiffMenuHndl(A5) PEA DiffMenuEasy ;add 'easy' _AppendMenu MOVE.L DiffMenuHndl(A5),-(SP) PEA DiffMenuHard ;add 'hard' _AppendMenu MOVE.L DiffMenuHndl(A5),-(SP) PEA GameMenuLine ;add a line _AppendMenu MOVE.L DiffMenuHndl(A5),-(SP) PEA DiffMenuHelp ;add help _AppendMenu MOVE.L DiffMenuHndl(A5),-(SP) CLR -(SP) _InsertMenu MOVE.L DiffMenuHndl(A5),-(SP) ;disable the line MOVE #3,-(SP) _DisableItem ;make Cheat menu CLR.L -(SP) ;space for hndl result MOVE #4,-(SP) ;menu id PEA CheatMenuTitle _NewMenu MOVE.L (SP),CheatMenuHndl(A5) PEA CheatMenuYou ;add player cheat _AppendMenu MOVE.L CheatMenuHndl(A5),-(SP) PEA CheatMenuMe ;add computer cheat _AppendMenu MOVE.L CheatMenuHndl(A5),-(SP) PEA GameMenuLine ;add a line _AppendMenu MOVE.L CheatMenuHndl(A5),-(SP) PEA CheatMenuHelp;add help _AppendMenu MOVE.L CheatMenuHndl(A5),-(SP) CLR -(SP) _InsertMenu MOVE.L CheatMenuHndl(A5),-(SP) ;disable line MOVE #3,-(SP) _DisableItem _DrawMenuBar ;init the dialogs' item list handles LEA aboutItemsList,A0 LEA aboutItemsHndl,A1 MOVE.L A0,(A1) LEA diffItemsList,A0 LEA diffItemsHndl,A1 MOVE.L A0,(A1) ;make the "about" dialog, but don't show it (yet) CLR.L -(SP) ;space for dialog ptr result CLR.L -(SP) ;nil ptr (wStorage on heap) PEA aboutRect;window coordinates PEA aboutTitle ;title MOVE #false,-(SP) ;not visible MOVE #dBoxProc,-(SP) ;window type MOVE.L #-1,-(SP);window in front MOVE #false,-(SP) ;no closebox MOVE.L #1,-(SP) ;reference value PEA aboutItemsHndl _NewDialog MOVE.L (SP)+,aboutPointer(A5) ;save pointer ;make the difficulty dialog, but don't show it (yet). ;this dialog window is the same dialog used by the 'cheat' ; menu option CLR.L -(SP) ;space for dialog ptr result CLR.L -(SP) ;nil ptr (wStorage on heap) PEA diffRect ;window coordinates PEA diffTitle;title MOVE #false,-(SP) ;not visible MOVE #dBoxProc,-(SP) ;window type MOVE.L #-1,-(SP);window in front MOVE #false,-(SP) ;no closebox MOVE.L #1,-(SP) ;reference value PEA diffItemsHndl _NewDialog MOVE.L (SP)+,diffPointer(A5) ;save pointer ;make score window, but don't show it (yet) CLR.L -(SP) ;space for window ptr result CLR.L -(SP) ;nil ptr (wStorage on heap) PEA sRect ;window coordinates PEA sTitle ;title MOVE #false,-(SP) ;not visible MOVE #noGrowDocProc,-(SP) ;window type MOVE.L #-1,-(SP);window in front MOVE #false,-(SP) ;no closebox MOVE.L #2,-(SP) ;reference value _NewWindow MOVE.L (SP)+,sPointer(A5);save pointer ;make game window and show it CLR.L -(SP) ;space for window ptr result CLR.L -(SP) ;nil ptr (wStorage on heap) PEA wRect ;window coordinates PEA wTitle ;title MOVE #true,-(SP);visible MOVE #noGrowDocProc,-(SP) ;window type MOVE.L #-1,-(SP);window in front MOVE #false,-(SP) ;no closebox MOVE.L #3,-(SP) ;reference value _NewWindow ;leave pointer on stack for _SetPort MOVE.L (SP),wPointer(A5) _SetPort ;make game window the active window ;set up to draw normal, 12 point Chicago text _PenNormal MOVE #0,-(SP) ;chicago _TextFont MOVE #0,-(SP) ;normal _TextFace MOVE #12,-(SP);12 point _TextSize ;get a ptr on the heap for a string of length maxNumStrSize. ; the ptr is returned in A0. This string is used later. ; maxNumStrSize = length(max longint) -> ; length('±2147483648') = 11 MOVEQ #11,D0 _NewPtr MOVE.L A0,strPointer(A5) ;save string ptr ;init randSeed with current time MOVE.L (A5),A0 ;get QuickDraw globals ptr MOVE.L Time,randSeed(A0) ;init some other stuff CLR MeWon(A5) CLR YouWon(A5) CLR Cats(A5) MOVE #true,GameOver(A5);no current game MOVE #false,ShowFlag(A5) ;score currently ; not shown MOVE #true,EasyFlag(A5);start off easy MOVE #false,PlayerCheats(A5) ;no cheating ; to begin with MOVE #false,ComputerCheats(A5) ;------------ GetEvent ;------------ _SystemTask ;wait for an event CLR -(SP) MOVE #everyEvent,-(SP) PEA EventRecord _GetNextEvent MOVE (SP)+,D0 BEQ GetEvent ;what event was it? MOVE What,D0 CMP #12,D0 ;12 = MaxEvents BGE GetEvent ASL #1,D0 MOVE EventTable(D0),D0 JMP EventTable(D0) EventTable DCGetEvent-EventTable ;null event #0 DCmouseDown-EventTable ;mDown #1 DCGetEvent-EventTable ;mouse up #2 DCkeyDown-EventTable;key down #3 DCGetEvent-EventTable ;key up #4 DCGetEvent-EventTable ;auto key #5 DCupdate-EventTable ;update event #6 DCGetEvent-EventTable ;disk event #7 DCGetEvent-EventTable ;activate #8 DCGetEvent-EventTable ;abort #9 DCGetEvent-EventTable ;network #10 DCGetEvent-EventTable ;I/O driver #11 ;--------------- mouseDown ;--------------- CLR -(SP) ;space for integer result MOVE.L Where,-(SP) PEA fndWindow(A5) _FindWindow;where was mouse pressed? CLR.L D0 MOVE (SP)+,D0 ASL #1,D0 MOVE WindowTable(D0),D0 JMP WindowTable(D0) ;ignore mouse presses inDsk,inSystemWindow,inDragRegion, ; inGrowRegion and inGoAwayRegion. WindowTable DCGetEvent-WindowTable DCinMenuBr-WindowTable DCGetEvent-WindowTable DCinContentRegion-WindowTable DCGetEvent-WindowTable DCGetEvent-WindowTable DCGetEvent-WindowTable ;------------ inMenuBr ;------------ CLR.L -(SP) ;space for longint result MOVE.L Where,-(SP) _MenuSelect;which menu and which item? MOVE (SP)+,menuChoiceID(A5) MOVE (SP)+,menuItem(A5) ;jump to here for command keypress DecodeMenuBar CMPI #1,menuChoiceID(A5) BEQ inAppleMenu CMPI #2,menuChoiceID(A5) BEQ inGameMenu CMPI #3,menuChoiceID(A5) BEQ inDiffMenu CMPI #4,menuChoiceID(A5) BEQ inCheatMenu JMP GetEvent ;---------------- inAppleMenu ;---------------- CMPI #1,menuItem(A5) ;is it 'about' ? BNE ReturnFromMenu MOVE.L wPointer(A5),-(SP);unhilite current ; front wndw MOVE #false,-(SP) _HiliteWindow ;bring about wnds to the front, then show it and make it the ; current port MOVE.L aboutPointer(A5),-(SP) _BringToFront MOVE.L aboutPointer(A5),-(SP) _ShowWindow MOVE.L aboutPointer(A5),-(SP) _SetPort ;set up some static text in the 'about' dialog MOVE #8,-(SP) ;typeface = outline _TextFace ;center the text in the 'about' dialog. ;set up the 'windowWidth' variable used by CenterString LEA aboutRect,A0 MOVE right(A0),windowWidth(A5) ;get right MOVE left(A0),D0;get left SUB D0,windowWidth(A5);width= right - left ;print title of program LEA tttString,A0 JSR CenterString MOVE D0,-(SP) ;h MOVE #28,-(SP);v _MoveTo PEA tttString _DrawString ;print 'written by...' MOVE #0,-(SP) ;type face = normal _TextFace LEA wbString,A0 LEA wbRect,A1 MOVEQ #60,D0 JSR SnakeText ;print date written LEA wwString,A0 LEA wwRect,A1 MOVEQ #80,D0 JSR SnakeText ;outline the default response ("OK") in the standard way ;note: the outline is drawn before the actual button is drawn ; (the button is drawn when _ModalDialog is called) LEA OKrect,A0 JSR DefaultOutline ;wait for user response CLR.L -(SP) ;nil ptr (no special filter routine) PEA itemHit(A5) _ModalDialog ;wait for click or <ret> keypress MOVE itemHit(A5),D0 CMPI #1,D0 ;if they clicked 'OK' then leave BEQ @2 PEA tyString ;else print 'Thank You' PEA tyRect MOVE #114,-(SP) JSR GrowText MOVEQ #15,D1 ;let them look at it for a while @4 MOVE #30000,D0 @3 DBRA D0,@3 DBRA D1,@4 @2 MOVE.L aboutPointer(A5),-(SP) ;goodbye dialog _HideWindow ReturnFromMenu ;unhilite Apple menu CLR -(SP) _HiLiteMenu JMP GetEvent tttString DC.B 11,'Tic Tac Toe' wbStringDC.B24,'written by Mike',170,' Scanlin',0 wbRect DC49,51,65,208 wwStringDC.B35,'28 February 1986 York University' wwRect DC69,30,85,263 tyStringDC.B9,'Thank you' tyRect DC103,110,119,179 itemHit DS1 ;---------------- inGameMenu ;---------------- CMPI #1,menuItem(A5) ;new game BEQ NewGame CMPI #2,menuItem(A5) ;reverse positions BEQ ReversePositions CMPI #3,menuItem(A5) ;show/hide scores BEQ ShowScores CMPI #4,menuItem(A5) ;clear scores BEQ ClearScores ; #5 is a line CMPI #6,menuItem(A5) ;quit BEQ Finish JMP GetEvent ;------------ NewGame ;------------ MOVEQ #8,D0 ;clear the old board LEA BoardCond,A0 @1 CLR (A0)+ DBRA D0,@1 MOVE #false,GameOver(A5) ;enable Reverse Positions MOVE.L GameMenuHndl(A5),-(SP) MOVE #2,-(SP) _EnableItem MOVEQ #2,D0 ;toss a coin to see who's first JSR RandomBounds CMPI #1,D1 ;on a 1, the player starts BEQ @2;on a 2, the computer starts CMPI #false,EasyFlag(A5) BEQ @3;if diff=easy, choose randomly MOVEQ #4,D1 ; else take middle square BRA @4 ;randomly choose a square to start in @3 MOVEQ#9,D0 JSR RandomBounds SUBQ #1,D1 ;number from 0-8 @4 ASL #1,D1 ;D1 = table offset LEA BoardCond,A0 MOVE #square,(A0,D1) @2 JSR DrawWindowContents JMP ReturnFromMenu ;---------------------------- DrawWindowContents ;---------------------------- MOVE.L wPointer(A5),-(SP) _SetPort ;draw board MOVE #2,-(SP) MOVE #2,-(SP) _PenSize ;pen = 2x2 square MOVEQ #3,D3 ;number of lines to draw - 1 LEA BoardLines,A2 @1 MOVE (A2)+,-(SP) MOVE (A2)+,-(SP) _MoveTo;set pen location MOVE (A2)+,-(SP) MOVE (A2)+,-(SP) _LineTo;draw line DBRA D3,@1 ;draw pieces. Board is numbered like this: ; 0 1 2 ;3 4 5 ; 6 7 8 MOVEQ #8,D3 ;number of positions to check -1 LEA BoardLocs,A2 LEA BoardCond,A3 @4 MOVE.L A2,-(SP) ;A2 points to rect _EraseRect ;erase old piece MOVE (A3)+,D0 BEQ @2;if empty, do next position MOVE.L A2,-(SP) ;points to which square CMPI #square,D0 ;is it a square? BNE @3 _FrameRect ;draw square BRA @2 @3 _FrameOval ;draw circle @2 ADDA #8,A2 ;point to next position in table DBRA D3,@4 MOVE #1,-(SP) MOVE #1,-(SP) _PenSize ;restore normal size pen RTS BoardCond DCB 9,0 ;0=empty, 1=sqaure, 2=circle BoardLocs DC30,60,62,92 ;sqaure 0 coordinates DC30,113,62,145 ;1 DC30,167,62,199 ;2 DC83,60,115,92 ;3 DC83,113,115,145;4 DC83,167,115,199;5 DC137,60,169,92 ;6 DC137,113,169,145 ;7 DC137,167,169,199 ;8 BoardLinesDC49,71,209,71 ;(h1,v1) (h2,v2) DC49,125,209,125 DC101,19,101,179 DC155,19,155,179 ;----------------------- ReversePositions ;----------------------- ;switch all squares and circles MOVEQ #8,D0 ;number of positions to check -1 LEA BoardCond,A0 @1 CMPI #empty,(A0)+ ;if empty, ; don't bother switching BEQ @4 ;code for square=1, code for circle=2 ; 3 EOR 1 = 2; and 3 EOR 2 = 1 EORI #3,-2(A0);switch pieces @4 DBRA D0,@1 JSR DrawWindowContents;draw new board JMP ReturnFromMenu ;---------------- ShowScores ;---------------- ; If the scores are not shown, then show them. If they are ; currently shown, then hide them. In either case, toggle the ; menu option from show to hide or visa versa. MOVE.L sPointer(A5),-(SP);for Show/Hide CMPI #false,ShowFlag(A5) ;test flag BEQ @2 _HideWindow LEA ShowText,A0 BRA @1 @2 _ShowWindow JSR DrawScoreWindow LEA HideText,A0 @1 MOVE.L GameMenuHndl(A5),-(SP) MOVE #3,-(SP) ;item number to change MOVE.L A0,-(SP) ;pointer to item text _SetItem ; flip the condition flag. ; true EOR true = false; and true EOR false = true. EORI #true,ShowFlag(A5) JMP ReturnFromMenu ShowFlagDS1 ShowTextDC.B10,'Show Score',0 HideTextDC.B10,'Hide Score',0 ;--------------- ClearScores ;--------------- CLR MeWon(A5) CLR YouWon(A5) CLR Cats(A5) JSR DrawScoreWindow JMP ReturnFromMenu ;------------- inDiffMenu ;------------- CMPI #1,menuItem(A5) BEQ EasyGame CMPI #2,menuItem(A5) BEQ HardGame ; #3 is a line CMPI #4,menuItem(A5) BEQ DiffHelp JMP GetEvent ;-------------- EasyGame ;-------------- ;if they selected a difficulty level, change the menu ; appearance accordingly MOVE.L DiffMenuHndl(A5),-(SP) ;check easy MOVE #1,-(SP) MOVE #true,-(SP) _CheckItem MOVE.L DiffMenuHndl(A5),-(SP) ;uncheck hard MOVE #2,-(SP) MOVE #false,-(SP) _CheckItem MOVE #true,EasyFlag(A5);difficulty=easy JMP ReturnFromMenu ;------------- HardGame ;------------- MOVE.L DiffMenuHndl(A5),-(SP) ;uncheck easy MOVE #1,-(SP) MOVE #false,-(SP) _CheckItem MOVE.L DiffMenuHndl(A5),-(SP) ;check hard MOVE #2,-(SP) MOVE #true,-(SP) _CheckItem MOVE #false,EasyFlag(A5) ;difficulty=hard JMP ReturnFromMenu ;--------- DiffHelp ;--------- MOVE.L wPointer(A5),-(SP) ;unhilite current ; front wndw MOVE #false,-(SP) _HiliteWindow ;bring diff wndw to the front, show it and then make it the ; current port MOVE.L diffPointer(A5),-(SP) _BringToFront MOVE.L diffPointer(A5),-(SP) _ShowWindow MOVE.L diffPointer(A5),-(SP) _SetPort ;set up some static text MOVE #8,-(SP) ;text face = outline _TextFace LEA diffRect,A0 MOVE right(A0),windowWidth(A5) ;get right MOVE left(A0),D0 SUB D0,windowWidth(A5);subtract left LEA diffString,A0 JSR CenterString MOVE D0,-(SP) ;h MOVE #28,-(SP);v _MoveTo PEA diffString _DrawString MOVE #0,-(SP) ;text face = normal _TextFace MOVE #20,-(SP) MOVE #70,-(SP) _MoveTo PEA easyString _DrawString MOVE #20,-(SP) MOVE #95,-(SP) _MoveTo PEA hardString _DrawString ;outline the default response ("OK") in the standard way LEA OKrect,A0 JSR DefaultOutline ;get user response CLR.L -(SP) ;nil ptr (no special filter routine) PEA itemHit(A5) _ModalDialog ;wait for response MOVE.L diffPointer(A5),-(SP) _HideWindow JMP ReturnFromMenu diffStringDC.B 10,'Difficulty',0 easyStringDC.B 30,'Easy: computer moves randomly',0 hardStringDC.B 34,'Hard: computer thinks about moves',0 ;---------------- inCheatMenu ;---------------- CMPI #1,menuItem(A5) BEQ PlayerCheat CMPI #2,menuItem(A5) BEQ ComputerCheat ;#3 is a line CMPI #4,menuItem(A5) BEQ CheatHelp JMP GetEvent ;--------------- PlayerCheat ;--------------- ;toggle player cheat flag EORI #true,PlayerCheats(A5) ;check/uncheck player cheats MOVE.L CheatMenuHndl(A5),-(SP) MOVE #1,-(SP) MOVE PlayerCheats(A5),-(SP) _CheckItem JMP ReturnFromMenu ;------------------- ComputerCheat ;------------------- ;toggle computer cheat flag EORI #true,ComputerCheats(A5) MOVE.L CheatMenuHndl(A5),-(SP) MOVE #2,-(SP) MOVE ComputerCheats(A5),-(SP) _CheckItem JMP ReturnFromMenu ;------------ CheatHelp ;------------ MOVE.L wPointer(A5),-(SP);unhilite current ; front wndw MOVE #false,-(SP) _HiliteWindow ;bring cheat wndw (which is the diff wndw) it to the front, ; show it and then make it the current port MOVE.L diffPointer(A5),-(SP) _BringToFront MOVE.L diffPointer(A5),-(SP) _ShowWindow MOVE.L diffPointer(A5),-(SP) _SetPort ;set up some static text MOVE #8,-(SP) ;text face = outline _TextFace LEA diffRect,A0 MOVE right(A0),windowWidth(A5) ;get right MOVE left(A0),D0 SUB D0,windowWidth(A5);subtract left LEA cheatString,A0 JSR CenterString MOVE D0,-(SP) ;h MOVE #28,-(SP);v _MoveTo PEA cheatString _DrawString MOVE #0,-(SP) ;text face = normal _TextFace ;draw some strings MOVE #30,-(SP) MOVE #65,-(SP) _MoveTo PEA inAnyCase1 _DrawString MOVE #30,-(SP) MOVE #90,-(SP) _MoveTo PEA inAnyCase2 _DrawString MOVE #30,-(SP) MOVE #115,-(SP) _MoveTo PEA inAnyCase3 _DrawString ;outline the default response ("OK") in the standard way LEA OKrect,A0 JSR DefaultOutline ;get user response CLR.L -(SP) ;nil ptr (no special filter routine) PEA itemHit(A5) _ModalDialog ;wait for response MOVE.L diffPointer(A5),-(SP) _HideWindow JMP ReturnFromMenu cheatString DC.B 8,'Cheating',0 inAnyCase1DC.B 33,'Anyone who is allowed to cheat is' inAnyCase2DC.B 32,'permitted to move on any square,',0 inAnyCase3DC.B 33,'even if someone is already there.' ;--------------------- inContentRegion ;--------------------- ;did they click in the game window? MOVE.L wPointer(A5),D0 CMP.L fndWindow(A5),D0 BNE @3;no, ignore click CMPI #true,GameOver(A5);is a game in ; progress? BEQ @3;no, ignore click ;player makes a move ;---------------------------- @10PEA Where ;where did they click? _GlobalToLocal MOVEQ #8,D3 ;check all 9 squares LEA BoardCond,A2 LEA BoardLocs,A3 @1 CLR -(SP) ;space for boolean result MOVE.L Where,-(SP) MOVE.L A3,-(SP) ;square coords _PtInRect;is pt in this square? MOVE (SP)+,D0 BNE @2;yes, we have a valid click ADDA #2,A2 ;update BoardCond pointer ADDA #8,A3 ;update BoardLocs pointer DBRA D3,@1 BRA @3;not in square -- ignore click ;if player cheating is allowed, skip check @2 CMPI #true,PlayerCheats(A5) BEQ @15 MOVE (A2),D0 ;check if taken BNE @3;if not empty then ignore click @15MOVE #circle,(A2) ;put the player there JSR DrawWindowContents JSR CheckWin ;check if player has won CMPI #true,D0 ;if player didn't win, then BNE @7; computer will make a move ADDQ #1,YouWon(A5) BRA @11 ;computer makes a move ;--------------------------------- ;if the board is full, then cats game @7 LEA BoardCond,A0 MOVEQ #8,D0 @4 MOVE (A0)+,D1 BEQ @5;if we get one empty, then stop DBRA D0,@4 BRA @12 ;cats game -- all positions used @5 CMPI #false,EasyFlag(A5) BNE @16 ;choose randomly if not thinking JSR ComputerThinks CMPI #true,MadeMove(A5);if we didn't move ; when thinking BEQ @14 ; then choose ; randomly @16LEA BoardCond,A2 @6 MOVEQ#9,D0 JSR RandomBounds SUBQ #1,D1 ;get a number in the range 0-8 ASL #1,D1 ;calc table offset CMPI #true,ComputerCheats(A5) BNE @18 ;if not cheating, ; make sure it's empty CMPI #square,(A2,D1) ;if cheating, make ;sure it's not a square. BEQ @6;yes, it's a square. ; try again. BRA @17 ;take it (it's a circle or empty) @18CMPI #empty,(A2,D1) BNE @6;this space is full. try again. @17MOVE #square,(A2,D1) ;put a square here. @14JSR DrawWindowContents JSR CheckWin ;check if computer has won CMPI #true,D0 BNE @8 ADDQ #1,MeWon(A5) BRA @11 @8 MOVEQ#8,D0 ;did comp make last ; possible move? LEA BoardCond,A0 ;look for empty positions @9 MOVE (A0)+,D1 BEQ @3;there's still space left, ; game not over DBRA D0,@9 @12ADDQ #1,Cats(A5);cats game @11MOVE #true,GameOver(A5) ;disable Reverse Positions MOVE.L GameMenuHndl(A5),-(SP) MOVE #2,-(SP) _DisableItem JSR DrawScoreWindow ;update score wndw @3 JMP GetEvent ;--------------------- ComputerThinks ;--------------------- ; output A2 = points to BoardCond ; D1 = 2 * square to move to (0-8) [BoardCond table offset] ; priority of checks: ; 1st - check if computer can win in next move ; 2nd - check if computer can block player win in next move ; 3rd - check if middle square is taken ; 4th - decide randomly ; computer's piece = square, player = circle MOVE #false,MadeMove(A5) ;check 8 ways of winning MOVEQ #7,D0 LEA offsets,A1 LEA BoardCond,A0 MOVEQ #square,D1 @1 MOVE (A1)+,A2 MOVE (A1)+,A3 MOVE (A1)+,A4 JSR CheckPosition CMPI #true,MadeMove(A5) BEQ @5 DBRA D0,@1 ;check 8 ways of blocking MOVEQ #7,D0 LEA offsets,A1 LEA BoardCond,A0 MOVEQ #circle,D1 @2 MOVE (A1)+,A2 MOVE (A1)+,A3 MOVE (A1)+,A4 JSR CheckPosition CMPI #true,MadeMove(A5) BEQ @5 DBRA D0,@2 ;check if middle square is taken CMPI #true,ComputerCheats(A5) BNE @3 CMPI #square,(A0,A3) BEQ @5 @4 MOVE #square,(A0,A3) MOVE #true,MadeMove(A5) BRA @5 @3 CMPI #empty,(A0,A3) BEQ @4 @5 RTS offsets DC0,6,12,2,8,14,4,10,16 DC0,2,4,6,8,10,12,14,16 DC0,8,16,4,8,12 MadeMoveDS1 ;------------------ CheckPosition ;------------------ ; used to see if any 2 of a (A0,A2), b (A0,A3) and c (A0,A4) are ; occupied by the computer. If so, it tries to take the 3rd (to ; make a win). If the computer is allowed to cheat, it takes the ; 3rd regardless of what's already there. On defense, this ; routine is used to check if the computer can take the 3rd ; square to block a player win. The boolean variable MadeMove ; reflects whether a move was made after this procedure has ; finished. CMP (A0,A2),D1 ;if a and b and (not c) BNE @2 CMP (A0,A3),D1 BNE @2 CMPI #true,ComputerCheats(A5) BNE @3 CMPI #square,(A0,A4) BEQ @2 @4 MOVE #square,(A0,A4) MOVE #true,MadeMove(A5) BRA @8 @3 CMPI #empty,(A0,A4) BEQ @4 @2 CMP (A0,A2),D1 ;if a and c and (not b) BNE @5 CMP (A0,A4),D1 BNE @5 CMPI #true,ComputerCheats(A5) BNE @6 CMPI #square,(A0,A3) BEQ @5 @7 MOVE #square,(A0,A3) MOVE #true,MadeMove(A5) BRA @8 @6 CMPI #empty,(A0,A3) BEQ @7 @5 CMP (A0,A3),D1 ;if b and c and (not a) BNE @8 CMP (A0,A4),D1 BNE @8 CMPI #true,ComputerCheats(A5) BNE @9 CMPI #square,(A0,A2) BEQ @8 @10MOVE #square,(A0,A2) MOVE #true,MadeMove(A5) BRA @8 @9 CMPI #empty,(A0,A2) BEQ @10 @8 RTS ;----------- keyDown ;----------- BTST #cmdKey,Modifiers ;was it a ; command key? BEQ @1;no, ignore keypress CLR.L -(SP) ;space for longint result MOVE Message+2,-(SP) ;key that was ; pressed _MenuKey MOVE (SP)+,menuChoiceID(A5) MOVE (SP)+,menuItem(A5) JMP DecodeMenuBar @1 JMP GetEvent ;-------- update ;-------- ; redraw the contents of the game window MOVE.L wPointer(A5),-(SP) _BeginUpdate JSR DrawWindowContents MOVE.L wPointer(A5),-(SP) _EndUpdate JMP GetEvent ;------- Finish ;------- ; release memory occupied windows and dialogs MOVE.L aboutPointer(A5),-(SP) _DisposDialog MOVE.L diffPointer(A5),-(SP) _DisposDialog MOVE.L sPointer(A5),-(SP) _DisposWindow MOVE.L wPointer(A5),-(SP) _DisposWindow RTS ;-------- resume ;-------- ; if the "resume" button is clicked, return to finder. ; this could be dangerous if the program seriously messed up ; RAM in some way _ExitToShell ;------------ CheckWin ;------------ ; check if someone has won (if there are 3 in a row of any ; non-blank) output: D0 = false if no win, true if won. LEA BoardCond,A0 MOVE (A0),D0 ;check row 1 AND 2(A0),D0 AND 4(A0),D0 BNE Win @1 MOVE 6(A0),D0 ;check row 2 AND 8(A0),D0 AND 10(A0),D0 BNE Win @2 MOVE 12(A0),D0;check row 3 AND 14(A0),D0 AND 16(A0),D0 BNE Win @3 MOVE (A0),D0 ;check column 1 AND 6(A0),D0 AND 12(A0),D0 BNE Win @4 MOVE 2(A0),D0 ;check column 2 AND 8(A0),D0 AND 14(A0),D0 BNE Win @5 MOVE 4(A0),D0 ;check column 3 AND 10(A0),D0 AND 16(A0),D0 BNE Win @6 MOVE (A0),D0 ;check neg slope diagonal AND 8(A0),D0 AND 16(A0),D0 BNE Win @7 MOVE 4(A0),D0 ;check pos slope diagonal AND 8(A0),D0 AND 12(A0),D0 ;at this point, if D0=0 then (D0=false and leave) else set ; D0=true BEQ NoWin WinMOVE #true,D0 NoWin RTS ;-------------------- RandomBounds ;-------------------- ; input D0 = max number to return ; output D1 = (random mod D0) + 1 (always positive) MOVE D0,-(SP) ;save D0 CLR -(SP) ;space for integer result _Random MOVE (SP)+,D1 ;get random num ANDI #$7FFF,D1;result = pos 15-bit num MOVE (SP)+,D0 ;restore D0 DIVS D0,D1 SWAP D1;remainder (MOD) is in low word ADDQ #1,D1 RTS ;----------------------- DrawScoreWindow ;----------------------- MOVE.L sPointer(A5),-(SP);make score ;wndw current port _SetPort ;erase old scores LEA scoreRect,A0 MOVE.L A0,-(SP) _EraseRect ;do computer's score MOVE #15,-(SP);set pen location MOVE #15,-(SP) _MoveTo PEA 'Me:' ;draw a string _DrawString MOVE.L strPointer(A5),A0 ;convert score to ; string CLR.L D0 MOVE MeWon(A5),D0 JSR NumToString CLR -(SP) ;right justify the number MOVE.L strPointer(A5),-(SP) _StringWidth MOVEQ #75,D0 SUB (SP)+,D0 MOVE D0,-(SP) ;h MOVE #15,-(SP);v _MoveTo MOVE.L strPointer(A5),-(SP);draw the score _DrawString ;do player's score MOVE #15,-(SP) MOVE #30,-(SP) _MoveTo PEA 'You:' _DrawString MOVE.L strPointer(A5),A0 CLR.L D0 MOVE YouWon(A5),D0 JSR NumToString CLR -(SP) MOVE.L strPointer(A5),-(SP) _StringWidth MOVEQ #75,D0 SUB (SP)+,D0 MOVE D0,-(SP) ;h MOVE #30,-(SP);v _MoveTo MOVE.L strPointer(A5),-(SP) _DrawString ;do cats games MOVE #15,-(SP) MOVE #45,-(SP) _MoveTo PEA 'Cats:' _DrawString MOVE.L strPointer(A5),A0 CLR.L D0 MOVE Cats(A5),D0 JSR NumToString CLR -(SP) MOVE.L strPointer(A5),-(SP) _StringWidth MOVEQ #75,D0 SUB (SP)+,D0 MOVE D0,-(SP) ;h MOVE #45,-(SP);v _MoveTo MOVE.L strPointer(A5),-(SP) _DrawString ;set game window to be the active port MOVE.L wPointer(A5),-(SP) _SetPort RTS scoreRect DC1,50,60,75 ;---------------------- GetRandomString ;---------------------- ; get a random number, then fall through to NumToString to ; covert to string CLR -(SP) ;space for integer result _Random CLR.L D0;set up D0 as longint for _NumToString MOVE (SP)+,D0 MOVE.L strPointer(A5),A0 ;addr of string ;---------------- NumToString ;---------------- ; convert a number to a string ; input D0 = number ; A0 = addr of string to put number in MOVE #0,-(SP) ;routine selector _Pack7 ;_Pack7 = _NumToString ;A0 now points to string with the number (unchanged from ; before) RTS ;--------------- CenterString: ;--------------- ; input A0 = addr of string ; windowWidth = width of window to center in ; (should be set before calling this routine) CLR -(SP) ;space for integer result MOVE.L A0,-(SP) _StringWidth MOVE windowWidth(A5),D0 SUB (SP)+,D0 ; D0 = offset to start printing ASR #1,D0 ;(windowWidth - StrWidth) div 2 RTS windowWidth:DS 1 ;------------------ DefaultOutline ;------------------ ;make a thick oval around a button to show that it is the default ; (the one that will be selected if RETURN or ENTER is ; pressed). ; input: A0 = pointer to the rect to draw around LEA workRect(A5),A1 ;make a copy of ; the rect MOVE.L (A0)+,(A1)+ MOVE.L (A0),(A1) PEA penState(A5) ;save penState _GetPenState MOVE #3,-(SP) ;make a fat pen MOVE #3,-(SP) _PenSize PEA workRect(A5) ;make rect bigger MOVE #-4,-(SP) MOVE #-4,-(SP) _InsetRect PEA workRect(A5) ;make the fat border MOVE #16,-(SP) MOVE #16,-(SP) _FrameRoundRect PEA penState(A5) ;restore penState _SetPenState RTS workRectDS4 penStateDS9 ;-------------- ; other stuff ;-------------- wTitle DC.B11,'Tic Tac Toe' wRect DC60,120,260,380 wPointerDS.L1 sTitle DC.B5,'Score' sRect DC60,401,112,491 sPointerDS.L1 aboutTitleDC.B 1,' ' aboutRect DC85,107,256,397 aboutPointerDS.L 1 aboutItemsHndl DC.L1 aboutItemsList DC3 ;number of items in list - 1 DC.L 0 ;button #1 OKrect DC145,213,165,278 DC.B 4 ;ctrlItem+butCtrl DC.B 2,'OK' DC.L 0 ;button #2 DC145,10,165,130 DC.B 4 ;ctrlItem+butCtrl DC.B 11,'That',39,'s nice',0 DC.L 0 ;Mike icon DC1,1,32,32 DC.B 160 ;iconItem+itemDisable DC.B 2 DC128 DC.L 0 ;Fishtree icon DC1,258,32,289 DC.B 160 ;iconItem+itemDisable DC.B 2 DC129 diffTitle DC.B 1,' ' diffRectDC85,107,256,397 diffPointer DS.L 1 diffItemsHndl DC.L1 diffItemsList DC0 ;number of items in list - 1 DC.L 0 ;button #1 DC145,213,165,278 DC.B 4 ;ctrlItem+butCtrl DC.B 2,'OK' EventRecord What DC0 Message DC.L0 When DC.L0 Where DC.L0 Modifiers DC0 AppleMenuHndl DS.L1 AppleMenuTitle DC.B1,20 AppleMenuAbout DC.B17,'About Tic Tac Toe' GameMenuHndlDS.L 1 GameMenuTitle DC.B4,'Game',0 GameMenuNew DC.B 10,'New Game/N',0 GameMenuRev DC.B 19,'Reverse Positions/R' GameMenuShowDC.B 12,'Show Score/S',0 GameMenuClear DC.B12,'Clear Scores',0 GameMenuLineDC.B 1,'-' GameMenuQuitDC.B 6,'Quit/Q',0 DiffMenuHndlDS.L 1 DiffMenuTitle DC.B10,'Difficulty',0 DiffMenuEasyDC.B 6,'Easy!',18,0 DiffMenuHardDC.B 4,'Hard',0 DiffMenuHelpDC.B 25,'What',39,'s with these levels?' CheatMenuHndl DS.L1 CheatMenuTitle DC.B6,'Cheats',0 CheatMenuYouDC.B 23,'Player Allowed to Cheat' CheatMenuMe DC.B 25,'Computer Allowed to Cheat' CheatMenuHelp DC.B25,'What',39,'s all this Cheating?' menuChoiceIDDS 1 menuItemDS1 strPointerDS.L 1 fndWindow DS.L 1 EasyFlagDS1 PlayerCheatsDS 1 ComputerCheats DS1 MeWon DS1 YouWon DS1 Cats DS1 GameOverDS1 resource 'ICON' 128 'MIKEFACE' DC.L $003FF800,$03FFFF80,$0FFFFFE0,$1FFFFFF0 DC.L $1FFFFFF0,$3E3FF878,$3803C038,$3000001C DC.L $70FC3F1C,$7303C0DC,$74624C3C,$7DF25F3C DC.L $7C652C3C,$3C05A03C,$3A08905C,$39F0CF98 DC.L $18004010,$08077010,$0407D820,$04000020 DC.L $04015020,$0406FA20,$04176D20,$021DFF20 DC.L $02100240,$011FFE40,$01080480,$0087F880 DC.L $00400100,$00200200,$00180C00,$0007F000 resource 'ICON' 129 'FISHTREE' DC.L $00670000,$0098E7C0,$03021830,$04010388 DC.L $0F008C08,$1878B008,$10848008,$13021F04 DC.L $14012084,$08194044,$08641C48,$08842210 DC.L $090C2110,$061010E0,$00100800,$00100800 DC.L $00200400,$0021C400,$00222400,$00200400 DC.L $0021C400,$00222400,$00100400,$00160800 DC.L $00120800,$00088800,$00085000,$00045000 DC.L $00045000,$0002A000,$0001C000,$00008000 RESOURCE FILE LINK FILE [ TicTacToe SnakeText GrowText /RESOURCES TicTacToe_rsrc $
- SPREAD THE WORD:
- Slashdot
- Digg
- Del.icio.us
- Newsvine