Commits

zetro  committed 2541e05

Implemented HPP (lattice gas cellular automaton).
Renamed BrianBrain and LangtonAnt and corrected Turmite.

  • Participants
  • Parent commits 7ff1627

Comments (0)

Files changed (8)

File src/org/bitbucket/zetro/squaredino/backend/AbstractAutomaton.java

 package org.bitbucket.zetro.squaredino.backend;
 
-import java.util.Collection;
 import java.util.List;
 
 /**
 	 */
 	public static final String STANDARD_RULE = GameOfLife.STANDARD_RULE;
 	/**
-	 * Rules.
-	 */
-	protected Collection<Integer> born, survive;
-	/**
 	 * Cellular automaton.
 	 */
 	protected CellularAutomaton automaton;
 
 	/**
-	 * Automaton constructor. Uses standard rule "3/23".
+	 * Automaton constructor using standard rules.
 	 */
 	public AbstractAutomaton() {
 		parseRule(AbstractAutomaton.STANDARD_RULE);
 			name = name.toLowerCase();
 			if (name.equals("life")) {
 				automaton = new GameOfLife(value == null ? GameOfLife.STANDARD_RULE : value);
-			} else if (name.equals("brian") || name.equals("brain")) {
-				automaton = new BrianBrain();
+			} else if (name.equals("brians-brain") || name.equals("brian") || name.equals("brain")) {
+				automaton = new BriansBrain();
 			} else if (name.equals("langtons-ant") || name.equals("langton") || name.equals("ant")) {
-				automaton = new LangtonAnt(value == null ? LangtonAnt.STANDARD_RULE : value);
+				automaton = new LangtonsAnt(value == null ? LangtonsAnt.STANDARD_RULE : value);
 			} else if (name.equals("turmite")) {
 				automaton = new Turmite(value == null ? Turmite.STANDARD_RULE : value);
+			} else if (name.equals("hpp")) {
+				automaton = new HPP();
+			} else {
+				throw new IllegalArgumentException("Unknown rule type in rule \"" + rule + "\"");
 			}
 		}
 	}
 	 * @return the rules as a string.
 	 */
 	public String getRuleAsString() {
-		String prepend;
+		String name;
 		if (automaton instanceof GameOfLife) {
-			prepend = "";
-		} else if (automaton instanceof BrianBrain) {
-			prepend = "brian";
-		} else if (automaton instanceof LangtonAnt) {
-			prepend = "langton:";
+			name = "";
+		} else if (automaton instanceof BriansBrain) {
+			name = "Brians-Brain";
+		} else if (automaton instanceof LangtonsAnt) {
+			name = "Langtons-Ant";
 		} else if (automaton instanceof Turmite) {
-			prepend = "turmite:";
+			name = "Turmite";
+		} else if (automaton instanceof HPP) {
+			name = "HPP";
 		} else {
-			prepend = "<unknown>:";
+			name = "<Unknown>";
 		}
 
-		return prepend + automaton.getRule();
+		String rule = automaton.getRule();
+		return name + (rule != null ? (name.isEmpty() ? "" : ":") + rule : "");
 	}
 }

File src/org/bitbucket/zetro/squaredino/backend/BriansBrain.java

+package org.bitbucket.zetro.squaredino.backend;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Implementation of Brian's Brain.
+ */
+public class BriansBrain extends CellularAutomaton {
+	/**
+	 * States.
+	 */
+	public static final Cell OFF = null,
+			ON = Cell.createCell(1),
+			DYING = Cell.createCell(2);
+
+	@Override
+	public List<Cell> getStates() {
+		return Arrays.asList(ON, DYING, OFF);
+	}
+
+	@Override
+	public Cell getNextState(Cell cell, Cell... neighbours) {
+		if (cell == null) {
+			if (getCount(neighbours, ON) == 2) {
+				return ON;
+			} else {
+				return cell;
+			}
+		} else if (cell.equals(ON)) {
+			return DYING;
+		} else if (cell.equals(DYING)) {
+			return OFF;
+		}
+		System.err.println("Warning: Invalid cell " + cell);
+		return null;
+	}
+
+	@Override
+	public String getRule() {
+		return null;
+	}
+}

File src/org/bitbucket/zetro/squaredino/backend/HPP.java

