forked from ilikecats/papercats
Update bot mode, add paper-io bot
This commit is contained in:
parent
ebc5e93ae5
commit
2bea2e8b7a
@ -3,6 +3,16 @@ if (process.argv.length < 3) {
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
var oldlog = console.log;
|
||||
console.log = function(msg) {
|
||||
return oldlog('[' + new Date() + '] ' + msg);
|
||||
}
|
||||
|
||||
//TODO: add a land claiming algo (with coefficient parameters)
|
||||
//TODO: add weight to the max land area and last land area, and also the number
|
||||
//of kills
|
||||
//TODO: genetic gene pooling
|
||||
|
||||
var core = require("../game-core");
|
||||
var client = require("../client");
|
||||
|
||||
@ -15,27 +25,62 @@ var THRESHOLD = 10;
|
||||
|
||||
var startFrame = -1;
|
||||
var endFrame = -1;
|
||||
var coeffs = [1,3,1,1,3];
|
||||
var grid, others, user;
|
||||
var coeffs = [1, -3, 1, -1, -3, 5, 4];
|
||||
var grid, others, user, playerPortion;
|
||||
var DIST_TYPES = {
|
||||
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];}
|
||||
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];}
|
||||
coeff: function() {return (1 - AGGRESSIVE) * coeffs[3];}
|
||||
}, edge: {
|
||||
check: function(loc) {return loc.row <= 1 || loc.col <= 1 || loc.row >= GRID_SIZE - 1 || loc.col >= GRID_SIZE - 1},
|
||||
coeff: function() {return -coeffs[4];}
|
||||
coeff: function() {return coeffs[4];}
|
||||
}
|
||||
};
|
||||
|
||||
function generateLandDirections() {
|
||||
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]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var LAND_CLAIMS = {
|
||||
rectDims: function() {
|
||||
var
|
||||
}, rectSpread: function() {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
function foundProto(func) {
|
||||
return function(loc) {
|
||||
return others.some(function(other) {
|
||||
@ -145,8 +190,74 @@ function printGrid() {
|
||||
console.log(str);
|
||||
}
|
||||
|
||||
function update(frame) {
|
||||
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 + ", ";
|
||||
}
|
||||
//console.log(str);
|
||||
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++;
|
||||
}
|
||||
}
|
||||
|
||||
//console.log(weights)
|
||||
|
||||
//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);
|
||||
}
|
||||
}
|
||||
|
||||
function calcFavorability(params) {
|
||||
return params.portion + params.kills * 50 + params.survival / 100;
|
||||
}
|
||||
|
||||
client.allowAnimation = false;
|
||||
client.renderer = {
|
||||
addPlayer: function(player) {
|
||||
playerPortion[player.num] = 0;
|
||||
},
|
||||
disconnect: function() {
|
||||
var dt = (endFrame - startFrame);
|
||||
startFrame = -1;
|
||||
@ -155,75 +266,23 @@ client.renderer = {
|
||||
console.log("I killed " + client.kills + " player(s).");
|
||||
console.log("Coefficients: " + coeffs)
|
||||
|
||||
var mutation = Math.min(10, Math.pow(2, -dt / 150));
|
||||
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: function(frame) {
|
||||
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 + ", ";
|
||||
}
|
||||
//console.log(str);
|
||||
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++;
|
||||
}
|
||||
}
|
||||
|
||||
//console.log(weights)
|
||||
|
||||
//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);
|
||||
}
|
||||
update: update,
|
||||
updateGrid: function(row, col, before, after) {
|
||||
before && playerPortion[before.num]--;
|
||||
after && playerPortion[after.num]++;
|
||||
}
|
||||
};
|
||||
|
||||
|
243
client-modes/paper-io-bot-mode.js
Normal file
243
client-modes/paper-io-bot-mode.js
Normal file
@ -0,0 +1,243 @@
|
||||
if (process.argv.length < 3) {
|
||||
console.log("Usage: node game-client-bot.js <socket-url> [<name>]")
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
var oldlog = console.log;
|
||||
console.log = function(msg) {
|
||||
return oldlog('[' + new Date() + '] ' + msg);
|
||||
}
|
||||
|
||||
|
||||
var core = require("../game-core");
|
||||
var client = require("../client");
|
||||
|
||||
var GRID_SIZE = core.GRID_SIZE;
|
||||
var CELL_WIDTH = core.CELL_WIDTH;
|
||||
var MOVES = [[-1, 0], [0, 1], [1, 0], [0, -1]]
|
||||
|
||||
var THRESHOLD = 10;
|
||||
|
||||
var startFrame = -1;
|
||||
var endFrame = -1;
|
||||
var grid, others, user, playerPortion = {}, claim = [];
|
||||
|
||||
function mod(x) {
|
||||
x %= 4;
|
||||
if (x < 0) {
|
||||
x += 4;
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
function connect() {
|
||||
client.connectGame(process.argv[2], process.argv[3] || '[PAPER-IO-BOT]', function(success, msg) {
|
||||
if (!success) {
|
||||
setTimeout(connect, 1000);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function Loc(row, col) {
|
||||
if (this.constructor != Loc) {
|
||||
return new Loc(row, col);
|
||||
}
|
||||
|
||||
this.row = row;
|
||||
this.col = col;
|
||||
}
|
||||
|
||||
function update(frame) {
|
||||
if (startFrame == -1) {
|
||||
startFrame = frame;
|
||||
}
|
||||
endFrame = frame;
|
||||
|
||||
if (frame % 6 == 1) {
|
||||
grid = client.grid;
|
||||
others = client.getOthers();
|
||||
|
||||
//Note: the code below isn't really my own code. This code is in fact the
|
||||
//approximate algorithm used by the paper.io game. It has been modified from
|
||||
//the original code (i.e. deobfuscating) and made more efficient in some
|
||||
//areas, otherwise, the original logic is about the same.
|
||||
var row = user.row, col = user.col, dir = user.currentHeading;
|
||||
var thres = (1 + 2 * Math.random()) * .01 * GRID_SIZE * GRID_SIZE;
|
||||
|
||||
if (row < 0 || col < 0 || row >= GRID_SIZE || col >= GRID_SIZE) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (grid.get(row, col) === user) {
|
||||
//When we are inside our territory
|
||||
weights = [25, 25, 25, 25];
|
||||
weights[dir] = 100;
|
||||
weights[mod(dir + 2)] = -9999;
|
||||
|
||||
for (var nd = 0; nd < 4; nd++) {
|
||||
for (var S = 1; S < 20; S++) {
|
||||
var nr = MOVES[nd][0] * S + row;
|
||||
var nc = MOVES[nd][1] * S + col;
|
||||
|
||||
if (nr < 0 || nc < 0 || nr >= GRID_SIZE || nc >= GRID_SIZE) {
|
||||
if (S > 1) {
|
||||
weights[nd]--;
|
||||
} else {
|
||||
weights[nd] = -9999;
|
||||
}
|
||||
} else {
|
||||
if (grid.get(nr, nc) !== user) {
|
||||
weights[nd]--;
|
||||
}
|
||||
|
||||
var tailed = undefined;
|
||||
for (var o of others) {
|
||||
if (o.tail.hitsTail(new Loc(nr, nc))) {
|
||||
tailed = o;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (tailed) {
|
||||
if (o.name.indexOf("PAPER") != -1) { //Don't really try to kill our own kind
|
||||
weights[nd] += 3 * (30 - S);
|
||||
} else {
|
||||
weights[nd] += 30 * (30 - S);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//View a selection of choices based on the weights we computed
|
||||
var choices = [];
|
||||
for (var d = 0; d < 4; d++) {
|
||||
for (var S = 1; S < weights[d]; S++) {
|
||||
choices.push(d);
|
||||
}
|
||||
}
|
||||
|
||||
if (choices.length === 0) {
|
||||
choices.push(dir);
|
||||
}
|
||||
|
||||
dir = choices[Math.floor(Math.random() * choices.length)];
|
||||
} else if (playerPortion[user.num] < thres) {
|
||||
//Claim some land if we are relatively tiny and have little to risk.
|
||||
if (claim.length === 0) {
|
||||
var breadth = 4 * Math.random() + 2;
|
||||
var length = 4 * Math.random() + 2;
|
||||
var ccw = 2 * Math.floor(2 * Math.random()) - 1;
|
||||
|
||||
turns = [dir, mod(dir + ccw), mod(dir + ccw * 2), mod(dir + ccw * 3)];
|
||||
lengths = [breadth, length, breadth + 2 * Math.random() + 1, length];
|
||||
|
||||
for (var i = 0; i < turns.length; i++) {
|
||||
for (var j = 0; j < lengths[i]; j++) {
|
||||
claim.push(turns[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (claim.length !== 0) {
|
||||
dir = claim.shift();
|
||||
}
|
||||
} else {
|
||||
//We are playing a little bit more cautious when we are outside and have a
|
||||
//lot of land
|
||||
weights = [5, 5, 5, 5];
|
||||
weights[dir] = 50;
|
||||
weights[mod(dir + 2)] = -9999;
|
||||
|
||||
for (var nd = 0; nd < 4; nd++) {
|
||||
for (var S = 1; S < 20; S++) {
|
||||
var nr = MOVES[nd][0] * S + row;
|
||||
var nc = MOVES[nd][1] * S + col;
|
||||
|
||||
if (nr < 0 || nc < 0 || nr >= GRID_SIZE || nc >= GRID_SIZE) {
|
||||
if (S > 1) {
|
||||
weights[nd]--;
|
||||
} else {
|
||||
weights[nd] = -9999;
|
||||
}
|
||||
} else {
|
||||
if (user.tail.hitsTail(new Loc(nr, nc))) {
|
||||
if (S > 1) {
|
||||
weights[nd] -= 50 - S;
|
||||
} else {
|
||||
weights[nd] = -9999;
|
||||
}
|
||||
}
|
||||
|
||||
if (grid.get(nr, nc) === user) {
|
||||
weights[nd] += 10 + S;
|
||||
}
|
||||
|
||||
var tailed = undefined;
|
||||
for (var o of others) {
|
||||
if (o.tail.hitsTail(new Loc(nr, nc))) {
|
||||
tailed = o;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (tailed) {
|
||||
if (o.name.indexOf("PAPER") != -1) { //Don't really try to kill our own kind
|
||||
weights[nd] += 3 * (30 - S);
|
||||
} else {
|
||||
weights[nd] += 30 * (30 - S);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//View a selection of choices based on the weights we computed
|
||||
var choices = [];
|
||||
for (var d = 0; d < 4; d++) {
|
||||
for (var S = 1; S < weights[d]; S++) {
|
||||
choices.push(d);
|
||||
}
|
||||
}
|
||||
|
||||
if (choices.length === 0) {
|
||||
choices.push(dir);
|
||||
}
|
||||
|
||||
dir = choices[Math.floor(Math.random() * choices.length)];
|
||||
}
|
||||
client.changeHeading(dir);
|
||||
}
|
||||
}
|
||||
|
||||
function calcFavorability(params) {
|
||||
return params.portion + params.kills * 50 + params.survival / 100;
|
||||
}
|
||||
|
||||
client.allowAnimation = false;
|
||||
client.renderer = {
|
||||
addPlayer: function(player) {
|
||||
playerPortion[player.num] = 0;
|
||||
},
|
||||
disconnect: function() {
|
||||
var dt = (endFrame - startFrame);
|
||||
startFrame = -1;
|
||||
|
||||
console.log("I died... (survived for " + dt + " frames.)");
|
||||
console.log("I killed " + client.kills + " player(s).");
|
||||
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]++;
|
||||
}
|
||||
};
|
||||
|
||||
connect();
|
@ -17,7 +17,6 @@ var MIN_BAR_WIDTH = 65;
|
||||
var BAR_HEIGHT = SHADOW_OFFSET + CELL_WIDTH;
|
||||
var BAR_WIDTH = 400;
|
||||
|
||||
|
||||
var canvas, canvasWidth, canvasHeight, gameWidth, gameHeight, ctx, offctx, offscreenCanvas;
|
||||
|
||||
$(function () {
|
||||
|
Loading…
Reference in New Issue
Block a user