HW3 Solutions Page
Return to
homework page.
//
// Filenname: Slot.java
//
// Purpose: contains classes to implement a simple slot machine
// application which uses Threads to stop the reels of the
// machine after they are set in motion.
//
//
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
//
// Interface Name: ReelParts
//
// Purpose: This interface contains all the constant used for drawing the
// slot machine. It is also implemented by Reel and ReelPanel.
// We do this so a Reel can synchronize against either a Reel or a
// ReelPanel.
//
interface ReelParts
{
static String reelImageName = "reel.jpg"; // image used to draw reel
static int BAR = 0, APPLE = 1; //enum of shapes on reel
static int APPLEOFFSET = 120, BAROFFSET = 50; // where shapes are on image
static int REELHT = 160, REELWTH = 290; //height and width of all reels
//put together
static int LEFTX = 0, TOPY = 0; //coordinates for three reels
static int CENTERX = 90, RIGHTX = 180;
static int INDICATORX=0, INDICATORY=60, INDICATORWTH=5; //coordinates
//of indicator
static int BTNMSGHT = 40; //combined height of buttons and message label
}
//
// Class Name: Slot
//
// Purpose: main class for this application
//
public class Slot extends JFrame {
ReelPanel rp;
JPanel bp;
JLabel msgLabel;
//
// Method Name: Slot
//
// Purpose: constructor. Sets up the message label the slot machine
// reels and the buttons for spin/inserting a coin.
public Slot()
{
super("Slot Machine");
Container c = getContentPane();
c.setLayout(new BorderLayout());
setBackground(Color.white);
msgLabel = new JLabel(" "); // a message beginning with space means
// we still have to insert a coin.
c.add(msgLabel, BorderLayout.NORTH);
Image img = (new ImageIcon(ReelParts.reelImageName)).getImage();
rp = new ReelPanel(img, msgLabel); //panel with slot machine reels.
c.add(rp, BorderLayout.CENTER);
bp = ButtonPanel(); //set up buttons
c.add(bp, BorderLayout.SOUTH);
setSize(ReelParts.REELWTH,ReelParts.REELHT+
ReelParts.BTNMSGHT);
show();
}
//
// Method Name: ButtonPanel
//
// Purpose: sets up spin and insert coins buttons and their listeners
//
public JPanel ButtonPanel()
{
bp = new JPanel();
bp.setLayout(new GridLayout(1,2));
JButton spin = new JButton("Spin");
spin.addActionListener(
new ActionListener(){
public void actionPerformed(ActionEvent e)
{
if(msgLabel.getText().startsWith(" "))
{
msgLabel.setText(
" Please insert coin");
}
else
{
msgLabel.setText(" ");
rp.spin();
}
}
}
);
bp.add(spin,0);
JButton insertCoin = new JButton("Insert Coin");
insertCoin.addActionListener(
new ActionListener(){
public void actionPerformed(ActionEvent e)
{
if(msgLabel.getText().startsWith(" "))
{
msgLabel.setText("Spin Away");
}
else
{
msgLabel.setText(
"Return.Max 1 Coin.Please Spin.");
}
}
}
);
bp.add(insertCoin,1);
return bp;
}
//
// Method Name: main
//
// Purpose: instantiates a Slot object and sets up a window listener for our
// frame
public static void main (String args[])
{
Slot app = new Slot();
app.addWindowListener( new WindowAdapter() {
public void windowClosing(WindowEvent e)
{
System.exit(0);
}
});
}
}
//
// Class Name: ReelPanel
//
// Purpose: panel on which the reels of the slot machine are drawn.
// This class manages these reels and their animation.
class ReelPanel extends JPanel implements ReelParts
{
private Reel r1, r2, r3; // three reels on slot machine
private JLabel msgLabel; // so can say if user won on a given spin
private int amountPaid; // how much user has been paid so far
private Timer animationTimer; //timer used to animate reels.
private Thread payThread; //used to stop animationTimer and (if need be)
//pay user
private boolean payout; //flag used to determine if we pay the user this spin.
//
// Method Name: ReelPanel
//
// Purpose: constructor. Stes up three Reel, animationTimer, and payoutThread
//
ReelPanel(Image img, JLabel mLabel)
{
msgLabel=mLabel;
amountPaid=0; //at start haven't paid user anything.
r1 = new Reel(img,this, RIGHTX, TOPY, REELWTH, REELHT);
r2 = new Reel(img,r1, CENTERX, TOPY, RIGHTX, REELHT);
r3 = new Reel(img,r2, LEFTX, TOPY, CENTERX, REELHT);
initPayoutThread();
setBackground(Color.white);
setSize(getPreferredSize());
}
//
// Method Name: initPayoutThread
//
// Purpose: sets up the Timer to control reel animation and
// sets up a Thread to stop this animation after
// the reels have stopped spinning and (if need be)
// pay the user.
//
public void initPayoutThread()
{
animationTimer = new Timer(50, new ActionListener(){
public void actionPerformed(ActionEvent e)
{
move();
repaint();
}
});
payThread = new Thread(new Runnable(){
public void run()
{
while(true)
{
synchronized(r3)
{
try
{
r3.wait();
}
catch(InterruptedException e)
{
e.printStackTrace();
}
}
animationTimer.stop();
if(payout) {
try{
pay();
}
catch (TiltException e)
{
System.err.println("Machine out of money");
System.exit(-1);
}
}
}
}
});
payThread.start();
}
//
// Method Name: move
//
// Purpose: updates the animation for each of the reels.
//
public void move()
{
r1.move();
r2.move();
r3.move();
}
//
// Method Name: paintComponent
//
// Purpose: draws the slot machine reels and the indicator
// bar
//
public void paintComponent( Graphics g)
{
super.paintComponent(g);
r1.draw(g, this);
r2.draw(g, this);
r3.draw(g, this);
g.setColor(Color.yellow);
g.fillRect(INDICATORX, INDICATORY, REELWTH, INDICATORWTH);
}
//
// Method Name: pay
//
// Purpose: Tell user they won and handle total payout so far.
// If this is more than 2 dollars throw a TiltException.
public void pay() throws TiltException
{
msgLabel.setText(" You won $1.00.");
amountPaid +=1;
if( amountPaid >2) throw new TiltException();
}
//
// Method Name: spin
//
// Purpose: selects the final value for each reel then spins
// the three reels as in the description of the homework
// for a while until they stop on these values.
public synchronized void spin()
{
payout=false;
int tmp=0;
int r=0;
if(Math.random()<=.2) // win at least 20 percent of the time
{
r3.start(BAR);
r2.start(BAR);
r1.start(BAR);
payout =true;
}
else //otherwise randomly choose a symbol
{
tmp=r=(int)(Math.random()+.5);
r3.start(r);
tmp+=(r=(int)(Math.random()+.5));
r2.start(r);
tmp+=(r=(int)(Math.random()+.5));
r1.start(r);
if(tmp % 3 == 0) payout = true;
}
animationTimer.start();
notify();
}
//
// Method Name: getPreferredSize
//
// Purpose: returns the best size for drawing the slot machine reel
//
public Dimension getPreferredSize()
{
return new Dimension(REELWTH,REELHT);
}
}
//
// Class Name: Reel
//
// Purpose: this class is used to manage one reel on a slot machine
//
class Reel implements ReelParts
{
private int dx1, dy1, dx2, dy2;//location to draw reel to
private Image img; // image used to draw reel
private ReelParts rr; // object this Reel's stop Thread waits on
private int offset, animSpeed; // offset controls what part of
//of img to draw and animSpeed is the number of
//pixels through the animation we go / ActionEvent
private Thread reelStopper; //thread used to stop Reel spinning
private boolean stopReel = false; //used to tell move() whether to stop
//moving the Reel.
private int fruitOffset; // where in img the fruit we are supposed to
// stop on lives.
//
// Method Name: Reel
//
// Purpose: constructor of the class. image is the
// the image used to draw the reel. rightReel
// is the object this Reel's reelStopper Thread waits on.
// The destxi and destyi specify where to draw reel.
Reel(Image image, ReelParts rightReel,
int destx1, int desty1, int destx2, int desty2 )
{
img = image;
rr = rightReel;
dx1=destx1; dy1=desty1; dx2=destx2; dy2=desty2;
offset=(int)(Math.random()*30);
animSpeed= 15 + (int)(Math.random()*5);
reelStopper = new Thread(new Runnable(){
public void run()
{
while(true)
{
synchronized(rr)
{
try
{
rr.wait();
}
catch(InterruptedException e)
{
e.printStackTrace();
}
}
try{
Thread.sleep(1000+(int)(Math.random()*1000));
}
catch(InterruptedException e)
{
e.printStackTrace();
}
stopReel = true;
}
}
});
reelStopper.start();
}
//
// Method Name: move()
//
// Purpose: If Reel should still be spun advance it by animSpeed.
// Otherwise, notify object waiting on this Reel.
public void move()
{
if(stopReel && offset <= fruitOffset &&
fruitOffset <=offset+animSpeed)
{
synchronized(Reel.this)
{ Reel.this.notify(); }
}
else
{
offset +=animSpeed;
if(offset>160) offset=0;
}
}
//
// Method Name: draw
//
// Purpose: draws the reel with the Graphics g and ImageObsever ob
// passed to it.
//
public void draw(Graphics g, Component ob)
{
g.drawImage(img, dx1, dy1, dx2, dy2, 0, 0+offset,
70, REELHT+offset, ob);
}
//
// Method Name: start
//
// Purpose: starts the reel spinning, sets up the reels
// final resting position.
//
public void start(int reelFruit)
{
if(reelFruit == APPLE) fruitOffset = APPLEOFFSET;
if(reelFruit == BAR) fruitOffset = BAROFFSET;
stopReel = false;
}
}
//
// Class Name: TiltException
//
// Purpose: the kind of Exception thrown when the slot machine runs out of money
//
class TiltException extends Exception
{
TiltException()
{
super("TiltException");
}
}
|