+package org.bitbucket.zetro.squaredino.backend;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Implementation of HPP. <br> Example:
+ * <code>14P$P12.P$P12.P$P8.A3.P$P12.P$P12.P$P3.M8.P$P12.P$P12.P$14P!</code>
+ */
+public class HPP extends CellularAutomaton {
+	@Override
+	public List<Cell> getStates() {
+		return Arrays.asList(Cell.createCell(2),
+				Cell.createCell(4),
+				Cell.createCell(8),
+				Cell.createCell(1),
+				Cell.createCell(16),
+				null);
+	}
+
+	@Override
+	public Cell getNextState(Cell cell, Cell... neighbours) {
+		boolean isMirror = ((cell != null ? cell.state : 0) >>> 4) == 1;
+		int collectedState = isMirror ? 1 << 4 : 0;
+
+		// Collect any orthogonally neighbouring particles moving to this cell.
+		for (int i = 0; i < neighbours.length; i += 2) {
+			Cell neighbour = neighbours[i];
+			if (neighbour == null) {
+				continue;
+			}
+
+			int direction = (i / 2 + 2 + 1) % 4; // '/2' due to orthogonal neighbour, '+2' is opposite direction and '+1' is to get compliant with SENW
+			if ((neighbour.state & (1 << direction)) != 0) {
+				if (isMirror) {
+					direction = (direction + 2) % 4;
+				}
+				collectedState |= 1 << direction;
+			}
+		}
+
+		// Handle head-on collisions
+		if (collectedState == 5) { // EW collision -> NS
+			collectedState = 10;
+		} else if (collectedState == 10) { // SN collision -> WE
+			collectedState = 5;
+		}
+
+		// Create cell
+		return Cell.createCell(collectedState);
+	}
+
+	@Override
+	public String getRule() {
+		return null;
+	}
+}

File src/org/bitbucket/zetro/squaredino/backend/LangtonsAnt.java

+package org.bitbucket.zetro.squaredino.backend;
+
+import java.util.Arrays;
+import java.util.List;
+
+/**
+ * Implementation of Langton's Ant.
+ */
+public class LangtonsAnt extends CellularAutomaton {
+	/**
+	 * Standard rule.
+	 */
+	public static final String STANDARD_RULE = "RL";
+	private final String turns;
+
+	/**
+	 * LangtonAnt constructor. Standard Langton's Ant uses rule "RL".
+	 *
+	 * @param turns turn rules
+	 */
+	public LangtonsAnt(String turns) {
+		this.turns = turns;
+	}
+
+	@Override
+	public List<Cell> getStates() {
+		// Ground 1, ant on clear ground and facing N, empty
+		return Arrays.asList(Cell.createCell(1),
+				Cell.createCell(turns.length()),
+				null);
+	}
+
+	@Override
+	public Cell getNextState(Cell cell, Cell... neighbours) {
+		int cellState = cell != null ? cell.state : 0;
+		int cellGround = cellState;
+		if (cellGround >= turns.length()) {
+			cellGround = (cellGround - turns.length()) / 4;
+		}
+		if (cellGround < 0 || cellGround > turns.length() - 1) {
+			System.err.println("Warning: Invalid ground value " + cellGround + " in cell " + cell);
+			return null;
+		}
+
+		// Check if an ant moves from this cell
+		boolean isAnt = cellState >= turns.length();
+		if (isAnt) {
+			cellGround = (cellGround + 1) % turns.length(); // Flip ground
+		}
+
+		// Check if any orthogonal neighbour is an ant moving to this cell.
+		for (int i = 0; i < neighbours.length; i += 2) {
+			Cell neighbour = neighbours[i];
+			if (neighbour == null) {
+				continue;
+			}
+			int state = neighbour.state;
+
+			// Check if neighbour is an ant
+			isAnt = state >= turns.length();
+			if (isAnt) {
+				// Retrieve what the ant stands on
+				int ground = state;
+				if (ground >= turns.length()) {
+					ground = (ground - turns.length()) / 4;
+				}
+				if (cellGround < 0 || cellGround > turns.length() - 1) {
+					System.err.println("Warning: Invalid ground value " + cellGround + " in cell " + neighbour);
+					return null;
+				}
+
+				// Turn right or left
+				int direction = (state - turns.length()) % 4;
+				direction = (direction + (turns.charAt(ground) == 'R' ? 1 : -1) + 4) % 4;
+
+				// Did the ant turn and move to towards this cell?
+				if ((direction + 2) % 4 == i / 2) {
+					// Calculate new state
+					state = turns.length() + cellGround * 4 + direction;
+					return Cell.createCell(state);
+				}
+			}
+		}
+
+		// Ground cell
+		return Cell.createCell(cellGround);
+	}
+
+	@Override
+	public String getRule() {
+		return turns;
+	}
+}

File src/org/bitbucket/zetro/squaredino/backend/Turmite.java

 		//turns = "1R0|0N0"; // Binary counter
 		//turns = "1R1|0L1 1N0|0N0"; // Langton's Ant (2-state) 
 		//turns = "0N0|1U0"; // Bounce
