diff --git a/js/Makefile b/js/Makefile
new file mode 100644
index 0000000..186c4f2
--- /dev/null
+++ b/js/Makefile
@@ -0,0 +1,8 @@
+
+MAPS=$(wildcard ../maps/contest*.map ../maps/flood*.map)
+
+all: maps.js
+
+
+maps.js: mapsToJson.js $(MAPS)
+ ./mapsToJson.js $(MAPS) > maps.js
diff --git a/js/gui.js b/js/gui.js
new file mode 100644
index 0000000..abee99a
--- /dev/null
+++ b/js/gui.js
@@ -0,0 +1,184 @@
+var mineGui_mine, mineGui_curmap, mineGui_curndx, mineGui_moves = "";
+var mineGui_movesBackup = [];
+var mineGui_customNdx = 1;
+
+function mineGui_addCustomMap() {
+ var map = document.getElementById("mineGui_data").value;
+ var mine;
+ var selMap = document.getElementById("mineGui_selectMap");
+ document.getElementById("mineGui_addDataResult").textContent = "";
+ try {
+ mine = new Mine(map);
+ } catch (e) {
+ document.getElementById("mineGui_addDataResult").textContent = "Adding map failed: " + e;
+ return;
+ }
+ mineMaps["custom" + mineGui_customNdx] = map;
+ var entry = document.createElement("option");
+ entry.textContent = "custom" + mineGui_customNdx;
+ selMap.appendChild(entry);
+ mineGui_customNdx++;
+
+ window.setTimeout(function() {
+ selMap.selectedIndex = selMap.options.length - 1;
+ selMap.onchange();
+ }, 0);
+}
+
+function mineGui_start() {
+ var div = document.getElementById("mineGui");
+ var selMap = document.getElementById("mineGui_selectMap");
+ var inpMoves = document.getElementById("mineGui_moves");
+ var customMapInput = document.getElementById("mineGui_data")
+ var validMoves = {L:1,U:1,R:1,D:1,A:1,W:1};
+ document.getElementById("mineGui_addData").onclick = mineGui_addCustomMap;
+ selMap.onchange = function() {
+ var k = selMap.options[selMap.selectedIndex].text;
+ mineGui_setMap(mineMaps[k], selMap.selectedIndex);
+ selMap.blur();
+ };
+ var inpValidate = function() {
+ if (inpMoves.value == mineGui_moves) return;
+ mineGui_moves = "";
+ var txt = inpMoves.value.toUpperCase(), i;
+ for (i = 0; i < txt.length; ++i) {
+ if (validMoves[txt[i]]) mineGui_moves += txt[i];
+ }
+ inpMoves.blur();
+ mineGui_updateMine();
+ };
+ var delayInpValidate = function() { window.setTimeout(inpValidate, 0); };
+ inpMoves.onchange = delayInpValidate;
+ inpMoves.onpaste = delayInpValidate;
+ inpMoves.onkeypress = delayInpValidate;
+ document.onkeypress = function (event) {
+ if (document.activeElement === customMapInput) return;
+ if (event.ctrlKey || event.altKey || event.metaKey) return;
+ var handled = true;
+ if (event.keyCode == 8 || event.charCode == 8) {
+ // backspace -> undo
+ mineGui_moves = mineGui_moves.slice(0,-1);
+ mineGui_updateMine();
+ } else if (event.which === 0 && event.keyCode) {
+ switch (event.keyCode) {
+ case 33: // page up
+ if (selMap.selectedIndex > 0) {
+ selMap.selectedIndex--;
+ selMap.onchange();
+ }
+ break;
+ case 34: // page down
+ if (selMap.selectedIndex+1 < selMap.options.length) {
+ selMap.selectedIndex++;
+ selMap.onchange();
+ }
+ break;
+ case 37: // left
+ mineGui_move('L');
+ break;
+ case 38: // up
+ mineGui_move('U');
+ break;
+ case 39: // right
+ mineGui_move('R');
+ break;
+ case 40: // down
+ mineGui_move('D');
+ break;
+ default:
+ handled = false;
+ break;
+ }
+ } else if (event.charCode) {
+ console.log(event.charCode);
+ cmd = String.fromCharCode(event.charCode).toUpperCase();
+ if (validMoves[cmd]) {
+ mineGui_move(cmd);
+ } else if (cmd == 'R') { // redraw
+ mineGui_updateMine();
+ } else if (cmd == 'C') { // clear
+ mineGui_moves = "";
+ mineGui_updateMine();
+ } else {
+ handled = false;
+ }
+ } else {
+ handled = false;
+ }
+ if (handled) event.preventDefault();
+ };
+ for (k in mineMaps) {
+ if (mineMaps.hasOwnProperty(k)) {
+ var entry = document.createElement("option");
+ entry.textContent = k;
+ selMap.appendChild(entry);
+ }
+ //entry.add
+ }
+ selMap.selectedIndex = 0;
+ selMap.onchange();
+}
+
+function mineGui_move(cmd) {
+ for (i = 0; i < cmd.length; ++i) {
+ if (mineGui_mine.state != Mine.ALIVE) break;
+ mineGui_moves += cmd[i];
+ mineGui_mine.move(cmd[i]);
+ }
+ mineGui_show();
+}
+
+function mineGui_setMap(map, ndx) {
+ mineGui_movesBackup[mineGui_curndx] = mineGui_moves;
+ mineGui_curmap = map;
+ mineGui_curndx = ndx;
+ mineGui_moves = mineGui_movesBackup[mineGui_curndx];
+ if (!mineGui_moves) mineGui_moves = "";
+ mineGui_updateMine();
+}
+
+function mineGui_updateMine() {
+ // redo moves
+ mineGui_mine = new Mine(mineGui_curmap);
+ var cmd = mineGui_moves;
+ mineGui_moves = "";
+ for (i = 0; i < cmd.length; ++i) {
+ if (mineGui_mine.state != Mine.ALIVE) break;
+ mineGui_moves += cmd[i];
+ mineGui_mine.move(cmd[i]);
+ }
+ mineGui_show();
+}
+
+function mineGui_show() {
+ var waterLevel = Math.max(mineGui_mine.meta.Water, 0);
+ var map = mineGui_mine.toString().split(/\n/);
+ if (waterLevel > 0) {
+ document.getElementById("mineGui_mineMapWater").textContent = map.splice(-waterLevel).join("\n");
+ } else {
+ document.getElementById("mineGui_mineMapWater").textContent = "";
+ }
+ document.getElementById("mineGui_mineMap").textContent = map.join("\n");
+ document.getElementById("mineGui_moves").value = mineGui_moves;
+ var state = "";
+ switch (mineGui_mine.state) {
+ case Mine.ALIVE:
+ state = "Still mining";
+ break;
+ case Mine.LOST:
+ state = "Robot broken - " + mineGui_mine.reason;
+ break;
+ case Mine.ABORTED:
+ state = "Aborted";
+ break;
+ case Mine.WON:
+ state = "Won - " + mineGui_mine.reason;
+ break;
+ }
+ document.getElementById("mineGui_meta").textContent = mineGui_mine.metaText();
+ document.getElementById("mineGui_state").textContent = state;
+ document.getElementById("mineGui_score").textContent = mineGui_mine.score;
+ document.getElementById("mineGui_scoreMoves").textContent = mineGui_mine.moves;
+ document.getElementById("mineGui_scoreLambdas").textContent = mineGui_mine.found_lambdas;
+ document.getElementById("mineGui_belowWater").textContent = mineGui_mine.moves_below_water;
+}
diff --git a/js/index.html b/js/index.html
new file mode 100644
index 0000000..4b7005c
--- /dev/null
+++ b/js/index.html
@@ -0,0 +1,49 @@
+
+
+
+
+ ICFP Contest 2012 Simulator
+
+
ICFP Contest 2012 Simulator
+ Links: contest page.
+ This simulator may or may not perform according to specs, who knows...
+
+
+
+
+ Select Map:
+
Moves:
+
+
+
+
+
+
+
Score: 0
+
+ (0 moves,
+ found 0 Lambdas)
+
+
+
Status: 0
+
For 0 moves under water
+
+
+
+
+
Help:
+(L)eft/(U)p/(R)ight/(D)own work as expected (letter and arrow keys), (A)bort and (W)ait too.
+Special keys: PageUp/PageDown for map select, Backspace for undo, (C)lear and (R)edraw
+Be careful: a reload looses all custom maps and saved moves.
+