TweetFollow Us on Twitter

Tic Tac Toe
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

$

 

Community Search:
MacTech Search:

Software Updates via MacUpdate

Latest Forum Discussions

See All

Tokkun Studio unveils alpha trailer for...
We are back on the MMORPG news train, and this time it comes from the sort of international developers Tokkun Studio. They are based in France and Japan, so it counts. Anyway, semantics aside, they have released an alpha trailer for the upcoming... | Read more »
Win a host of exclusive in-game Honor of...
To celebrate its latest Jujutsu Kaisen crossover event, Honor of Kings is offering a bounty of login and achievement rewards kicking off the holiday season early. [Read more] | Read more »
Miraibo GO comes out swinging hard as it...
Having just launched what feels like yesterday, Dreamcube Studio is wasting no time adding events to their open-world survival Miraibo GO. Abyssal Souls arrives relatively in time for the spooky season and brings with it horrifying new partners to... | Read more »
Ditch the heavy binders and high price t...
As fun as the real-world equivalent and the very old Game Boy version are, the Pokemon Trading Card games have historically been received poorly on mobile. It is a very strange and confusing trend, but one that The Pokemon Company is determined to... | Read more »
Peace amongst mobile gamers is now shatt...
Some of the crazy folk tales from gaming have undoubtedly come from the EVE universe. Stories of spying, betrayal, and epic battles have entered history, and now the franchise expands as CCP Games launches EVE Galaxy Conquest, a free-to-play 4x... | Read more »
Lord of Nazarick, the turn-based RPG bas...
Crunchyroll and A PLUS JAPAN have just confirmed that Lord of Nazarick, their turn-based RPG based on the popular OVERLORD anime, is now available for iOS and Android. Starting today at 2PM CET, fans can download the game from Google Play and the... | Read more »
Digital Extremes' recent Devstream...
If you are anything like me you are impatiently waiting for Warframe: 1999 whilst simultaneously cursing the fact Excalibur Prime is permanently Vault locked. To keep us fed during our wait, Digital Extremes hosted a Double Devstream to dish out a... | Read more »
The Frozen Canvas adds a splash of colou...
It is time to grab your gloves and layer up, as Torchlight: Infinite is diving into the frozen tundra in its sixth season. The Frozen Canvas is a colourful new update that brings a stylish flair to the Netherrealm and puts creativity in the... | Read more »
Back When AOL WAS the Internet – The Tou...
In Episode 606 of The TouchArcade Show we kick things off talking about my plans for this weekend, which has resulted in this week’s show being a bit shorter than normal. We also go over some more updates on our Patreon situation, which has been... | Read more »
Creative Assembly's latest mobile p...
The Total War series has been slowly trickling onto mobile, which is a fantastic thing because most, if not all, of them are incredibly great fun. Creative Assembly's latest to get the Feral Interactive treatment into portable form is Total War:... | Read more »

Price Scanner via MacPrices.net

Early Black Friday Deal: Apple’s newly upgrad...
Amazon has Apple 13″ MacBook Airs with M2 CPUs and 16GB of RAM on early Black Friday sale for $200 off MSRP, only $799. Their prices are the lowest currently available for these newly upgraded 13″ M2... Read more
13-inch 8GB M2 MacBook Airs for $749, $250 of...
Best Buy has Apple 13″ MacBook Airs with M2 CPUs and 8GB of RAM in stock and on sale on their online store for $250 off MSRP. Prices start at $749. Their prices are the lowest currently available for... Read more
Amazon is offering an early Black Friday $100...
Amazon is offering early Black Friday discounts on Apple’s new 2024 WiFi iPad minis ranging up to $100 off MSRP, each with free shipping. These are the lowest prices available for new minis anywhere... Read more
Price Drop! Clearance 14-inch M3 MacBook Pros...
Best Buy is offering a $500 discount on clearance 14″ M3 MacBook Pros on their online store this week with prices available starting at only $1099. Prices valid for online orders only, in-store... Read more
Apple AirPods Pro with USB-C on early Black F...
A couple of Apple retailers are offering $70 (28%) discounts on Apple’s AirPods Pro with USB-C (and hearing aid capabilities) this weekend. These are early AirPods Black Friday discounts if you’re... Read more
Price drop! 13-inch M3 MacBook Airs now avail...
With yesterday’s across-the-board MacBook Air upgrade to 16GB of RAM standard, Apple has dropped prices on clearance 13″ 8GB M3 MacBook Airs, Certified Refurbished, to a new low starting at only $829... Read more
Price drop! Apple 15-inch M3 MacBook Airs now...
With yesterday’s release of 15-inch M3 MacBook Airs with 16GB of RAM standard, Apple has dropped prices on clearance Certified Refurbished 15″ 8GB M3 MacBook Airs to a new low starting at only $999.... Read more
Apple has clearance 15-inch M2 MacBook Airs a...
Apple has clearance, Certified Refurbished, 15″ M2 MacBook Airs now available starting at $929 and ranging up to $410 off original MSRP. These are the cheapest 15″ MacBook Airs for sale today at... Read more
Apple drops prices on 13-inch M2 MacBook Airs...
Apple has dropped prices on 13″ M2 MacBook Airs to a new low of only $749 in their Certified Refurbished store. These are the cheapest M2-powered MacBooks for sale at Apple. Apple’s one-year warranty... Read more
Clearance 13-inch M1 MacBook Airs available a...
Apple has clearance 13″ M1 MacBook Airs, Certified Refurbished, now available for $679 for 8-Core CPU/7-Core GPU/256GB models. Apple’s one-year warranty is included, shipping is free, and each... Read more

Jobs Board

Seasonal Cashier - *Apple* Blossom Mall - J...
Seasonal Cashier - Apple Blossom Mall Location:Winchester, VA, United States (https://jobs.jcp.com/jobs/location/191170/winchester-va-united-states) - Apple Read more
Seasonal Fine Jewelry Commission Associate -...
…Fine Jewelry Commission Associate - Apple Blossom Mall Location:Winchester, VA, United States (https://jobs.jcp.com/jobs/location/191170/winchester-va-united-states) Read more
Seasonal Operations Associate - *Apple* Blo...
Seasonal Operations Associate - Apple Blossom Mall Location:Winchester, VA, United States (https://jobs.jcp.com/jobs/location/191170/winchester-va-united-states) - Read more
Hair Stylist - *Apple* Blossom Mall - JCPen...
Hair Stylist - Apple Blossom Mall Location:Winchester, VA, United States (https://jobs.jcp.com/jobs/location/191170/winchester-va-united-states) - Apple Blossom Read more
Cashier - *Apple* Blossom Mall - JCPenney (...
Cashier - Apple Blossom Mall Location:Winchester, VA, United States (https://jobs.jcp.com/jobs/location/191170/winchester-va-united-states) - Apple Blossom Mall Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.