// // // worms.java : Simple Simulator for // "Self-Replicating Worms That Increase Structural Complexity through Gene Transmission" // Ver. 1.0 in Java // // Copyright (C) 1999-2000 // Hiroki Sayama // sayama@necsi.net // New England Complex Systems Institute // 24 Mt. Auburn St., Cambridge, MA 02138, USA // // Comments, questions and suggestions are welcome. // // import java.applet.Applet; import java.awt.*; import java.util.Random; public class worms extends Applet implements Runnable { Thread th = null; Graphics g; final int quiescent = -1, modeMask = 1, modeP = 0, modeA = 1, linkMask = 6, linkT = 0, linkR = 2, linkB = 4, linkL = 6, signMask = 56, signC = 8, signR = 16, signL = 32; int space = 50, pixel = 12, timestep, cell[], sp, interval, fromx, fromy, tox, toy; boolean periodic, running, onestep, displaying, displayswitch, magnifying, magnifyswitch, selectingpoint; Button pausebutton; Choice choiceofspace, choiceofboundary; TextField initialdensitytext, randomseedtext; Checkbox displaycheckbox, magnifycheckbox; Random rand = new Random(); /*** initialization ***/ public void init() { g = getGraphics(); setForeground(Color.black); setBackground(Color.white); setLayout(new BorderLayout()); Panel p = new Panel(); p.setLayout(new GridLayout(20, 1)); p.add(pausebutton = new Button("Start")); p.add(new Button("Step")); p.add(new Button("Reset")); p.add(new Label(" ")); p.add(new Label("Size of space")); choiceofspace = new Choice(); choiceofspace.addItem("50x50"); choiceofspace.addItem("100x100"); choiceofspace.addItem("200x200"); choiceofspace.select("200x200"); p.add(choiceofspace); p.add(new Label(" ")); p.add(new Label("Boundary conditions")); choiceofboundary = new Choice(); choiceofboundary.addItem("Cut off"); choiceofboundary.addItem("Periodic"); choiceofboundary.select("Cut off"); p.add(choiceofboundary); p.add(new Label(" ")); p.add(new Label("Initial density (0 - 1)")); p.add(initialdensitytext = new TextField("", 10)); p.add(new Label("(0.1 if blank)")); p.add(new Label(" ")); p.add(new Label("Seed of random numbers")); p.add(randomseedtext = new TextField("", 10)); p.add(new Label("(time is used if blank)")); p.add(new Label(" ")); p.add(displaycheckbox = new Checkbox("Display")); displaycheckbox.setState(true); displaying = true; p.add(magnifycheckbox = new Checkbox("Magnify")); magnifycheckbox.setState(false); magnifying = false; add("East", p); reinit(); } public void reinit() { int x, y; String tempstring; Double initialdensityDouble; double initialdensity = 0.0; running = onestep = displayswitch = magnifyswitch = selectingpoint = false; switch (choiceofspace.getSelectedIndex()) { case 0 : space = 50; pixel = 12; break; case 1 : space = 100; pixel = 6; break; case 2 : space = 200; pixel = 3; break; } fromx = fromy = 0; tox = toy = space - 1; magnifying = false; magnifycheckbox.setState(false); switch (choiceofboundary.getSelectedIndex()) { case 0 : periodic = false; break; case 1 : periodic = true; break; } tempstring = initialdensitytext.getText(); if (tempstring.equals("")) initialdensitytext.setText(tempstring = "0.1"); initialdensityDouble = Double.valueOf(tempstring); initialdensity = initialdensityDouble.doubleValue(); tempstring = randomseedtext.getText(); if (tempstring.equals("")) rand.setSeed(System.currentTimeMillis()); else rand.setSeed(Long.parseLong(tempstring)); cell = new int[space * space * 2]; sp = 0; for (x = 0; x < space; x ++) { for (y = 0; y < space; y ++) { if (rand.nextDouble() < initialdensity) cell[sp + y * space + x] = ((int) (rand.nextDouble() * 4.0)) << 1; else cell[sp + y * space + x] = quiescent; } } timestep = 0; showStatus("Time: " + Integer.toString(timestep)); } /*** graphic routines ***/ public void display_cell(int ip, int x, int y) { int cx, cy, rx, ry, lx, ly; if (cell[ip] == quiescent) return; if (x < fromx || x > tox || y < fromy || y > toy) return; int dx = (x - fromx) * pixel, dy = (y - fromy) * pixel; switch (pixel) { case 12: g.drawRect(dx + 3, dy + 3, 7, 7); if ((cell[ip] & modeMask) == modeA) { g.setColor(Color.lightGray); g.fillRect(dx + 4, dy + 4, 6, 6); g.setColor(Color.black); } cx = cy = rx= ry = lx = ly = 5; switch(cell[ip] & linkMask) { case linkT: g.fillRect(dx + 4, dy - 1, 6, 2); g.fillRect(dx + 5, dy + 1, 4, 2); cx = 5; cy = 7; rx = 3; ry = 5; lx = 7; ly = 5; break; case linkR: g.fillRect(dx + 13, dy + 4, 2, 6); g.fillRect(dx + 11, dy + 5, 2, 4); cx = 3; cy = 5; rx = 5; ry = 3; lx = 5; ly = 7; break; case linkB: g.fillRect(dx + 4, dy + 13, 6, 2); g.fillRect(dx + 5, dy + 11, 4, 2); cx = 5; cy = 3; rx = 7; ry = 5; lx = 3; ly = 5; break; case linkL: g.fillRect(dx - 1, dy + 4, 2, 6); g.fillRect(dx + 1, dy + 5, 2, 4); cx = 7; cy = 5; rx = 5; ry = 7; lx = 5; ly = 3; break; } if ((cell[ip] & signC) == signC) g.fillRect(dx + 1 + cx, dy + 1 + cy, 2, 2); if ((cell[ip] & signR) == signR) g.fillRect(dx + 1 + rx, dy + 1 + ry, 2, 2); if ((cell[ip] & signL) == signL) g.fillRect(dx + 1 + lx, dy + 1 + ly, 2, 2); break; case 6: switch(cell[ip] & linkMask) { case linkT: g.fillRect(dx + 2, dy , 4, 6); break; case linkR: g.fillRect(dx + 2, dy + 2, 6, 4); break; case linkB: g.fillRect(dx + 2, dy + 2, 4, 6); break; case linkL: g.fillRect(dx , dy + 2, 6, 4); break; } break; case 3: switch(cell[ip] & linkMask) { case linkT: g.fillRect(dx + 2, dy , 1, 3); break; case linkR: g.fillRect(dx + 2, dy + 2, 3, 1); break; case linkB: g.fillRect(dx + 2, dy + 2, 1, 3); break; case linkL: g.fillRect(dx , dy + 2, 3, 1); break; } break; } } public void erase_cell(int ip, int x, int y) { if (cell[ip] == quiescent) return; if (x < fromx || x > tox || y < fromy || y > toy) return; g.setColor(Color.white); int dx = (x - fromx) * pixel, dy = (y - fromy) * pixel; switch (pixel) { case 12: g.fillRect(dx + 3, dy + 3, 8, 8); switch(cell[ip] & linkMask) { case linkT: g.fillRect(dx + 4, dy - 1, 6, 2); g.fillRect(dx + 5, dy + 1, 4, 2); break; case linkR: g.fillRect(dx + 13, dy + 4, 2, 6); g.fillRect(dx + 11, dy + 5, 2, 4); break; case linkB: g.fillRect(dx + 4, dy + 13, 6, 2); g.fillRect(dx + 5, dy + 11, 4, 2); break; case linkL: g.fillRect(dx - 1, dy + 4, 2, 6); g.fillRect(dx + 1, dy + 5, 2, 4); break; } break; case 6: switch(cell[ip] & linkMask) { case linkT: g.fillRect(dx + 2, dy , 4, 6); break; case linkR: g.fillRect(dx + 2, dy + 2, 6, 4); break; case linkB: g.fillRect(dx + 2, dy + 2, 4, 6); break; case linkL: g.fillRect(dx , dy + 2, 6, 4); break; } break; case 3: switch(cell[ip] & linkMask) { case linkT: g.fillRect(dx + 2, dy , 1, 3); break; case linkR: g.fillRect(dx + 2, dy + 2, 3, 1); break; case linkB: g.fillRect(dx + 2, dy + 2, 1, 3); break; case linkL: g.fillRect(dx , dy + 2, 3, 1); break; } break; } g.setColor(Color.black); } public void blackFrame() { g.drawRect(0, 0, 601, 601); } public void paint(Graphics g) { int ip, x, y; blackFrame(); if (displaying) { for (ip = sp, y = 0; y < space; y ++) for (x = 0; x < space; x ++) display_cell(ip ++, x, y); } } /*** thread and event management routines ***/ public void start() { if (th == null) { th = new Thread(this); th.start(); } } public void stop() { if (th != null) { th.stop(); try{th.join();} catch(InterruptedException e){} th = null; } } public boolean action(Event ev, Object arg) { boolean selectoff = false; if (selectingpoint) { showStatus("Magnification cancelled"); magnifycheckbox.setState(false); selectoff = true; } if (ev.target instanceof Checkbox) { if (ev.target == displaycheckbox) displayswitch = true; else if ((ev.target == magnifycheckbox) && (!selectingpoint)) magnifyswitch = true; if (selectoff) selectingpoint = false; return true; } if (selectoff) selectingpoint = false; if (ev.target instanceof Button) { String button = (String) arg; if (button.equals("Stop")) { pausebutton.setLabel("Start"); running = onestep = false; } else if (button.equals("Start")) { pausebutton.setLabel("Stop"); running = true; } else if (button.equals("Step")) { onestep = true; if (running) { pausebutton.setLabel("Start"); running = false; } } else if (button.equals("Reset")) { if (running) pausebutton.setLabel("Start"); running = onestep = false; stop(); reinit(); repaint(); start(); } return true; } else if ((ev.target instanceof Choice) || (ev.target instanceof TextField)) { if (running) pausebutton.setLabel("Start"); running = onestep = false; stop(); reinit(); repaint(); start(); return true; } else return false; } public boolean mouseDown (Event ev, int x, int y) { if (!selectingpoint) return false; else if ((x >= 1) && (x <= 600) && (y >= 1) && (y <= 600)) { int bx = (x - 1) / pixel; int by = (y - 1) / pixel; fromx = bx < 25 ? 0 : bx - 25; if (fromx > space - 50) fromx = space - 50; fromy = by < 25 ? 0 : by - 25; if (fromy > space - 50) fromy = space - 50; tox = fromx + 49; toy = fromy + 49; magnifying = true; pixel = 12; showStatus("Magnified"); selectingpoint = false; return true; } else return false; } /*** state-transition rules ***/ public int state(int x, int y) { if (x >= 0 && x < space && y >= 0 && y < space) return cell[sp + y * space + x]; else if (!periodic) return quiescent; else { if (x < 0) x += space; if (x >= space) x -= space; if (y < 0) y += space; if (y >= space) y -= space; return cell[sp + y * space + x]; } } public int root(int ip, int x, int y) { int c = cell[ip], r = quiescent; if (c < 0) return r; switch (c & linkMask) { case linkT: if (((r = state(x, y - 1)) & linkMask) == linkB) r = quiescent; break; case linkR: if (((r = state(x + 1, y)) & linkMask) == linkL) r = quiescent; break; case linkB: if (((r = state(x, y + 1)) & linkMask) == linkT) r = quiescent; break; case linkL: if (((r = state(x - 1, y)) & linkMask) == linkR) r = quiescent; break; } return r; } public boolean linked(int ip, int x, int y) { int c = cell[ip], nc; if ((c & linkMask) != linkT && (nc = state(x, y - 1)) >= 0) if ((nc & linkMask) == linkB) return true; if ((c & linkMask) != linkR && (nc = state(x + 1, y)) >= 0) if ((nc & linkMask) == linkL) return true; if ((c & linkMask) != linkB && (nc = state(x, y + 1)) >= 0) if ((nc & linkMask) == linkT) return true; if ((c & linkMask) != linkL && (nc = state(x - 1, y)) >= 0) if ((nc & linkMask) == linkR) return true; return false; } public int encode(int ip, int x, int y) { int code = 0, nc; switch (cell[ip] & linkMask) { case linkT: if ((nc = state(x + 1, y)) >= 0) if ((nc & linkMask) == linkL) code += signL; if ((nc = state(x, y + 1)) >= 0) if ((nc & linkMask) == linkT) code += signC; if ((nc = state(x - 1, y)) >= 0) if ((nc & linkMask) == linkR) code += signR; break; case linkR: if ((nc = state(x, y - 1)) >= 0) if ((nc & linkMask) == linkB) code += signR; if ((nc = state(x, y + 1)) >= 0) if ((nc & linkMask) == linkT) code += signL; if ((nc = state(x - 1, y)) >= 0) if ((nc & linkMask) == linkR) code += signC; break; case linkB: if ((nc = state(x, y - 1)) >= 0) if ((nc & linkMask) == linkB) code += signC; if ((nc = state(x + 1, y)) >= 0) if ((nc & linkMask) == linkL) code += signR; if ((nc = state(x - 1, y)) >= 0) if ((nc & linkMask) == linkR) code += signL; break; case linkL: if ((nc = state(x, y - 1)) >= 0) if ((nc & linkMask) == linkB) code += signL; if ((nc = state(x + 1, y)) >= 0) if ((nc & linkMask) == linkL) code += signC; if ((nc = state(x, y + 1)) >= 0) if ((nc & linkMask) == linkT) code += signR; break; } return code; } public boolean hitbyT(int x, int y) { int s = state(x, y - 1); if (s >= 0) if ((s & modeMask) == modeA) { if ((s & linkMask) == linkT && (s & signC) == signC) return true; if ((s & linkMask) == linkR && (s & signL) == signL) return true; if ((s & linkMask) == linkL && (s & signR) == signR) return true; } return false; } public boolean hitbyR(int x, int y) { int s = state(x + 1, y); if (s >= 0) if ((s & modeMask) == modeA) { if ((s & linkMask) == linkR && (s & signC) == signC) return true; if ((s & linkMask) == linkB && (s & signL) == signL) return true; if ((s & linkMask) == linkT && (s & signR) == signR) return true; } return false; } public boolean hitbyB(int x, int y) { int s = state(x, y + 1); if (s >= 0) if ((s & modeMask) == modeA) { if ((s & linkMask) == linkB && (s & signC) == signC) return true; if ((s & linkMask) == linkL && (s & signL) == signL) return true; if ((s & linkMask) == linkR && (s & signR) == signR) return true; } return false; } public boolean hitbyL(int x, int y) { int s = state(x - 1, y); if (s >= 0) if ((s & modeMask) == modeA) { if ((s & linkMask) == linkL && (s & signC) == signC) return true; if ((s & linkMask) == linkT && (s & signL) == signL) return true; if ((s & linkMask) == linkB && (s & signR) == signR) return true; } return false; } public int nextState(int ip, int x, int y) { int c = cell[ip], n, r; boolean ht, hr, hb, hl; if (c == quiescent) { /*** quiescent ***/ ht = hitbyT(x, y); hr = hitbyR(x, y); hb = hitbyB(x, y); hl = hitbyL(x, y); if ( ht && !hr && !hb && !hl) return linkT; if (!ht && hr && !hb && !hl) return linkR; if (!ht && !hr && hb && !hl) return linkB; if (!ht && !hr && !hb && hl) return linkL; return quiescent; } else { /*** structure ***/ r = root(ip, x, y); if ((c & modeMask) == modeP) { /*** passive ***/ if (r >= 0) { n = c - (c & signMask) + (r & signMask); if (!linked(ip, x, y) && (n & signMask) != 0) n |= modeA; } else { n = c - (c & signMask) + encode(ip, x, y); n |= modeA; } if (hitbyT(x, y)) n |= state(x, y - 1) & signMask; if (hitbyR(x, y)) n |= state(x + 1, y) & signMask; if (hitbyB(x, y)) n |= state(x, y + 1) & signMask; if (hitbyL(x, y)) n |= state(x - 1, y) & signMask; return n; } else { /*** active ***/ if (r >= 0 && !linked(ip, x, y)) { n = c - modeA - (c & signMask) + (r & signMask); if (hitbyT(x, y)) n |= state(x, y - 1) & signMask; if (hitbyR(x, y)) n |= state(x + 1, y) & signMask; if (hitbyB(x, y)) n |= state(x, y + 1) & signMask; if (hitbyL(x, y)) n |= state(x - 1, y) & signMask; return n; } else return quiescent; } } } /*** simulation engine ***/ public void switchcheck() { boolean changed = false; if (displayswitch == true) { displayswitch = false; displaying = displaycheckbox.getState(); changed = true; } if (magnifyswitch == true) { magnifyswitch = false; if (magnifying) { magnifying = false; switch (choiceofspace.getSelectedIndex()) { case 0 : space = 50; pixel = 12; break; case 1 : space = 100; pixel = 6; break; case 2 : space = 200; pixel = 3; break; } fromx = fromy = 0; tox = toy = space - 1; showStatus("Magnification finished"); changed = true; } else { if (pixel == 12) magnifycheckbox.setState(false); else { pausebutton.setLabel("Start"); running = onestep = false; showStatus("Click where you want to magnify"); selectingpoint = true; while (selectingpoint) { try{th.sleep(1);} catch(InterruptedException e){} }; if (magnifying) changed = true; } } } if (changed) repaint(); } public void run() { int ip, np, x, y; while (true) { while (!running && !onestep) { switchcheck(); try{th.sleep(1);} catch(InterruptedException e){} } onestep = false; for (ip = sp, np = (sp == 0) ? space * space : 0, y = 0; y < space; y ++) for (x = 0; x < space; x ++) { if ((cell[np] = nextState(ip, x, y)) != cell[ip]) { if (displaying) { if (pixel == 12) { erase_cell(ip, x, y); display_cell(np, x, y); } else { if (cell[ip] < 0 || cell[np] < 0 || ((cell[ip] & linkMask) != (cell[np] & linkMask))) { erase_cell(ip, x, y); display_cell(np, x, y); } } } } ip ++; np ++; } sp = (sp == 0) ? space * space : 0; if (displaying) blackFrame(); timestep ++; showStatus("Time: " + Integer.toString(timestep)); switchcheck(); try{th.sleep(1);} catch(InterruptedException e){} } } }