HolonJ Forth
Using HolonJ
Clock
Tic Tac Toe

Java
Forth

Tic Tac Toe in HolonJForth

Module TicTacToe

This game was written by Arthur van Hoff in Java and is included as a
demo program with the JDK 1.1. See jdk1.1/demo/tictactoe.
The program has been converted to HolonJForth by Wolf Wejgaard. 

Groups

 1. Class & Fields

 2. Init

 3. Moves

 4. Display

 5. Mouse

 6. TestAsApp



Class & Fields

class: TicTacToe  extends Applet  implements MouseListener 

\ White's current position, the computer is white. 
int white 

\ Black's current position, the user is black. 
int black 

\ Order of importance: 4 0 2 6 8 1 3 5 7. 
static int [] moves 

static boolean [] won 

\ Who goes first in the next game? 
boolean first 

511 final static int DONE 

0 final static int OK 

1 final static int WIN 

2 final static int LOSE 

3 final static int STALEMATE 


Init

i: tictactoe.<init>   ( -- )
     initsuper ()
     true =: first  ; 

\ Inserts the squares in order of importance. 
: initMoves  ( -- )
     9 new moves  
     4 =: 0 moves  0 =: 1 moves  2 =: 2 moves
     6 =: 3 moves  8 =: 4 moves  1 =: 5 moves  
     3 =: 6 moves  5 =: 7 moves  7 =: 8 moves  ; 

\ Mark all positions with these bits set as winning. 
: isWon   ( int pos -- )
     DONE 0 
     do   i pos & pos = 
          if  true =: i won  then  
     loop  ; 

\ Replaces " 1 i << ". Creates integer with binary bit i set = 2^i. 
: bitpos   ( int b -- int )
     1 b <<  ; 

: wins   ( int s1 int s2 int s3 -- )
     s1 bitpos s2 bitpos s3 bitpos or or isWon  ; 

\ Fill in the winning positions. 
: initWon  ( -- )
     512 new won                             \ 1 9 <<

     0 1 2 wins  3 4 5 wins  6 7 8 wins      \ horizontal
     0 3 6 wins  1 4 7 wins  2 5 8 wins      \ vertical
     0 4 8 wins  2 4 6 wins  ;               \ diagonal 

\ Called when the class is loaded. 
: tictactoe.<clinit>   ( -- )
     initMoves initWon  ; 


Moves

i: isFree   ( int m -- boolean )
     m bitpos white and 0 = m bitpos black and 0 = and  ; 

\ Returns true if move m makes white win. 
i: whiteWins   ( int m -- boolean )
     m bitpos white or won  ; 

\ Returns true if move m gives black a chance to win. 
i: blackWins   ( int m -- boolean )
     int pw  m bitpos white or =: pw
     9 0 do
          i bitpos pw and 0 = i bitpos black and 0 = and 
          if  i bitpos black or won if true return then  then
     loop
     false  ; 

\ Computes the best move for white. If there is no winning move, and if
\ all moves let black win, returns the first free move. 
i: bestMove   ( -- int )
     9 0 do i isFree i whiteWins and if i return then 
     loop  
     9 0 do i moves isFree i moves blackWins not and
            if i moves return then  
     loop  
     9 0 do i moves isFree if i moves return then 
     loop  
     DONE  ; 

\ Computer move, returns true if legal. 
i: doMyMove   ( -- boolean )
     black white or DONE = if false return then
     bestMove bitpos white or =: white  
     true  ; 

\ User move, returns true if legal. 
i: yourMove   ( int m -- boolean )
     m 0 <  m 8 > or if false return then
     black white or  m bitpos  and 0 <> if false return then
     m bitpos black or =: black  
     true  ; 

\ Figure what the status of the game is. 
i: status   ( -- int )
     white won if WIN return then
     black won if LOSE return then
     white black or DONE = if STALEMATE return then
     OK  ; 


Display

Image whiteImage 

Image blackImage 

