[plugins/guess-player-levels] EXPERIMENTAL: search log to determine player level by burster range
This commit is contained in:
		@@ -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();
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user