Add solutions for part 1

This commit is contained in:
2020-11-15 13:57:48 -05:00
parent e4f9fd2682
commit 742db6d102
479 changed files with 202980 additions and 13 deletions

View File

@@ -0,0 +1,205 @@
/** 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;
}
}

View File

@@ -0,0 +1,370 @@
/** Manages the cellular automaton and interacts with the user. */
class CellularAutomatonController {
field CellularAutomaton automaton;
field Array config;
field Array rules;
field int numRules;
constructor CellularAutomatonController new() {
// Turned out it is easiest to manage the Automaton itself from here.
let automaton = CellularAutomaton.new();
// We need the cell state array to write the initial configuration
// by the user into it.
let config = automaton.getCurrentConfig();
// Array to switch between rules.
let numRules = 4;
let rules = Array.new(numRules);
let rules[0] = 57;
let rules[1] = 90;
let rules[2] = 101;
let rules[3] = 110;
return this;
}
method void dispose() {
// config gets deAllocated by CellularAutomaton!
do automaton.dispose();
do rules.dispose();
do Memory.deAlloc(this);
return;
}
/** Greets the user and guides through the initial configuration. */
method void initialConfiguration() {
do Screen.clearScreen();
do Screen.setColor(true);
do sayHello();
do printHelpInitialConfiguration();
do createInitialConfiguration();
do selectRule();
do finishConfiguration();
return;
}
/** Prints some dots on the screen to simulate loading, because why not? */
method void makeWaitDots() {
do Sys.wait(30);
do Output.printString(".");
do Sys.wait(30);
do Output.printString(".");
do Sys.wait(30);
do Output.printString(".");
do Sys.wait(30);
do Output.printString(" ");
return;
}
method void waitForEnter() {
var int key;
var boolean exit;
let key = 0;
let exit = false;
do Output.printString("<Enter> to continue. ");
while (~exit) {
while (key = 0) {
let key = Keyboard.keyPressed();
}
if (key = 128) { let exit = true; } // enter key
while (~(key = 0)) {
let key = Keyboard.keyPressed();
}
}
return;
}
method void sayHello() {
do Output.moveCursor(0, 0);
do Output.printString("> Welcome to cellular automaton 1D! ");
do makeWaitDots();
do Output.moveCursor(1, 0);
do Output.printString("> Let's configure the automaton");
do makeWaitDots();
return;
}
method void printHelpInitialConfiguration() {
do Output.moveCursor(3, 0);
do Output.printString("> Toggle the cells that should be alive with <space>");
do makeWaitDots();
do Output.moveCursor(4, 0);
do Output.printString("> Move with <h>/<l> and use <u>/<i> to change the step size");
do makeWaitDots();
do Output.moveCursor(6, 0);
do Output.printString("> stepSize = 1 - position = - activeCells = ");
return;
}
method void printStepSize(int n) {
do Output.moveCursor(6, 15);
do Output.printString(" ");
do Output.moveCursor(6, 15);
do Output.printInt(n);
return;
}
method void printCursorPosition(int n) {
do Output.moveCursor(6, 32);
do Output.printString(" ");
do Output.moveCursor(6, 32);
do Output.printInt(n);
return;
}
method void printActiveCellsj(int n) {
do Output.moveCursor(6, 52);
do Output.printString(" ");
do Output.moveCursor(6, 52);
do Output.printInt(n);
return;
}
method void createInitialConfiguration() {
var int key;
var boolean exit;
var int cursorPosition;
var int stepSize;
var int activeCells;
// Variables for printing the cell selction UI.
var int cursorY1, cursorY2, cellY1, cellY2;
let key = 0;
let cursorY1 = 90;
let cursorY2 = 95;
let cellY1 = 85;
let cellY2 = 88;
let stepSize = 1;
let cursorPosition = 0;
let activeCells = 0;
do printStepSize(stepSize);
do printCursorPosition(cursorPosition);
do printActiveCellsj(activeCells);
do Screen.drawLine(cursorPosition, cursorY1, cursorPosition, cursorY2);
let exit = false;
while (~exit) {
while (key = 0) {
let key = Keyboard.keyPressed();
}
if (key = 128) { // enter key
if (activeCells > 0) {
let exit = true;
} else {
do Output.moveCursor(9, 0);
do Output.printString("> Toggle at least one cell!");
}
} // enter key
if (key = 72) { // h key
do Screen.setColor(false);
do Screen.drawLine(cursorPosition, cursorY1, cursorPosition, cursorY2);
if ((cursorPosition - stepSize) > 0) {
let cursorPosition = cursorPosition - stepSize;
} else {
let cursorPosition = 0;
}
do Screen.setColor(true);
do Screen.drawLine(cursorPosition, cursorY1, cursorPosition, cursorY2);
do printCursorPosition(cursorPosition);
}
if (key = 76) { // l key
do Screen.setColor(false);
do Screen.drawLine(cursorPosition, cursorY1, cursorPosition, cursorY2);
if ((cursorPosition + stepSize) < 511) {
let cursorPosition = cursorPosition + stepSize;
} else {
let cursorPosition = 511;
}
do Screen.setColor(true);
do Screen.drawLine(cursorPosition, cursorY1, cursorPosition, cursorY2);
do printCursorPosition(cursorPosition);
}
if (key = 73) { // i key
if (stepSize < 512) {
let stepSize = stepSize * 2;
do printStepSize(stepSize);
}
}
if (key = 85) { // u key
if (stepSize > 1) {
let stepSize = stepSize / 2;
do printStepSize(stepSize);
}
}
if (key = 32) { // space key
if (config[cursorPosition]) { // cell is alive -> make dead
do Screen.setColor(false);
do Screen.drawLine(cursorPosition, cellY1, cursorPosition, cellY2);
let config[cursorPosition] = 0;
let activeCells = activeCells - 1;
} else { // cell is dead -> make alive
do Screen.setColor(true);
do Screen.drawLine(cursorPosition, cellY1, cursorPosition, cellY2);
let config[cursorPosition] = 1;
let activeCells = activeCells + 1;
}
do Screen.setColor(true);
do printActiveCellsj(activeCells);
}
while (~(key = 0)) {
let key = Keyboard.keyPressed();
}
}
return;
}
method void printRule(int n) {
do Output.moveCursor(12, 8);
do Output.printString(" ");
do Output.moveCursor(12, 8);
do Output.printInt(n);
return;
}
method void selectRule() {
var int i;
var int rule;
var int key;
var boolean exit;
let i = 1;
let rule = rules[i];
let exit = false;
do Output.moveCursor(10, 0);
do Output.printString("> Good. Let's select which rule you want to simulate");
do makeWaitDots();
do Output.moveCursor(11, 0);
do Output.printString("> Use <h>/<l> to switch between rules");
do makeWaitDots();
do Output.moveCursor(12, 0);
do Output.printString("> Rule selected. Confirm with <enter>.");
do printRule(rule);
while (~exit) {
while (key = 0) {
let key = Keyboard.keyPressed();
}
if (key = 128) { // enter key
let exit = true;
}
if (key = 72) { // h key -> prev rule
if (i > 0) {
let i = i - 1;
}
}
if (key = 76) {
if (i < (numRules - 1)) {
let i = i + 1;
}
}
let rule = rules[i];
do printRule(rule);
do automaton.setRule(rule);
while (~(key = 0)) {
let key = Keyboard.keyPressed();
}
}
return;
}
method void finishConfiguration() {
do Output.moveCursor(15, 0);
do Output.printString("> Okay, nice. We are ready to go");
do makeWaitDots();
do Output.moveCursor(16, 0);
do Output.printString("> Once you press enter you will no longer see the menu");
do makeWaitDots();
do Output.moveCursor(17, 0);
do Output.printString("> So remember the following keys (or at least <q> for quit)");
do makeWaitDots();
do Output.moveCursor(19, 0);
do Output.printString("> Controls: ");
do makeWaitDots();
do Output.moveCursor(20, 0);
do Output.printString("> <p>ause, <c>ontinue, <s>tep, <r>restart, <q>quit! ");
do Output.moveCursor(22, 0);
do Output.printString(" ");
do waitForEnter();
do Screen.clearScreen();
return;
}
method void run() {
var int key;
var boolean quit;
var boolean running;
var boolean restart;
var boolean step;
let quit = false;
let restart = false;
let running = true;
let step = false;
while (~quit) {
if (restart) {
do automaton.dispose();
let automaton = CellularAutomaton.new();
do initialConfiguration();
let quit = false;
let restart = false;
let running = true;
let step = false;
}
if (running) {
do automaton.draw();
do automaton.step();
}
if (step) {
do automaton.draw();
do automaton.step();
let step = false;
}
let key = Keyboard.keyPressed();
if (key = 82) { // r key
let restart = true;
}
if (key = 80) { // p key
let running = false;
}
if (key = 67) { // c key
let running = true;
}
if (key = 83) { // s key
if (~running) {
let step = true;
}
}
if (key = 81) { // q key
let quit = true;
}
while (~(key = 0)) {
let key = Keyboard.keyPressed();
}
}
return;
}
}