+		//turns = "1L1|1L1 1R1|0N0"; // Fibonacci Spiral
 
 		this.table = turns.split(" ");
 	}
 		boolean isAnt = (cellState & 0x01) == 1;
 		if (isAnt) {
 			int antState = (cellState & (((1 << (GROUND_START - STATE_START)) - 1) << STATE_START)) >>> STATE_START;
-
 			if (antState < 0 || antState > table.length - 1) {
 				System.err.println("Warning: Invalid ant state " + antState + " in cell " + cell);
 				return null;
 			cellGround = table[antState].charAt(0 + cellGround * 4) - '0';
 		}
 
-		// Check if any ortogonal neighbour is an ant moving to this cell.
+		// Check if any orthogonal neighbour is an ant moving to this cell.
 		for (int i = 0; i < neighbours.length; i += 2) {
 			Cell neighbour = neighbours[i];
-			int state = neighbour != null ? neighbour.state : 0;
+			if (neighbour == null) {
+				continue;
+			}
+			int state = neighbour.state;
 
 			// Check if neighbour is an ant moving to this cell.
 			isAnt = (state & 0x01) == 1;

File src/org/bitbucket/zetro/squaredino/backend/experimental/HashQuadTreeIterator.java

 		while (!stack.isEmpty()) {
 			final HashNode node = stack.pop();
 			final Point pos = positions.pop();
-			if (node instanceof HashContentNode || node.level == ignoreLevel) {
+			if (node instanceof HashContentNode || (node.level == ignoreLevel && node.size() != 0)) {
 				if (node instanceof HashContentNode) {
 					@SuppressWarnings("unchecked")
 					HashContentNode<T> contentNode = ((HashContentNode<T>) node);
 					}
 
 					@Override
+					@SuppressWarnings("unchecked")
 					public T getValue() {
-						if (node instanceof HashNode) {
-							return ((HashContentNode<T>) node).content;
+						if (node instanceof HashContentNode) {
+							return (T) ((HashContentNode) node).content;
 						}
 						return null;
 					}

File src/org/bitbucket/zetro/squaredino/gui/BoardPanel.java

 import java.awt.event.MouseWheelEvent;
 import java.awt.event.MouseWheelListener;
 import java.awt.image.BufferedImage;
-import java.awt.image.WritableRaster;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
 		Selection view = new Selection(topLeft,
 				new Point(topLeft.x + (int) (getWidth() / zoom) + 1, 
 						  topLeft.y + (int) (getHeight() / zoom) + 1));
-		int ignoreLevel;
-		if (zoom >= 1) ignoreLevel = 0;
-		else {
-			ignoreLevel = (int) (Math.log(1 / zoom) / Math.log(2));
-		}
+		int ignoreLevel = zoom < 1 ? (int) (Math.log(1 / zoom) / Math.log(2)) : 0;
 		Iterator<Map.Entry<Point, Cell>> cells = board.getLivingCells(view, ignoreLevel); 
 		int size = (int) Math.max(1, zoom);
-		if (ignoreLevel > 0) {
+		if (size == 1) {
 			int w = getWidth();
 			int h = getHeight();
 			BufferedImage image = new BufferedImage(w, h, BufferedImage.TYPE_INT_ARGB);
-			WritableRaster raster = image.getRaster();
 			
-			Object c = image.getColorModel().getDataElements(cellColors[1].getRGB(), null);
 			while(cells.hasNext()) {
-				Point p = cells.next().getKey(); 
+				Map.Entry<Point, Cell> entry = cells.next();
+				Point p = entry.getKey(); 
 				int x = paddx + tempaddx + (int) (p.x * zoom);
 				int y = paddy + tempaddy + (int) (p.y * zoom);
 				if (x >= 0 && y >= 0 && x < w && y < h) {
-					raster.setDataElements(x, y, c);
+					int state = entry.getValue() != null ? entry.getValue().state : 1;
+					image.setRGB(x, y, cellColors[state % cellColors.length].getRGB());
 				}
 			}
 			

File src/org/bitbucket/zetro/squaredino/io/SquareIO.java

 import java.io.Writer;
 import java.util.ArrayList;
 import java.util.Comparator;
-import java.util.HashMap;
 import java.util.Iterator;
 import java.util.Map;
 import java.util.Scanner;
 import java.util.SortedMap;
-import java.util.SortedSet;
 import java.util.TreeMap;
-import java.util.TreeSet;
 import org.bitbucket.zetro.squaredino.backend.AbstractAutomaton;
 import org.bitbucket.zetro.squaredino.backend.Board;
 import org.bitbucket.zetro.squaredino.backend.BoardFactory;
 import org.bitbucket.zetro.squaredino.backend.Cell;
-import org.bitbucket.zetro.squaredino.backend.GameOfLife;
 import org.bitbucket.zetro.squaredino.misc.Point;
 
 /**
 			}
 			if (state == 0) {
 				data.append("b");
-			} else if (state == 1) {
+			} else if (state == -1) {
 				data.append("o");
 			} else {
-				System.out.print(state + " -> ");
-
-				int multiplier = state / 25;
-				int add = state % 25;
+				int multiplier = (state - 1) / 24;
+				int add = (state - 1) % 24;
 
 				if (multiplier > 0) {
-					System.out.print((char) (('p' - 1) + multiplier));
 					data.append((char) (('p' - 1) + multiplier));
 				}
-				System.out.println((char) (('A' - 1) + add));
-				data.append((char) (('A' - 1) + add));
+				data.append((char) ('A' + add));
 			}
 		}
 	}