Files
N2T/projects/09/CellAutomaton1D/CellularAutomaton.jack
2020-11-15 13:57:48 -05:00

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;
}
}