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 ;