; **************************************************************************
; ttt.asm     - A Tic-Tac-Toe game - A86 file - Barry Block 2:280/901.42
; ------------- EBO-BBS A'dam +31-20-6002828 (2:280/901.42)
; To play against computer, put ! on command line
;I have taken a Tic-Tac-Toe batchfile and used it as a guide to write an
;assembly version. 1620 byte com file. 2 humans or play against pc. Pc has
;logic to go first or second with some random choices.
;
;I had played with saving and restoring the screen mode before setting it to
;mode 3, but checking for every adapter etc. is just too much code for what
;it's worth, I thought. Does anyone want to share a really good routine to
;do this?
;
;So that no characters above 127 are posted, the board routine became cryptic.
;Hoping for suggestions as to how:
;1) AI could be implemented
;   (maybe too difficult because look ahead will be needed?)
;2) macro(s) can be used to handle "if else"
;3) lookup tables might be used with the macro(s) to shorten code
;
; -[version]-+-[dates           ]-+-[notes                                 ]-
;   v1.00      12/15/96  15:08:54   Tic-Tac-Toe v1 1/6
; **************************************************************************
CODE   segment byte public
                assume  cs:CODE, ds:CODE
		org	100h
tictactoe       proc    far
start:          jmp     main
include         \nxdos\src\toolbox.asm
main:           
                call field     ;draw board
                call fill      ;(saves bytes, allows tabs in field)
Top:            call get       ;get a valid keystroke or compute move
                call fill               ;put data in squares
                call check              ;look for a win
                call who                ;toggle turn, see if tie (9 moves)
                jmp  short Top          ;get next move

field:          mov ax,3                ;set video mode 3 (cls)
                int 10h
                mov dx,offset board
                mov ah,9
                int 21h                 ;display frame
                mov dx,1226h            ;move cursor to prompt position
                mov ah,2
                int 10h
                ret

fill:           ;fill square data into frame
                mov cx,0                ;pointer to arrays
@L1:            mov bh,0                ;page number
                mov di,offset spos      ;square data screen positions
                shl cx,1                ;mul 2 for word array
                add di,cx               ;array pointer
                mov dx,[di]             ;load row,col pos.
                mov ah,2                ;set cursor position
                int 10h
                shr cx,1                ;restore cx for byte array
                mov si,offset sq        ;square data array
                add si,cx               ;array pointer
                call wrt_char_atr       ;write char and attrib for X O -
                inc cx
                cmp cx,9
                jnz @L1
                call Show_turn          ;subroutine of "who" routine
                ret

wrt_char_atr:           ;different colors for - X O
                mov al,b[si]
                mov bh,0
                mov bl,11               ;bright cyan  X
                cmp al,'X'
                jz color
                mov bl,13               ;bright magenta  O
                cmp al,'O'
                jz color
                mov bl,7                ;white -
color:
                push cx
                mov cx,1                ;one char
                mov ah,9                ;write char and attrib
                int 10h
                pop cx
                ret

get:
                cmp turn,'X'            ;is it O's turn?
                jz Kboard               ;no.
                cmp b[82h],'!'          ;is "O" the computer?
                jnz Kboard              ;no.
                call pcplay
                xor ah,ah
                inc al
                jmp short valid
Kboard:
                mov ah,0
                int 16h                 ;get a keystroke
                cmp ah,16               ;want to quit? scan code for Q
                jz _Quit
                sub al,30h              ;convert to digit
                cmp al,1                ;is it 1 to 9?
                jb beep
                cmp al,9
                ja beep
valid:                  ;digit is valid
                mov si,offset sq        ;see if square is owned
                xor ah,ah
                dec ax                  ;to human first square is 1, to computer it's 0
                add si,ax
                cmp b[si],196
                jnz beep
                mov dl,turn             ;update squares array
                mov b[si],dl
                ret
Quit:
                mov dx,offset quitter
                jmp show
beep:                   ;bad keyboard input or pcplay error
                mov dl,7                ;send a ctrl-7 (bell) to alert player
                mov ah,2
                int 21h
                jmp short get           ;try again

