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