// // Board.java // Anneal // // Created by sw on Thu Sep 12 2002. // Copyright (c) 2001 __MyCompanyName__. All rights reserved. // package net.tiac.home.sw.anneal; import java.lang.Integer; import java.awt.*; import java.awt.event.*; import java.applet.Applet; class Board extends Canvas implements Runnable { Dimension arrayDimension; InitPattern initPattern; int x_max, y_max, n_cells; int cell_width, cell_height; int x, y, x_vel, y_vel; ControlPanel controlPanel; boolean board[][]; WeightMap2D odds; RandomRange rg; int rules[]; int totalEnergy; int threshold; int speed = 0; int direction = +1; boolean busy = false; // Constructor: public Board( ) { } // setInitPattern needs to be called before init, ergh. public void setInitPattern( InitPattern s ) { this.initPattern = s; } public Dimension getSize( ) { // Netscape doesn't provide this. Rectangle b = this.bounds( ); return( new Dimension( b.width, b.height ) ); } public int localEnergy( int x, int y ) { int t = 0; for( int i = x - 1; i <= x + 1; i++ ) { for( int j = y - 1; j <= y + 1; j++ ) { t *= 2; if( board[i][j] ) { t++; } } } return( rules[ t ] ); } public int widerEnergy( int x, int y ) { int t = 0; for( int i = x - 1; i <= x + 1; i++ ) { for( int j = y - 1; j <= y + 1; j++ ) { t += localEnergy( i, j ); } } return( t ); } public int energyLossIfFlipped( int x, int y ) { int s0 = widerEnergy( x, y ); board[x][y] = !board[x][y]; int s1 = widerEnergy( x, y ); board[x][y] = !board[x][y]; return( s0 - s1 ); } public void init( int cell_width, int cell_height, ControlPanel controlPanel ) { this.cell_width = cell_width; this.cell_height = cell_height; // The InitPattern class can't handle cell width or height != 1 initPattern.setMaxDimension( this.getSize() ); arrayDimension = this.initPattern.arrayDimension(); x_max = arrayDimension.width; y_max = arrayDimension.height; n_cells = x_max * y_max; this.board = new boolean[x_max][y_max]; this.odds = new WeightMap2D( x_max, y_max ); this.rg = new RandomRange( 0L ); // Fixed rules & behavior. // But initializing the board uses Math.random(). this.rules = new int [512]; for( int i = 0; i < 512; i++ ) rules[i] = i; // Shuffle the values: for( int i = 511; i >= 1; i-- ) { int j = rg.nextInt( i ); int tmp = rules[ i ]; rules[ i ] = rules[ j ]; rules[ j ] = tmp; } this.controlPanel = controlPanel; controlPanel.setMileage( 0 ); for( int i = 2; i < x_max - 2; i++ ) { for( int j = 2; j < y_max - 2; j++ ) { this.board[i][j] = initPattern.cell( i, j ); } } totalEnergy = 0; for( int i = 1; i < x_max - 1; i++ ) { for( int j = 1; j < y_max - 1; j++ ) { totalEnergy += localEnergy( i, j ); } } threshold = totalEnergy / n_cells; for( int i = 2; i < x_max - 2; i++ ) { for( int j = 2; j < y_max - 2; j++ ) { int e = energyLossIfFlipped( i, j ) + threshold; odds.set( i, j, e > 0? e + 1 : 1 ); } } controlPanel.setMileage( /* totalEnergy */ odds.getTotal() ); x = x_max / 2; y = y_max / 2; x_vel = 0; y_vel = 1; speed = 0; direction = +1; this.setBackground( Color.white ); repaint(); } public void paint( Graphics g ) { g.setColor(Color.black); for ( int y = 0; y < y_max; y++ ) { for ( int x = 0; x < x_max; x++ ) { if ( board[x][y] ) { g.fillRect( x * cell_width, y * cell_height, cell_width, cell_height); } else { g.clearRect( x * cell_width, y * cell_height, cell_width, cell_height); } } } } public void propagate( int nSteps, int direction ) { // Kludge: Many cycles in one call, to minimize calls to this method! Graphics g = getGraphics(); int e; for( int s = 0; s < nSteps; s++ ) { Point p = odds.pick( rg ); x = p.x; y = p.y; e = energyLossIfFlipped( x, y ); board[x][y] = !board[x][y]; totalEnergy -= e; threshold = totalEnergy / n_cells; for( int i = x - 2; i <= x + 2; i++ ) { if( i >= 2 && i < x_max - 2 ) { for( int j = y - 2; j <= y + 2; j++ ) { if( j >= 2 && j < y_max - 2 ) { e = energyLossIfFlipped( i, j ) + threshold; odds.set( i, j, e > 0? e + 1 : 1 ); } } } } if ( board[x][y] ) { g.fillRect( x * cell_width, y * cell_height, cell_width, cell_height); } else { g.clearRect( x * cell_width, y * cell_height, cell_width, cell_height); } } //this.controlPanel.setMileage( // this.controlPanel.getMileage( ) + nSteps * direction ); this.controlPanel.setMileage( /* totalEnergy */ odds.getTotal() ); } public synchronized void setSpeedDirection( int speed, int direction ) { this.speed = speed; this.direction = direction; } public void step( ) { signalStopWaitForResponse( ); propagate( 1, this.direction ); } Thread thread = null; public void start() { if ( thread == null ) { thread = new Thread( this ); thread.start(); } } public void stop() { if ( thread != null && thread.isAlive() ) { thread.stop(); } this.busy = false; thread = null; } // Tell the board to stop "propogating"; wait till it stops. // This is called by one thread while the other is (possibly) // doing the propogating. public void signalStopWaitForResponse( ) { while( true ) { this.speed = 0; if( !this.busy ) break; try { Thread.sleep( 100 ); } catch (InterruptedException e) {}; } } public boolean supposedToBeGoing( ) { return( this.speed != 0 && this.direction != 0 ); } public void run() { int speed, abs_speed, dir, sleepMs; while (true) { for( int i = 1; i <= 10; i++ ) { this.busy = true; synchronized ( this ) { speed = this.speed; dir = this.direction; } int nSteps = ( i * speed / 10 ) - ( (i-1) * speed / 10 ); if ( isVisible() && nSteps != 0 && dir != 0 ) { propagate( nSteps, dir ); } this.busy = false; if( supposedToBeGoing() ) { sleepMs = ( 1000 - nSteps ) / 10 + 1; } else { sleepMs = 100; } try { Thread.sleep( sleepMs ); } catch (InterruptedException e){}; } } } }