Commits

zetro committed e91fd66

Added support for multiple states in RLE import/export.

Comments (0)

Files changed (6)

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

 	}
 
 	private void parseRule(String rule) throws IllegalArgumentException {
+		if (rule == null) {
+			throw new IllegalArgumentException("Invalid rule: " + rule);
+		}
+
 		if (!rule.contains(":") && rule.contains("/")) {
 			automaton = new GameOfLife(rule);
 		} else {
 				automaton = new GameOfLife(value == null ? GameOfLife.STANDARD_RULE : value);
 			} else if (name.equals("brian") || name.equals("brain")) {
 				automaton = new BrianBrain();
-			} else if (name.equals("langton") || name.equals("ant")) {
+			} else if (name.equals("langtons-ant") || name.equals("langton") || name.equals("ant")) {
 				automaton = new LangtonAnt(value == null ? LangtonAnt.STANDARD_RULE : value);
 			} else if (name.equals("turmite")) {
 				automaton = new Turmite(value == null ? Turmite.STANDARD_RULE : value);
 	}
 
 	/**
-	 * Sets the rules for this automaton. <br />Syntax: '/' separates two
-	 * strings containing numbers 0-8. Both 3/23 and B3/S23 is allowed.
+	 * Sets the rules for this automaton.
 	 *
 	 * @param rule the new rule
-	 * @throws IllegalArgumentException thrown if rule is null, isn't separated
-	 * by '/' or separated strings contain characters out of range.
+	 * @throws IllegalArgumentException thrown if rule is null or invalid.
 	 */
 	public void setRule(String rule) throws IllegalArgumentException {
 		parseRule(rule);

src/org/bitbucket/zetro/squaredino/backend/BrianBrain.java

 		} else if (cell.equals(DYING)) {
 			return OFF;
 		}
-		throw new IllegalArgumentException(String.valueOf(cell));
+		System.err.println("Warning: Invalid cell " + cell);
+		return null;
 	}
 
 	@Override

src/org/bitbucket/zetro/squaredino/backend/Cell.java

 		hash = 71 * hash + this.state;
 		return hash;
 	}
+
+	@Override
+	public String toString() {
+		return "Cell{" + "state=" + state + '}';
+	}
 }

src/org/bitbucket/zetro/squaredino/backend/LangtonAnt.java

 	 * Standard rule.
 	 */
 	public static final String STANDARD_RULE = "RL";
