/** Represents a 1D cellular automaton that can be printed to the screen. */ class CellularAutomaton { field Array currentConfig; // an array where each field represents one cell field int currentRule; // the current rule used by nextState field int currentRow; // the row on the screen where the last state was printed field int size; // cells per row field boolean debug; // print debugging help to screen if true /** Create a new automaton where size is the number of cells per row. */ constructor CellularAutomaton new() { var int i; let currentRule = 0; let currentRow = 0; let size = 512; let debug = false; let currentConfig = Array.new(size); let i = 0; while (i < size) { let currentConfig[i] = 0; let i = i + 1; } return this; } /** Returns a reference to the current config. Be careful, step creates a * new configuration so using the reference after calling step points to * invalid memory. */ method Array getCurrentConfig() { return currentConfig; } method void dispose() { do currentConfig.dispose(); do Memory.deAlloc(this); return; } method void setRule(int rule) { let currentRule = rule; return; } /** Draws the current configuration to the currentRow on the screen. */ method void draw() { if (currentRow > 255) { // This works, but is slow and I don't know how to make it faster. // So we simply start from the top instead again. // do drawLastRow(); let currentRow = 0; do Screen.clearScreen(); do drawNewRow(); } else { do drawNewRow(); } if (debug) { do Screen.setColor(true); do Output.moveCursor(0, 0); do Output.printInt(currentRow); } return; } /** If we are not yet in the last row we can simply * print the new config to the next row. */ method void drawNewRow() { var int i; let i = 0; while (i < size) { if (currentConfig[i]) { do Screen.setColor(true); } else { do Screen.setColor(false); } do Screen.drawPixel(i, currentRow); let i = i + 1; } return; } method void copy(int sourceAddress, int targetAddress) { var int value; let value = Memory.peek(sourceAddress); do Memory.poke(targetAddress, value); return; } /** If we are in the last row we first have to move all existing rows one * row up. We can then draw the last row. XXX: This works, but we are * currently not using it because it is too slow. */ method void drawLastRow() { var int i; var int sourceAddress; // Screen Base + Words = 16384 + 8160 = 24544 let i = 16383; while (i < 24544) { let sourceAddress = i + 32; do copy(sourceAddress, i); let i = i + 1; } let i = 0; while (i < size) { if (currentConfig[i]) { do Screen.setColor(true); } else { do Screen.setColor(false); } do Screen.drawPixel(i, currentRow); let i = i + 1; } return; } method int rule_57(int left, int middle, int right) { if (left = 1) { if (middle = 1) { if (right = 1) { return 0; } } } if (left = 1) { if (middle = 1) { if (right = 0) { return 0; } } } if (left = 0) { if (middle = 1) { if (right = 0) { return 0; } } } if (left = 0) { if (middle = 0) { if (right = 1) { return 0; } } } return 1; } method int rule_90(int left, int middle, int right) { let middle = left + right; if (middle = 1) { return 1; } return 0; } method int rule_101(int left, int middle, int right) { if (left = 1) { if (middle = 1) { if (right = 1) { return 0; } } } if (left = 1) { if (middle = 0) { if (right = 0) { return 0; } } } if (left = 0) { if (middle = 1) { if (right = 1) { return 0; } } } if (left = 0) { if (middle = 0) { if (right = 1) { return 0; } } } return 1; } method int rule_110(int left, int middle, int right) { if (left = 1) { if (middle = 1) { if (right = 1) { return 0; } } } if (left = 1) { if (middle = 0) { if (right = 0) { return 0; } } } if (left = 0) { if (middle = 0) { if (right = 0) { return 0; } } } return 1; } /** Calculates the next configuration based on currentConfig and * currentRule */ method void step() { var int i, limit; var Array newConfig; let newConfig = Array.new(size); let currentRow = currentRow + 1; if (currentRule = 57) { // Calculate new state of first cell let newConfig[0] = rule_57(0, currentConfig[0], currentConfig[1]); // Calculate new state of last cell let newConfig[511] = rule_57(currentConfig[510], currentConfig[511], 0); // All the other cells let i = 1; // Skip first cell and last cell while (i < 511) { let newConfig[i] = rule_57(currentConfig[i - 1], currentConfig[i], currentConfig[i + 1]); let i = i + 1; } } if (currentRule = 90) { let newConfig[0] = rule_90(0, currentConfig[0], currentConfig[1]); let newConfig[511] = rule_90(currentConfig[510], currentConfig[511], 0); let i = 1; while (i < 511) { let newConfig[i] = rule_90(currentConfig[i - 1], currentConfig[i], currentConfig[i + 1]); let i = i + 1; } } if (currentRule = 101) { let newConfig[0] = rule_101(0, currentConfig[0], currentConfig[1]); let newConfig[511] = rule_101(currentConfig[510], currentConfig[511], 0); let i = 1; while (i < 511) { let newConfig[i] = rule_101(currentConfig[i - 1], currentConfig[i], currentConfig[i + 1]); let i = i + 1; } } if (currentRule = 110) { let newConfig[0] = rule_110(0, currentConfig[0], currentConfig[1]); let newConfig[511] = rule_110(currentConfig[510], currentConfig[511], 0); let i = 1; while (i < 511) { let newConfig[i] = rule_110(currentConfig[i - 1], currentConfig[i], currentConfig[i + 1]); let i = i + 1; } } do currentConfig.dispose(); let currentConfig = newConfig; return; } }