Add solutions for part 1
This commit is contained in:
205
projects/09/CellAutomaton1D/CellularAutomaton.jack
Normal file
205
projects/09/CellAutomaton1D/CellularAutomaton.jack
Normal 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;
|
||||
}
|
||||
}
|
||||
370
projects/09/CellAutomaton1D/CellularAutomatonController.jack
Normal file
370
projects/09/CellAutomaton1D/CellularAutomatonController.jack
Normal 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;
|
||||
}
|
||||
}
|
||||
32
projects/09/CellAutomaton1D/Main.jack
Normal file
32
projects/09/CellAutomaton1D/Main.jack
Normal 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;
|
||||
}
|
||||
}
|
||||
20
projects/09/CellAutomaton1D/readme.md
Normal file
20
projects/09/CellAutomaton1D/readme.md
Normal 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!
|
||||
Reference in New Issue
Block a user