// 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; } } }