206 lines
6.8 KiB
Plaintext
206 lines
6.8 KiB
Plaintext
/** 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;
|
|
}
|
|
}
|