MonteCarlo.java:
// SourceAvailable
// Written by Jin S. Choi, jsc@alum.mit.edu
import java.applet.Applet;
import java.text.DecimalFormat;
import java.awt.*;
import java.awt.event.*;
public class MonteCarlo extends Applet
implements Runnable, ActionListener {
private Label count;
private Label piApprox;
private Button startButton;
private Button stopButton;
private MonteCarloCanvas drawArea;
private Panel messageArea;
private int numPoints = 0;
private int pointsInCircle = 0;
private DecimalFormat df = new DecimalFormat("0.000000000000");
// If set to true, restarts the calculation.
private boolean restart_p = false;
private Thread monteCarloThread;
public void init() {
// Create the UI.
setLayout(new BorderLayout());
drawArea = new MonteCarloCanvas();
messageArea = makeMessageArea();
add(makeButtonArea(), "East");
add(drawArea, "Center");
add(messageArea, "South");
validate();
}
// Lay out the message area at the bottom.
private Panel makeMessageArea() {
messageArea = new Panel();
GridBagLayout gb = new GridBagLayout();
GridBagConstraints gbc = new GridBagConstraints();
messageArea.setLayout(gb);
Label countLabel = new Label("N", Label.CENTER);
Label piLabel = new Label("pi (approx)", Label.CENTER);
count = new Label("00000000", Label.CENTER);
piApprox = new Label("0.000000000000", Label.CENTER);
gbc.weightx = 1.0;
gbc.weighty = 1.0;
gbc.gridwidth = GridBagConstraints.RELATIVE;
gbc.fill = GridBagConstraints.HORIZONTAL;
gb.setConstraints(count, gbc);
messageArea.add(count);
gbc.gridwidth = GridBagConstraints.REMAINDER;
gb.setConstraints(piApprox, gbc);
messageArea.add(piApprox);
gbc.gridwidth = GridBagConstraints.RELATIVE;
gb.setConstraints(countLabel, gbc);
messageArea.add(countLabel);
gbc.gridwidth = GridBagConstraints.REMAINDER;
gb.setConstraints(piLabel, gbc);
messageArea.add(piLabel);
return messageArea;
}
// Lay out the buttons on the right.
private Panel makeButtonArea() {
Panel buttonArea = new Panel();
GridBagLayout gb = new GridBagLayout();
GridBagConstraints gbc = new GridBagConstraints();
buttonArea.setLayout(gb);
startButton = new Button("Restart");
stopButton = new Button("Stop");
startButton.addActionListener(this);
stopButton.addActionListener(this);
gbc.gridwidth = GridBagConstraints.REMAINDER;
gbc.fill = GridBagConstraints.HORIZONTAL;
gbc.insets = new Insets(1, 3, 1, 3);
gb.setConstraints(startButton, gbc);
buttonArea.add(startButton);
gb.setConstraints(stopButton, gbc);
buttonArea.add(stopButton);
return buttonArea;
}
// Called from Thread.start(); part of the Runnable interface.
public void run() {
while (Thread.currentThread() == monteCarloThread) {
if (restart_p) {
restart_p = false;
drawArea.clear();
pointsInCircle = 0;
numPoints = 0;
}
// MonteCarloCanvas.addPoint() returns true if the point added
// was within the circle.
if (drawArea.addPoint()) {
pointsInCircle++;
}
numPoints++;
count.setText("" + numPoints);
piApprox.setText("" + df.format((double) pointsInCircle * 4 / numPoints));
// Try really hard to yield execution to the drawing thread.
// On some platforms (Netscape under Linux, for example),
// it is really hard to give the drawing thread (the one that
// calls update()) a chance to run. We need to sleep at least 5
// milliseconds to actually yield it; less won't do. We don't
// want to slow down our calculation too much, so we just do it
// every 100 points.
Thread.yield();
if (numPoints % 100 == 0) {
try {
Thread.sleep(5);
} catch (InterruptedException e) {
}
}
}
}
public void start() {
if (monteCarloThread == null) {
monteCarloThread = new Thread(this);
monteCarloThread.setPriority(Thread.MIN_PRIORITY);
}
monteCarloThread.start();
startButton.setLabel("Restart");
}
public void stop() {
monteCarloThread = null;
startButton.setLabel("Start");
}
// Handle button presses.
public void actionPerformed(ActionEvent e) {
String cmd = e.getActionCommand();
if (cmd.equals("Stop")) {
stop();
} else if (cmd.equals("Start")) {
start();
} else if (cmd.equals("Restart")) {
restart_p = true;
}
}
}
MonteCarloCanvas.java:
// SourceAvailable
// Written by Jin S. Choi, jsc@alum.mit.edu
import java.lang.Math;
import java.awt.*;
public class MonteCarloCanvas extends Canvas {
// Keep an array of a thousand points around. If we calculate more
// than that in between calls to update(), we throw the rest on the floor.
private Point points[] = new Point[1000];
private Color colors[] = new Color[1000];
private Color lightBlue = new Color(150, 150, 255);
private int curPoint = 0;
// Set to true to clear the display and redraw the border.
private boolean clear_p = true;
public MonteCarloCanvas() {
for (int i = 0; i < 1000; i++) {
points[i] = new Point();
}
}
// Returns true if point added was in the circle.
public boolean addPoint() {
Dimension size = getSize();
int xoffset = 0;
int yoffset = 0;
// Center the square inside the drawing area, and leave space
// for the border.
if (size.width > size.height) {
xoffset = (size.width - size.height) / 2 + 1;
yoffset = 1;
size.height -= 2;
size.width = size.height;
} else if (size.height > size.width) {
yoffset = (size.height - size.width) / 2 + 1;
xoffset = 1;
size.width -= 2;
size.height = size.width;
}
double x = Math.random();
double y = Math.random();
double magX = .5 - x;
double magY = .5 - y;
boolean inUnitCircle = Math.sqrt(magX*magX + magY*magY) <= .5;
// Synchronize on points so that the update thread doesn't come and
// try to draw while we're updating it.
synchronized (points) {
if (curPoint < 1000) {
if (inUnitCircle) {
colors[curPoint] = Color.blue;
} else {
colors[curPoint] = lightBlue;
}
int xloc = (int) (x * size.width + xoffset);
int yloc = (int) (y * size.height + yoffset);
points[curPoint].setLocation(xloc, yloc);
curPoint++;
}
}
// Ask ourselves to redraw.
repaint();
return inUnitCircle;
}
public void clear() {
clear_p = true;
}
// Drawing function. We override this instead of paint() since we
// don't want to erase and redraw the whole screen (don't want to
// keep around that much state anyway).
public void update(Graphics g) {
if (clear_p) {
clear_p = false;
g.setColor(getBackground());
Dimension size = getSize();
g.fillRect(0, 0, size.width, size.height);
g.setColor(Color.black);
// Calculate where to put the border.
int xoffset = 0;
int yoffset = 0;
if (size.width > size.height) {
xoffset = (size.width - size.height) / 2;
size.width = size.height;
} else if (size.height > size.width) {
yoffset = (size.height - size.width) / 2;
size.height = size.width;
}
// Draw the border.
g.drawRect(xoffset, yoffset, size.width - 1, size.height - 1);
}
// Synchronized on points so we don't draw it and truncate it while
// a point is being added.
synchronized (points) {
for (int i = 0; i < curPoint; i++) {
Point p = points[i];
g.setColor(colors[i]);
g.fillRect(p.x, p.y, 1, 1);
}
curPoint = 0;
}
}
}
Back