2017-09-04 12:53:25 -04:00
|
|
|
if (process.argv.length < 3) {
|
2019-02-21 12:02:18 -05:00
|
|
|
console.log("Usage: node bot.js <socket-url> [<name>]")
|
2019-01-15 10:42:15 -05:00
|
|
|
process.exit(1);
|
2017-09-04 12:53:25 -04:00
|
|
|
}
|
|
|
|
|
2017-11-17 02:49:43 -05:00
|
|
|
//TODO: add a land claiming algo (with coefficient parameters)
|
2019-02-21 11:50:51 -05:00
|
|
|
//TODO: add weight to the max land area and last land area, and also the number of kills
|
2017-11-17 02:49:43 -05:00
|
|
|
//TODO: genetic gene pooling
|
|
|
|
|
2019-02-21 20:36:35 -05:00
|
|
|
var core = require("./src/core");
|
|
|
|
var client = require("./src/game-client");
|
2020-03-04 05:53:32 -05:00
|
|
|
var { consts } = require("./config.json");
|
2017-09-04 12:53:25 -04:00
|
|
|
|
2019-02-21 11:50:51 -05:00
|
|
|
var MOVES = [[-1, 0], [0, 1], [1, 0], [0, -1]];
|
2017-11-03 19:05:50 -04:00
|
|
|
|
|
|
|
var AGGRESSIVE = Math.random();
|
|
|
|
var THRESHOLD = 10;
|
|
|
|
|
|
|
|
var startFrame = -1;
|
|
|
|
var endFrame = -1;
|
2019-01-15 10:42:15 -05:00
|
|
|
var coeffs = [0.6164220147940495, -2.519369747858328, 0.9198978109542851, -1.2158956330674564, -3.072901620397528, 5, 4];
|
2019-01-16 03:57:36 -05:00
|
|
|
var grid, others, user, playerPortion = {};
|
2017-11-03 19:05:50 -04:00
|
|
|
var DIST_TYPES = {
|
2019-01-15 10:42:15 -05:00
|
|
|
land: {
|
|
|
|
check: function(loc) {
|
|
|
|
return grid.get(loc.row, loc.col) === user;
|
|
|
|
},
|
|
|
|
coeff: function() {
|
|
|
|
return coeffs[0];
|
|
|
|
}
|
|
|
|
}, tail: {
|
|
|
|
check: function(loc) {
|
|
|
|
return tail(user, loc)
|
|
|
|
},
|
|
|
|
coeff: function() {
|
|
|
|
return coeffs[1];
|
|
|
|
}
|
|
|
|
}, oTail: {
|
|
|
|
check: foundProto(tail),
|
|
|
|
coeff: function() {
|
|
|
|
return AGGRESSIVE * coeffs[2];
|
|
|
|
}
|
|
|
|
}, other: {
|
|
|
|
check: foundProto(function(other, loc) {
|
|
|
|
return other.row === this.row && other.col === this.col;
|
|
|
|
}),
|
|
|
|
coeff: function() {
|
|
|
|
return (1 - AGGRESSIVE) * coeffs[3];
|
|
|
|
}
|
|
|
|
}, edge: {
|
|
|
|
check: function(loc) {
|
2019-02-21 12:39:14 -05:00
|
|
|
return loc.row <= 1 || loc.col <= 1 || loc.row >= consts.GRID_COUNT - 1 || loc.col >= consts.GRID_COUNT - 1
|
2019-01-15 10:42:15 -05:00
|
|
|
},
|
|
|
|
coeff: function() {
|
|
|
|
return coeffs[4];
|
|
|
|
}
|
|
|
|
}
|
2017-11-03 19:05:50 -04:00
|
|
|
};
|
|
|
|
|
2017-11-17 02:49:43 -05:00
|
|
|
function generateLandDirections() {
|
2019-01-15 10:42:15 -05:00
|
|
|
function mod(x) {
|
|
|
|
x %= 4;
|
|
|
|
if (x < 0) x += 4;
|
|
|
|
return x;
|
|
|
|
}
|
|
|
|
|
|
|
|
var breadth = Math.floor(Math.random() * coeffs[5]) + 1;
|
|
|
|
var spread = Math.floor(Math.random() * coeffs[6]) + 1;
|
|
|
|
var extra = Math.floor(Math.random() * 2) + 1;
|
|
|
|
var ccw = Math.floor(Math.random() * 2) * 2 - 1;
|
|
|
|
|
|
|
|
var dir = user.currentHeading;
|
|
|
|
var turns = [dir, mod(dir + ccw), mod(dir + ccw * 2), mod(dir + ccw * 3)];
|
|
|
|
var lengths = [breadth, spread, breadth + extra, spread];
|
|
|
|
|
|
|
|
var moves = [];
|
|
|
|
for (var i = 0; i < turns.length; i++) {
|
|
|
|
for (var j = 0; j < lengths[i]; j++) {
|
|
|
|
moves.push(turns[i]);
|
|
|
|
}
|
|
|
|
}
|
2017-11-17 02:49:43 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
var LAND_CLAIMS = {
|
2019-01-15 10:42:15 -05:00
|
|
|
rectDims: function() {},
|
|
|
|
rectSpread: function() {}
|
2017-11-17 02:49:43 -05:00
|
|
|
}
|
|
|
|
|
2017-11-03 19:05:50 -04:00
|
|
|
function foundProto(func) {
|
2020-03-04 07:07:38 -05:00
|
|
|
return loc => {
|
|
|
|
return others.some(other => {
|
2019-01-15 10:42:15 -05:00
|
|
|
return func(other, loc);
|
|
|
|
});
|
2020-03-04 07:07:38 -05:00
|
|
|
};
|
2017-11-03 19:05:50 -04:00
|
|
|
}
|
2017-09-04 12:53:25 -04:00
|
|
|
|
2017-09-04 13:23:12 -04:00
|
|
|
function connect() {
|
2019-02-21 20:36:35 -05:00
|
|
|
var prefixes = consts.PREFIXES.split(" ");
|
|
|
|
var names = consts.NAMES.split(" ");
|
2019-02-22 02:10:20 -05:00
|
|
|
var name = process.argv[3] || [prefixes[Math.floor(Math.random() * prefixes.length)], names[Math.floor(Math.random() * names.length)]].join(" ");
|
2020-03-04 07:07:38 -05:00
|
|
|
client.connectGame(process.argv[2], "[BOT] " + name, (success, msg) => {
|
2019-02-22 00:22:21 -05:00
|
|
|
if (!success) {
|
|
|
|
console.error(msg);
|
|
|
|
setTimeout(connect, 1000);
|
|
|
|
}
|
2019-01-15 10:42:15 -05:00
|
|
|
});
|
2017-09-04 13:23:12 -04:00
|
|
|
}
|
2017-09-04 12:53:25 -04:00
|
|
|
|
2017-11-03 19:05:50 -04:00
|
|
|
function Loc(row, col, step) {
|
2019-01-15 10:42:15 -05:00
|
|
|
if (this.constructor != Loc) return new Loc(row, col, step);
|
|
|
|
this.row = row;
|
|
|
|
this.col = col;
|
|
|
|
this.step = step;
|
2017-11-03 19:05:50 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
//Projects vector b onto vector a
|
|
|
|
function project(a, b) {
|
2019-01-15 10:42:15 -05:00
|
|
|
var factor = (b[0] * a[0] + b[1] * a[1]) / (a[0] * a[0] + a[1] * a[1]);
|
|
|
|
return [factor * a[0], factor * a[1]];
|
2017-11-03 19:05:50 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
function tail(player, loc) {
|
2019-01-15 10:42:15 -05:00
|
|
|
return player.tail.hitsTail(loc);
|
2017-11-03 19:05:50 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
function traverseGrid(dir) {
|
2019-02-21 12:39:14 -05:00
|
|
|
steps = new Array(consts.GRID_COUNT * consts.GRID_COUNT);
|
2019-01-15 10:42:15 -05:00
|
|
|
for (var i in steps) {
|
|
|
|
steps[i] = -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
distWeights = {};
|
|
|
|
for (var type in DIST_TYPES) {
|
|
|
|
distWeights[type] = 0;
|
|
|
|
}
|
|
|
|
|
2020-03-04 05:53:32 -05:00
|
|
|
var { row, col } = user;
|
2019-02-21 12:39:14 -05:00
|
|
|
var minRow = Math.max(0, row - 10), maxRow = Math.min(consts.GRID_COUNT, row + 10);
|
|
|
|
var minCol = Math.max(0, col - 10), maxCol = Math.min(consts.GRID_COUNT, col + 10);
|
2019-01-15 10:42:15 -05:00
|
|
|
|
|
|
|
var proj = 0;
|
|
|
|
for (var i = 1; i >= -1; i-=2) {
|
|
|
|
proj = (1 + THRESHOLD) * i;
|
|
|
|
while (proj != 0) {
|
|
|
|
proj -= i;
|
|
|
|
var normRange = Math.abs(proj);
|
|
|
|
for (var norm = -normRange; norm <= normRange; norm++) {
|
|
|
|
for (var distType in distWeights) {
|
|
|
|
var move = MOVES[dir];
|
|
|
|
var delta = THRESHOLD - Math.abs(proj);
|
|
|
|
var dist = Math.sign(proj) * delta * delta / (Math.abs(norm) + 1)
|
|
|
|
var loc = {row: proj * move[0] + norm * move[1], col: proj * move[1] + norm * move[0]};
|
|
|
|
|
|
|
|
loc.row += user.row;
|
|
|
|
loc.col += user.col;
|
|
|
|
|
2019-02-21 12:39:14 -05:00
|
|
|
if (loc.row < 0 || loc.row >= consts.GRID_COUNT || loc.col < 0 || loc.col >= consts.GRID_COUNT) continue;
|
2019-01-15 10:42:15 -05:00
|
|
|
if (DIST_TYPES[distType].check(loc)) distWeights[distType] += dist;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return distWeights;
|
2017-11-03 19:05:50 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
function printGrid() {
|
2019-02-21 12:39:14 -05:00
|
|
|
var chars = new core.Grid(consts.GRID_COUNT);
|
|
|
|
for (var r = 0; r < consts.GRID_COUNT; r++) {
|
|
|
|
for (var c = 0; c < consts.GRID_COUNT; c++) {
|
2019-01-15 10:42:15 -05:00
|
|
|
if (tail(user, {row: r, col: c})) chars.set(r, c, "t");
|
|
|
|
else {
|
|
|
|
var owner = grid.get(r, c);
|
|
|
|
chars.set(r, c, owner ? "" + owner.num % 10 : ".");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
for (var p of others) {
|
|
|
|
chars.set(p.row, p.col, "x");
|
|
|
|
}
|
|
|
|
chars.set(user.row, user.col, "^>V<"[user.currentHeading]);
|
|
|
|
|
|
|
|
var str = "";
|
2019-02-21 12:39:14 -05:00
|
|
|
for (var r = 0; r < consts.GRID_COUNT; r++) {
|
2019-01-15 10:42:15 -05:00
|
|
|
str += "\n";
|
2019-02-21 12:39:14 -05:00
|
|
|
for (var c = 0; c < consts.GRID_COUNT; c++) {
|
2019-01-15 10:42:15 -05:00
|
|
|
str += chars.get(r, c);
|
|
|
|
}
|
|
|
|
}
|
2019-02-22 00:22:21 -05:00
|
|
|
console.log(str);
|
2017-11-03 19:05:50 -04:00
|
|
|
}
|
|
|
|
|
2017-11-17 02:49:43 -05:00
|
|
|
function update(frame) {
|
2019-01-15 10:42:15 -05:00
|
|
|
if (startFrame == -1) startFrame = frame;
|
|
|
|
endFrame = frame;
|
|
|
|
if (frame % 6 == 1) {
|
|
|
|
grid = client.grid;
|
|
|
|
others = client.getOthers();
|
|
|
|
//printGrid();
|
|
|
|
var weights = [0, 0, 0, 0];
|
|
|
|
for (var d of [3, 0, 1]) {
|
|
|
|
var weight = 0;
|
|
|
|
|
|
|
|
d = (d + user.currentHeading) % 4;
|
|
|
|
distWeights = traverseGrid(d);
|
|
|
|
|
|
|
|
var str = d + ": "
|
|
|
|
for (var distType in DIST_TYPES) {
|
|
|
|
var point = distWeights[distType] * DIST_TYPES[distType].coeff();
|
|
|
|
weight += point;
|
|
|
|
str += distType + ": " + point + ", ";
|
|
|
|
}
|
2019-02-22 00:22:21 -05:00
|
|
|
//console.log(str);
|
2019-01-15 10:42:15 -05:00
|
|
|
weights[d] = weight;
|
|
|
|
}
|
|
|
|
|
|
|
|
var low = Math.min(0, Math.min.apply(this, weights));
|
|
|
|
var total = 0;
|
|
|
|
|
|
|
|
weights[(user.currentHeading + 2) % 4] = low;
|
|
|
|
for (var i = 0; i < weights.length; i++) {
|
|
|
|
weights[i] -= low * (1 + Math.random());
|
|
|
|
total += weights[i];
|
|
|
|
}
|
|
|
|
|
|
|
|
if (total == 0) {
|
|
|
|
for (var d of [-1, 0, 1]) {
|
|
|
|
d = (d + user.currentHeading) % 4;
|
|
|
|
while (d < 0) d += 4;
|
|
|
|
weights[d] = 1;
|
|
|
|
total++;
|
|
|
|
}
|
|
|
|
}
|
2019-02-22 00:22:21 -05:00
|
|
|
//console.log(weights)
|
2019-01-15 10:42:15 -05:00
|
|
|
//Choose a random direction from the weighted list
|
|
|
|
var choice = Math.random() * total;
|
|
|
|
var d = 0;
|
|
|
|
while (choice > weights[d]) {
|
|
|
|
choice -= weights[d++];
|
|
|
|
}
|
|
|
|
client.changeHeading(d);
|
|
|
|
}
|
2017-11-17 02:49:43 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
function calcFavorability(params) {
|
2019-01-15 10:42:15 -05:00
|
|
|
return params.portion + params.kills * 50 + params.survival / 100;
|
2017-11-17 02:49:43 -05:00
|
|
|
}
|
|
|
|
|
2017-09-04 13:23:12 -04:00
|
|
|
client.allowAnimation = false;
|
2017-09-04 12:53:25 -04:00
|
|
|
client.renderer = {
|
2019-01-15 10:42:15 -05:00
|
|
|
addPlayer: function(player) {
|
|
|
|
playerPortion[player.num] = 0;
|
|
|
|
},
|
|
|
|
disconnect: function() {
|
|
|
|
var dt = (endFrame - startFrame);
|
|
|
|
startFrame = -1;
|
2019-02-22 00:22:21 -05:00
|
|
|
console.log(`[${new Date()}] I died... (survived for ${dt} frames.)`);
|
|
|
|
console.log(`[${new Date()}] I killed ${client.kills} player(s).`);
|
|
|
|
console.log("Coefficients: " + coeffs);
|
2019-01-15 10:42:15 -05:00
|
|
|
|
|
|
|
var mutation = Math.min(10, Math.pow(2, calcFavorability(params)));
|
|
|
|
for (var i = 0; i < coeffs.length; i++) {
|
|
|
|
coeffs[i] += Math.random() * mutation * 2 - mutation;
|
|
|
|
}
|
|
|
|
connect();
|
|
|
|
},
|
|
|
|
removePlayer: function(player) {
|
|
|
|
delete playerPortion[player.num];
|
|
|
|
},
|
|
|
|
setUser: function(u) {
|
|
|
|
user = u;
|
|
|
|
},
|
|
|
|
update: update,
|
|
|
|
updateGrid: function(row, col, before, after) {
|
|
|
|
before && playerPortion[before.num]--;
|
|
|
|
after && playerPortion[after.num]++;
|
|
|
|
}
|
2017-09-04 12:53:25 -04:00
|
|
|
};
|
|
|
|
|
2017-09-04 13:23:12 -04:00
|
|
|
connect();
|