View File

@@ -0,0 +1,32 @@
/** Implements a cellular automaton.
*
* In the first state the user can define the initial configuration (the cells
* that are alive in the beginning. If the user is not sure about which cells
* to enable choosing the one in the middle (255/256) is ususually a good
* start.
*
* Next, the user selects the rule they want to simulate. A list of all rules
* is available on Wikipedia. If the user is not sure which rule they want Rule
* 90 is a good start to get the idea.
*
* https://en.wikipedia.org/wiki/Elementary_cellular_automaton
*
* In the second stage the user can select the initial configuration of the
* automaton. The keys 'h' and 'l' move the cursor one pixel to the left or
* right respectively. By pressing the space bar the user can toggle the
* current pixel. By pressing the 'r' key the simulation is started. Pressing
* 'q' ends the simulator.
*/
/** Initializes a new Cellular Automaton Game and runs it. */
class Main {
function void main() {
var CellularAutomatonController controller;
let controller = CellularAutomatonController.new();
do controller.initialConfiguration();
do controller.run();
do controller.dispose();
return;
}
}

View File

@@ -0,0 +1,20 @@
Cellular Automaton 1D
---------------------
This project implements a one dimensional cellular automaton. It is like the
famous *Game of Life*, but in 1D. If you are not familiar with the concept you
can read more on
[mathworld.wolfram.com](https://mathworld.wolfram.com/CellularAutomaton.html)
so I recommend simply try the application. It should be self explanatory and
you will be surprised.
The program runs in two stages. In the first stage the user configures the
initial population of the one automaton as well as the rule that is used for
creating the following populations. If you are not familiar with the rules
*Rule 90* with an initial cell in the middle is good enough.
In the second stage the program computes and visualizes the automaton. The
program can be paused with `p`, continued with `c`, single stepped with `s`,
reset with `r`, and terminated with `q`.
Have fun!