check:
                inc trnct             ;update turn count     ;b
                cld                     ;waste a byte
                mov cx,3
                mov di,offset sq
                mov al,turn
                rep scasb
                jz winner               ;row win?
                mov cx,3
                mov di,offset sq+3
                rep scasb
                jz winner
                mov cx,3
                mov di,offset sq+6
                rep scasb
                jz winner
                mov di,offset sq        ;column 1 win?
                mov cx,3
@L2:            cmp b[di],al
                jnz C2
                add di,3
                loop @L2
                jmp short winner
C2:             mov di,offset sq+1      ;column 2 win?
                mov cx,3
@L3:            cmp b[di],al
                jnz C3
                add di,3
                loop @L3
                jmp short winner
C3:             mov di,offset sq+2      ;column 3 win?
                mov cx,3
@L4:            cmp b[di],al
                jnz D1
                add di,3
                loop @L4
                jmp short winner
D1:                     ;diagonal 1 win?
                mov di,offset sq
                mov cx,3
@L5:            cmp b[di],al
                jnz D2
                add di,4
                loop @L5
                jmp short winner
D2:                     ;diagonal 2 win?
                mov di,offset sq+2
                mov cx,3
@L6:            cmp b[di],al
                jnz no
                add di,2
                loop @L6
                jmp short winner
no:             ret

winner:
                mov dx,offset xwin      ;dx to win message
                cmp turn,'X'
                jz  show
                mov [xwin+2],'O'        ;poke O into message
                jmp short show

xo_tie:         mov dx,offset tie

show:                   ;display game result
                mov ah,9
                int 21h

                pop ax                  ;game over. play again?
                cmp dx,offset quitter   ;no.
                jnz again
Fine:
                mov ah,4ch              ;exit program
                int 21h
again:          mov dx,offset another
                mov ah,9
                int 21h                 ;ask
key:
                mov ah,0
                int 16h                 ;get answer
                cmp ah,21               ;y scan code
                jz Y
                cmp ah,49               ;n scan code
jz Fine                 ;exit
mov dl,7                ;Not y/n. Send a ctrl-7 (bell) to alert player
mov ah,2
int 21h
jmp short key
Y:
mov di,offset sq        ;clear square data
mov al,196
mov cx,9
rep stosb
mov trnct,0             ;set move zero
cmp first,'O'           ;toggle who goes first
jnz @O
mov turn,'X'
mov first,'X'
jz X
@O:
mov turn,'O'
mov first,'O'
X:
jmp start               ;start fresh game

who:                    ;show who's turn it is
cmp trnct,9             ;all squares filled?
jz  xo_tie              ;yes

cmp turn,'X'            ;toggle turn
jz  O
mov turn,'X'
jmp short Show_turn
O:
mov turn,'O'

Show_turn:              ;* This is also called by fill routine, too *
mov dx,1206h            ;update display turn prompt
mov ah,2                ;set cursor position
int 10h
mov dl,turn
mov ah,2                ;write char
int 21h
mov dx,1226h            ;move cursor to prompt position
mov ah,2                ;set cursor position
int 10h
ret

pcplay:                 ;first square is 0
cmp trnct,0             ;turn count, first move?
jnz sec
call rand
shl al,1                ;take corner or center on first move
ret
sec:
cmp trnct,1             ;on second move take center or corner
jnz NotSecond
mov al,4
cmp [sq+4],196          ;is center available?
jz _ret                 ;ret
@D2:
call rand               ;pick a corner, but not always the same one.
cmp al,2                ;if rand chose 2 try again (sq 5 is not a corner)
je @D2
shl al,1                ;corners are 0,2,6,8. perfect for 2X
_ret: ret
NotSecond:
cmp trnct,3             ;on forth move avoid two-way fork (X first)
jz Forth
jmp NotForth
Forth:
cmp [sq+5],'X'
jnz L
cmp [sq+6],'X'
jnz L
mov al,1
ret
L:
cmp [sq+7],'X'
jnz L0
cmp [sq+2],'X'
jnz L0
mov al,5
ret
L0:
mov al,3
cmp [sq],'X'
jnz L1
cmp [sq+8],'X'
jnz L1
ret
L1:
cmp [sq+2],'X'
jnz L2
cmp [sq+6],'X'
jnz L2
ret
L2:
cmp [sq],196            ;1
jnz L3
cmp [sq+3],'X'
jnz L3
cmp [sq+1],'X'
jnz L3
mov al,0
ret
L3:
cmp [sq+2],196          ;3
jnz L4
cmp [sq+1],'X'
jnz L4
cmp [sq+5],'X'
jnz L4
mov al,2
ret
L4:
cmp [sq+6],196          ;7
jnz L5
cmp [sq+7],'X'
jnz L5
cmp [sq+3],'X'
jnz L5
mov al,6
ret
L5:
cmp [sq+8],196          ;9
jnz L6
cmp [sq+5],'X'
jnz L6
cmp [sq+7],'X'
jnz L6
mov al,8
ret
L6:

