Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 37 additions & 9 deletions index.html
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
<html lang="en">

<head>
<title>Langton's Ant Music v2</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Langton's Ant Music v3-DEV</title>
<meta name="viewport" content="width=device-width">
<meta property="og:site_name" content="Langton's Ant Music" />
<meta property="og:title" content="Langton's Ant Music" />
<meta property="og:description" content="Langton's Ant plays the drums!" />
Expand All @@ -23,13 +23,35 @@
<script src="https://cdn.jsdelivr.net/npm/[email protected]/src-noconflict/ace.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/seedrandom.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/mousetrap.min.js"></script>
<script>
onerror = function (event, source, lineno, colno, error) {
if (/resizeobserver/i.test(event)) return;
try {
var err = ("" +
"\nline: " + (lineno || "unknown") +
"\ncolumn: " + (colno || "unknown") +
"\nfile: " + (source || "unknown") +
"\ntraceback: " + (error == null ? "not available (" + event + " ??)" : error.stack)
);
fetch("/report_error/" + encodeURIComponent(err));
document.documentElement.innerHTML = "Fatal error: please copy and <a href=\"https://github.com/dragoncoder047/langton-music/issues\">report</a>: <pre>" + err + "</pre>";
}
catch (e) {
document.documentElement.innerHTML = "Double fatal error: please copy and <a href=\"https://github.com/dragoncoder047/langton-music/issues\">report</a>: <pre>" + e.stack + "</pre>";
throw e;
}
throw error;
}
</script>
</head>

<body>
<main class="flex-column">
<div class="heading padding">Langton's Ant Music version 2</div>
<div class="flex-row padding">
<div id="mainhead" class="heading padding">Langton's Ant Music version 3-DEV</div>
<div class="flex-row">
<div class="expanding" id="statuswrapper"><output id="statusbar"></output></div>
</div>
<div class="flex-row padding">
<a href="#editor">Edit</a>&nbsp;&nbsp;-&nbsp;&nbsp;
<a href="#xml">Formatting</a>&nbsp;&nbsp;-&nbsp;&nbsp;
<a href="#help">Help</a>&nbsp;&nbsp;-&nbsp;&nbsp;
Expand Down Expand Up @@ -219,7 +241,8 @@ <h2>Ant Breeds - <code>&lt;breed&gt;</code> tag</h2>
command. In some cases the command needs an argument; this goes inside the tag.</p>
<p>Scroll to the bottom for the list of supported commands and species of ants.</p>
<h3>Interpolations</h3>
<p>Inside each <code>&lt;command&gt;</code>, the parameter can also include
<p><strong>TODO: rewrite this for the new language</strong></p>
<!-- <p>Inside each <code>&lt;command&gt;</code>, the parameter can also include
interpolations.</p>
<p>There are two types of interpolations: fixed and expressions.</p>
<h4>Fixed Interpolations</h4>
Expand Down Expand Up @@ -361,7 +384,7 @@ <h4>Expression Interpolations</h4>
</table>
<p>An (incomplete) test suite is available at <a
href="https://dragoncoder047.github.io/langton-music/interpoltest.html">https://dragoncoder047.github.io/langton-music/interpoltest.html</a>.
</p>
</p> -->
<h2>Actual Ants - <code>&lt;ant&gt;</code> tag</h2>
<p>Ants are simpler and do not have any contents.</p>
<p>A full <code>&lt;ant&gt;</code> looks like this:
Expand Down Expand Up @@ -541,7 +564,8 @@ <h3>Screenshot</h3>
</p>
<h2>Other issues?</h2>
<p>If all else fails, or something appears to be broken, you can
<a href="https://github.com/dragoncoder047/langton-music/issues">report it on Github</a>.</p>
<a href="https://github.com/dragoncoder047/langton-music/issues">report it on Github</a>.
</p>
</div>
</div>
</div>
Expand Down Expand Up @@ -593,8 +617,11 @@ <h1>Keyboard Shortcuts</h1>
</div>
</div>
</div>
<script src="js/lib/marked.js"></script>
<script src="js/darkmode.js"></script>
<script src="js/enhancedevents.js"></script>
<script src="js/actionman.js"></script>
<script src="js/dialog.js"></script>
<script src="js/interpol.js"></script>
<script src="js/ant.js"></script>
<script src="js/notes.js"></script>
Expand All @@ -604,7 +631,8 @@ <h1>Keyboard Shortcuts</h1>
<script src="js/world.js"></script>
<script src="js/actions.js"></script>
<script src="js/media.js"></script>
<script src="js/main.js"></script>
<script src="js/app.js"></script>
<script src="js/main_load.js"></script>
</body>

