summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTom Smeding <tom.smeding@gmail.com>2018-08-28 01:22:34 +0200
committerTom Smeding <tom.smeding@gmail.com>2018-08-28 01:22:34 +0200
commit447bdb63e3b5aa71dbee8648e7d1ed51b8ee915f (patch)
tree2ef49c532a82be0a6d6ace3a5b6be748dd4eae7a
InitialHEADmaster
-rw-r--r--.gitignore4
-rw-r--r--.gitmodules3
m---------beta-decay.github.io0
-rw-r--r--package-lock.json34
-rw-r--r--package.json15
-rwxr-xr-xrun_game.js140
-rwxr-xr-xrun_tour.js122
-rw-r--r--tour_worker.js52
8 files changed, 370 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..7e19765
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,4 @@
+node_modules
+logs_dir
+output.txt
+seeds.txt
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000..fc1e638
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "beta-decay.github.io"]
+ path = beta-decay.github.io
+ url = https://github.com/beta-decay/beta-decay.github.io
diff --git a/beta-decay.github.io b/beta-decay.github.io
new file mode 160000
+Subproject dd661437e864ca5946bc066175d0792a0bc92a2
diff --git a/package-lock.json b/package-lock.json
new file mode 100644
index 0000000..4ca99b6
--- /dev/null
+++ b/package-lock.json
@@ -0,0 +1,34 @@
+{
+ "name": "tour",
+ "version": "1.0.0",
+ "lockfileVersion": 1,
+ "requires": true,
+ "dependencies": {
+ "minimist": {
+ "version": "0.0.8",
+ "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz",
+ "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0="
+ },
+ "mkdirp": {
+ "version": "0.5.1",
+ "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.1.tgz",
+ "integrity": "sha1-MAV0OOrGz3+MR2fzhkjWaX11yQM=",
+ "requires": {
+ "minimist": "0.0.8"
+ }
+ },
+ "nan": {
+ "version": "2.11.0",
+ "resolved": "https://registry.npmjs.org/nan/-/nan-2.11.0.tgz",
+ "integrity": "sha512-F4miItu2rGnV2ySkXOQoA8FKz/SR2Q2sWP0sbTxNxz/tuokeC8WxOhPMcwi0qIyGtVn/rrSeLbvVkznqCdwYnw=="
+ },
+ "xorshift128plus": {
+ "version": "0.0.1",
+ "resolved": "https://registry.npmjs.org/xorshift128plus/-/xorshift128plus-0.0.1.tgz",
+ "integrity": "sha1-j+z5WZwWD2RKnyMfKWOPwFaRdUc=",
+ "requires": {
+ "nan": "^2.2.0"
+ }
+ }
+ }
+}
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..a055e78
--- /dev/null
+++ b/package.json
@@ -0,0 +1,15 @@
+{
+ "name": "tour",
+ "version": "1.0.0",
+ "description": "",
+ "main": "run_tour.js",
+ "scripts": {
+ "test": "echo \"Error: no test specified\" && exit 1"
+ },
+ "author": "Tom Smeding <tom.smeding@gmail.com> (https://tomsmeding.com)",
+ "license": "MIT",
+ "dependencies": {
+ "mkdirp": "^0.5.1",
+ "xorshift128plus": "0.0.1"
+ }
+}
diff --git a/run_game.js b/run_game.js
new file mode 100755
index 0000000..20fd8c0
--- /dev/null
+++ b/run_game.js
@@ -0,0 +1,140 @@
+#!/usr/bin/env node
+
+if (process.argv.length != 4) {
+ console.log("Usage: ./run_game.js <seed> <num_turns>");
+ console.log("Seed needs to be a 32-digit hex number");
+ process.exit(1);
+}
+
+process.__state = {};
+process.__state.seed = process.argv[2];
+process.__state.num_turns = +process.argv[3];
+
+// -------------------- SHIMS --------------------
+
+window = {};
+localStorage = (function() {
+ const backing = new Map();
+
+ const getItem = function(key) {
+ if (backing.has(key)) return backing.get(key);
+ else return null;
+ };
+ const setItem = function(key, value) {
+ backing.set(key, value + "");
+ };
+ const removeItem = function(key) {
+ backing.delete(key);
+ };
+ const clear = function() {
+ backing.clear();
+ };
+
+ const proxy = new Proxy(backing, {
+ get: function(obj, prop) {
+ if (prop == "length") return backing.size;
+ if (prop == "key") throw new Error("localStorage.key() not supported");
+ if (prop == "getItem") return getItem;
+ if (prop == "setItem") return setItem;
+ if (prop == "removeItem") return removeItem;
+ if (prop == "clear") return clear;
+
+ return getItem(prop);
+ },
+ set: function(obj, prop, value) {
+ setItem(prop, value);
+ return true;
+ }
+ });
+
+ return proxy;
+})();
+
+window.localStorage = localStorage;
+
+document = {
+ addEventListener: function(evname) {
+ if (evname == "DOMContentLoaded") {}
+ else {
+ console.log("Foreign addEventListener call!");
+ process.exit(1);
+ }
+ },
+ getElementById: function(id) {
+ // console.log("gEBI(" + id + ")");
+ if (id == "gameInput") return {value: process.__state.num_turns};
+ if (id == "fpsInput") return {value: Infinity};
+ return {};
+ }
+};
+
+
+// -------------------- MAKE MATH.RANDOM REPEATABLE --------------------
+
+Math.random = (function() {
+ const xorshift128plus = require("xorshift128plus");
+ const rng = xorshift128plus.fromHex(process.__state.seed);
+
+ return function() {
+ return rng.next();
+ };
+})();
+
+
+// -------------------- LOAD BASE CONTROLLER --------------------
+
+for (const file of [
+ "./beta-decay.github.io/art_attack/botData.js",
+ "./beta-decay.github.io/art_attack/controller.js"]) {
+ eval(require("fs").readFileSync(file).toString());
+}
+
+
+// -------------------- REMOVE ANNOYING ENTRIES --------------------
+
+for (let i = 0; i < botData.length; i++) {
+ if (botData[i].name == "Muncher") {
+ botData.splice(i, 1);
+ i--;
+ }
+}
+
+
+// -------------------- PATCH UP THINGS --------------------
+
+updateBoard = function() {
+ console.log("-- TURN: " + turnNumber + " --");
+};
+drawGrid = function() {};
+drawBots = function() {};
+colourGrid = function() {};
+findWinner = function() {
+ const arr = findArea();
+ for (let i = 0; i < arr.length; i++) {
+ if (arr[i] === undefined) arr[i] = 0;
+ }
+
+ /* const winners = [];
+ const maxScore = Math.max.apply(Math, arr);
+
+ for (let i = 0; i < arr.length; i++) {
+ if (arr[i] == maxScore && !botData[i].eliminated) {
+ winners.push(botData[i].name);
+ }
+ }
+
+ console.log(arr);
+ console.log(JSON.stringify(winners)); */
+
+ for (let i = 0; i < arr.length; i++) {
+ arr[i] = [botData[i].name, arr[i]];
+ }
+
+ console.log(JSON.stringify(arr));
+};
+
+
+// -------------------- RUN THE GAME --------------------
+
+initialise();
+runGame();
diff --git a/run_tour.js b/run_tour.js
new file mode 100755
index 0000000..ef6492f
--- /dev/null
+++ b/run_tour.js
@@ -0,0 +1,122 @@
+#!/usr/bin/env node
+
+if (process.argv.length != 7) {
+ console.log("Usage: ./run_tour.js <seed_file> <num_turns> <num_workers> <output_file> <logs_dir>");
+ console.log("<seed_file> should contain a 32-hex-digit seed on each line. One game will be run for each seed. The games will all have <num_turns> turns.");
+ console.log("The tournament will be run using <num_workers> parallel workers.")
+ console.log("Output results will be written to <output_file>.")
+ console.log("Game log files will be written to the directory <logs_dir>.");
+ process.exit(1);
+}
+
+const fs = require("fs");
+const child_process = require("child_process");
+const mkdirp = require("mkdirp");
+
+const seed_file = process.argv[2];
+const num_turns = +process.argv[3];
+const num_workers = +process.argv[4];
+const output_file = process.argv[5];
+const logs_dir = process.argv[6];
+
+mkdirp.sync(logs_dir);
+
+const output_stream = fs.createWriteStream(output_file);
+
+const seeds =
+ fs.readFileSync(seed_file).toString()
+ .split("\n").filter(s => s.length > 0);
+
+
+console.log(seeds.length + " games to play using " + num_workers + " workers.");
+
+function register_result(result) {
+ output_stream.write(result + "\n");
+
+ const seed = result.split(" ", 1)[0];
+ console.log("Finished game with seed " + seed);
+}
+
+const workers = [];
+const current_seed = new Array(num_workers).fill(null);
+const start_at = new Array(num_workers).fill(null);
+
+function distribute_work() {
+ if (seeds.length == 0) return;
+
+ for (let i = 0; i < num_workers; i++) {
+ if (current_seed[i] == null) {
+ const seed = seeds.shift();
+ current_seed[i] = seed;
+ start_at[i] = new Date();
+ workers[i].stdin.write(seed + "\n");
+
+ console.log("Started game with seed " + seed);
+ }
+ }
+}
+
+function check_finished() {
+ for (let i = 0; i < num_workers; i++) {
+ if (current_seed[i] != null) return false;
+ }
+
+ console.log("\nFINISHED! Shutting down...");
+
+ output_stream.end();
+ for (const worker of workers) {
+ worker.stdin.end();
+ }
+
+ console.log("All done.");
+ setTimeout(() => process.exit(0), 500);
+}
+
+function spawn_worker(index) {
+ const worker = child_process.spawn(
+ "node",
+ ["tour_worker.js", num_turns.toString(), logs_dir],
+ { stdio: ["pipe", "pipe", "inherit"] }
+ );
+
+ let buffer = "";
+ worker.stdout.on("data", (data) => {
+ buffer += data;
+ let idx = buffer.indexOf("\n");
+ while (idx != -1) {
+ const line = buffer.slice(0, idx);
+ buffer = buffer.slice(idx + 1);
+ idx = buffer.indexOf("\n");
+
+ register_result(line);
+ current_seed[index] = null;
+ start_at[index] = null;
+ distribute_work();
+ if (check_finished()) return;
+ }
+ });
+
+ return worker;
+}
+
+for (let i = 0; i < num_workers; i++) {
+ workers.push(spawn_worker(workers.length));
+}
+
+distribute_work();
+
+// Check for stuck games once in a while
+setInterval(function() {
+ const max_game_time = 1350 * num_turns;
+ const now = new Date();
+
+ for (let i = 0; i < num_workers; i++) {
+ if (current_seed[i] != null && now - start_at[i] > max_game_time) {
+ console.log("Game for seed " + current_seed[i] + " is taking too long, KILLING!");
+ workers[i].kill("SIGKILL");
+
+ console.log("Spawning new worker and hoping everything's fine...");
+ workers[i] = spawn_worker(i);
+ }
+ }
+}, 60 * 1000);
diff --git a/tour_worker.js b/tour_worker.js
new file mode 100644
index 0000000..2516375
--- /dev/null
+++ b/tour_worker.js
@@ -0,0 +1,52 @@
+#!/usr/bin/env node
+
+if (process.argv.length != 4) {
+ console.log("Usage: ./tour_worker.js <num_turns> <logs_dir>");
+ console.log("Runs a game with each of the seeds given on stdin with the specified number of turns.");
+ console.log("Game logs will be written to hopefully uniquely named files in the pre-existing directory <logs_dir>.");
+ console.log("Writes results to stdout, in the format 'seed result', where <result> is the game result.");
+ console.log("Quits on EOF on stdin.");
+ process.exit(1);
+}
+
+const fs = require("fs");
+const child_process = require("child_process");
+
+const num_turns = +process.argv[2];
+const logs_dir = process.argv[3];
+
+let buffer = "";
+process.stdin.on("data", function(data) {
+ buffer += data;
+ let idx = buffer.indexOf("\n");
+ while (idx != -1) {
+ const line = buffer.slice(0, idx);
+ buffer = buffer.slice(idx + 1);
+ idx = buffer.indexOf("\n");
+
+ handle_line(line);
+ }
+});
+
+function handle_line(seed) {
+ const logfile = logs_dir + "/" + seed + "-" + new Date().getTime() + ".txt";
+ const file = fs.openSync(logfile, "w");
+
+ const proc = child_process.spawn(
+ "node",
+ ["run_game.js", seed, num_turns + ""],
+ {stdio: ["inherit", file, "inherit"]}
+ );
+ fs.closeSync(file);
+
+ proc.on("exit", function(code, signal) {
+ if (code !== 0) {
+ console.log(seed + " crash");
+ } else {
+ let result = fs.readFileSync(logfile).toString().trim().split("\n");
+ result = result[result.length - 1];
+
+ console.log(seed + " " + result);
+ }
+ });
+}