NotForth:               ;deal with 2 character text strings
mov bx,4f4fh            ;"OO" can pc win?
call _Test
cmp al,10               ;was a square selected?
jnz @ret                ;yes.
mov bx,5858h            ;no. test 'XX' can human win?
call _Test
cmp al,10               ;was a square selected?
jnz @ret                ;yes.
mov bx,0c44fh           ;no. test '-O'
call _Test
cmp al,10               ;was a square selected?
jnz @ret                ;yes.
mov bx,04fc4h           ;no. test 'O-'
call _Test
cmp al,10               ;was a square selected?
jnz @ret                ;yes.
mov si,offset sq        ;nothing? Select first unused square.
mov cx,0
@N:
lodsb
cmp al,196
jz @N1
inc cx
jmp short @N
@N1:
mov ax,cx
@ret: ret

_Test:                  ;if a square is not claimed, check all stright lines
                mov al,10      ; that include this square. al,10 is a flag
                cmp [sq+4],196
                jnz @T2
                call @5        ;if O is first check center for win or block
                cmp al,10
                jz @T2
                ret
@T2:            cmp [sq],196            ;1 - Is square one occupied?
                jnz @T3                 ;no. check next square
                mov di,offset sq+1      ;yes. test row
                cmp [di],bx             ;test both char's
                jnz @T2b                ;keep looking for 2 of a kind. (OO or XX)
                mov al,0                ;al sq1
                ret                     ;ret. Match found.
@T2b:                   ;testing next stright line that uses target square
                cmp [sq+3],bl           ;half of the two char's
                jnz @T2c
                cmp [sq+6],bh           ;other half
                jnz @T2c
                mov al,0
                ret
@T2c:
                cmp [sq+4],bl
                jnz @T3
                cmp [sq+8],bh
                jnz @T3
                mov al,0
                ret
@T3:
                cmp [sq+2],196          ;3
                jnz @T4
                mov di,offset sq
                cmp [di],bx
                jnz @T3b
                mov al,2
                ret
@T3b:
                cmp [sq+5],bl
                jnz @T3c
                cmp [sq+8],bh
                jnz @T3c
                mov al,2
                ret
@T3c:
                cmp [sq+4],bl
                jnz @T4
                cmp [sq+6],bh
                jnz @T4
                mov al,2
                ret
@T4:
                cmp [sq+6],196          ;7
                jnz @T5
                mov di,offset sq+7
                cmp [di],bx
                jnz @T4b
                mov al,6
                ret
@T4b:
                cmp [sq],bl
                jnz @T4c
                cmp [sq+3],bh
                jnz @T4c
                mov al,6
                ret
@T4c:
                cmp [sq+4],bl
                jnz @T5
                cmp [sq+2],bh
                jnz @T5
                mov al,6
                ret
@T5:
                cmp [sq+8],196          ;9
                jnz @T6
                mov di,offset sq+6
                cmp [di],bx
                jnz @T5b
                mov al,8
                ret
@T5b:
                cmp [sq+2],bl
                jnz @T5c
                cmp [sq+5],bh
                jnz @T5c
                mov al,8
                ret
@T5c:
                cmp [sq],bl
                jnz @T6
                cmp [sq+4],bh
                jnz @T6
                mov al,8
                ret