-	private static final int GROUND_START = 3;
 	private final String turns;
 
 	/**
 	@Override
 	public List<Cell> getStates() {
 		// Ground 1, ant on clear ground and facing N, empty
-		return Arrays.asList(Cell.createCell(1 << GROUND_START), Cell.createCell(1), null);
+		return Arrays.asList(Cell.createCell(1),
+				Cell.createCell(turns.length()),
+				null);
 	}
 
 	@Override
 	public Cell getNextState(Cell cell, Cell... neighbours) {
-		int ground = cell != null ? cell.state >>> 3 : 0;
-
-		if (ground < 0 || ground > turns.length() - 1) {
-			System.err.println("Warning: Invalid ground value " + ground);
+		int cellState = cell != null ? cell.state : 0;
+		int cellGround = cellState;
+		if (cellGround >= turns.length()) {
+			cellGround -= turns.length();
+			cellGround /= 4;
+		}
+		if (cellGround < 0 || cellGround > turns.length() - 1) {
+			System.err.println("Warning: Invalid ground value " + cellGround + " in cell " + cell);
 			return null;
 		}
 
+		boolean isAnt = cellState >= turns.length();
+		if (isAnt) {
+			cellGround = (cellGround + 1) % turns.length(); // Flip ground
+		}
+
+		// Check if any ortogonal 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;
-			boolean isAnt = (state & 0x01) == 1;
-			int direction = (state & 0x06) >>> 1;
 
-			if (isAnt && (direction + 2) % 4 == i / 2) {
+			// Check if neighbour is an ant
+			isAnt = state >= turns.length();
+			if (isAnt) {
+				// Retrieve what the ant stands on
+				int ground = state;
+				if (state >= turns.length()) {
+					ground -= turns.length();
+					ground /= 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;
-				ground = (ground + 1) % turns.length();
 
-				state = (ground << GROUND_START) + (direction << 1) + 1;
-
-				return Cell.createCell(state);
+				// 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);
+				}
 			}
 		}
 
-		return ground != 0 ? Cell.createCell(ground << GROUND_START) : null;
+		// Ground cell
+		return Cell.createCell(cellGround);
 	}
 
 	@Override

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

 		int ground = cell != null ? cell.state >>> GROUND_START : 0;
 
 		if (ground < 0 || ground > table[0].length() / 4) {
-			System.err.println("Warning: Invalid ground value " + ground);
+			System.err.println("Warning: Invalid ground value " + ground + " in cell " + cell);
 			return null;
 		}
 
+		// Check if any ortogonal 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;
 			int antState = (state & (((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);
+				System.err.println("Warning: Invalid ant state " + antState + " in cell " + neighbour);
 				return null;
 			}
 
+			// Check if neighbour is an ant moving to this cell.
 			if (isAnt && (direction + 2) % 4 == i / 2) {
+				// Update state
 				int newGround = table[antState].charAt(0 + ground * 4) - '0';
-				int turn = table[antState].charAt(1 + ground * 4);
 				antState = table[antState].charAt(2 + ground * 4) - '0';
 
+				// Update direction
+				int turn = table[antState].charAt(1 + ground * 4);
 				if (turn == 'R') { // Right
 					direction++;
 				} else if (turn == 'L') { // Left
 				}
 				direction = (direction + 4) % 4;
 
+				// Calculate new state
 				state = (newGround << GROUND_START) + (antState << STATE_START) + (direction << 1) + 1;
-
 				return Cell.createCell(state);
 			}
 		}
 
+		// Ground cell
 		return ground != 0 ? Cell.createCell(ground << GROUND_START) : null;
 	}
 

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;
 		// Sorting cells
 		int sx = Integer.MAX_VALUE;
 		int sy = Integer.MAX_VALUE;
-		SortedMap<Integer, SortedSet<Point>> s = new TreeMap<Integer, SortedSet<Point>>();
+		SortedMap<Integer, SortedMap<Point, Integer>> s = new TreeMap<Integer, SortedMap<Point, Integer>>();
 		for (Iterator<Map.Entry<Point, Cell>> it = boardData.getBoard().getLivingCells(); it.hasNext();) {
 			Map.Entry<Point, Cell> entry = it.next();
 
 				sx = x;
 			}
 			if (!s.containsKey(y)) {
-				s.put(y, new TreeSet<Point>(new Comparator<Point>() {
+				s.put(y, new TreeMap<Point, Integer>(new Comparator<Point>() {
 					@Override
 					public int compare(Point o1, Point o2) {
 						return o1.x - o2.x;
 					}
 				}));
 			}
-			s.get(y).add(entry.getKey());
+			s.get(y).put(entry.getKey(), entry.getValue().state);
 		}
 
 		// Writing cells
 		StringBuilder data = new StringBuilder();
 		int y = sy;
-		for (Iterator<Map.Entry<Integer, SortedSet<Point>>> it = s.entrySet().iterator(); it.hasNext();) {
-			Map.Entry<Integer, SortedSet<Point>> entry = it.next();
+		for (Iterator<Map.Entry<Integer, SortedMap<Point, Integer>>> it = s.entrySet().iterator(); it.hasNext();) {
+			Map.Entry<Integer, SortedMap<Point, Integer>> entry = it.next();
 			int dy = entry.getKey() - y;
 			y = entry.getKey();
 			if (dy > 0) {
 
 			int x = sx - 1;
 			int alive = 0;
-			for (Point point : entry.getValue()) {
+			int cellState = 0;
+			for (Iterator<Map.Entry<Point, Integer>> it2 = entry.getValue().entrySet().iterator(); it2.hasNext();) {
+				Map.Entry<Point, Integer> entry2 = it2.next();
+				Point point = entry2.getKey();
+				int state = entry2.getValue();
+
 				if (data.length() > 70) {
 					writer.append(data + "\n");
 					data = new StringBuilder();
 				int dx = point.x - x;
 				x = point.x;
 				if (dx > 1) {
-					if (alive > 0) {
-						if (alive > 1) {
-							data.append(alive);
-						}
-						data.append("o");
-					}
+					appendCell(data, cellState, alive);
+					appendCell(data, 0, dx - 1);
 					alive = 0;
-					if (dx > 2) {
-						data.append(dx - 1);
-					}
-					data.append("b");
+				}
+				if (cellState != state) {
+					appendCell(data, cellState, alive);
+					cellState = state;
+					alive = 0;
 				}
 				alive++;
 			}
-			if (alive > 1) {
-				data.append(alive);
-			}
-			data.append("o");
+			appendCell(data, cellState, alive);
 		}
 		data.append("!");
 		writer.append(data);
 		writer.close();
 	}
 
+	private static void appendCell(StringBuilder data, int state, int amount) {
+		if (amount > 0) {
+			if (amount > 1) {
+				data.append(amount);
+			}
+			if (state == 0) {
+				data.append("b");
+			} else if (state == 1) {
+				data.append("o");
+			} else {
+				System.out.print(state + " -> ");
+
+				int multiplier = state / 25;
+				int add = state % 25;
+
+				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));
+			}
+		}
+	}
+
 	/**
 	 * Loads a board and its data from a reader. Reads in RLE format.
 	 *
 				if (parsedHeader) {
 					System.err.println("Warning: #-line after header");
 				}
+				if (line.length() == 1) {
+					continue;
+				}
 				char type = line.charAt(1);
 				String nline = line.length() >= 2 ? line.substring(2).trim() : "";
 
 				} else {
 					//System.out.println("Cell Line: " + line);
 					int runCount = 1;
+					char[] cs = line.toCharArray();
 					StringBuilder buf = new StringBuilder();
-					for (char c : line.toCharArray()) {
+					for (int ii = 0; ii < cs.length; ii++) {
+						char c = cs[ii];
 						if (c >= '0' && c <= '9') {
 							buf.append(c);
 							continue;
 						} else if (buf.length() > 0) {
 							runCount = Integer.parseInt(buf.toString());
 							buf.setLength(0);
+						} else if (c >= 'p' && c <= 'z' && ii + 1 < cs.length) {
+							char c2 = cs[ii + 1];
+							if (c2 >= 'A' && c2 <= 'Z') {
+								int state = (c - 'p' + 1) * 24 + (c2 - 'A' + 1);
+
+								Cell cell = Cell.createCell(state);
+								for (int i = 0; i < runCount; i++) {
+									board.addCell(cell, xPos, yPos);
+									xPos++;
+								}
+								runCount = 1;
+								ii++;
+								continue;
+							}
 						}
-						if (c == 'b') { // dead cell
+						if (c >= 'A' && c <= 'Z') {
+							int state = c - 'A' + 1;
+
+							Cell cell = Cell.createCell(state);
+							for (int i = 0; i < runCount; i++) {
+								board.addCell(cell, xPos, yPos);
+								xPos++;
+							}
+							runCount = 1;
+							continue;
+						} else if (c == 'b' || c == '.') { // dead cell
 							xPos += runCount;
 							runCount = 1;
 						} else if (c == 'o') { // alive cell
+							Cell cell = Cell.createCell(1);
 							for (int i = 0; i < runCount; i++) {
-								Cell cell = GameOfLife.ALIVE;
 								board.addCell(cell, xPos, yPos);
 								xPos++;
 							}