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

Summon your guild and prepare for war in...
Netmarble is making some pretty big moves with their latest update for Seven Knights Idle Adventure, with a bunch of interesting additions. Two new heroes enter the battle, there are events and bosses abound, and perhaps most interesting, a huge... | Read more »
Make the passage of time your plaything...
While some of us are still waiting for a chance to get our hands on Ash Prime - yes, don’t remind me I could currently buy him this month I’m barely hanging on - Digital Extremes has announced its next anticipated Prime Form for Warframe. Starting... | Read more »
If you can find it and fit through the d...
The holy trinity of amazing company names have come together, to release their equally amazing and adorable mobile game, Hamster Inn. Published by HyperBeard Games, and co-developed by Mum Not Proud and Little Sasquatch Studios, it's time to... | Read more »
Amikin Survival opens for pre-orders on...
Join me on the wonderful trip down the inspiration rabbit hole; much as Palworld seemingly “borrowed” many aspects from the hit Pokemon franchise, it is time for the heavily armed animal survival to also spawn some illegitimate children as Helio... | Read more »
PUBG Mobile teams up with global phenome...
Since launching in 2019, SpyxFamily has exploded to damn near catastrophic popularity, so it was only a matter of time before a mobile game snapped up a collaboration. Enter PUBG Mobile. Until May 12th, players will be able to collect a host of... | Read more »
Embark into the frozen tundra of certain...
Chucklefish, developers of hit action-adventure sandbox game Starbound and owner of one of the cutest logos in gaming, has released their roguelike deck-builder Wildfrost. Created alongside developers Gaziter and Deadpan Games, Wildfrost will... | Read more »
MoreFun Studios has announced Season 4,...
Tension has escalated in the ever-volatile world of Arena Breakout, as your old pal Randall Fisher and bosses Fred and Perrero continue to lob insults and explosives at each other, bringing us to a new phase of warfare. Season 4, Into The Fog of... | Read more »
Top Mobile Game Discounts
Every day, we pick out a curated list of the best mobile discounts on the App Store and post them here. This list won't be comprehensive, but it every game on it is recommended. Feel free to check out the coverage we did on them in the links below... | Read more »
Marvel Future Fight celebrates nine year...
Announced alongside an advertising image I can only assume was aimed squarely at myself with the prominent Deadpool and Odin featured on it, Netmarble has revealed their celebrations for the 9th anniversary of Marvel Future Fight. The Countdown... | Read more »
HoYoFair 2024 prepares to showcase over...
To say Genshin Impact took the world by storm when it was released would be an understatement. However, I think the most surprising part of the launch was just how much further it went than gaming. There have been concerts, art shows, massive... | Read more »

Price Scanner via MacPrices.net

You can save $300-$480 on a 14-inch M3 Pro/Ma...
Apple has 14″ M3 Pro and M3 Max MacBook Pros in stock today and available, Certified Refurbished, starting at $1699 and ranging up to $480 off MSRP. Each model features a new outer case, shipping is... Read more
24-inch M1 iMacs available at Apple starting...
Apple has clearance M1 iMacs available in their Certified Refurbished store starting at $1049 and ranging up to $300 off original MSRP. Each iMac is in like-new condition and comes with Apple’s... Read more
Walmart continues to offer $699 13-inch M1 Ma...
Walmart continues to offer new Apple 13″ M1 MacBook Airs (8GB RAM, 256GB SSD) online for $699, $300 off original MSRP, in Space Gray, Silver, and Gold colors. These are new MacBook for sale by... Read more
B&H has 13-inch M2 MacBook Airs with 16GB...
B&H Photo has 13″ MacBook Airs with M2 CPUs, 16GB of memory, and 256GB of storage in stock and on sale for $1099, $100 off Apple’s MSRP for this configuration. Free 1-2 day delivery is available... Read more
14-inch M3 MacBook Pro with 16GB of RAM avail...
Apple has the 14″ M3 MacBook Pro with 16GB of RAM and 1TB of storage, Certified Refurbished, available for $300 off MSRP. Each MacBook Pro features a new outer case, shipping is free, and an Apple 1-... Read more
Apple M2 Mac minis on sale for up to $150 off...
Amazon has Apple’s M2-powered Mac minis in stock and on sale for $100-$150 off MSRP, each including free delivery: – Mac mini M2/256GB SSD: $499, save $100 – Mac mini M2/512GB SSD: $699, save $100 –... Read more
Amazon is offering a $200 discount on 14-inch...
Amazon has 14-inch M3 MacBook Pros in stock and on sale for $200 off MSRP. Shipping is free. Note that Amazon’s stock tends to come and go: – 14″ M3 MacBook Pro (8GB RAM/512GB SSD): $1399.99, $200... Read more
Sunday Sale: 13-inch M3 MacBook Air for $999,...
Several Apple retailers have the new 13″ MacBook Air with an M3 CPU in stock and on sale today for only $999 in Midnight. These are the lowest prices currently available for new 13″ M3 MacBook Airs... Read more
Multiple Apple retailers are offering 13-inch...
Several Apple retailers have 13″ MacBook Airs with M2 CPUs in stock and on sale this weekend starting at only $849 in Space Gray, Silver, Starlight, and Midnight colors. These are the lowest prices... Read more
Roundup of Verizon’s April Apple iPhone Promo...
Verizon is offering a number of iPhone deals for the month of April. Switch, and open a new of service, and you can qualify for a free iPhone 15 or heavy monthly discounts on other models: – 128GB... Read more

Jobs Board

Relationship Banker - *Apple* Valley Financ...
Relationship Banker - Apple Valley Financial Center APPLE VALLEY, Minnesota **Job Description:** At Bank of America, we are guided by a common purpose to help Read more
IN6728 Optometrist- *Apple* Valley, CA- Tar...
Date: Apr 9, 2024 Brand: Target Optical Location: Apple Valley, CA, US, 92308 **Requisition ID:** 824398 At Target Optical, we help people see and look great - and Read more
Medical Assistant - Orthopedics *Apple* Hil...
Medical Assistant - Orthopedics Apple Hill York Location: WellSpan Medical Group, York, PA Schedule: Full Time Sign-On Bonus Eligible Remote/Hybrid Regular Apply Now Read more
*Apple* Systems Administrator - JAMF - Activ...
…**Public Trust/Other Required:** None **Job Family:** Systems Administration **Skills:** Apple Platforms,Computer Servers,Jamf Pro **Experience:** 3 + years of Read more
Liquor Stock Clerk - S. *Apple* St. - Idaho...
Liquor Stock Clerk - S. Apple St. Boise Posting Begin Date: 2023/10/10 Posting End Date: 2024/10/14 Category: Retail Sub Category: Customer Service Work Type: Part Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.