int xoff 

int yoff 

i: drawLines   ( Graphics g Dimension d -- )
     xoff    0       xoff     d height  g  drawLine
     xoff 2* 0       xoff 2*  d height  g  drawLine
     0       yoff    d width  yoff      g  drawLine
     0       yoff 2* d width  yoff 2*   g  drawLine  ; 

i: paintImage   ( Image i int r int c Graphics g -- )
     i  r xoff * 10 +  c yoff * 10 +  this g drawImage drop  ; 

\ i is the index of the inner loop, j is the index of the outer loop. 
i: paintImages  ( Graphics g -- )
     int m  0 =: m
     3 0 do
          3 0 do
               m bitpos white and 
               if   whiteImage i j g PaintImage
               else m bitpos black and        
                    if  blackImage i j g PaintImage  then 
               then 1 += m    
          loop 
     loop  ; 

\ Overwrites Frame.paint(). Called whenever the frame is created, moved
\ or resized. 
i: paint   ( Graphics g --  )
     Dimension d  
     this getSize =: d    
     colorBlack g setColor
     d width 3 / =: xoff  
     d height 3 / =: yoff 
     g d drawLines  
     g paintImages ; 


Mouse

boolean isApplet 

i: sound   ( string file -- )
     isApplet
     if   getCodeBase file play  
     else file type space white . black . status . cr
     then  ; 

i: NewGame   ( -- )
     " audio/return.au" sound 
     0 =: white 0 =: black
     first if random 9 i>d d* d>i bitpos =: white then
     first not =: first 
     this repaint  ; 

\ Returns true if x,y is a correct move. 
i: isCorrectMove   ( int x int y -- boolean )
     Dimension d  this getSize =: d 
     x 3 * d width  /                   \ row
     y 3 * d height /                   \ column
     3 * + yourMove  ; 

i: myMove   ( -- )
     doMyMove 
     if   this repaint status case
          WIN of " audio/yahoo1.au" sound endof
          LOSE of " audio/yahoo2.au" sound endof
          STALEMATE of endof
          drop " audio/ding.au" sound endcase
     else " audio/beep.au" sound 
     then  ; 

i: evaluate   ( -- )
     status case
     WIN of " audio/yahoo1.au" sound endof
     LOSE of " audio/yahoo2.au" sound endof
     STALEMATE of endof
     drop myMove endcase ; 

\ The user has clicked in the applet. Status is OK until a game is
\ finished. 
i: mouseReleased   ( mouseEvent e -- )
     status OK = 
     if   e getX e getY isCorrectMove 
          if   this repaint  evaluate 
          else " audio/beep.au" sound
          then
     else NewGame 
     then  ; 

i: mousePressed   ( mouseEvent e -- )    ; 

i: mouseClicked   ( mouseEvent e -- )    ; 

i: mouseEntered   ( mouseEvent e -- )    ; 

i: mouseExited   ( mouseEvent e -- )    ; 

i: getAppletInfo   ( -- string )
     " TicTacToe by Arthur van Hoff"  ; 

\ Initialize the applet. Load images. 
i: TicTacToe.init   ( -- )
     getCodebase " images/not.gif" getImage =: whiteImage
     getCodebase " images/cross.gif" getImage =: blackImage
     this this addMouseListener
     true =: isApplet  ; 



TestAsApp

\ The applet can be turned into an application without modifications in
\ principle. However, sounds are not supported by applications and
\ generate an error message. We replace the sound by its filename.

i: tttApplication   ( -- )
     " images/not.gif" getImageFile =: whiteImage
     " images/cross.gif" getImageFile =: blackImage
     Frame app " Tic Tac Toe" new app ( string -- ) 
     this app addComponent drop
     this this addMouselistener
     200 200 app setSize
     app showWindow  ; 

\ TicTacToe as an application. No sounds. 
: ttt.main   ( String [] args -- )
     tictactoe t  new t () 
     t tttApplication  ;