/* * Pacinco.java * The board with hopping electrons on it. I don't know how to * spell "pa-ching-ko." * This canvas shows a toy model of nonequilibrium charges moving on * a torus (wrapping around the edges). The model says that all particles * move equally likely left and right but half slightly prefer to move * up and half slightly prefer to move down. The model is similar to that * of Beate Schmittman and Royze Zia of Virginia Tech Physics, but it does * not include the possibility and up and down particle can exchange charge * (switch places). * * Drew Dolgert September 1999 */ import java.awt.*; import java.lang.*; public class Pacinco extends Canvas implements Runnable { private int rw, rh; // width and height of the canvas int threadSleepTime = 40; // How long the thread should pause between updates. boolean frozen = true; // Whether the board is stopped. boolean ugo = false; // Whether the user has asked us to start. private Graphics offscreen; // Place to draw to make animation smoother. Image offImage; // A handle to the offscreen graphics for the system. /* About the balls, there are nBalls of them, and the first nBalls/2 are going * up while the second nBalls/2 are going down. A cheap convention. */ int bx[]; // x position of balls. (primitive, I know.) int by[]; // y position of balls. int board[][]; // current board int newBoard[][]; // new boards. If you didn't distinguish the two, // a chance order of updates could start a conga line of motion. Dimension thisSize; // size of board. Thread animatorThread; // The board updates itself independent of user controls. int nBalls = 0; int nBoardSizeX = 0; int nBoardSizeY = 0; /* The weight is this: Consider equal probability of movement in each direction. * Then say the chance of moving up is u, of down is d. The weight is an * epsilon = (u-d)/(u+d) where we set u+d=0.5, or a total fifty percent chance. */ double dWeight = 0.4; double dMobility = 0.8; // This is the average percent of balls that may move. Color bgColor, upColor, downColor; public Pacinco(int nboardx, int nboardy, int nball) { changeSize(); // Begin by making sure we agree with our boundaries. nBoardSizeX = nboardx; nBoardSizeY = nboardy; nBalls = 2*(nball/2); // If there are too many balls, we could lock up placing them. if (nBalls>nBoardSizeX*nBoardSizeY-1) nBalls=nBoardSizeX*nBoardSizeY-1; // The try-catch is in case we ask for a board that is too large. try { this.fillBoard(); } catch ( OutOfMemoryError e ) { nBoardSizeX = nBoardSizeY = 10; nBalls = 50; this.fillBoard(); // This message should show up on the java console--as if you could find it. System.out.println("Not enough memory for a "+nboardx+" by "+nboardy+" board."); } } // Here begin the "settor" methods. There will be no "gettor" methods // unless we feel like being complete. public void setThreadSleep(int ts) { threadSleepTime = ts; } public void setWeight(double dw) { dWeight = dw; } public void setMobility(double dm) { dMobility = dm; } public void setBGColor(Color bg) { bgColor = bg; } public void setUpColor(Color up) { upColor = up; } public void setDownColor(Color dn) { downColor = dn; } // These methods are commands to the canvas to start, stop, reset. public void reset() { boolean restart; restart = !frozen; this.stop(); // Make sure it is stopped. this.fillBoard(); if (restart) this.start(); } // User Start -- The user has requested that we start. We distinguish // this in case we leave a page and return. In that case, the applet // receives a start command from the browser, and we want to know whether // the applet used to be running before we left the page in order to // know whether to restart it. public void ustart() { ugo = true; // "user go" this.start(); } public void ustop() { ugo = false; this.stop(); } // These are effectively called by the browser. public void astart() { if (ugo) this.start(); } public void astop() { this.stop(); } // Every thread must have a start and stop. public void start() { if (frozen) { frozen = false; if (animatorThread == null) animatorThread = new Thread(this); if (animatorThread !=null) animatorThread.start(); } } public void stop() { frozen = true; } // This method gets called once for the length of the thread's life. public void run() { long startTime; while (!frozen) { startTime = System.currentTimeMillis(); repaint(); // ask the system to draw us. double drand; // Lock the board while we update it so we aren't accessing // it while folks (update()) try to draw it. synchronized (bx) { // I know I lost the indentation. for (int i=0; idrand) { drand = drand/dMobility; // Milking here. if (drand<0.25) { this.moveLeft(i); } else if (drand<0.5) { this.moveRight(i); } else if (drand<0.75+0.5*dWeight) { if (2*i>=nBalls) this.moveUp(i); else this.moveDown(i); } else { if (2*i>=nBalls) this.moveDown(i); else this.moveUp(i); } } else { this.dontMove(i); } } } // the this.moveX() commands put info into newBoard. Get it back. for (int i=0;i0) Thread.sleep(td); // If the allotted time is all used, still call sleep because // it gives the system a moment to catch its breath. // That way we might not cream slower machines. else Thread.sleep(1); } catch (InterruptedException e) { ; } } animatorThread = null; frozen = true; } private void moveLeft(int i) { int nx = bx[i]-1; if (nx<0) nx = nBoardSizeX-1; if (board[nx][by[i]]==0) { newBoard[bx[i]][by[i]]=0; if (2*inBoardSizeX-1) nx = 0; if (board[nx][by[i]]==0) { newBoard[bx[i]][by[i]]=0; if (2*inBoardSizeY-1) ny = 0; if (board[bx[i]][ny]==0) { newBoard[bx[i]][by[i]]=0; if (2*i0) rw = thisSize.width; else rw = 300; if (thisSize != null && thisSize.height>0) rh = thisSize.height; else rh = 300; offImage=createImage(thisSize.width,thisSize.height); if (offImage!=null) offscreen=offImage.getGraphics(); repaint(); } private void fillBoard() { bx = new int[nBalls]; by = new int[nBalls]; board = new int[nBoardSizeX][nBoardSizeY]; newBoard = new int[nBoardSizeX][nBoardSizeY]; int i = 0; int j = 0; while (i