</html>
</html>
30 changes: 7 additions & 23 deletions js/actionman.js
Original file line number Diff line number Diff line change
@@ -1,32 +1,16 @@
/**
* manager for Action callbacks
*/
class ActionManager {
class ActionManager extends XEventEmitter {
constructor() {
/**
* @type {Object<string, Function[]>}
*/
this.listeners = {};
super();
}
action(name, callback) {
if (!(name in this.listeners)) {
this.listeners[name] = [callback];
} else {
this.listeners[name].push(callback);
}
action(name, callback) {
this.on("action." + name, callback);
}
trigger(name, payload) {
var cbs = this.listeners[name];
if (!cbs) {
console.warn("no callbacks for action " + name);
return;
}

trigger(name, payload) {
console.group(name);
console.info('payload:', payload);
try {
for (var callback of cbs) {
callback(payload);
}
this.emit("action." + name, payload);
}
finally {
console.groupEnd();
Expand Down
16 changes: 0 additions & 16 deletions js/actions.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,3 @@
/**
* Save the world state to `localStorage`.
*/
function savelocal() {
try {
showStatus('Saving...');
Expand All @@ -12,9 +9,6 @@ function savelocal() {
}
}

/**
* Shares the world content useing `navigator.share`.
*/
function share() {
if (location.protocol.startsWith('file')) {
showStatus('You must use the Web version to be able to share.', 'red');
Expand All @@ -35,10 +29,6 @@ function share() {
}
}

/**
* Copies the world content to the clipboard.
* @param {boolean} bbcode Whether the XML should be wrapped in `[code][/code]` tags.
*/
function copy(bbcode) {
if (location.protocol.startsWith('file')) {
showStatus('You must use the Web version to be able to copy.', 'red');
Expand All @@ -60,9 +50,6 @@ function copy(bbcode) {
}
}

/**
* Reads the contents of the user's clipboard and lods it.
*/
function openclip() {
if (location.protocol.startsWith('file')) {
showStatus('You must use the Web version to be able to open from clipboard.', 'red');
Expand All @@ -86,9 +73,6 @@ function openclip() {
}
}

/**
* Takes a screenshot of the canvas and downloads it.
*/
function savescreenshot() {
var a = document.createElement('a');
a.setAttribute('href', playfield.toDataURL());
Expand Down
119 changes: 0 additions & 119 deletions js/ant.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,3 @@
/**
* Checks to make sure the node nesting is correct.
* @param {HTMLElement} node The XML node to be checked for validity.
* @param {string} name The name of the node that `node` should be.
* @param {string} inside The name of the elemnt that weaps this one, in case of a mismatch and an error.
* @returns {boolean} Whether the node is a `#text` node and should be skipped.
* @throws {string} When `node.nodeName !== name`.
*/
function checknode(node, name, inside) {
// returns true if it should be skipped.
// returns false if it is ok to go.
Expand All @@ -16,25 +8,13 @@ function checknode(node, name, inside) {
return false;
}

/**
* Manager class for creating ants.
*/
class Breeder {
constructor() {
this.breeds = {};
}
/**
* Removes all the ant breeds.
*/
empty() {
this.breeds = {};
}
/**
* Registers a breed.
* @param {string} breedName The name of the breed.
* @param {function} klass The class constructor for the ant.
* @param {HTMLElement} cases The `<case>` tags that describe the ant's behavior.
*/
addBreed(breedName, klass, cases) {
if (breedName in this.breeds) throw `Breed ${breedName} is already defined.`;
var allCases = {};
Expand All @@ -59,25 +39,9 @@ class Breeder {
}
this.breeds[breedName] = [klass, allCases];
}
/**
* Serializes the stored breeds to XML.
* @returns {string}
*/
dumpBreeds() {
return Object.getOwnPropertyNames(this.breeds).map(breed => ` <breed species="${this.breeds[breed][0].name}" name="${breed}">\n${Object.getOwnPropertyNames(this.breeds[breed][1]).map(p => [p, this.breeds[breed][1][p].map(sc => sc.map(cd => ` <command name="${cd[0]}">${cd[1]}</command>`).join('\n')).join('</action>\n <action>')]).map(c => ` <case state="${c[0].split(':')[0]}" cell="${c[0].split(':')[1]}">\n <action>\n${c[1]}\n </action>\n </case>`).join('\n')}\n </breed>`).join('\n');
}
/**
* Creates a new ant.
* @param {string} breed Name of the breed
* @param {World} world
* @param {number} x
* @param {number} y
* @param {AntDirection} dir
* @param {number} state
* @param {Ant[]} antsList Reference to list of ants.
* @param {string} id Arbitrary ant ID.
* @returns
*/
createAnt(breed, world, x, y, dir, state, antsList, id) {
if (!(breed in this.breeds)) throw `Unknown ant breed ${breed}`;
var klass = this.breeds[breed][0];
Expand All @@ -86,86 +50,23 @@ class Breeder {
}
}

/**
* @typedef {0|1|2|3} AntDirection
*/

/**
* Class for an Ant.
*/
class Ant {
/**
*
* @param {Breeder} breeder Reverence to the `Breeder` that produced this ant.
* @param {Ant[]} antList A reference to the list of ants this is a member of.
* @param {string} breed The name of this breed.
* @param {World} world A reference to the `World` this ant lives in.
* @param {object} commands Serialized commands processed by the `breeder`.
* @param {number} initialState The state of the ant.
* @param {number} x X-position
* @param {number} y Y-position
* @param {AntDirection} dir Direction.
* @param {string} [id] The arbitrary ID of the ant.
*/
constructor(breeder, antList, breed, world, commands, initialState, x, y, dir, id) {
/**
* @type {Breeder}
*/
this.breeder = breeder;
/**
* @type {Ant[]}
*/
this.antList = antList;
/**
* @type {string}
*/
this.breed = breed;
/**
* @type {World}
*/
this.world = world;
/**
* @type {number}
*/
this.state = initialState;
/**
* @type {number}
*/
this.x = x;
/**
* @type {number}
*/
this.y = y;
/**
* @type {AntDirection}
*/
this.dir = dir;
/**
* @type {object}
*/
this.commands = commands;
/**
* @type {any[][]}
*/
this.queue = [];
/**
* @type {boolean}
*/
this.halted = false;
/**
* @type {boolean}
*/
this.dead = false;
/**
* @type {string}
*/
this.id = id || `${this.breed}-${randuuid()}`;
}
/**
* Processes `#name` substitutions and `#exp;` interpolations for this ant.
* @param {string} arg Raw, unprocessed argument.
* @returns {string} Processed string.
*/
processInserts(arg) {
var vars = ['dir', 'state'];
// Do simple inserts
Expand All @@ -182,9 +83,6 @@ class Ant {
arg = processExpressions(arg);
return arg;
}
/**
* Advances the ant one tick, executing the `<action>`.
*/
tick() {
this.ensureQueueNotEmpty();
var commands = this.queue.shift();
Expand All @@ -193,9 +91,6 @@ class Ant {
this[`do_${name}`](this.processInserts(arg || ''));
}
}
/**
* If the queue is not empty, fetches more commands from the world and rules.
*/
ensureQueueNotEmpty() {
if (this.queue.length === 0) {
var what = this.commands[`${this.state}:${this.world.getCell(this.x, this.y)}`] ?? [];
Expand All @@ -206,9 +101,6 @@ class Ant {
this.queue.push([]);
}
}
/**
* Draws the ant on the context.
*/
draw(ctx) {
ctx.save();
ctx.translate(this.world.cellSize * this.x, this.world.cellSize * this.y);
Expand Down Expand Up @@ -243,13 +135,6 @@ class Ant {
ctx.beginPath(); ctx.arc(-1, -4.5, 0.5, 0, 2 * Math.PI); ctx.fill();
ctx.restore();
}
/**
* Checks the argument is a number, and returns it.
* @param {string} arg The argument to be checked.
* @param {string} methodname The method that requires a number argument.
* @param {number} default_ The default if the argument is the empty string.
* @returns {number}
*/
numarg(arg, methodname, default_ = 1) {
arg = arg || default_;
var argNum = parseInt(arg);
Expand Down Expand Up @@ -325,10 +210,6 @@ class Ant {
}
}

/**
* A random 8-character hexadecimal UUID.
* @returns {string}
*/
function randuuid() {
return Math.floor(Math.random() * (2 ** 32)).toString(16).padStart(8, '0');
}
Loading