/* Copyright (C) 2002 J. M. Spivey */ import java.awt.*; public class Arena extends Panel { /* Three things needed for speed: keep an off-screen image and update it incrementally so that paint() doesn't need constantly to redraw every cell. Give a bounding rectangle for the change after each move. Keep a graphics context ready prepared for the off-screen image. */ final int N = 640; final int MAXSCALE = 32; final int ARROWTHRESH = 8; final int GRIDTHRESH = 8; public static final String colname[] = { "white", "red", "green", "yellow" }; public static final Color color[] = { Color.white, Color.red, Color.green, Color.yellow }; private static final Color bgcolor = Color.cyan; private static final Color fgcolor = Color.black; private int pixel[][] = new int[N][N]; private Image image; private Graphics imgGraphics; private int scale = MAXSCALE, offx = (N - N/scale)/2, offy = (N - N/scale)/2; private Label state = new Label(); private int dir = 0, x = N/2, y = N/2; private final int dx[] = { 1, 0, -1, 0 }; private final int dy[] = { 0, 1, 0, -1 }; private final int dirinc[] = { -1, 1, 0 }; private Machine machine; public Arena(Machine machine) { this.machine = machine; // setBackground(bgcolor); setLayout(null); add(state); state.setBackground(fgcolor); state.setForeground(bgcolor); state.setLocation(5, 5); state.setSize(60, 20); updateState(); } public void update(Graphics g) { paint(g); } public synchronized void paint(Graphics g) { if (image == null) { // First time: make the image image = createImage(N, N); imgGraphics = image.getGraphics(); refreshImage(); } // Draw the cells from the image buffer g.drawImage(image, 0, 0, null); if (scale >= ARROWTHRESH) { // Draw the turmite as an arrow g.setColor(fgcolor); int xx = (x-offx)*scale+scale/2-1, yy = (y-offy)*scale+scale/2-1; int u = scale/2-1, v = scale/4, w = scale/8; int x1 = xx+(u-w)*dx[dir], y1 = yy+(u-w)*dy[dir]; g.drawLine(2*xx-x1, 2*yy-y1, x1, y1); g.drawLine(x1, y1, x1+v*(dy[dir]-dx[dir]), y1+v*(dx[dir]-dy[dir])); g.drawLine(x1, y1, x1-v*(dy[dir]+dx[dir]), y1-v*(dx[dir]+dy[dir])); } // Draw the child components (i.e. state label) super.paint(g); } private void drawPixel(int i, int j) { Graphics g = imgGraphics; // Update a single pixel in the image: may do nothing if outside // visible area. int size = (scale >= GRIDTHRESH ? scale-1 : scale); g.setColor(color[pixel[i][j]]); g.fillRect((i-offx) * scale, (j-offy) * scale, size, size); } private void refreshImage() { Graphics g = imgGraphics; // The image contains just the visible part of the arena g.setColor(bgcolor); g.fillRect(0, 0, N, N); for (int i = offx; i < offx + N/scale; i++) for (int j = offy; j < offy + N/scale; j++) drawPixel(i, j); } public void updateState() { state.setText("State = " + (machine.getState() + 1)); } public synchronized void play() { if (x < 0 || x >= N || y < 0 || y >= N) return; int c = pixel[x][y]; pixel[x][y] = machine.draw(c); drawPixel(x, y); repaint((x-offx)*scale, (y-offy)*scale, scale, scale); // The formula on the next line takes into account that -1 % 4 = -1, // and not 3 as it should be in all honesty. dir = (dir + dirinc[machine.move(c)] + 4) % 4; x += dx[dir]; y += dy[dir]; machine.next(c); repaint((x-offx)*scale, (y-offy)*scale, scale, scale); updateState(); state.repaint(); } public void clear() { dir = 0; x = y = N/2; for (int i = 0; i < N; i++) for (int j = 0; j < N; j++) pixel[i][j] = 0; if (scale >= GRIDTHRESH) refreshImage(); else { // For speed, just blank out the image Graphics g = imgGraphics; g.setColor(color[0]); g.fillRect(0,0,N,N); } machine.reset(); repaint(); } public int getScale() { return scale; } public synchronized void setScale(int scale) { this.scale = scale; offx = (N - N/scale)/2; offy = (N - N/scale)/2; refreshImage(); repaint(); } public Dimension getMinimumSize() { return new Dimension(N, N); } public Dimension getPreferredSize() { return new Dimension(N, N); } }