/** 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(" 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 "); do makeWaitDots(); do Output.moveCursor(4, 0); do Output.printString("> Move with / and use / 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 / to switch between rules"); do makeWaitDots(); do Output.moveCursor(12, 0); do Output.printString("> Rule selected. Confirm with ."); 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 for quit)"); do makeWaitDots(); do Output.moveCursor(19, 0); do Output.printString("> Controls: "); do makeWaitDots(); do Output.moveCursor(20, 0); do Output.printString(">

ause, ontinue, tep, restart, 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; } }