[plugins/guess-player-levels] EXPERIMENTAL: search log to determine player level by burster range
This commit is contained in:
parent
6c50fc9eb1
commit
047fce5225
@ -20,6 +20,7 @@
|
||||
|
||||
// use own namespace for plugin
|
||||
window.plugin.guessPlayerLevels = function() {};
|
||||
window.plugin.guessPlayerLevels.BURSTER_RANGES = [0, 42, 48, 58, 72, 90, 112, 138, 168];
|
||||
|
||||
// we prepend a hash sign (#) in front of the player name in storage in order to prevent accessing a pre-defined property
|
||||
// (like constructor, __defineGetter__, etc.
|
||||
@ -178,24 +179,208 @@ window.plugin.guessPlayerLevels.extractPortalData = function(data) {
|
||||
}
|
||||
|
||||
window.plugin.guessPlayerLevels.extractChatData = function(data) {
|
||||
var attackData = {};
|
||||
function addAttackMessage(nick, timestamp, portal) {
|
||||
if(!attackData[nick]) attackData[nick] = {};
|
||||
if(!attackData[nick][timestamp]) attackData[nick][timestamp] = [];
|
||||
attackData[nick][timestamp].push(portal);
|
||||
}
|
||||
|
||||
data.raw.result.forEach(function(msg) {
|
||||
var plext = msg[2].plext;
|
||||
|
||||
// search for "x deployed an Ly Resonator on z"
|
||||
if(plext.plextType == 'SYSTEM_BROADCAST'
|
||||
&& plext.markup.length==5
|
||||
&& plext.markup[0][0] == 'PLAYER'
|
||||
&& plext.markup[1][0] == 'TEXT'
|
||||
&& plext.markup[1][1].plain == ' deployed an '
|
||||
&& plext.markup[2][0] == 'TEXT'
|
||||
&& plext.markup[2][0] == 'TEXT'
|
||||
&& plext.markup[3][0] == 'TEXT'
|
||||
&& plext.markup[3][1].plain == ' Resonator on ') {
|
||||
var nick = plext.markup[0][1].plain;
|
||||
var lvl = parseInt(plext.markup[2][1].plain.substr(1));
|
||||
window.plugin.guessPlayerLevels.savePlayerLevel(nick, lvl, true);
|
||||
}
|
||||
|
||||
// search for "x destroyed an Ly Resonator on z"
|
||||
if(plext.plextType == 'SYSTEM_BROADCAST'
|
||||
&& plext.markup.length==5
|
||||
&& plext.markup[0][0] == 'PLAYER'
|
||||
&& plext.markup[1][0] == 'TEXT'
|
||||
&& plext.markup[1][1].plain == ' destroyed an '
|
||||
&& plext.markup[2][0] == 'TEXT'
|
||||
&& plext.markup[3][0] == 'TEXT'
|
||||
&& plext.markup[3][1].plain == ' Resonator on ') {
|
||||
var nick = plext.markup[0][1].plain;
|
||||
var portal = plext.markup[4][1];
|
||||
addAttackMessage(nick, msg[1], portal)
|
||||
}
|
||||
|
||||
// search for "Your Lx Resonator on y was destroyed by z"
|
||||
if(plext.plextType == 'SYSTEM_NARROWCAST'
|
||||
&& plext.markup.length==6
|
||||
&& plext.markup[0][0] == 'TEXT'
|
||||
&& plext.markup[0][1].plain == 'Your '
|
||||
&& plext.markup[1][0] == 'TEXT'
|
||||
&& plext.markup[2][0] == 'TEXT'
|
||||
&& plext.markup[2][1].plain == ' Resonator on '
|
||||
&& plext.markup[3][0] == 'PORTAL'
|
||||
&& plext.markup[4][0] == 'TEXT'
|
||||
&& plext.markup[4][1].plain == ' was destroyed by '
|
||||
&& plext.markup[5][0] == 'PLAYER') {
|
||||
var nick = plext.markup[5][1].plain;
|
||||
var portal = plext.markup[3][1];
|
||||
addAttackMessage(nick, msg[1], portal)
|
||||
}
|
||||
|
||||
// search for "Your Portal x neutralized by y"
|
||||
// search for "Your Portal x is under attack by y"
|
||||
if(plext.plextType == 'SYSTEM_NARROWCAST'
|
||||
&& plext.markup.length==4
|
||||
&& plext.markup[0][0] == 'TEXT'
|
||||
&& plext.markup[0][1].plain == 'Your Portal '
|
||||
&& plext.markup[1][0] == 'PORTAL'
|
||||
&& plext.markup[2][0] == 'TEXT'
|
||||
&& (plext.markup[2][1].plain == ' neutralized by ' || plext.markup[2][1].plain == ' is under attack by ')
|
||||
&& plext.markup[3][0] == 'PLAYER') {
|
||||
var nick = plext.markup[3][1].plain;
|
||||
var portal = plext.markup[1][1];
|
||||
addAttackMessage(nick, msg[1], portal)
|
||||
}
|
||||
});
|
||||
|
||||
for(nick in attackData) {
|
||||
for(timestamp in attackData[nick]) {
|
||||
// remove duplicates
|
||||
var latlngs = [];
|
||||
var portals = {};
|
||||
attackData[nick][timestamp].forEach(function(portal) {
|
||||
if(portals.hasOwnProperty(portal.guid))
|
||||
return;
|
||||
portals[portal.guid] = 1;
|
||||
latlngs.push({x: portal.lngE6/1E6, y:portal.latE6/1E6});
|
||||
});
|
||||
if(latlngs.length < 2) // we need at least 2 portals to calculate burster range
|
||||
continue;
|
||||
|
||||
window.plugin.guessPlayerLevels.handleAttackData(nick, latlngs);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
window.plugin.guessPlayerLevels.handleAttackData = function(nick, latlngs) {
|
||||
/*
|
||||
This is basically the smallest enclosing circle problem. The algorithm is for points on a plane, but for our ranges
|
||||
(X8 has 168m) this should work.
|
||||
http://www.cs.uu.nl/docs/vakken/ga/slides4b.pdf
|
||||
http://nayuki.eigenstate.org/page/smallest-enclosing-circle
|
||||
http://everything2.com/title/Circumcenter
|
||||
*/
|
||||
var circle = {
|
||||
x: latlngs[0].x,
|
||||
y: latlngs[0].y,
|
||||
radius: 0
|
||||
};
|
||||
for(var i=1; i<latlngs.length; i++) {
|
||||
var latlng = latlngs[i];
|
||||
if(!window.plugin.guessPlayerLevels.isPointInCircle(latlng, circle))
|
||||
circle = window.plugin.guessPlayerLevels.calculateCircleWithAnchor(latlngs.slice(0, i + 1), latlng);
|
||||
}
|
||||
|
||||
// don't know under what circumstances, but sometimes the calculation fails. ignore nonsense data
|
||||
if(circle.x == 0 || circle.y == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// circle.range is useless, because it is calculated in degrees (simplified algorithm!)
|
||||
var latlng = L.latLng(circle.y, circle.x);
|
||||
var range = 0;
|
||||
for(var i=0; i<latlngs.length; i++) {
|
||||
var d = latlng.distanceTo([latlngs[i].y, latlngs[i].x]);
|
||||
if(d > range)
|
||||
range = d;
|
||||
}
|
||||
|
||||
// same as above. ignore nonsense data
|
||||
if(range > 1000) {
|
||||
return;
|
||||
}
|
||||
|
||||
var burster = window.plugin.guessPlayerLevels.BURSTER_RANGES;
|
||||
for(var i=1; i<burster.length; i++) {
|
||||
if(range > burster[i]) {
|
||||
window.plugin.guessPlayerLevels.savePlayerLevel(nick, Math.min(i+1, MAX_PORTAL_LEVEL), false);
|
||||
}
|
||||
}
|
||||
|
||||
//L.circle(latlng, range, {
|
||||
// weight:1,
|
||||
// title: nick + ", " + range + "m"
|
||||
//}).addTo(map);
|
||||
}
|
||||
|
||||
window.plugin.guessPlayerLevels.calculateCircleWithAnchor = function(latlngs, anchor) {
|
||||
var circle = {
|
||||
x: anchor.x,
|
||||
y: anchor.y,
|
||||
radius: 0
|
||||
};
|
||||
for(var i=0; i<latlngs.length; i++) {
|
||||
var p = latlngs[i];
|
||||
if(!window.plugin.guessPlayerLevels.isPointInCircle(p, circle)) {
|
||||
if(circle.radius == 0) // for the first two points
|
||||
circle = window.plugin.guessPlayerLevels.calculateCircleFromBisector(p, anchor);
|
||||
else
|
||||
circle = window.plugin.guessPlayerLevels.calculateCircleWithAnchors(latlngs.slice(0, i + 1), anchor, p);
|
||||
}
|
||||
}
|
||||
return circle;
|
||||
}
|
||||
|
||||
window.plugin.guessPlayerLevels.calculateCircleWithAnchors = function(latlngs, a, b) {
|
||||
var circle = window.plugin.guessPlayerLevels.calculateCircleFromBisector(a, b);
|
||||
for(var i=0; i<latlngs.length; i++) {
|
||||
var c = latlngs[i];
|
||||
if(!window.plugin.guessPlayerLevels.isPointInCircle(c, circle)) {
|
||||
var dA = a.x^2 + a.y^2;
|
||||
var dB = b.x^2 + b.y^2;
|
||||
var dC = c.x^2 + c.y^2;
|
||||
|
||||
circle.x = (dA*(c.y-b.y) + dB*(a.y-c.y) + dC*(b.y-a.y)) / 2*(a.x*(c.y-b.y) + b.x*(a.y-c.y) + c.x*(b.y-a.y));
|
||||
circle.y = -(dA*(c.x-b.x) + dB*(a.x-c.x) + dC*(b.x-a.x)) / 2*(a.x*(c.y-b.y) + b.x*(a.y-c.y) + c.x*(b.y-a.y));
|
||||
|
||||
circle.radius = Math.max(
|
||||
window.plugin.guessPlayerLevels.getDistance(a, circle),
|
||||
window.plugin.guessPlayerLevels.getDistance(b, circle),
|
||||
window.plugin.guessPlayerLevels.getDistance(c, circle)
|
||||
);
|
||||
}
|
||||
}
|
||||
return circle;
|
||||
}
|
||||
|
||||
window.plugin.guessPlayerLevels.calculateCircleFromBisector = function(p, q) {
|
||||
var circle = {
|
||||
x: (p.x + q.x) / 2,
|
||||
y: (p.y + q.y) / 2,
|
||||
radius: window.plugin.guessPlayerLevels.getDistance(p, q) / 2
|
||||
};
|
||||
|
||||
return circle;
|
||||
}
|
||||
|
||||
window.plugin.guessPlayerLevels.isPointInCircle = function(point, circle) {
|
||||
var d = window.plugin.guessPlayerLevels.getDistance(point, circle);
|
||||
return (d - 1E-10) <= circle.radius; // subtract a small epsilon to return true even if point is on the edge
|
||||
}
|
||||
|
||||
window.plugin.guessPlayerLevels.getDistance = function(a, b) {
|
||||
var dx = a.x - b.x;
|
||||
var dy = a.y - b.y;
|
||||
return Math.sqrt(dx*dx + dy*dy);
|
||||
}
|
||||
|
||||
window.plugin.guessPlayerLevels.savePlayerLevel = function(nick, level, certain) {
|
||||
var cache = window.plugin.guessPlayerLevels._loadLevels();
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user