@T6:
                cmp [sq+1],196          ;2
                jnz @T7
                cmp [sq],bl
                jnz @T6b
                cmp [sq+2],bh
                jnz @T6b
                mov al,1
                ret
@T6b:
                cmp [sq+4],bl
                jnz @T7
                cmp [sq+7],bh
                jnz @T7
                mov al,1
                ret
@T7:
                cmp [sq+3],196          ;4
                jnz @T8
                mov di,offset sq+4
                cmp [di],bx
                jnz @T7b
                mov al,3
                ret
@T7b:
                cmp [sq],bl
                jnz @T8
                cmp [sq+6],bh
                jnz @T8
                mov al,3
                ret
@T8:
                cmp [sq+5],196          ;6
                jnz @T9
                mov di,offset sq+3
                cmp [di],bx
                jnz @T8b
                mov al,5
                ret
@T8b:
                cmp [sq+2],bl
                jnz @T9
                cmp [sq+8],bh
                jnz @T9
                mov al,5
                ret
@T9:
                cmp [sq+7],196          ;8
                jnz @T
                cmp [sq+6],bl
                jnz @T9b
                cmp [sq+8],bh
                jnz @T9b
                mov al,7
                ret
@T9b:
                cmp [sq+1],bl
                jnz @T
                cmp [sq+4],bh
                jnz @T
                mov al,7
@T:             ret

@5:                     ;check center if O goes first for bx==XX  and bx==OO
                cmp bx,5858h
                jz @5a
                cmp bx,4f4fh
                jz @5a
                ret
@5a:            mov al,4                ;temp set al,4
                cmp [sq],bl             ;1 9
                jnz @5b
                cmp [sq+8],bh
                jnz @5b
                ret
@5b:                    ;3 7
                cmp [sq+2],bl
                jnz @5c
                cmp [sq+6],bh
                jnz @5c
                ret
@5c:                    ;2 8
                cmp [sq+1],bl
                jnz @5d
                cmp [sq+7],bh
                jnz @5d
                ret
@5d:                    ;4 6
                cmp [sq+3],bl
                jnz @5e
                cmp [sq+5],bh
                jnz @5e
                ret
@5e:                    ;reset flag al,10
                mov al,10
                ret

rand:                                   ;my simplest rand routine
                mov ax,2C00h            ;Request function 2Ch.
                int 21h                 ;Dos's Get System Time.
                or  ax,1                ;make ax odd
                mul dx                  ;mix well
                mov ah,0                ;avoid divide overflow
                mov dx,5                ;Limit + 1
                div dl
                mov al,ah               ;Remainder in al
ret
exit:           nop
                dosexit
                int 20h
tictactoe       endp

another         db 'Play again? y/n $'
quitter         db 13,10,'Quitting Tic-Tac-Toe',13,10,36
xwin            db 13,10,'X has WON!',13,10,36
tie             db 13,10,'X and O have tied.',13,10,36
b               db 0 
trnct           db 0
turn            db 'X'
first           db 'X'
sq              db 9 dup (196)          ;square data array
spos            dw 0506h,0511h,051ch    ;square data screen positions
                dw 0906h,0911h,091ch
                dw 0d06h,0d11h,0d1ch
board           db 13,10,10
                db 9,      '   Tic-Tac-Toe',13,10,10
                db '  1        ',179,' 2        ',179,' 3',13,10
                db 9,      '   ',179,'          ',179,    ,13,10
                db 9,      '   ',179,'          ',179,    ,13,10
                db 11 dup (196),197,10 dup (196),197,10 dup (196),13,10
                db '  4        ',179,' 5        ',179,' 6',13,10
                db 9,      '   ',179,'          ',179,    ,13,10
                db 9,      '   ',179,'          ',179,    ,13,10
                db 11 dup (196),197,10 dup (196),197,10 dup (196),13,10
                db '  7        ',179,' 8        ',179,' 9',13,10
                db 9,      '   ',179,'          ',179,    ,13,10
                db 9,      '   ',179,'          ',179,    ,13,10,10
                db 9,      '     Q = Quit'                ,13,10,10
                db "It is  's turn to pick a square (1-9) ",13,10,10,36
CODE   ends
       end start
