// race.java implements the proverbial race between the Tortoise // and the hare. The tortoise is slow but sure, and the hare // is quick but suffering from attention deficit disorder, // he can't focus on the race at hand. ;) To spice things up the turtle // nips the hare in the butt with his sharp, sharp teeth whenever he's // close enough to his arch-nemesis the bunny. // so anyway, the position and height of both the tortoise and hare are // updated by synchronized threads. The methods are locked so that only one // thread can update position at a time. this simulates the ticking of a clock. // with that much said, let's dig into it import java.awt.*; import java.applet.Applet; // the main class public class race extends Applet implements Runnable { // locations of the hare and tortoise static int harePosition = 72; static int tortHeight = 5; static int tortPosition = 0; static int hareHeight = 111; static Image tortImage; // your basic turtle static Image hareImage; // your basic hare static Image boardImage; // the background, a game board static Image tortBite; // your basic turtle biting your basic hare static Image hareBite; // your basic hair getting it's butt bit off private Thread thread; // the thread that repaints() private Image buffer; // image object for double buffering private Graphics gOffScreen; // graphics object for double buffering static boolean someOneHasOne = false; // has any one won the race yet? static int raceLength = 350; // the race is this long public void init() { // load all the images tortImage = getImage ( getDocumentBase() , "tort.gif" ); tortBite = getImage ( getDocumentBase() , "tortbite.gif" ); hareImage = getImage ( getDocumentBase() , "hare.gif" ); hareBite = getImage ( getDocumentBase() , "harebite.gif" ); boardImage = getImage ( getDocumentBase() , "board.gif" ); setBackground( Color.white ); setForeground( Color.white ); //get everything set for the double buffering buffer = createImage( getSize().width, getSize().height ); // Get off-screen graphics context: gOffScreen = buffer.getGraphics(); } public void start() { if ( thread == null ) { thread = new Thread( this ); thread.start(); } // construct the threads, and get em started isReady ready = new isReady(); makeReady mReady = new makeReady ( ready ); makeUnReady mUReady = new makeUnReady ( ready ); mReady.start(); mUReady.start(); } // Override java.awt.Component.update public void update ( Graphics g ) { paint (g); } // Override java.awt.Component.paint public void paint ( Graphics g ) { // Fill background: gOffScreen.setColor( getBackground() ); // fill with background gOffScreen.fillRect( 0, 0, getSize().width, getSize().height ); // draw the gameboard gOffScreen.drawImage ( boardImage , 0 , 0 , this ); // if the turtle and hare are close enough BITE 'EM // but not if the race hasn't started yet, okay. if ((tortPosition >= (harePosition - 72 - 6)) && (tortPosition <= (harePosition - 72 + 6)) && (tortPosition > 0) ) { gOffScreen.drawImage ( tortBite , tortPosition , tortHeight , this ); gOffScreen.drawImage ( hareBite , harePosition , hareHeight , this ); g.drawImage( buffer, 0, 0, this ); } // if they aren't fighting, draw this normal images else { gOffScreen.drawImage ( tortImage , tortPosition , tortHeight , this ); gOffScreen.drawImage ( hareImage , harePosition , hareHeight , this ); } // Draw the buffer in the applet window: g.drawImage( buffer, 0, 0, this ); } // Implement java.lang.Runnable.run: public void run() { // this thread repaints frequently while ( thread != null ) { repaint(); try { Thread.sleep(20); } catch ( InterruptedException e ) { }; // Do nothing } } } // this class calls the set ready method that keeps updating the turtle info class makeReady extends Thread { private isReady holdIt; public makeReady ( isReady r ) { holdIt = r; } public void run () { try { sleep( (int) ( 10000 ) ); } catch( InterruptedException e ) { System.err.println( "Exception " + e.toString() ); } while ( !(race.someOneHasOne) ) { holdIt.setReady ( ); // sleep for a random interval try { sleep( (int) ( Math.random() * 100 ) ); } catch( InterruptedException e ) { System.err.println( "Exception " + e.toString() ); } } } } // this thread calls the get ready method which keeps the hare up to date class makeUnReady extends Thread { private isReady holdIt; public makeUnReady ( isReady r ) { holdIt = r ; } public void run() { try { sleep( (int) ( 10000 ) ); } catch( InterruptedException e ) { System.err.println( "Exception " + e.toString() ); } holdIt.getReady(); while ( !(race.someOneHasOne) ) { // sleep for a random interval try { sleep( (int) ( Math.random() * 100 ) ); } catch( InterruptedException e ) { System.err.println ( "Exception " + e.toString() ); } holdIt.getReady(); } } } // contains those setready and getready methods, for keeping tabs // on the animals, uh, competitors class isReady { // the lock private boolean writeable = true; // this method deals with that pesky turtle public synchronized void setReady ( ) { // the lock! while ( !writeable ) { try { wait(); } catch ( InterruptedException e ) { System.err.println ( "Exception: " + e.toString() ); } } // the amount by which we will update the hare's // position is randomized int delta = 0; double dDelta = ( Math.random() * 6 ); if ( dDelta <= 5 ) delta = 0; else delta = 9; race.harePosition+=delta; // the amount by which we will update the hare's // height is randomized, this keep's the animal // bouncing up and down dDelta = ( Math.random() * 5 ); race.hareHeight += ((int) dDelta) -2; // the next two ifs are out of bounds checks if (race.hareHeight > 112 ) race.hareHeight-=2; if (race.hareHeight < 108 ) race.hareHeight+=2; // check to see if someone has won yet if (race.harePosition >= ( race.raceLength + 72 ) ) race.someOneHasOne = true; // set the lock so the other thread can run writeable = false; // notify it to run notify(); } public synchronized void getReady() { // again, the lock. while ( writeable ) { try { wait(); } catch ( InterruptedException e ) { System.err.println( "Exception: " + e.toString() ); } } // the amount by which we will update the tortoise's // position is randomized int delta = (int) ( Math.random() * 2 ); race.tortPosition+=++delta; // the amount by which we will update the tort's // height is randomized, this keep's the animal // bouncing up and down double dDelta = ( Math.random() * 5 ); race.tortHeight += ((int) dDelta) -2; // the next two ifs are out of bounds checks if (race.tortHeight > 10 ) race.tortHeight-=2; if (race.tortHeight < 0 ) race.tortHeight+=2; // check to see if someone has won yet if (race.tortPosition > race.raceLength ) race.someOneHasOne = true; // set the lock so the other thread can run writeable = true; // notify it to run notify(); } }