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 ;
1998-2013 Wolf Wejgaard