import java.awt.*; import java.awt.event.*; import java.awt.image.BufferedImage; class PongExample extends Frame implements ActionListener, KeyEventDispatcher { volatile static Object syncAll=new Object(); volatile Button btnStart=new Button("Start Game"); volatile Button btnStop=new Button("Stop Game"); volatile PongCanvas canvasPong=new PongCanvas(); volatile BallMoveThread thrBall=new BallMoveThread(); volatile ArtificialIntelligenceThread thrAI=new ArtificialIntelligenceThread(); public static void main(String args[]) { try { PongExample pFrame=new PongExample(); Dimension dimScreen=Toolkit.getDefaultToolkit().getScreenSize(); pFrame.setSize(dimScreen.width, dimScreen.height-40); pFrame.setVisible(true); } catch(Exception ex) { ex.printStackTrace(); } } PongExample() { super("Pong"); setIconImage(new BufferedImage(1, 1, BufferedImage.TYPE_INT_RGB)); addWindowListener(new WindowAdapter() { public void windowClosing(WindowEvent we) { System.exit(0); } }); KeyboardFocusManager manager=KeyboardFocusManager.getCurrentKeyboardFocusManager(); manager.addKeyEventDispatcher(this); Panel tempPan=new Panel(); tempPan.add(btnStart); btnStart.addActionListener(this); tempPan.add(btnStop); btnStop.addActionListener(this); add("North", tempPan); add("Center", canvasPong); thrBall.start(); thrAI.start(); } public void actionPerformed(ActionEvent ae) { Object evSource=ae.getSource(); if(evSource==btnStart) { canvasPong.startGame(); } else if(evSource==btnStop) { canvasPong.blnUserWins=true; canvasPong.stopGame(); canvasPong.repaint(); } } public boolean dispatchKeyEvent(KeyEvent ke) { Object evSource=ke.getSource(); if(evSource instanceof TextField) return false; else if(evSource instanceof TextArea) return false; if(ke.getID()==KeyEvent.KEY_PRESSED) { int keyCode=ke.getKeyCode(); if(keyCode==KeyEvent.VK_UP) { canvasPong.arrowUp(); } else if(keyCode==KeyEvent.VK_DOWN) { canvasPong.arrowDown(); } } return true; } class PongCanvas extends Canvas { volatile int intSliderWidth=20; volatile int intSliderHeight=150; volatile int intUserSliderPosition=-1; volatile int intAISliderPosition=-1; volatile int intUserSliderPositionIncrement=10; volatile int intBallWidth=11; volatile int intBallHeight=11; volatile double dblBallX=0.0d; volatile double dblBallY=0.0d; volatile double dblBallMoveX=0.0d; volatile double dblBallMoveY=0.0d; volatile int intEdgeWidth=10; volatile boolean blnUserWins=true; volatile boolean blnGameActive=false; PongCanvas() { super(); } public void startGame() { stopGame(); Dimension dimCanvas=getSize(); //Set the user and AI slider positions to the middle of the screen intUserSliderPosition=dimCanvas.height/2-intSliderHeight/2; intAISliderPosition=dimCanvas.height/2-intSliderHeight/2; //Set the ball position to the middle of the screen dblBallX=(double)(dimCanvas.width/2); dblBallY=(double)(dimCanvas.height/2); double dblMoveX=Math.random(); //Don't let the ball move x be too small of a number if(dblMoveX<=0.1d) dblMoveX=0.1d; //Make dblMoveY smaller than dblMoveX double dblMoveY=dblMoveX/2.0d*Math.random(); //Don't let the ball move y be too small of a number if(dblMoveY<=0.05d) dblMoveY=0.05d; //Half of the time dblMoveX is positive and half of the time dblMoveX is negative if(Math.random()>0.5d) dblMoveX=-dblMoveX; //Half of the time dblMoveY is positive and half of the time dblMoveY is negative if(Math.random()>0.5d) dblMoveY=-dblMoveY; //Get the magnitude of the vector formed by dblMoveX and dblMoveY double dblDistance=Math.sqrt(Math.pow(dblMoveX, 2.0d)+Math.pow(dblMoveY, 2.0d)); //Make the vector formed by dblMoveX and dblMoveY into a unit vector dblMoveX=dblMoveX/dblDistance; dblMoveY=dblMoveY/dblDistance; dblBallMoveX=dblMoveX; dblBallMoveY=dblMoveY; //Scale up the vector formed by dblBallMoveX and dblBallMoveY up by a factor of 10 dblBallMoveX*=10.0d; dblBallMoveY*=10.0d; //If the ball is moving toward the AI's side of the screen then have the AI compute where the ball will meet with the AI's slider if(dblMoveX>0.0d) { thrAI.computeSliderMove(); } else { //The ball isn't moving toward the AI, so don't have the AI move thrAI.intAISliderPositionStop=intAISliderPosition; } //Set the boolean so that the game is marked as active which is evaluated in "paint" of PongCanvas and "run" of BallMoveThread blnGameActive=true; repaint(); } public void stopGame() { thrAI.stopSliderMove(); blnGameActive=false; } public void arrowUp() { synchronized(syncAll) { if((intUserSliderPosition-intUserSliderPositionIncrement)<0) intUserSliderPosition=0; else intUserSliderPosition=intUserSliderPosition-intUserSliderPositionIncrement; } repaint(); } public void arrowDown() { synchronized(syncAll) { if((intUserSliderPosition+intSliderHeight+intUserSliderPositionIncrement)>getSize().height) intUserSliderPosition=getSize().height-intSliderHeight; else intUserSliderPosition=intUserSliderPosition+intUserSliderPositionIncrement; } repaint(); } public void paint(Graphics graph) { Dimension dimCanvas=getSize(); if(!blnGameActive) { //If blnGameActive is false then print out who won in the middle of the screen if(blnUserWins) { String strWins="User Wins"; FontMetrics fMetr=graph.getFontMetrics(); int intWinsWidth=fMetr.stringWidth(strWins); int intWinsHeight=fMetr.getHeight(); graph.drawString(strWins, dimCanvas.width/2-intWinsWidth/2, dimCanvas.height/2+intWinsHeight/2); } else { String strWins="User Loses"; FontMetrics fMetr=graph.getFontMetrics(); int intWinsWidth=fMetr.stringWidth(strWins); int intWinsHeight=fMetr.getHeight(); graph.drawString(strWins, dimCanvas.width/2-intWinsWidth/2, dimCanvas.height/2+intWinsHeight/2); } return; } synchronized(syncAll) { Color clrPrev=graph.getColor(); graph.setColor(Color.black); //Draw the northern edge graph.fillRect(0, 0, dimCanvas.width, intEdgeWidth); //Draw the southern edge graph.fillRect(0, dimCanvas.height-intEdgeWidth, dimCanvas.width, intEdgeWidth); graph.setColor(Color.red); //Draw the user's slider graph.fillRect(0, intUserSliderPosition, intSliderWidth, intSliderHeight); //Draw the AI's slider graph.fillRect(dimCanvas.width-intSliderWidth, intAISliderPosition, intSliderWidth, intSliderHeight); graph.setColor(Color.green); //Draw the ball graph.fillOval(((int)Math.rint(dblBallX))-intBallWidth/2, ((int)Math.rint(dblBallY))-intBallHeight/2, intBallWidth, intBallHeight); } } } class BallMoveThread extends Thread { volatile long lngSleepTime=50l; volatile boolean blnKeepAlive=true; BallMoveThread() { super(); } public void run() { try { while(blnKeepAlive) { if(!canvasPong.blnGameActive) { Thread.sleep(1000); continue; } synchronized(syncAll) { //Move the ball's position canvasPong.dblBallX+=canvasPong.dblBallMoveX; canvasPong.dblBallY+=canvasPong.dblBallMoveY; //Check to see if the ball's position places it for bouncing off of the user's slider Rectangle user=new Rectangle(0, canvasPong.intUserSliderPosition, canvasPong.intSliderWidth, canvasPong.intSliderHeight); if(user.contains(canvasPong.dblBallX, canvasPong.dblBallY)) { canvasPong.dblBallMoveX=-canvasPong.dblBallMoveX; canvasPong.dblBallX=canvasPong.intSliderWidth+1; thrAI.computeSliderMove(); } //Check to see if the ball's position places it for bouncing off of the AI's slider Rectangle ai=new Rectangle(canvasPong.getSize().width-canvasPong.intSliderWidth, canvasPong.intAISliderPosition, canvasPong.intSliderWidth, canvasPong.intSliderHeight); if(ai.contains(canvasPong.dblBallX, canvasPong.dblBallY)) { canvasPong.dblBallMoveX=-canvasPong.dblBallMoveX; canvasPong.dblBallX=canvasPong.getSize().width-canvasPong.intSliderWidth-1; } //Check to see if the ball's position places it for bouncing off of the northern edge Rectangle northernEdge=new Rectangle(0, 0, canvasPong.getSize().width, canvasPong.intEdgeWidth); if(northernEdge.contains(canvasPong.dblBallX, canvasPong.dblBallY)) { canvasPong.dblBallMoveY=-canvasPong.dblBallMoveY; } //Check to see if the ball's position places it for bouncing off of the southern edge Rectangle southernEdge=new Rectangle(0, canvasPong.getSize().height-canvasPong.intEdgeWidth, canvasPong.getSize().width, canvasPong.intEdgeWidth); if(southernEdge.contains(canvasPong.dblBallX, canvasPong.dblBallY)) { canvasPong.dblBallMoveY=-canvasPong.dblBallMoveY; } if(canvasPong.dblBallX<0.0d) { //If the dblBallX is less than 0 then the ball passed by the user's slider, so the AI wins canvasPong.blnUserWins=false; canvasPong.stopGame(); } else if(canvasPong.dblBallX>canvasPong.getSize().width) { //If the dblBallX is greater than PongCanvas's width then the ball passed by the AI's slider, so the user wins canvasPong.blnUserWins=true; canvasPong.stopGame(); } } canvasPong.repaint(); Thread.sleep(lngSleepTime); } } catch(Exception ex) { ex.printStackTrace(); } } } class ArtificialIntelligenceThread extends Thread { volatile long lngSleepTime=100l; volatile int intAISliderPositionIncrement=10; volatile int intAISliderPositionStop=0; volatile boolean blnKeepAlive=true; ArtificialIntelligenceThread() { super(); } public void computeSliderMove() { synchronized(syncAll) { //Save the dblBallX and dblBallY for resetting them after the computing of this function is done double dblBallXPrevious=canvasPong.dblBallX; double dblBallYPrevious=canvasPong.dblBallY; //Save the dblBallMoveX and dblBallMoveY for resetting them after the computing of this function is done double dblBallMoveXPrevious=canvasPong.dblBallMoveX; double dblBallMoveYPrevious=canvasPong.dblBallMoveY; while(true) { //Move the ball's position canvasPong.dblBallX+=canvasPong.dblBallMoveX; canvasPong.dblBallY+=canvasPong.dblBallMoveY; //Check to see if the ball's position places it for bouncing off of the northern edge Rectangle northernEdge=new Rectangle(0, 0, canvasPong.getSize().width, canvasPong.intEdgeWidth); if(northernEdge.contains(canvasPong.dblBallX, canvasPong.dblBallY)) { canvasPong.dblBallMoveY=-canvasPong.dblBallMoveY; } //Check to see if the ball's position places it for bouncing off of the southern edge Rectangle southernEdge=new Rectangle(0, canvasPong.getSize().height-canvasPong.intEdgeWidth, canvasPong.getSize().width, canvasPong.intEdgeWidth); if(southernEdge.contains(canvasPong.dblBallX, canvasPong.dblBallY)) { canvasPong.dblBallMoveY=-canvasPong.dblBallMoveY; } //If dblBallX is greater than PongCanvas's width then that is where the ball will eventually arrive at, so have the AI move its slider accordingly if(canvasPong.dblBallX>canvasPong.getSize().width) { intAISliderPositionStop=((int)Math.rint(canvasPong.dblBallY))-canvasPong.intSliderHeight/2; if(intAISliderPositionStop<0) intAISliderPositionStop=0; else if((intAISliderPositionStop+canvasPong.intSliderHeight)>canvasPong.getSize().height) intAISliderPositionStop=canvasPong.getSize().height-canvasPong.intSliderHeight; break; } } //Reset the variables canvasPong.dblBallX=dblBallXPrevious; canvasPong.dblBallY=dblBallYPrevious; canvasPong.dblBallMoveX=dblBallMoveXPrevious; canvasPong.dblBallMoveY=dblBallMoveYPrevious; } } public void stopSliderMove() { synchronized(syncAll) { //Set the field so that it will not move in the "run" function intAISliderPositionStop=canvasPong.intAISliderPosition; } } public void run() { try { while(blnKeepAlive) { if(intAISliderPositionStop==canvasPong.intAISliderPosition) { //If this evaluated to true then don't have the slider move Thread.sleep(500); continue; } synchronized(syncAll) { if(intAISliderPositionStopcanvasPong.intAISliderPosition) { //Move the AI's slider to the south canvasPong.intAISliderPosition+=intAISliderPositionIncrement; if(canvasPong.intAISliderPosition>intAISliderPositionStop) canvasPong.intAISliderPosition=intAISliderPositionStop; } } canvasPong.repaint(); Thread.sleep(lngSleepTime); } } catch(Exception ex) { ex.printStackTrace(); } } } }