summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--README12
-rw-r--r--analyze.py24
-rw-r--r--geometry.py27
-rw-r--r--gui.py154
-rw-r--r--interval_utils.py30
-rw-r--r--main.py147
-rw-r--r--mechanics.py15
-rw-r--r--nogui.py41
-rw-r--r--pathfinding.py17
-rw-r--r--reversing_game_mechanics/game_mechanics.txt54
-rw-r--r--reversing_game_mechanics/splits_and_ejects/README50
-rw-r--r--reversing_game_mechanics/stuff.txt117
-rw-r--r--reversing_game_mechanics/zoomlevel/README32
-rw-r--r--reversing_game_mechanics/zoomlevel/filter_data.py10
-rw-r--r--reversing_game_mechanics/zoomlevel/stats.pickle.xzbin0 -> 581852 bytes
-rw-r--r--reversing_game_mechanics/zoomlevel/win.mass.1116
-rw-r--r--reversing_game_mechanics/zoomlevel/win.mass.2455
-rw-r--r--reversing_game_mechanics/zoomlevel/win.mass.3126
-rw-r--r--reversing_game_mechanics/zoomlevel/win.mass.4130
-rw-r--r--reversing_game_mechanics/zoomlevel/win.mass.58
-rw-r--r--reversing_game_mechanics/zoomlevel/win.mass.64
-rw-r--r--reversing_game_mechanics/zoomlevel/win.mass.gnuplot9
-rw-r--r--reversing_game_mechanics/zoomlevel/win.size.1116
-rw-r--r--reversing_game_mechanics/zoomlevel/win.size.2150
-rw-r--r--reversing_game_mechanics/zoomlevel/win.size.369
-rw-r--r--reversing_game_mechanics/zoomlevel/win.size.477
-rw-r--r--reversing_game_mechanics/zoomlevel/win.size.58
-rw-r--r--reversing_game_mechanics/zoomlevel/win.size.64
-rw-r--r--reversing_game_mechanics/zoomlevel/win.size.all424
-rw-r--r--reversing_game_mechanics/zoomlevel/win.size.all.filtered227
-rw-r--r--reversing_game_mechanics/zoomlevel/win.size.gnuplot19
-rw-r--r--stats.py479
-rw-r--r--strategy.py162
-rw-r--r--subscriber.py190
34 files changed, 3404 insertions, 99 deletions
diff --git a/README b/README
index 57da257..fa3410e 100644
--- a/README
+++ b/README
@@ -3,9 +3,13 @@ prerequisites: python3, pygame and websocket-client
git submodule update --init
python3 main.py
-move mouse = move
-left click, space = split
-right click, w = eject some mass
+space = split
+w = eject some mass
r = respawn
-s = lock input. cycles through: user lock -> user and bot lock -> bot lock -> no lock
+s = lock input. toggle between "user controls" and "bot controls". hold shift for "nobody controls"
+move mouse = move
+mouse left/mid/right click: set markers (currently unused)
esc = quit
+
+the stats.py module automatically creates/overwrites the stats.pickle file (roughly 1MB per 5 minutes currently)
+which can be loaded using Stats.load() and analyzed using the Stats.analyze_* functions, or using analyze.py
diff --git a/analyze.py b/analyze.py
new file mode 100644
index 0000000..c9c3ac8
--- /dev/null
+++ b/analyze.py
@@ -0,0 +1,24 @@
+from stats import *
+import sys
+
+if len(sys.argv) >= 2:
+ files = sys.argv[1:]
+else:
+ files = ["stats.pickle"]
+
+
+s = Stats.load(files[0])
+for f in files[1:]:
+ s.merge(f)
+
+s.analyze_speed()
+print("\n" + "-"*40 + "\n")
+s.analyze_visible_window(False)
+for i in ["split cell", "ejected mass", "virus", "virus2", "virus3"]:
+ s.analyze_deviations(i)
+print("")
+for i in ["split cell", "ejected mass", "virus"]:
+ s.analyze_distances(i)
+
+s.analyze_virus_sizes()
+s.analyze_remerge()
diff --git a/geometry.py b/geometry.py
new file mode 100644
index 0000000..d69add8
--- /dev/null
+++ b/geometry.py
@@ -0,0 +1,27 @@
+import math
+def distance_point_line(p, l1, l2):
+ # (x - l1.x) * (l2.y-l1.y)/(l2.x-l1.x) + l1.y = y
+ # x * (l2.y-l1.y)/(l2.x-l1.x) - l1.x * (l2.y-l1.y)/(l2.x-l1.x) + l1.y - y = 0
+ # x * (l2.y-l1.y) - l1.x * (l2.y-l1.y) + l1.y * (l2.x-l1.x) - y * (l2.x-l1.x) = 0
+ # ax + by + c = 0
+ # with a = (l2.y-l1.y), b = -(l2.x-l1.x), c = l1.y * (l2.x-l1.x) - l1.x * (l2.y-l1.y)
+ a = (l2.y-l1.y)
+ b = -(l2.x-l1.x)
+ c = l1.y * (l2.x-l1.x) - l1.x * (l2.y-l1.y)
+
+ d = math.sqrt(a**2 + b**2)
+ a/=d
+ b/=d
+ c/=d
+
+ assert (abs(a*l1.x + b*l1.y + c) < 0.001)
+ assert (abs(a*l2.x + b*l2.y + c) < 0.001)
+
+ return abs(a*p.x + b*p.y + c)
+
+def is_colinear(points, epsilon=1):
+ for point in points:
+ if distance_point_line(point, points[0], points[-1]) > epsilon:
+ return False
+ return True
+
diff --git a/gui.py b/gui.py
index 68aa48e..2a8460c 100644
--- a/gui.py
+++ b/gui.py
@@ -6,6 +6,10 @@ from pygame.locals import *
import sys
import math
import time
+import mechanics
+from agarnet.agarnet.vec import Vec
+
+running = True
font_fallback = False
try:
@@ -28,13 +32,47 @@ marker_updated = [True, True, True]
screensize=(1280, 800)
screen=pygame.display.set_mode(screensize,HWSURFACE|DOUBLEBUF|RESIZABLE)
+
+vignette = 1.
+
+def enable_vignette(factor=0.15):
+ global vignette
+ if factor:
+ vignette = 1+factor
+ else:
+ vignette = 1
+
+def draw_bar(rect, val, thresh=None, min=0, max=1, color=(0,0,0), barcolor=None, exceedcolor=(255,0,0), threshcolor=None):
+ v = (val-min)/(max-min)
+ t = (thresh-min)/(max-min)
+
+ if barcolor == None:
+ barcolor_=color
+ else:
+ barcolor_=barcolor
+ if thresh != None and threshcolor==None:
+ threshcolor_ = ((128+color[0])//2, (128+color[1])//2, (128+color[2])//2)
+ else:
+ threshcolor_ = threshcolor
+
+ for i in range(0, 1 if v<t else 3):
+ draw_box( ((rect[0][0]-i,rect[0][1]-i),(rect[1][0]+2*i, rect[1][1]+2*i)) , color if v<t or exceedcolor==None else exceedcolor, False, False)
+
+ draw_box(((rect[0][0]+2,rect[0][1]+2), ((rect[1][0]-4)*v, rect[1][1]-4)), barcolor_, True, False)
+ if thresh != None:
+ if exceedcolor != None and v >= t:
+ draw_box(((rect[0][0]+2 + (rect[1][0]-4)*t , rect[0][1]+2) , ((rect[1][0]-4)*(v-t) , rect[1][1]-4)), exceedcolor, True, False)
+
+ draw_line((rect[0][0]+2+(rect[1][0]-4)*t, rect[0][1]+1), (rect[0][0]+2+(rect[1][0]-4)*t, rect[0][1]+rect[1][1]-1), threshcolor_, False)
+
+
def draw_line(p1, p2, color, global_coords=True):
if global_coords:
p1 = world_to_win_pt(p1, c.player.center)
p2 = world_to_win_pt(p2, c.player.center)
- gfxdraw.line(screen, p1[0], p1[1], p2[0], p2[1], color)
+ gfxdraw.line(screen, int(p1[0]), int(p1[1]), int(p2[0]), int(p2[1]), color)
def draw_box(rect, color, filled=False, global_coords=True):
if global_coords:
@@ -60,6 +98,11 @@ def draw_circle(pos, r, color, filled=False, global_coords=True):
gfxdraw.circle(screen, pos[0], pos[1], r, color)
gfxdraw.aacircle(screen, pos[0], pos[1], r, color)
+def hilight_cell(cell, color_inner, color_outer, r=20):
+ draw_circle(cell.pos, cell.size+r, color_outer, True)
+ draw_circle(cell.pos, cell.size+r/2, color_inner, True)
+ draw_cell(cell)
+
def draw_polygon(polygon, color, filled=False, global_coords=True):
if len(polygon) > 2:
if global_coords:
@@ -103,12 +146,31 @@ def draw_text(pos, text, color, font_size=16, global_coords=True, draw_centered=
def update():
pygame.display.update()
-def calc_zoom():
- zoom1 = screensize[0] / 2051.
- zoom2 = screensize[1] / 1216.
- return max(zoom1,zoom2)
+def update_zoom():
+ global zoom
+ global calculated_visible_width, calculated_visible_height
+
+ ratio = 1.7 # reverse engineered value.
+
+ size = sum(map(lambda cell : cell.size, c.player.own_cells))
+
+ # reverse engineered formula
+ diag_server = mechanics.viewport_diag(size) if size > 0 else 10000
+
+ # calculate screen diag, if we would have a screen with our width, but correct ratio
+ diag1 = math.sqrt(screensize[0]**2 * (1 + 1/1.7**2))
+ # calculate screen diag, if we would have a screen with our height, but correct ratio
+ diag2 = math.sqrt(screensize[1]**2 * (1 + 1.7**2))
+
+ # what we expect to be visible from server
+ calculated_visible_width = diag_server / math.sqrt(1+1/ratio**2)
+ calculated_visible_height = diag_server / math.sqrt(1+ ratio**2)
+
+ zoom1 = screensize[0] / calculated_visible_width
+ zoom2 = screensize[1] / calculated_visible_height
+
+ zoom = min(zoom1,zoom2) / vignette
-zoom = calc_zoom()
def world_to_win_length(l):
return int(l*zoom)
@@ -141,7 +203,21 @@ def generate_virus(spikes, spike_length, radius, global_coords):
return points
def draw_cell(cell):
+ font_size = 16
+ virus_sizes = {100:1, 106:2, 113:3, 119:4, 125:5, 131:6, 136:7}
+
cx,cy = world_to_win_pt(cell.pos,c.player.center)
+ try:
+ mov_ang = cell.movement_angle
+ p2 = cell.pos + Vec( math.cos(mov_ang + mechanics.eject_delta*math.pi/180), math.sin(mov_ang + mechanics.eject_delta*math.pi/180) ) * (cell.size+700)
+ p3 = cell.pos + Vec( math.cos(mov_ang - mechanics.eject_delta*math.pi/180), math.sin(mov_ang - mechanics.eject_delta*math.pi/180) ) * (cell.size+700)
+
+ cx2,cy2 = world_to_win_pt(p2,c.player.center)
+ cx3,cy3 = world_to_win_pt(p3,c.player.center)
+ except (AttributeError, TypeError):
+ cx2,cy2=cx,cy
+ cx3,cy3=cx,cy
+
radius = world_to_win_length(cell.size)
if cell.is_virus:
@@ -156,19 +232,24 @@ def draw_cell(cell):
gfxdraw.filled_polygon(screen, polygon2, color)
gfxdraw.aapolygon(screen, polygon2, color)
+
+ draw_text((cx, cy), "%s / 7" % virus_sizes.get(cell.size, "?"), (64,0,0), font_size*2, False, True)
+ draw_text((cx, cy + radius + 10), str(cell.cid), (0,0,0), font_size, False, True)
else:
color=(int(cell.color[0]*255), int(cell.color[1]*255), int(cell.color[2]*255))
+
if not (cell.is_ejected_mass or cell.is_food):
+ gfxdraw.aapolygon(screen, [(cx,cy),(cx2,cy2),(cx3,cy3),(cx,cy)] ,(255,127,127))
+
gfxdraw.filled_circle(screen, cx, cy, radius, color)
gfxdraw.aacircle(screen, cx, cy, radius, (0,0,0))
gfxdraw.aacircle(screen, cx, cy, int(radius/2), (255,255,255))
gfxdraw.circle(screen, cx, cy, int(radius/2), (255,255,255))
- font_size = 16
-
draw_text((cx, cy + radius + 10), cell.name, (0, 0, 0), font_size, False, True)
+ draw_text((cx, cy + radius + 10 + font_size), str(cell.cid), (0,0,0), font_size, False, True)
# surface = draw_text(cell.name, (0, 0, 0), font_size)
# screen.blit(surface, (cx - (surface.get_width()/2), cy + radius + 5))
@@ -191,6 +272,19 @@ def draw_leaderboard():
# screen.blit(surface, (5, next_y))
next_y += surface.get_height()+5
+def draw_visible_window_borders():
+ global screen
+
+ vignette_color=(192,192,192)
+ vignette_width = int(max(0, (screensize[0]-world_to_win_length(calculated_visible_width))/2))
+ vignette_height = int(max(0, (screensize[1]-world_to_win_length(calculated_visible_height))/2))
+
+ screen.fill(vignette_color, rect=((0,0),(vignette_width,screensize[1])))
+ screen.fill(vignette_color, rect=((screensize[0]-vignette_width,0),(vignette_width,screensize[1])))
+ screen.fill(vignette_color, rect=((0,0),(screensize[0],vignette_height)))
+ screen.fill(vignette_color, rect=((0,screensize[1]-vignette_height),(screensize[0],vignette_height)))
+
+
def draw_world_borders():
top = int((c.world.top_left[0] - c.player.center[1])*zoom + screensize[1]/2)
left = int((c.world.top_left[1] - c.player.center[0])*zoom + screensize[0]/2)
@@ -226,13 +320,37 @@ def draw_markers():
for i in [0, 1, 2]:
draw_marker(marker[i], colors[i], marker_updated[i])
+def draw_debug():
+ for cell in c.world.cells.values():
+ parent = None
+ shoot_vec = None
+ settled = None
+ try:
+ parent = cell.parent
+ shoot_vec = cell.shoot_vec
+ settled = cell.calmed_down
+ except AttributeError:
+ pass
+
+ if parent != None:
+ draw_line(cell.pos, parent.pos,(255,0,0))
+ draw_circle(parent.pos,3,(255,0,0),True)
+
+
+ if shoot_vec != None and settled != True:
+ shoot_vec = 400 * shoot_vec / shoot_vec.len()
+ draw_line(cell.pos, cell.pos+shoot_vec, (0,255,255))
+
+
def draw_frame():
- global screen, movement, zoom, screensize, input, bot_input, marker, marker_updated
+ global screen, movement, zoom, screensize, input, bot_input, marker, marker_updated, running
pygame.event.pump()
clock.tick()
+ update_zoom()
clear_screen()
+ draw_visible_window_borders()
draw_world_borders()
food = list(filter(lambda x: x.is_food, c.world.cells.values()))
@@ -267,23 +385,23 @@ def draw_frame():
if event.type == VIDEORESIZE:
screensize = event.dict['size']
screen=pygame.display.set_mode(screensize, HWSURFACE|DOUBLEBUF|RESIZABLE)
- zoom = calc_zoom()
+ update_zoom()
pygame.display.update()
if event.type == QUIT:
pygame.display.quit()
if event.type == KEYDOWN:
if event.key == K_s:
- if not input and bot_input:
+ if event.mod & KMOD_SHIFT and (input or bot_input):
input = False
bot_input = False
- elif not input and not bot_input:
+ elif not input and bot_input:
input = True
bot_input = False
else:
input = False
bot_input = True
if event.key == K_ESCAPE:
- pygame.quit()
+ running = False
if event.key == K_r:
c.send_respawn()
if event.type == MOUSEBUTTONDOWN:
@@ -291,11 +409,11 @@ def draw_frame():
marker[event.button-1] = win_to_world_pt(event.pos, c.player.center)
marker_updated[event.button-1] = True
print("set marker "+str(event.button-1)+" to "+str(event.pos))
+ if event.type == KEYDOWN:
+ if event.key == K_w:
+ c.send_shoot()
+ if event.key == K_SPACE:
+ c.send_split()
if input:
- if event.type == KEYDOWN:
- if event.key == K_w:
- c.send_shoot()
- if event.key == K_SPACE:
- c.send_split()
if event.type == MOUSEMOTION:
c.send_target(*win_to_world_pt(event.pos, c.player.center))
diff --git a/interval_utils.py b/interval_utils.py
index 0095c06..7123893 100644
--- a/interval_utils.py
+++ b/interval_utils.py
@@ -70,13 +70,37 @@ def intervals_intersect(int1_, int2_):
return False
-def check_cell_in_interval(origin, cell, interval):
+def intersection(int1s, int2s):
+ #expects merged, canonicalized intervals, returns overlap-free canonicalized intervals
+
+ result = []
+
+ for int1 in int1s:
+ for int2 in int2s:
+ if (max(int1[0],int2[0]) <= min(int1[1],int2[1])):
+ result += [(max(int1[0],int2[0]), min(int1[1],int2[1]))]
+
+ return result
+
+def interval_area(ints):
+ result = 0
+ for i in merge_intervals(ints):
+ result += i[1]-i[0]
+ return result
+
+def interval_occupied_by_cell(origin, cell):
ang = get_point_angle(origin, cell.pos)
dist = math.sqrt( (cell.pos[0]-origin[0])**2 + (cell.pos[1]-origin[1])**2 )
- corridor_halfwidth = math.asin(cell.size / dist)
+ if cell.size >= dist:
+ corridor_halfwidth = math.pi/2
+ else:
+ corridor_halfwidth = math.asin(cell.size / dist)
+
+ return (ang-corridor_halfwidth, ang+corridor_halfwidth)
- return intervals_intersect(interval, (ang-corridor_halfwidth, ang+corridor_halfwidth))
+def check_cell_in_interval(origin, cell, interval):
+ return intervals_intersect(interval, interval_occupied_by_cell(origin,cell))
def get_cells_in_interval(origin, interval, cells):
return list(filter(lambda x: check_point_in_interval(origin, x.pos, interval), cells))
diff --git a/main.py b/main.py
index a74f5b4..e980fb3 100644
--- a/main.py
+++ b/main.py
@@ -7,16 +7,79 @@ import sys
import math
import time
import random
-import gui
+import nogui as gui # might be overridden later.
import stats
-from subscriber import DummySubscriber
+from subscriber import EnhancingSubscriber
from interval_utils import *
from pathfinding import PathfindingTesterStrategy
+import time
+
+class Clock:
+ def __init__(self):
+ self.t = time.time()
+ self.fps_t = time.time()
+ self.fps = 27
+ self.cnt = 0
+ self.newfps = False
+
+ def tic(self):
+ t = time.time()
+ result = t-self.t
+ self.t=t
+ return result
+
+ def getfps(self):
+ self.cnt+=1
+ if time.time() > self.fps_t + 1:
+ self.fps_t += 1
+ self.fps = self.cnt
+ self.cnt = 0
+ self.newfps = True
+ else:
+ self.newfps = False
+ return self.fps
+
+class ProblemException(BaseException):
+ pass
+
+class ProblemManager:
+ def __init__(self, setup):
+ self.setup = setup
+ self.problems = {}
+ for t in setup:
+ self.problems[t] = []
+
+ def report(self, prob):
+ self.problems[prob] += [time.time()]
+ self.problems[prob] = list(filter(lambda t : time.time() < t + self.setup[prob][1], self.problems[prob]))
+
+ if len(self.problems[prob]) > self.setup[prob][0]:
+ print("PROBLEM: "+prob)
+ if self.setup[prob][2]:
+ raise ProblemException
+
+probs = ProblemManager({"network lag":(100,5,True), "strategy lag":(5,2,False), "gui lag":(5,2,False), "high fps":(5,6,True), "low fps":(5,6,True)})
+
+
+if "--nogui" in sys.argv:
+ sys.argv.remove("--nogui")
+else:
+ try:
+ import gui
+ except:
+ print("ERROR: could not import gui... running without gui.")
+
# global vars
-sub = DummySubscriber()
+sub = EnhancingSubscriber()
c = client.Client(sub)
-stats = stats.Stats()
+sub.set_client(c)
+stats = stats.Stats(c)
+
+try:
+ nick = sys.argv[2]
+except:
+ nick = ""
for i in range(1,10): # 10 connection attempts
print("trying to connect, attempt "+str(i))
@@ -25,8 +88,11 @@ for i in range(1,10): # 10 connection attempts
try:
token = sys.argv[1]
addr, *_ = utils.get_party_address(token)
+ print("using party token")
+
except:
addr, token, *_ = utils.find_server()
+ print("joining random game")
# connect
c.connect(addr,token)
@@ -37,27 +103,64 @@ for i in range(1,10): # 10 connection attempts
c.disconnect()
-c.player.nick="test cell pls ignore"
+c.player.nick=nick
# initialize GUI
gui.set_client(c)
# initialize strategy
-strategy = PathfindingTesterStrategy(c)
-
-# main loop
-while True:
- c.on_message()
-
- gui.draw_frame()
-
- if len(list(c.player.own_cells)) > 0:
- target = strategy.process_frame()
-
- if gui.bot_input and target != None:
- c.send_target(target[0], target[1])
-
- stats.log_pos(c.player.center)
- stats.log_mass(c.player.total_mass)
- gui.update()
+strategy = PathfindingTesterStrategy(c, gui)
+
+autorespawn_counter = 60
+
+clock = Clock()
+
+try:
+ # main loop
+ while gui.running:
+ c.on_message()
+ if clock.tic() > 1./20:
+ print("NETWORK LAG")
+ probs.report("network lag")
+
+ gui.draw_frame()
+ if clock.tic() > 1./40:
+ print("GUI SLOW")
+ probs.report("gui lag")
+
+ if len(list(c.player.own_cells)) > 0:
+ target = strategy.process_frame()
+
+ if gui.bot_input:
+ c.send_target(target[0], target[1])
+
+ stats.process_frame()
+
+ if clock.tic() > 1./25.:
+ print("STRATEGY LAG")
+ probs.report("strategy lag")
+
+ gui.draw_debug()
+ gui.update()
+
+ if not c.player.is_alive:
+ if autorespawn_counter == 0:
+ c.send_respawn()
+ autorespawn_counter = 60
+ else:
+ autorespawn_counter-=1
+
+ fps = clock.getfps()
+ if clock.newfps:
+ print("FPS: %3d" % fps)
+ if fps < 24:
+ probs.report("low fps")
+ if fps > 50:
+ probs.report("high fps")
+except ProblemException:
+ print("Exiting due to a problem such as low/high fps, network lags etc")
+
+stats.save("stats.pickle")
+
+print("bye")
diff --git a/mechanics.py b/mechanics.py
new file mode 100644
index 0000000..3aaa405
--- /dev/null
+++ b/mechanics.py
@@ -0,0 +1,15 @@
+from agarnet.agarnet.world import Cell
+
+def speed(size_or_cell):
+ if isinstance(size_or_cell, Cell):
+ if size_or_cell.is_virus or size_or_cell.is_ejected_mass or size_or_cell.is_food:
+ return 0
+ else:
+ return speed(size_or_cell.size)
+ else:
+ return 86 / (size_or_cell**0.45)
+
+def viewport_diag(sizesum):
+ return 370 * max(sizesum,70)**0.431776
+
+eject_delta = 22 # how many degrees do ejects deviate from the original direction (maximum)
diff --git a/nogui.py b/nogui.py
new file mode 100644
index 0000000..b2989de
--- /dev/null
+++ b/nogui.py
@@ -0,0 +1,41 @@
+running = True
+bot_input = True
+
+def draw_bar(rect, val, thresh=None, min=0, max=1, color=(0,0,0), barcolor=None, exceedcolor=(255,0,0), threshcolor=None):
+ pass
+
+def draw_line(p1, p2, color, global_coords=True):
+ pass
+
+def draw_box(rect, color, filled=False, global_coords=True):
+ pass
+
+def draw_circle(pos, r, color, filled=False, global_coords=True):
+ pass
+
+def hilight_cell(cell, color_inner, color_outer, r=20):
+ pass
+
+def draw_polygon(polygon, color, filled=False, global_coords=True):
+ pass
+
+def draw_path(path, color, global_coords=True):
+ pass
+
+def draw_arc(pos, r, bounds, color, global_coords=True):
+ pass
+
+def draw_text(pos, text, color, font_size=16, global_coords=True, draw_centered=False):
+ pass
+
+def update():
+ pass
+
+def set_client(cl):
+ pass
+
+def draw_debug():
+ pass
+
+def draw_frame():
+ pass
diff --git a/pathfinding.py b/pathfinding.py
index beec89a..9386275 100644
--- a/pathfinding.py
+++ b/pathfinding.py
@@ -1,5 +1,3 @@
-from gui import marker, marker_updated
-import gui
import math
@@ -75,9 +73,10 @@ grid_radius=int(1100/30)*30
grid_density=30
class PathfindingTesterStrategy:
- def __init__(self, c):
+ def __init__(self, c, gui):
self.c = c
self.path = None
+ self.gui = gui
def build_grid(self):
grid = [[0 for x in range(int(2*grid_radius//grid_density+1))] for x in range(int(2*grid_radius//grid_density+1))]
@@ -110,8 +109,8 @@ class PathfindingTesterStrategy:
return grid
def plan_path(self):
- goalx = int((marker[0][0] - self.c.player.center[0] + grid_radius)/grid_density)
- goaly = int((marker[0][1] - self.c.player.center[1] + grid_radius)/grid_density)
+ goalx = int((self.gui.marker[0][0] - self.c.player.center[0] + grid_radius)/grid_density)
+ goaly = int((self.gui.marker[0][1] - self.c.player.center[1] + grid_radius)/grid_density)
grid = self.build_grid()
@@ -130,8 +129,8 @@ class PathfindingTesterStrategy:
return True
def process_frame(self):
- if marker_updated[0]:
- marker_updated[0]=False
+ if self.gui.marker_updated[0]:
+ self.gui.marker_updated[0]=False
self.path = self.plan_path()
for node in self.path:
@@ -140,7 +139,7 @@ class PathfindingTesterStrategy:
for (node1,node2) in zip(self.path,self.path[1:]):
- gui.draw_line(node1.point, node2.point, (0,0,0))
+ self.gui.draw_line(node1.point, node2.point, (0,0,0))
if self.path:
relx, rely = self.path[0].point[0]-self.c.player.center.x, self.path[0].point[1]-self.c.player.center.y
@@ -153,4 +152,4 @@ class PathfindingTesterStrategy:
if self.path:
return self.path[0].point
- return marker[0]
+ return self.gui.marker[0]
diff --git a/reversing_game_mechanics/game_mechanics.txt b/reversing_game_mechanics/game_mechanics.txt
new file mode 100644
index 0000000..2488481
--- /dev/null
+++ b/reversing_game_mechanics/game_mechanics.txt
@@ -0,0 +1,54 @@
+Created 2015-09-01, with data between 0 and 2 weeks old.
+
+Units used:
+ distances, sizes are measured in raw agar.io coordinates.
+ times are measured in frames. The game ran at 27-28 FPS at the time
+ where this document was created
+ speeds are measured in distance / number of frames
+
+
+
+cellspeed = 86.05 / cellsize**0.45
+
+zoom level is 369.399 * sum(own_cells.size) ** 0.431776
+
+ejecting mass:
+ prerequisite: size >= 60, i.e. mass >= 35
+ you will lose 16 mass, but only gain 12 mass if you eat the blob
+ one can eat ejected mass if mass >= 18 i.e. size >= 43
+
+ 75% of the ejects had a deviation smaller than 0.39 rad = 22.16 deg
+ from the original moving direction of the shooting cell
+
+ they fly 44-45px, starting from shooting cell's border.
+
+splitting:
+ 75% of the splits had a deviation smaller than 0.02 rad = 1.19 deg
+ from the original moving direction of the parent
+
+ flight distance: unknown, somewhere between 100 - 650, max = 1205
+
+ re-union will usually become possible somewhere after 745 - 790 frames
+ this means that your cells are possible to glide into each
+ other. depending on your movement pattern they will actually
+ merge sooner or later.
+
+feeding viruses:
+ 75% of the splits had a deviation smaller than 0.00 rad = 0.11 deg
+ from the ejected mass movement vector.
+
+ maximum distance is 580 from the midpoint of the parent virus.
+ not sure about the minimum (too few data), but probably also 580?
+
+ Viruses can have the following sizes:
+ 100, 106, 113, 119, 125, 131, 136.
+ Sometimes 141 is also observed, but those viruses will split
+ almost instantly (bug in the server software?)
+ i.e., feed a virus with size 100 6 times to maximum size, and one more
+ time to make it duplicate.
+
+eating:
+ a cell can eat us, if their size/mass(??) is at least 1.25* our size/mass
+ a cell eats another, if the smaller one's midpoint is within the
+ larger one's circle (with radius = size)
+
diff --git a/reversing_game_mechanics/splits_and_ejects/README b/reversing_game_mechanics/splits_and_ejects/README
new file mode 100644
index 0000000..14fa5ad
--- /dev/null
+++ b/reversing_game_mechanics/splits_and_ejects/README
@@ -0,0 +1,50 @@
+Data collected with revision 165dd41, four parallel instances, 30-45 minutes each, on
+2015-08-28. Analyzed with analyze.py
+
+split cell eject/split direction deviations: mean = -0.04179961392956227, stddev=0.4556678701725402, ndata=159
+ 75% of the splits had a deviation smaller than 0.02 rad = 1.31 deg
+
+ejected mass eject/split direction deviations: mean = -0.0016847086620534303, stddev=0.872858965604425, ndata=352
+ 75% of the splits had a deviation smaller than 0.46 rad = 26.47 deg
+
+
+split cell eject/split distances: mean = 388.2279635920042, stddev=222.71465106976927, ndata=314
+ 75% of the values lie in the interval 381.25 plusminus 225.53
+
+ejected mass eject/split distances: mean = 442.90229450857305, stddev=189.2221703217239, ndata=252
+ 75% of the values lie in the interval 535.71 plusminus 8.61
+
+distances are measured between "spawn point of cell" and "end point of movement".
+Spawnpoint is usually near "parentcell.midpoint + parentcell.size".
+
+
+
+Now if we measure distances between "midpoint of parent cell" and "end point of movement" by
+applying the following patch:
+
+diff --git a/stats.py b/stats.py
+index bb88c3e..1c0a196 100644
+--- a/stats.py
++++ b/stats.py
+@@ -338,7 +338,7 @@ class Stats:
+ # print(str(n) + "\t" + str(x))
+
+ def analyze_distances(self, celltype):
+- ds = [v[0] for v in self.data.eject_distlogs[celltype]]
++ ds = [v[1] for v in self.data.eject_distlogs[celltype]]
+
+ try:
+ mean, stddev = fit_gaussian(ds)
+
+we get this:
+
+split cell eject/split distances: mean = 560.4528176561469, stddev=276.25260008531626, ndata=314
+ 75% of the values lie in the interval 556.62 plusminus 322.76
+
+ejected mass eject/split distances: mean = 767.2502438544719, stddev=168.80422060053823, ndata=252
+ 75% of the values lie in the interval 732.30 plusminus 86.28
+
+
+As one can see, the "plusminus" values are much larger than above. So measuring between "spawnpoint" and
+"endpoint" is more appropriate.
+
diff --git a/reversing_game_mechanics/stuff.txt b/reversing_game_mechanics/stuff.txt
new file mode 100644
index 0000000..fbbddc4
--- /dev/null
+++ b/reversing_game_mechanics/stuff.txt
@@ -0,0 +1,117 @@
+as of 2015-08-30
+
+CELL SIZE VS SPEED
+
+ size**0.45 * speed = 86.05616001328154
+
+
+
+
+SIZE VS VIEWPORT
+
+ with 1 cells, depending on sum(size)
+ median ratio = 1.7025611175785798
+ diag / size**0.33 = 608.971483054539
+
+ with 2 cells, depending on sum(size)
+ median ratio = 1.6963503649635037
+ diag / size**0.33 = 585.5509541758322
+
+ with 3 cells, depending on sum(size)
+ median ratio = 1.6898326898326899
+ diag / size**0.58 = 170.29929514108093
+
+ with 4 cells, depending on sum(size)
+ median ratio = 1.650784427658338
+ diag / size**0.0 = 3158.567553889486
+
+ with 1 cells, depending on sum(mass)
+ median ratio = 1.7025611175785798
+ diag / size**0.17 = 1270.6199859482824
+
+ with 2 cells, depending on sum(mass)
+ median ratio = 1.6972934472934473
+ diag / size**0.16 = 1407.4522241811242
+
+ with 3 cells, depending on sum(mass)
+ median ratio = 1.6975546975546976
+ diag / size**0.28 = 910.623966202271
+
+ with 4 cells, depending on sum(mass)
+ median ratio = 1.6625734116390818
+ diag / size**0.0 = 3141.1700855829763
+
+
+
+
+EJECT/SPLIT DIRECTIONS
+
+ split cell eject/split direction deviations: mean = 0.0009390500296252917, stddev=0.31212271930689983, ndata=621
+ 75% of the splits had a deviation smaller than 0.02 rad = 1.19 deg
+
+ ejected mass eject/split direction deviations: mean = -0.021378484138331356, stddev=0.730695490707546, ndata=1585
+ 75% of the splits had a deviation smaller than 0.39 rad = 22.16 deg
+
+ virus eject/split direction deviations: mean = -0.16497762078310707, stddev=0.07294211158929148, ndata=5
+ 75% of the splits had a deviation smaller than 0.25 rad = 14.41 deg
+ (wrt "emitted mass begin -- parent virus")
+
+ virus2 eject/split direction deviations: mean = -0.001211356391619578, stddev=0.0038334565337591105, ndata=5
+ 75% of the splits had a deviation smaller than 0.00 rad = 0.11 deg
+ (wrt "emitted mass flight direction")
+
+ virus3 eject/split direction deviations: mean = -0.13739240722341658, stddev=0.056773374975376475, ndata=5
+ 75% of the splits had a deviation smaller than 0.21 rad = 11.82 deg
+ (wrt "emitter of the mass -- parent virus")
+
+
+
+
+EJECT/SPLIT DISTANCES
+
+ split cell eject/split distances: mean = 378.6264099585539, stddev =214.15995855502896, ndata=1226
+ split cell meann = 23.37846655791191, stddevn =17.23260859398865
+ 75% of the distances lie in the interval 370.30 plusminus 218.60
+ 80% of the distances lie in the interval 370.30 plusminus 262.32
+ max = 1205.46
+ 75% of the flight lengths lie in the interval 20.00 plusminus 9.00
+ 78% of the flight lengths lie in the interval 20.00 plusminus 10.80
+
+ ejected mass eject/split distances: mean = 473.3307839719213, stddev =159.4625848157587, ndata=1121
+ ejected mass meann = 42.015165031222125, stddevn =8.5656796143937
+ 75% of the distances lie in the interval 534.64 plusminus 2.10
+ 77% of the distances lie in the interval 534.64 plusminus 2.52
+ max = 637.28
+ 75% of the flight lengths lie in the interval 44.00 plusminus 1.00
+ 79% of the flight lengths lie in the interval 44.00 plusminus 1.20
+
+ virus eject/split distances: mean = 396.47928995805, stddev =219.79929069475193, ndata=9
+ virus meann = 42.666666666666664, stddevn =6.879922480183431
+ 75% of the distances lie in the interval 510.53 plusminus 363.80
+ 88% of the distances lie in the interval 510.53 plusminus 436.56
+ max = 580.08 (ndata = 13)
+ 75% of the flight lengths lie in the interval 45.00 plusminus 10.00
+ 77% of the flight lengths lie in the interval 45.00 plusminus 12.00
+
+
+
+
+VIRUS SIZES
+
+ I've seen the following 7 virus sizes:
+ 100: 386018 times
+ 106: 124015 times
+ 113: 72084 times
+ 119: 41825 times
+ 125: 24954 times
+ 131: 373398 times
+ 136: 11550 times
+
+
+
+
+REMERGES
+
+ 75% of the remerge durations lie at 32.00 plusminus 30.00 frames
+ 75% of the remerges were started after 767.00 plusminus 20.00 frames
+
diff --git a/reversing_game_mechanics/zoomlevel/README b/reversing_game_mechanics/zoomlevel/README
new file mode 100644
index 0000000..a2acc90
--- /dev/null
+++ b/reversing_game_mechanics/zoomlevel/README
@@ -0,0 +1,32 @@
+The goal is to find out the formula to calculate the appropriate zoom level,
+or equivalent: to calculate the size of the world viewport depending on, well,
+dunno.
+
+Data collected using bea9a124317, on 2015-08-25 *iirc*. (-> stats.pickle.xz)
+
+Data analyzed using ea4515d0516, with:
+
+ python analyze.py stats.pickle > temp
+
+then manually, create win.{size,mass}.{1,2,3,4,5,6} by cutting the
+analyze.py output into pieces (depending on number of cells).
+
+view with
+
+ gnuplot win.{size,mass}.gnuplot
+
+the gray lines in win.size.gnuplot denote the filtering conditions of
+filter_data.py
+
+Then merge and filter the data using
+
+ cat win.size.[123456] | python filter_data.py > win.size.all.filtered
+
+win.size.gnuplot will automatically fit a "a * size**b" - like function.
+
+Result: visible diagonal = 369.399 * sum(sizes)**0.431776
+
+Also, analyze.py tells us that the viewport ratio is 1.7 : 1
+
+Additionally, if size<70, then the value 70 instead of "size" is used (found
+out by tinkering around manually)
diff --git a/reversing_game_mechanics/zoomlevel/filter_data.py b/reversing_game_mechanics/zoomlevel/filter_data.py
new file mode 100644
index 0000000..e801d78
--- /dev/null
+++ b/reversing_game_mechanics/zoomlevel/filter_data.py
@@ -0,0 +1,10 @@
+import sys
+
+for line in sys.stdin:
+ line = line.split()
+ size, diag = int(line[0]), float(line[1])
+ upper = size**0.4 * 460 + 50
+ lower = size**0.407 * 460 - 400
+ if lower < diag and diag < upper:
+ print(str(size) + "\t" + str(diag) + "\t\t" + line[2])
+
diff --git a/reversing_game_mechanics/zoomlevel/stats.pickle.xz b/reversing_game_mechanics/zoomlevel/stats.pickle.xz
new file mode 100644
index 0000000..c915674
--- /dev/null
+++ b/reversing_game_mechanics/zoomlevel/stats.pickle.xz
Binary files differ
diff --git a/reversing_game_mechanics/zoomlevel/win.mass.1 b/reversing_game_mechanics/zoomlevel/win.mass.1
new file mode 100644
index 0000000..1bac0fb
--- /dev/null
+++ b/reversing_game_mechanics/zoomlevel/win.mass.1
@@ -0,0 +1,116 @@
+0.0 9957.478596512272 16
+24.01 2120.5192760265113 245
+25.0 1969.829688069504 88
+26.01 1907.5106814904077 196
+27.04 1652.2645066695586 49
+28.09 1860.7818249327352 96
+29.16 1851.8933554608375 58
+30.25 1468.638825579659 70
+31.36 2251.943604977709 106
+32.49 2004.575765592311 166
+33.64 2109.127781809343 362
+34.81 2126.4564420650613 137
+36.0 1994.1740144731602 91
+37.21 2173.5730951592127 58
+38.44 1940.5630626186824 52
+39.69 2232.8013794334684 39
+40.96 2133.3471353720192 71
+42.25 2239.5713875650404 100
+43.56 2259.5827048373335 868
+44.89 2240.224319125208 818
+46.24 2307.553032976707 443
+47.61 2226.5634956138124 579
+49.0 2356.425258734085 336
+50.41 2293.2538019155227 397
+51.84 2249.2098612623945 273
+53.29 2331.4519081465096 451
+54.76 2392.639755583778 300
+56.25 2404.2996901384818 277
+57.76 2403.8011565019265 161
+59.29 2357.5313359529287 262
+60.84 2377.4242364374095 157
+62.41 2339.8472172344927 145
+64.0 2438.0258407162137 228
+65.61 2488.3331770484433 301
+67.24 2489.7003835803216 352
+68.89 2499.0544211761376 291
+70.56 2455.6426857342253 304
+72.25 2498.9455776386967 154
+73.96 2378.6727391551785 346
+75.69 2474.5797623030867 702
+77.44 2366.7634017788937 727
+79.21 2442.546417163858 373
+81.0 2515.553616999646 238
+82.81 2614.3163542310635 207
+84.64 2594.101771326638 66
+86.49 2544.643000501249 67
+88.36 2651.225377066235 95
+90.25 2681.96290056369 148
+92.16 2685.0344504307577 170
+94.09 2679.5546271722096 426
+96.04 2693.4938277263605 569
+98.01 2726.821592990638 54
+100.0 2696.036349903317 53
+102.01 2704.8558187082726 56
+104.04 2754.743726737571 88
+106.09 2719.114010114324 180
+108.16 2791.1581825471662 122
+110.25 2794.8023185907086 84
+112.36 2803.209767391659 133
+114.49 2805.824121358999 90
+116.64 2796.2517769328283 250
+118.81 2803.5384784232942 261
+121.0 2837.5144052497776 142
+123.21 2835.092414719492 107
+125.44 2846.737255174773 63
+127.69 2877.452345391666 63
+129.96 2851.5078467365297 53
+132.25 2780.2778638114573 113
+134.56 2891.1260781916794 242
+136.89 2870.733878296628 69
+139.24 2892.3390188565377 88
+141.61 2730.286798122131 145
+144.0 2715.363695713707 389
+146.41 2238.8311682661556 281
+148.84 2913.41998345587 61
+151.29 2955.795155283938 28
+153.76 2385.1079640133694 322
+156.25 3029.202205201891 67
+158.76 3015.162516349658 39
+161.29 3024.4353191959653 64
+163.84 3046.6238691377707 139
+166.41 3020.514029101669 40
+169.0 3015.3225366451265 112
+171.61 3021.6586504765887 92
+174.24 3040.8763539479864 73
+176.89 3065.0941257977706 58
+179.56 2976.10500486794 66
+182.25 3026.624522467232 98
+184.96 3013.2540550043236 84
+187.69 2927.263910206936 80
+190.44 2981.869380103696 80
+193.21 2977.177354475208 52
+196.0 3049.6730972351775 42
+198.81 2354.0403564934904 65
+201.64 2332.625130620006 4
+219.04 3144.616033794905 7
+222.01 3228.400223020684 11
+225.0 3220.1863300125974 71
+228.01 3248.3468103021264 119
+231.04 3253.712494981694 66
+234.09 3238.846245192877 57
+237.16 3176.819950831334 43
+240.25 3301.4617974467005 36
+243.36 3296.8477368541 132
+246.49 3316.3596005258537 113
+249.64 3286.2537942161434 155
+252.81 3083.007946794818 110
+256.0 2909.176000175995 64
+259.21 2641.6837433727756 35
+262.44 2646.5972492995606 65
+265.69 2727.742106578259 49
+268.96 2692.4384858339845 44
+272.25 2498.7140692764347 49
+275.56 2352.3938870860893 36
+278.89 2375.1743093928917 57
+282.24 2304.796954180563 121
diff --git a/reversing_game_mechanics/zoomlevel/win.mass.2 b/reversing_game_mechanics/zoomlevel/win.mass.2
new file mode 100644
index 0000000..8c08b58
--- /dev/null
+++ b/reversing_game_mechanics/zoomlevel/win.mass.2
@@ -0,0 +1,455 @@
+40.5 2459.633306003153 36
+40.96 7513.947364734464 1
+41.41 2471.8181567421175 53
+42.34 2602.3854441646417 92
+43.25 2635.398451847462 1
+43.29 2641.4407053727327 15
+43.56 8967.320224013414 2
+44.18 2311.2580989582275 2
+44.2 2693.2903668189956 8
+44.260000000000005 2490.116463139827 12
+45.129999999999995 2369.7818464997995 3
+45.17 2718.7594597536577 30
+45.25 2628.344345781199 17
+46.08 2604.0188171363125 7
+46.1 2714.2109350601327 145
+46.16 2705.3711390491326 30
+46.24 9047.885167264227 3
+46.260000000000005 2529.635546872316 4
+47.05 2559.953905835025 100
+47.09 2748.116627801666 7
+47.29 2672.3012554725187 14
+48.04 2696.738771182704 64
+48.2 2656.4521452493736 2
+49.010000000000005 2747.4096891435756 35
+49.05 1728.9814920929605 28
+49.129999999999995 2674.728023556788 33
+50.0 2718.0345840331024 25
+50.02 1517.5836056046467 115
+50.18 2718.969290006785 29
+50.41 5962.59423405618 1
+51.010000000000005 2762.8052772499186 111
+51.25 2680.7913010900347 24
+52.02 2694.0736812492714 74
+52.04 2728.1513887612614 26
+52.2 2111.7618236912986 65
+52.34 2760.7872065771385 4
+53.05 2689.30344141378 18
+53.09 2777.4263266556686 37
+53.29 2745.1777720213313 68
+54.08 2724.0163362212056 132
+54.1 2821.2488369514663 28
+54.16 2788.0634856473407 51
+54.4 2800.803456153252 243
+54.76 9453.725720582335 1
+55.129999999999995 2814.639586163742 45
+55.17 2818.2379246614364 28
+55.25 2750.93911237599 213
+55.370000000000005 2803.9293857014304 140
+55.53 2788.3961339809666 16
+56.18 2337.0556262100395 51
+56.2 2843.388295678239 34
+56.25 9562.746467411964 1
+56.260000000000005 2824.395333518309 23
+56.36 2756.9746099665117 22
+56.68 2844.9411241711136 10
+57.25 2762.5328957317415 12
+57.29 2829.027394706527 12
+57.370000000000005 2757.496328193385 54
+57.49 2056.103110254931 16
+57.650000000000006 2840.9859204156573 76
+58.34 2798.28965620073 189
+58.5 2573.206948537175 148
+58.82000000000001 2401.801199100375 7
+59.41 2654.4522975559385 20
+59.45 2790.700270541428 8
+59.53 2643.41162137114 5
+59.650000000000006 2746.3783424721364 20
+59.81 2307.9033775268845 17
+60.5 2822.01009920234 2
+60.519999999999996 2569.880347409194 21
+60.68 2517.190894628375 44
+60.82000000000001 2588.53163009456 46
+60.84 4983.02087091756 1
+61.61 2886.1119174418723 20
+61.650000000000006 2569.880347409194 15
+61.85 2246.855580583674 57
+62.010000000000005 2406.397514958823 14
+62.120000000000005 2831.1287501630864 138
+62.72 2905.0335626288384 16
+62.8 2513.3963077875324 18
+63.04 1903.5934439895511 3
+63.05 2840.752893160544 44
+63.22 2224.2942701000693 34
+63.7 2887.0635600900787 158
+63.81 2721.133771059409 9
+63.85 2906.4015551881334 10
+63.97 2513.3963077875324 8
+64.0 2783.724483493293 31
+64.25 2190.683226758264 40
+64.45 2511.4816344142355 32
+64.97 2819.3029280302603 91
+65.0 2905.691828119424 50
+65.16 2769.544727929123 26
+65.3 2769.2159540201988 110
+65.7 2169.302652927894 1
+66.13 2917.1830590485747 32
+66.17 1978.7786637216402 36
+66.37 2866.9461104108673 26
+66.53 2828.3638379812455 176
+66.97 2169.302652927894 2
+67.28 2914.5826459374935 4
+67.30000000000001 1456.4751971798216 33
+67.6 2892.723630075988 18
+68.26 2169.302652927894 1
+68.45 2870.029442357691 68
+68.49000000000001 1493.238427043719 9
+68.56 3071.149133467797 1
+68.85 2876.960201323612 40
+68.89 8841.674502038626 2
+69.62 2759.3203873417815 37
+69.64 2934.570667065286 37
+69.7 1503.1999201703013 20
+70.12 2944.063178669914 15
+70.56 6246.4982190023875 1
+70.81 2861.632051819381 66
+70.85 2948.546930269213 16
+70.93 1951.1483798009826 158
+71.21000000000001 2883.3539498299547 18
+72.0 2895.3979001166663 37
+72.02000000000001 2944.6481963046112 6
+72.08 2900.384802056444 30
+72.18 2046.8927670984624 2
+72.32 2937.0192372539886 65
+73.21000000000001 2890.895017118401 34
+73.25 2878.285079695894 36
+73.45 2660.3768530041 49
+74.42 2244.9518925803286 5
+74.44 2922.8884686214083 47
+74.6 2709.7276615925816 7
+74.74000000000001 2194.8769897194693 16
+75.65 2959.984628338465 37
+75.69 2994.2386344444894 15
+75.77000000000001 2779.7426139842514 10
+76.05000000000001 2211.8340805765697 4
+76.88 2980.0191274553927 13
+76.9 2640.6423460968736 21
+76.96000000000001 3009.2705096085997 39
+77.12 2918.4278301852864 123
+77.38 2188.1373357264392 6
+77.6 2235.5270072177614 9
+78.00999999999999 2934.982112381607 84
+78.13 2953.3900859859336 45
+78.17 2975.29695996887 29
+78.73 2162.7568055609026 8
+78.97 2329.121937555009 16
+79.21 9939.87610586772 1
+79.38 2913.7070889161114 56
+79.4 3010.1975018260846 33
+79.46000000000001 2199.218497557712 8
+79.53999999999999 2982.9921220144047 45
+80.1 2333.552227827781 6
+80.45 2793.836072499602 9
+80.65 2684.162811753415 43
+80.69 2939.1537897837193 6
+81.25 2526.8612941750484 23
+81.92 3014.607768848213 4
+81.94 2141.8881856903736 105
+82.0 2930.1754213698537 36
+82.42 2628.9505510754666 21
+82.81 6944.246107389916 1
+82.93 2891.7330443870505 17
+83.21000000000001 2881.0282886497316 60
+83.25 2973.785466371103 36
+83.3 2722.2448457109804 21
+83.81 2954.6356797412436 40
+83.88 2832.668000313485 2
+84.5 3021.231536972961 13
+84.52000000000001 2897.546720934798 92
+84.58 3001.166606504877 12
+84.85000000000001 2906.164654660847 113
+85.0 3019.600139091267 2
+85.00999999999999 2457.3501988931084 1
+85.81 3025.457651331448 112
+85.93 3023.894178042611 27
+86.05 2289.4422901658822 17
+86.21000000000001 3080.046265886277 21
+86.42 2991.139080684815 148
+87.12 2650.2371591991537 58
+87.14 3061.160890903972 40
+87.2 2986.7247948212434 14
+87.3 2296.176386952884 65
+87.41 2866.022505145415 36
+88.01 2712.7950530771764 36
+88.45 3071.8894836891513 42
+88.49000000000001 3028.9313296936925 21
+88.57 3061.068114237251 33
+88.69 3102.4778806624877 32
+89.78 3057.227829259704 10
+89.80000000000001 3014.607768848213 61
+89.86 2269.5003855474447 32
+89.96000000000001 3097.339826367136 48
+91.13 2865.656120332654 23
+91.17 3106.295060035347 30
+91.25 3075.8616678908043 57
+91.37 3118.5017235845808 22
+92.48 2388.5782381994522 258
+92.56 3017.995692508523 37
+92.66 3129.7420021465027 8
+92.80000000000001 3128.0374038684386 8
+92.97999999999999 3120.59817983668 1
+93.85 2621.0017169013836 65
+93.89 3152.9827148273425 16
+93.97 3112.9114989025948 81
+94.09 9051.577210630201 1
+94.25 3144.593614443685 7
+94.44999999999999 3136.026307287616 39
+95.22 2358.5930127938564 67
+95.24000000000001 2628.2302030073392 75
+95.3 3157.6879199819605 6
+95.4 3069.05881338237 46
+95.72 3148.735619260531 4
+96.61 3138.517643729281 30
+96.65 3154.2341384240963 35
+96.73 3104.341637126945 3
+96.85 2934.527219161547 16
+97.00999999999999 3145.1621579816833 4
+98.0 3169.7397369500227 12
+98.02 3173.4405619138356 51
+98.08000000000001 2803.5762875299115 29
+98.18 3156.589456993101 6
+98.32 2872.565578015583 43
+99.41 3167.053520229805 51
+99.53 2792.167616745098 19
+99.65 3192.3842187305713 50
+99.81 2941.8161057414854 13
+100.82 3106.0143270757785 4
+100.84 3196.712842905975 42
+101.0 2792.6850878679465 45
+101.14 3075.295758134492 67
+101.32 2934.527219161547 73
+102.25 3169.335577057122 11
+102.28999999999999 3064.378077196089 1
+102.37 2867.3070990042206 13
+102.49000000000001 2972.5687544613666 78
+102.65 2899.1883346895556 9
+103.69999999999999 3107.673084479769 14
+103.75999999999999 3107.5662502994205 20
+103.86 3207.86486623112 147
+104.89 3004.323051870421 19
+105.16999999999999 3062.840674929076 13
+105.25 3115.385208926819 37
+105.37 3124.894398215722 47
+105.85 2480.4487497225173 1
+106.58 2407.431203586096 4
+106.6 3050.954276943527 19
+106.66 3204.720580643498 57
+106.9 2971.6609833559414 9
+107.68 2869.1868185951225 90
+108.05 2715.207542711975 40
+108.09 3034.582837887277 35
+108.16 8923.322083170595 1
+108.16999999999999 3132.7657109972333 42
+108.45 3206.7556813701913 41
+108.65 2434.9080064758095 21
+109.52 2983.3921968122127 42
+109.53999999999999 3021.7852339304327 68
+109.6 3228.6035681080452 52
+109.69999999999999 2382.238443145438 26
+110.02 3201.5621187164243 16
+110.65 2278.747462971709 143
+111.00999999999999 3198.4935516583428 24
+111.13 3270.7872141122234 2
+111.25 2250.734324614969 1
+111.41 2266.3973614527526 19
+111.61 3248.176873262908 16
+112.5 3209.6263022351995 52
+112.52 3236.8775077225273 21
+112.58 3265.214388060913 9
+112.68 3261.2028455770733 27
+112.82 2247.8979069343873 12
+113.0 3248.630788501519 44
+114.00999999999999 3246.263698469365 7
+114.05 3283.7113149605584 11
+114.13 3138.9547623372973 9
+114.25 3212.693107036525 19
+114.61 3167.6016163652903 14
+115.53999999999999 3279.512158843141 30
+115.69999999999999 3252.1334535962696 27
+116.02 3175.914986267737 15
+117.09 3308.273416753821 12
+117.16999999999999 3253.6510261550793 13
+117.28999999999999 3282.2183047445214 52
+117.45 3194.5548985735086 48
+118.58 3271.134359820764 28
+118.6 3166.3411060718017 2
+118.66 3274.657997409806 12
+118.75999999999999 3276.1114144668522 71
+118.81 8069.929987304723 2
+118.9 3058.5586474677907 6
+119.08 3124.7374289690324 33
+120.13 3299.07865926231 7
+120.25 3283.969549188908 169
+120.37 3282.865364281636 18
+120.53 3209.4538476195603 85
+121.75999999999999 3202.652026055906 19
+122.0 3247.845439672276 63
+123.28999999999999 2896.068024062971 10
+123.49 3257.744618597351 2
+123.65 3335.6368207585188 17
+124.82 3061.1470072507136 3
+124.84 2652.874101799782 137
+125.32 3291.808317627258 3
+125.44 7472.861366304074 1
+126.41 3291.879098630446 96
+126.45 2954.865309959153 30
+126.81 3347.0017926496544 16
+128.0 2969.6642907911323 30
+128.01999999999998 3156.169355405378 113
+128.5 3339.6354591481986 10
+129.61 3133.040216786245 36
+130.01 3337.7336622325033 8
+131.22 3179.471811480643 3
+131.24 3020.9197275002193 42
+131.72 3079.234482789513 4
+132.25 5694.3046107492355 1
+132.85 3265.6763464862834 6
+132.89 2765.289315785963 54
+133.25 3299.167319188283 1
+133.45 3240.6210824470054 39
+134.5 3285.5763573534555 75
+135.2 3389.542299485286 8
+135.49 2913.1991006451995 84
+136.17000000000002 3337.108329077736 61
+136.97 3390.6201497661164 15
+137.86 2278.6425783786276 15
+138.5 3412.359887233467 24
+139.49 2278.6425783786276 7
+139.57 3314.6464366505215 12
+140.05 3394.3603226528558 10
+141.2 2268.637696945019 37
+141.3 3211.592906954429 16
+141.62 3412.2980526325655 9
+142.93 2764.389625215664 104
+143.20999999999998 3397.420492079248 31
+144.57999999999998 3046.709700644287 25
+144.82 3239.8902759198495 12
+145.0 3390.017846560693 4
+146.20999999999998 3388.321118194083 1
+146.25 3230.394712724747 5
+146.41 6656.233319227925 1
+146.45 3202.770831639379 19
+147.65 3406.1809993011234 1
+147.92 2490.0461843106445 65
+147.94 3282.4850342385416 38
+148.1 3091.0713353140204 13
+148.84 8177.841524509998 1
+149.64999999999998 3420.9264826944177 59
+149.89 2971.110398487407 7
+151.38 3450.2268041391135 48
+151.39999999999998 3435.114699686169 27
+151.56 2916.207468613987 33
+153.13 3376.8393506354428 53
+153.25 2747.6792025271075 6
+154.89999999999998 3029.6093807618167 19
+156.64999999999998 3184.1678661779124 13
+156.69 2963.002024973996 6
+158.44 3278.1976755528335 43
+158.5 2984.3982643072286 41
+160.32999999999998 2929.2353951159334 19
+161.3 3395.699927849927 140
+162.18 2900.2127508167396 26
+164.05 2828.9547539683276 18
+165.94 2834.0869781995048 17
+167.85 2761.479675825987 42
+169.78 2820.7943916563645 30
+182.25 9562.855274446016 1
+182.41 3317.3273881243617 4
+184.34 3322.925969683947 1
+186.29000000000002 3360.9024085801716 3
+188.26 3392.7009888877624 14
+192.08 3491.7183448840774 81
+192.16 3420.003654968807 17
+193.21 9577.942211143269 1
+194.05 3640.672602693079 29
+194.09 3480.2909361143934 84
+198.01 3485.7327780539918 20
+198.25 2896.212181453562 2
+200.0 3494.622726418404 38
+200.18 2942.880901429754 38
+201.64 9784.782930653086 1
+202.01 3437.9077939933177 13
+202.25 2967.623291457324 18
+204.04000000000002 3296.5171014268985 26
+204.2 2866.873035207524 20
+204.34 2955.8289869341224 7
+206.09 3148.6176331844426 31
+206.17000000000002 2744.5052377432257 80
+208.16 3158.060955713173 41
+208.26 2737.0440259520856 26
+210.17000000000002 2736.23335993113 43
+210.25 2968.037904070634 10
+212.26 2868.054567123854 99
+212.36 2936.9548855915373 9
+214.29000000000002 3114.007867684345 21
+214.49 2857.8037021461078 22
+216.4 3288.7281736257864 22
+216.64 2758.698424982332 10
+218.53 3553.019701605945 81
+218.81 2711.7280468365557 27
+220.68 3634.7426318791818 43
+221.0 2744.636952312637 21
+221.22 2778.7531376500506 16
+222.73000000000002 3435.003639008262 25
+223.20999999999998 2813.6142237343056 32
+225.44 2884.218091615126 33
+227.45 2920.3643608289703 13
+229.48000000000002 2901.059806346639 1
+229.7 2872.4863794281077 1
+231.04 4566.623698094688 1
+231.13 3745.4198696541353 4
+231.97000000000003 2805.2536427211 23
+233.3 3798.324367402026 6
+234.26 2917.5335473649657 54
+235.49 3780.4034176262194 43
+236.29000000000002 3127.261581639758 26
+237.7 3790.5283272915926 21
+238.34 3509.404507890192 65
+239.93 3780.8000476089715 23
+240.41 3691.2167370665193 62
+242.18 3810.5668869605215 12
+242.5 3690.6977660057723 7
+242.72 3783.2076337415056 103
+244.45 3796.823145736446 88
+244.61 3815.123720143293 18
+245.04999999999998 3808.475941895918 66
+246.74 3792.3610060225014 23
+248.89 3815.631009413777 26
+249.64 7526.270258235482 1
+250.88 3824.0011767780616 2
+251.06 3777.8631526300683 14
+253.13 3826.4825884877614 43
+253.25 3750.3314520186077 26
+255.38 3834.963624338567 11
+255.45999999999998 3795.2923471058193 13
+257.65 3861.3113057612954 13
+257.69 3831.301214992108 83
+259.88 3807.41697217418 30
+259.94 3846.839351987551 55
+260.0 3830.0822445477593 38
+262.25 3857.996630376963 115
+262.33 3867.2832841673235 26
+262.45 3831.658257204053 60
+264.42 3542.066346075409 27
+264.52 3801.982772186113 32
+264.68 3832.571069138836 36
+264.82 3437.7121752700587 18
+266.81 3789.4339946751943 30
+267.05 3784.653220573848 128
+269.12 3747.1441125208944 93
+269.44 3049.4078113627243 51
+269.62 2788.9302608706444 40
+271.85 2863.6000069842157 18
+272.05 2737.5529218628817 19
+278.48 2678.195287875774 32
+278.89 9754.961199307765 1
diff --git a/reversing_game_mechanics/zoomlevel/win.mass.3 b/reversing_game_mechanics/zoomlevel/win.mass.3
new file mode 100644
index 0000000..ba343eb
--- /dev/null
+++ b/reversing_game_mechanics/zoomlevel/win.mass.3
@@ -0,0 +1,126 @@
+60.769999999999996 2830.58244889634 7
+65.46000000000001 3420.4585949840116 1
+66.53 7887.098642720275 1
+66.81 2838.9575551599924 6
+67.7 3143.764145097402 42
+68.26 9958.89833264704 1
+68.33 2134.831609284442 24
+68.59 3184.6464481948383 18
+69.26 1980.0426763077608 8
+69.66 3112.852389690202 14
+70.17 1914.7461972804647 7
+70.75 3137.9294128453557 3
+71.10000000000001 1793.7293552818942 4
+71.18 1948.351354350647 3
+71.86 3120.9783722416278 17
+72.99000000000001 3145.5927899205262 21
+73.14 1833.039006677163 12
+73.9 3145.5927899205262 4
+74.13 1833.8105136572863 8
+74.83 3056.016361212747 21
+75.14 1914.4221060152852 8
+75.78 2881.213980252074 7
+76.16999999999999 1943.2395632036726 13
+76.75 2829.041180329477 6
+76.96000000000001 5461.392679527814 1
+77.14 2411.4835682624916 11
+77.22 2287.895102490497 30
+77.9 2809.5213115404554 26
+78.0 3147.2827963181194 58
+78.17 2468.258090232867 21
+79.24 2355.969651757 20
+79.4 8508.168428046074 1
+80.21 2702.4124407647328 32
+81.2 2982.131452501717 24
+82.25 3122.2384598233366 19
+83.32 3265.692269642074 28
+84.33 3261.3567728784287 5
+84.97 3468.918707609044 1
+85.42 3261.3567728784287 5
+86.45 3274.0054978573266 11
+87.53999999999999 3332.2054258403696 15
+88.65 3361.016661666526 3
+89.78 3355.109089135553 14
+90.83 3373.971102425153 20
+91.98 3259.075482402947 7
+93.05 3265.9854561831717 10
+95.31 3411.079741079062 17
+96.38 2931.623611584543 7
+96.42 3402.4616088943603 17
+96.75999999999999 3406.882592635091 13
+97.61 3376.63575175055 7
+97.85 3455.7640255086862 3
+98.74000000000001 3323.912303295621 14
+98.94 3456.7860795831725 45
+99.65 6587.192725281385 1
+99.89 3373.551392820332 8
+101.06 3336.6609057559326 7
+101.14 7665.473892721832 1
+103.30000000000001 3425.875800434102 11
+104.45 3329.9831831407196 10
+104.75 3409.622413112631 38
+104.89 4313.037096988617 1
+105.30000000000001 3410.078298221318 28
+105.62 3299.869694397038 6
+105.66 3416.254235269969 37
+105.69999999999999 3447.78842158274 23
+106.59 3479.379973501026 105
+106.61000000000001 3463.3119986510023 47
+106.81 3258.0682927157927 5
+106.85000000000001 3271.244564382186 8
+106.97 3457.1297343316464 86
+107.00999999999999 3471.528913893704 33
+107.9 3448.322055725074 8
+107.94 3347.817946065765 4
+108.02000000000001 3247.0983970308016 1
+108.06 3240.0327158842083 8
+108.36000000000001 3461.478441359992 20
+109.29 3211.9752489706393 6
+110.51 2842.601801167374 8
+110.81 3206.3019196575983 20
+111.41 9875.263895208067 1
+111.82000000000001 3040.0726964992136 77
+112.04999999999998 2418.4798944791746 26
+112.5 7247.468592550091 1
+113.04 3227.447288492873 11
+115.4 3130.8784709726438 1
+116.61000000000001 3295.738460497131 2
+117.84 3356.7450007410453 26
+118.6 7029.594867415903 1
+119.09 3271.6653251822686 40
+119.13 3291.6629535844036 39
+120.38 3236.8164915546263 19
+121.63 3177.624899197512 3
+122.9 3060.851025450275 21
+122.99 3527.7692668313784 35
+123.49 7155.990497478319 1
+124.14 3476.978573416868 10
+124.19 2975.672697055239 40
+124.73 2989.411982313579 27
+125.5 3051.0473611532157 4
+125.9 2898.4592458752977 3
+126.41 7350.145372712025 1
+126.83 3049.9652457036295 25
+127.01 2860.5029278083252 25
+128.2 2748.142099673887 18
+129.41 2691.989970263634 1
+130.98 2670.9842755059417 25
+134.3 2809.3913931668544 10
+135.17000000000002 2945.6654596202875 13
+136.98000000000002 2946.8554426710516 4
+137.87 3046.0080433249022 11
+139.7 3122.5574454283465 14
+139.71 3321.6188824126107 59
+140.37 3465.600669436685 28
+140.61 3162.9835598687514 22
+141.32 3303.806289720994 4
+141.54000000000002 3590.769416155819 43
+141.63 3137.2424197055607 2
+142.49 3182.529968437061 1
+143.46 3185.424932406978 15
+168.81 2838.9635432671553 12
+202.01 9721.27075026717 1
+216.49 3464.951947718756 23
+216.91000000000003 3738.3038934789665 3
+218.3 3524.6476419636615 4
+264.42 9773.704773523703 1
diff --git a/reversing_game_mechanics/zoomlevel/win.mass.4 b/reversing_game_mechanics/zoomlevel/win.mass.4
new file mode 100644
index 0000000..aa0de62
--- /dev/null
+++ b/reversing_game_mechanics/zoomlevel/win.mass.4
@@ -0,0 +1,130 @@
+75.78 3431.546152975361 6
+76.63 3497.8764986774477 14
+76.96000000000001 5461.392679527814 1
+77.5 3311.0096647397454 23
+78.35 3389.6768282536905 36
+79.24 3468.8963662813567 13
+79.4 8508.168428046074 1
+80.13 3486.8789769649306 21
+80.14999999999999 3507.4472198452254 16
+81.03999999999999 3426.4974828533 11
+81.08 3490.3886603070437 102
+81.93 3354.6057890607653 35
+82.03 3519.1081256477473 63
+82.86 3395.7847399386196 11
+83.0 3404.603501143709 4
+83.87 3419.91418020979 18
+84.78 3187.2913265028033 64
+85.71000000000001 3413.126719007075 8
+85.77 3115.806476660577 49
+96.38999999999999 3615.2654121101536 87
+97.25999999999999 3652.2186407716613 3
+97.47999999999999 3627.734279133465 58
+98.26 3649.020964587625 1
+98.36999999999999 3593.768078215399 9
+98.58999999999999 3627.231726813163 147
+98.94 5135.193180397404 1
+99.21000000000001 3664.0061408245483 74
+99.24 3584.0772871131003 3
+99.65 6587.192725281385 1
+100.13 3584.0772871131003 2
+100.18 3573.493808585654 1
+100.38 2493.193935497197 1
+101.02 3677.116261420082 10
+101.14 7665.473892721832 1
+101.17 3670.228875696991 45
+101.32999999999998 2823.253796597111 33
+101.42999999999999 2493.193935497197 53
+102.12 3707.9920442201596 9
+102.15 3654.805603585504 14
+102.26 2755.466203748469 1
+103.06 3683.1563909234155 43
+103.09 3675.8706451669377 16
+103.33 2441.627530971913 36
+103.99000000000001 3684.5929218843157 4
+105.09 3716.556605246313 8
+106.10000000000001 3684.0620244507286 15
+107.13 3732.4611183507322 7
+107.15 3715.699799499416 23
+109.23 3690.992549437075 16
+110.3 3509.5711988788603 21
+110.5 2476.2915821849415 5
+110.98 3468.918707609044 5
+111.32999999999998 3555.6774319389547 25
+111.41 9875.263895208067 1
+111.51 2465.023326461638 1
+112.41999999999999 3459.260036481791 17
+112.53999999999999 2540.530850039023 35
+113.52999999999999 3586.7179983935175 15
+114.66 3585.6561463698663 10
+115.80999999999999 3670.9509667114867 23
+115.85000000000001 3782.2038548973005 17
+116.86 3734.5910083970375 112
+116.88000000000001 3796.5963704349715 139
+116.97999999999999 3743.2312244904133 16
+117.92999999999999 3784.865915722775 12
+117.97 3800.631526470305 29
+118.03 3744.2478550437872 29
+118.6 7029.594867415903 1
+118.82 3733.0740683784993 1
+119.02 3689.460258628625 9
+119.91 3723.439404636525 3
+120.13 3625.2627490983327 4
+121.2 3526.8682992139074 20
+122.13 3630.5692391138887 8
+122.33 3313.4086678223075 20
+123.26 3661.392221546334 13
+123.46000000000001 3634.4388837893534 11
+123.49 7155.990497478319 1
+124.41 3814.323662197533 12
+124.57000000000001 3220.6027386189685 5
+124.63000000000001 3634.4388837893534 40
+125.53999999999999 3831.4364147144606 11
+125.60000000000001 3827.5533961004385 69
+126.41 7350.145372712025 1
+126.63000000000001 3590.791834679365 5
+126.71000000000001 3812.6926180850196 47
+126.77 3832.2616038052515 5
+126.77000000000001 3832.5211023554716 6
+126.83 9346.260482139367 1
+127.70000000000002 3814.195852338996 13
+127.72 3636.3681056790715 28
+127.80000000000001 3813.7278350716115 66
+127.86 3826.231827790888 3
+127.96000000000001 3856.772873789692 16
+128.85000000000002 3768.2490628938 180
+128.95 3838.2737005065183 19
+129.13 3854.4753209743085 28
+130.34 3835.5570651471216 1
+130.98 7269.414075425887 1
+131.57 3876.429542762257 43
+132.76 3793.3906996248093 27
+135.07 2951.3117422597024 5
+202.01 9721.27075026717 1
+204.5 3049.217932519747 3
+205.93 2999.563301549077 1
+207.38 2918.4139528175233 2
+208.85 2928.8649678672455 1
+210.34 2923.3942601024582 1
+211.79 2926.6731966517887 1
+213.29999999999998 2921.918034442445 3
+213.39999999999998 3027.5617252171755 7
+213.42 3092.014553652683 2
+214.82999999999998 3092.7083599977545 25
+214.86999999999998 3098.9601481787404 28
+216.29999999999998 3112.9012191201955 7
+216.35999999999999 2853.4514188960707 21
+217.86999999999998 2832.8236796525125 40
+219.29999999999998 2666.3600657075553 5
+220.82999999999998 2658.3432810681165 14
+222.27999999999997 2840.7354681490497 2
+223.74999999999997 2840.7354681490497 9
+225.17999999999998 2840.7354681490497 2
+226.63 2840.7354681490497 4
+228.1 2840.7354681490497 63
+264.42 9773.704773523703 1
+264.52 3771.882951524344 154
+265.45 3702.6188029555515 4
+266.39 3661.2525179233403 51
+267.3 3601.816902620121 2
+269.17 3550.685708423093 49
diff --git a/reversing_game_mechanics/zoomlevel/win.mass.5 b/reversing_game_mechanics/zoomlevel/win.mass.5
new file mode 100644
index 0000000..f4b686e
--- /dev/null
+++ b/reversing_game_mechanics/zoomlevel/win.mass.5
@@ -0,0 +1,8 @@
+126.83 9346.260482139367 1
+129.32999999999998 3926.380139517823 7
+130.22 3995.193362028927 19
+130.98 7269.414075425887 1
+131.06 2999.2432378851836 13
+131.23000000000002 3979.0382003695313 11
+133.33999999999997 3046.871510254412 16
+134.51 3030.115674359644 1
diff --git a/reversing_game_mechanics/zoomlevel/win.mass.6 b/reversing_game_mechanics/zoomlevel/win.mass.6
new file mode 100644
index 0000000..7994d85
--- /dev/null
+++ b/reversing_game_mechanics/zoomlevel/win.mass.6
@@ -0,0 +1,4 @@
+126.83 9346.260482139367 1
+127.14 3564.1046561513876 2
+128.13 3713.46266980025 16
+129.01999999999998 3981.4374288691265 52
diff --git a/reversing_game_mechanics/zoomlevel/win.mass.gnuplot b/reversing_game_mechanics/zoomlevel/win.mass.gnuplot
new file mode 100644
index 0000000..a948115
--- /dev/null
+++ b/reversing_game_mechanics/zoomlevel/win.mass.gnuplot
@@ -0,0 +1,9 @@
+min(a,b)=(a<b)?a:b
+max(a,b)=(a>b)?a:b
+plot "win.mass.1" using 1:2:(min(1,$3/100)) lt rgb "red" pt 2 ps variable, \
+ "win.mass.2" using 1:2:(min(1,$3/100)) lt rgb "blue" pt 2 ps variable, \
+ "win.mass.3" using 1:2:(min(1,$3/100)) lt rgb "green" pt 2 ps variable, \
+ "win.mass.4" using 1:2:(1) lt rgb "purple" pt 2 ps variable, \
+ "win.mass.5" using 1:2:(1) lt rgb "cyan" pt 2 ps variable, \
+ "win.mass.6" using 1:2:(1) lt rgb "orange" pt 2 ps variable
+pause -1
diff --git a/reversing_game_mechanics/zoomlevel/win.size.1 b/reversing_game_mechanics/zoomlevel/win.size.1
new file mode 100644
index 0000000..8357f6a
--- /dev/null
+++ b/reversing_game_mechanics/zoomlevel/win.size.1
@@ -0,0 +1,116 @@
+0 9957.478596512272 16
+49 2120.5192760265113 245
+50 1969.829688069504 88
+51 1907.5106814904077 196
+52 1652.2645066695586 49
+53 1860.7818249327352 96
+54 1851.8933554608375 58
+55 1468.638825579659 70
+56 2251.943604977709 106
+57 2004.575765592311 166
+58 2109.127781809343 362
+59 2126.4564420650613 137
+60 1994.1740144731602 91
+61 2173.5730951592127 58
+62 1940.5630626186824 52
+63 2232.8013794334684 39
+64 2133.3471353720192 71
+65 2239.5713875650404 100
+66 2259.5827048373335 868
+67 2240.224319125208 818
+68 2307.553032976707 443
+69 2226.5634956138124 579
+70 2356.425258734085 336
+71 2293.2538019155227 397
+72 2249.2098612623945 273
+73 2331.4519081465096 451
+74 2392.639755583778 300
+75 2404.2996901384818 277
+76 2403.8011565019265 161
+77 2357.5313359529287 262
+78 2377.4242364374095 157
+79 2339.8472172344927 145
+80 2438.0258407162137 228
+81 2488.3331770484433 301
+82 2489.7003835803216 352
+83 2499.0544211761376 291
+84 2455.6426857342253 304
+85 2498.9455776386967 154
+86 2378.6727391551785 346
+87 2474.5797623030867 702
+88 2366.7634017788937 727
+89 2442.546417163858 373
+90 2515.553616999646 238
+91 2614.3163542310635 207
+92 2594.101771326638 66
+93 2544.643000501249 67
+94 2651.225377066235 95
+95 2681.96290056369 148
+96 2685.0344504307577 170
+97 2679.5546271722096 426
+98 2693.4938277263605 569
+99 2726.821592990638 54
+100 2696.036349903317 53
+101 2704.8558187082726 56
+102 2754.743726737571 88
+103 2719.114010114324 180
+104 2791.1581825471662 122
+105 2794.8023185907086 84
+106 2803.209767391659 133
+107 2805.824121358999 90
+108 2796.2517769328283 250
+109 2803.5384784232942 261
+110 2837.5144052497776 142
+111 2835.092414719492 107
+112 2846.737255174773 63
+113 2877.452345391666 63
+114 2851.5078467365297 53
+115 2780.2778638114573 113
+116 2891.1260781916794 242
+117 2870.733878296628 69
+118 2892.3390188565377 88
+119 2730.286798122131 145
+120 2715.363695713707 389
+121 2238.8311682661556 281
+122 2913.41998345587 61
+123 2955.795155283938 28
+124 2385.1079640133694 322
+125 3029.202205201891 67
+126 3015.162516349658 39
+127 3024.4353191959653 64
+128 3046.6238691377707 139
+129 3020.514029101669 40
+130 3015.3225366451265 112
+131 3021.6586504765887 92
+132 3040.8763539479864 73
+133 3065.0941257977706 58
+134 2976.10500486794 66
+135 3026.624522467232 98
+136 3013.2540550043236 84
+137 2927.263910206936 80
+138 2981.869380103696 80
+139 2977.177354475208 52
+140 3049.6730972351775 42
+141 2354.0403564934904 65
+142 2332.625130620006 4
+148 3144.616033794905 7
+149 3228.400223020684 11
+150 3220.1863300125974 71
+151 3248.3468103021264 119
+152 3253.712494981694 66
+153 3238.846245192877 57
+154 3176.819950831334 43
+155 3301.4617974467005 36
+156 3296.8477368541 132
+157 3316.3596005258537 113
+158 3286.2537942161434 155
+159 3083.007946794818 110
+160 2909.176000175995 64
+161 2641.6837433727756 35
+162 2646.5972492995606 65
+163 2727.742106578259 49
+164 2692.4384858339845 44
+165 2498.7140692764347 49
+166 2352.3938870860893 36
+167 2375.1743093928917 57
+168 2304.796954180563 121
diff --git a/reversing_game_mechanics/zoomlevel/win.size.2 b/reversing_game_mechanics/zoomlevel/win.size.2
new file mode 100644
index 0000000..6168704
--- /dev/null
+++ b/reversing_game_mechanics/zoomlevel/win.size.2
@@ -0,0 +1,150 @@
+64 7513.947364734464 1
+66 8967.320224013414 2
+68 9047.885167264227 3
+71 5962.59423405618 1
+73 6812.90341924792 1
+74 9453.725720582335 1
+75 9562.746467411964 1
+78 4983.02087091756 1
+80 7724.4858728591125 1
+83 8841.674502038626 2
+84 6246.4982190023875 1
+87 9393.411414390408 1
+89 9939.87610586772 1
+90 2459.633306003153 36
+91 2476.9733547214432 54
+92 2602.3854441646417 92
+93 2641.4407053727327 16
+94 2490.116463139827 22
+95 2708.966592632696 50
+96 2712.70344859146 186
+97 2601.976556389392 122
+98 2696.738771182704 66
+99 2717.4488035655795 96
+100 2664.1535241047955 169
+101 2756.752618571351 135
+102 2690.486944774124 169
+103 2757.303936819443 122
+104 2788.0634856473407 455
+105 2799.2456126606685 442
+106 2824.3450568229086 140
+107 2810.7694676013543 170
+108 2763.8178304656767 344
+109 2737.670725269933 72
+110 2784.720632307665 251
+111 2785.9047004519016 159
+112 2875.0377388827437 260
+113 2811.6308434785674 181
+114 2870.785606763417 187
+115 2835.5659047181393 273
+116 2866.9461104108673 57
+117 2881.5004771819836 117
+118 2909.6427615774414 109
+119 2619.34037497993 258
+120 2939.450458844306 263
+121 2906.4929038275664 204
+122 2948.7314221542797 121
+123 2953.9094434325502 74
+124 2985.440838469254 88
+125 2975.29695996887 115
+126 2954.414493601059 126
+127 2896.9544352647317 185
+128 2949.3024599047144 314
+129 2910.3683615652503 209
+130 2979.817444072707 119
+131 3028.992076582572 177
+132 2808.3669632012125 177
+133 3073.615623333536 128
+134 3060.4393475447278 151
+135 3086.498663534459 133
+136 2561.4052783579564 312
+137 3126.1601046651467 227
+138 2979.8711381534604 198
+139 3155.1700429612347 90
+140 3151.571195451564 231
+141 3164.168927222439 154
+142 3129.1259162903625 232
+143 2847.00140498736 255
+144 3187.418077378617 219
+145 3138.7672739468912 97
+146 3195.2003067100504 89
+147 3194.6325297285757 158
+148 3195.955099809758 204
+149 3237.0166820700815 62
+150 3255.597180242052 127
+151 3254.93502239292 60
+152 3281.352160314403 73
+153 3279.3377380196753 125
+154 3255.6776560341473 152
+155 3271.2471627805808 279
+156 3247.845439672276 82
+157 3326.7739628655268 29
+158 2729.1090121136604 144
+159 3286.8233904485955 142
+160 3261.8871838247255 153
+161 3328.385344277312 44
+162 3046.827366294323 49
+163 3154.2986542177646 184
+164 3295.002427920198 83
+165 3357.799874918099 76
+166 3378.6307877600357 39
+167 3370.5692397575813 30
+168 3195.0414707793702 62
+169 2902.5402667318845 135
+170 3258.0098219618676 41
+171 3262.259952854769 26
+172 3422.502739224616 116
+173 3417.901402907931 66
+174 3439.523368142743 108
+175 3349.7058079777694 59
+176 3029.6093807618167 19
+177 3441.201679646225 19
+178 3409.1941863144143 224
+179 2929.2353951159334 19
+180 2900.2127508167396 26
+181 2828.9547539683276 18
+182 2834.0869781995048 17
+183 2761.479675825987 42
+184 2820.7943916563645 30
+191 3317.3273881243617 4
+192 3322.925969683947 1
+193 3360.9024085801716 3
+194 3392.7009888877624 14
+196 3491.7183448840774 98
+197 3560.370767209505 113
+199 3481.1977823731877 22
+200 3588.148547649609 76
+201 3452.222617387239 31
+202 3451.376536977674 53
+203 3230.5573822484566 111
+204 3113.641758455844 67
+205 2726.8489140397933 53
+206 2873.5044805950797 108
+207 3104.405418111494 43
+208 3249.613515481495 32
+209 3475.2795571004067 108
+210 3639.9057680110345 80
+211 3325.2151810070877 57
+212 2884.218091615126 33
+213 2920.3643608289703 13
+214 2906.8321244956683 2
+215 2813.3490718359144 27
+216 2939.36336644519 60
+217 3755.401043830073 69
+218 3585.3395933997663 86
+219 3758.964219036941 85
+220 3790.129945001886 122
+221 3810.992521640524 172
+222 3792.3610060225014 23
+223 3815.631009413777 26
+224 3795.2923471058193 16
+225 3826.4825884877614 69
+226 3831.510668130783 54
+227 3832.1638795855274 156
+228 3833.123269606653 120
+229 3859.508388383163 141
+230 3830.84912258366 86
+231 3783.793995449541 158
+232 3746.1261324199963 184
+233 2863.6000069842157 37
+236 2678.195287875774 32
diff --git a/reversing_game_mechanics/zoomlevel/win.size.3 b/reversing_game_mechanics/zoomlevel/win.size.3
new file mode 100644
index 0000000..564d37c
--- /dev/null
+++ b/reversing_game_mechanics/zoomlevel/win.size.3
@@ -0,0 +1,69 @@
+115 7887.098642720275 1
+116 9958.89833264704 1
+124 5461.392679527814 1
+126 8508.168428046074 1
+135 2830.58244889634 7
+137 4313.037096988617 1
+140 3420.4585949840116 1
+141 3163.25923692637 7
+142 3149.401212929213 43
+143 3176.547339486695 42
+144 3058.668010752393 23
+145 3126.600230282087 10
+146 3120.9783722416278 24
+147 3145.5927899205262 21
+148 3099.849351178215 16
+149 3050.773016794268 30
+150 2843.040801676965 15
+151 2624.4180307260503 19
+152 3056.6098867863398 125
+153 2468.258090232867 21
+154 2355.969651757 21
+155 2702.4124407647328 32
+156 2982.131452501717 24
+157 3142.504256162591 21
+158 3265.692269642074 28
+159 3282.8310952590905 6
+160 3261.3567728784287 5
+161 3274.0054978573266 11
+162 3332.2054258403696 15
+163 3361.016661666526 3
+164 3355.109089135553 14
+165 3373.971102425153 20
+166 3259.075482402947 7
+167 3265.9854561831717 10
+169 3411.079741079062 17
+170 3398.256611852613 37
+171 3409.9420816195693 33
+172 3456.7860795831725 82
+173 3373.551392820332 8
+174 3336.6609057559326 7
+175 3430.284244782056 15
+176 3437.843510109208 76
+177 3458.1489268104115 281
+178 3477.0933263287598 38
+179 3252.7631330916183 13
+180 3240.0327158842083 9
+181 3159.548227199579 60
+182 3040.0726964992136 77
+184 3227.447288492873 11
+186 3130.8784709726438 1
+187 3295.738460497131 2
+188 3356.7450007410453 26
+189 3489.3794577259723 114
+190 3467.191514756576 29
+191 3003.702382061179 30
+192 3060.0434637436115 34
+193 2897.203134058777 78
+194 2949.2990692705275 26
+195 3061.7589715717336 37
+196 3085.414720908682 39
+197 3373.3670123483453 109
+198 3590.769416155819 47
+199 3188.3251088933825 3
+200 3185.424932406978 15
+201 9721.27075026717 1
+225 2838.9635432671553 12
+228 9773.704773523703 1
+253 3503.388645297578 26
+254 3524.6476419636615 4
diff --git a/reversing_game_mechanics/zoomlevel/win.size.4 b/reversing_game_mechanics/zoomlevel/win.size.4
new file mode 100644
index 0000000..69a1ad2
--- /dev/null
+++ b/reversing_game_mechanics/zoomlevel/win.size.4
@@ -0,0 +1,77 @@
+124 5461.392679527814 1
+126 8508.168428046074 1
+141 6587.192725281385 1
+142 7665.473892721832 1
+149 9875.263895208067 1
+154 7029.594867415903 1
+157 7155.990497478319 1
+159 7350.145372712025 1
+172 5135.193180397404 1
+174 3431.546152975361 6
+175 3497.8764986774477 14
+176 3311.0096647397454 23
+177 3389.6768282536905 36
+178 3468.8963662813567 13
+179 3485.865889560297 37
+180 3490.3886603070437 113
+181 3515.5905620535505 98
+182 3499.1920496023076 15
+183 3419.91418020979 18
+184 3187.2913265028033 64
+185 3123.9286163419292 57
+195 3615.2654121101536 88
+196 3637.733635108541 62
+197 3628.236210612534 156
+198 3661.8806643581383 4
+199 3664.0061408245483 76
+200 3677.116261420082 12
+201 3637.3748775731106 146
+202 3685.892700554372 53
+203 3635.77777098656 56
+205 3716.556605246313 8
+206 3684.0620244507286 15
+207 3715.699799499416 30
+208 3468.918707609044 5
+209 3690.992549437075 16
+210 3509.5711988788603 26
+211 3555.6774319389547 26
+212 3433.242199437727 52
+213 3586.7179983935175 15
+214 3585.6561463698663 10
+215 3746.0168179013826 40
+216 3784.0027748404204 267
+217 3786.887112127849 70
+218 3689.460258628625 10
+219 3681.7335590724106 7
+220 3526.8682992139074 20
+221 3674.9727890149065 28
+222 3664.5142925086266 24
+223 3634.4388837893534 57
+224 3824.525199289449 80
+225 3828.2075440080307 63
+226 3813.7278350716115 126
+227 3816.3041021386125 227
+228 9773.704773523703 2
+229 3870.390807140798 48
+230 3793.3906996248093 27
+286 3049.217932519747 3
+287 2999.563301549077 1
+288 2918.4139528175233 2
+289 2928.8649678672455 1
+290 2923.3942601024582 1
+291 2926.6731966517887 1
+292 3092.7083599977545 12
+293 3092.7083599977545 53
+294 3101.7430583463874 28
+295 2832.8236796525125 40
+296 2666.3600657075553 5
+297 2658.3432810681165 14
+298 2840.7354681490497 2
+299 2840.7354681490497 9
+300 2840.7354681490497 2
+301 2840.7354681490497 4
+302 2840.7354681490497 63
+322 3771.882951524344 154
+323 3668.3926998073694 55
+324 3601.816902620121 2
+325 3550.685708423093 49
diff --git a/reversing_game_mechanics/zoomlevel/win.size.5 b/reversing_game_mechanics/zoomlevel/win.size.5
new file mode 100644
index 0000000..537d012
--- /dev/null
+++ b/reversing_game_mechanics/zoomlevel/win.size.5
@@ -0,0 +1,8 @@
+195 9346.260482139367 1
+196 7269.414075425887 1
+251 3926.380139517823 7
+252 3995.193362028927 19
+253 3979.0382003695313 11
+254 2999.2432378851836 13
+256 3046.871510254412 16
+257 3030.115674359644 1
diff --git a/reversing_game_mechanics/zoomlevel/win.size.6 b/reversing_game_mechanics/zoomlevel/win.size.6
new file mode 100644
index 0000000..1e93f8a
--- /dev/null
+++ b/reversing_game_mechanics/zoomlevel/win.size.6
@@ -0,0 +1,4 @@
+195 9346.260482139367 1
+276 3564.1046561513876 2
+277 3713.46266980025 16
+278 3981.4374288691265 52
diff --git a/reversing_game_mechanics/zoomlevel/win.size.all b/reversing_game_mechanics/zoomlevel/win.size.all
new file mode 100644
index 0000000..e4520ab
--- /dev/null
+++ b/reversing_game_mechanics/zoomlevel/win.size.all
@@ -0,0 +1,424 @@
+0 9957.478596512272 16
+49 2120.5192760265113 245
+50 1969.829688069504 88
+51 1907.5106814904077 196
+52 1652.2645066695586 49
+53 1860.7818249327352 96
+54 1851.8933554608375 58
+55 1468.638825579659 70
+56 2251.943604977709 106
+57 2004.575765592311 166
+58 2109.127781809343 362
+59 2126.4564420650613 137
+60 1994.1740144731602 91
+61 2173.5730951592127 58
+62 1940.5630626186824 52
+63 2232.8013794334684 39
+64 2133.3471353720192 71
+65 2239.5713875650404 100
+66 2259.5827048373335 868
+67 2240.224319125208 818
+68 2307.553032976707 443
+69 2226.5634956138124 579
+70 2356.425258734085 336
+71 2293.2538019155227 397
+72 2249.2098612623945 273
+73 2331.4519081465096 451
+74 2392.639755583778 300
+75 2404.2996901384818 277
+76 2403.8011565019265 161
+77 2357.5313359529287 262
+78 2377.4242364374095 157
+79 2339.8472172344927 145
+80 2438.0258407162137 228
+81 2488.3331770484433 301
+82 2489.7003835803216 352
+83 2499.0544211761376 291
+84 2455.6426857342253 304
+85 2498.9455776386967 154
+86 2378.6727391551785 346
+87 2474.5797623030867 702
+88 2366.7634017788937 727
+89 2442.546417163858 373
+90 2515.553616999646 238
+91 2614.3163542310635 207
+92 2594.101771326638 66
+93 2544.643000501249 67
+94 2651.225377066235 95
+95 2681.96290056369 148
+96 2685.0344504307577 170
+97 2679.5546271722096 426
+98 2693.4938277263605 569
+99 2726.821592990638 54
+100 2696.036349903317 53
+101 2704.8558187082726 56
+102 2754.743726737571 88
+103 2719.114010114324 180
+104 2791.1581825471662 122
+105 2794.8023185907086 84
+106 2803.209767391659 133
+107 2805.824121358999 90
+108 2796.2517769328283 250
+109 2803.5384784232942 261
+110 2837.5144052497776 142
+111 2835.092414719492 107
+112 2846.737255174773 63
+113 2877.452345391666 63
+114 2851.5078467365297 53
+115 2780.2778638114573 113
+116 2891.1260781916794 242
+117 2870.733878296628 69
+118 2892.3390188565377 88
+119 2730.286798122131 145
+120 2715.363695713707 389
+121 2238.8311682661556 281
+122 2913.41998345587 61
+123 2955.795155283938 28
+124 2385.1079640133694 322
+125 3029.202205201891 67
+126 3015.162516349658 39
+127 3024.4353191959653 64
+128 3046.6238691377707 139
+129 3020.514029101669 40
+130 3015.3225366451265 112
+131 3021.6586504765887 92
+132 3040.8763539479864 73
+133 3065.0941257977706 58
+134 2976.10500486794 66
+135 3026.624522467232 98
+136 3013.2540550043236 84
+137 2927.263910206936 80
+138 2981.869380103696 80
+139 2977.177354475208 52
+140 3049.6730972351775 42
+141 2354.0403564934904 65
+142 2332.625130620006 4
+148 3144.616033794905 7
+149 3228.400223020684 11
+150 3220.1863300125974 71
+151 3248.3468103021264 119
+152 3253.712494981694 66
+153 3238.846245192877 57
+154 3176.819950831334 43
+155 3301.4617974467005 36
+156 3296.8477368541 132
+157 3316.3596005258537 113
+158 3286.2537942161434 155
+159 3083.007946794818 110
+160 2909.176000175995 64
+161 2641.6837433727756 35
+162 2646.5972492995606 65
+163 2727.742106578259 49
+164 2692.4384858339845 44
+165 2498.7140692764347 49
+166 2352.3938870860893 36
+167 2375.1743093928917 57
+168 2304.796954180563 121
+64 7513.947364734464 1
+66 8967.320224013414 2
+68 9047.885167264227 3
+71 5962.59423405618 1
+73 6812.90341924792 1
+74 9453.725720582335 1
+75 9562.746467411964 1
+78 4983.02087091756 1
+80 7724.4858728591125 1
+83 8841.674502038626 2
+84 6246.4982190023875 1
+87 9393.411414390408 1
+89 9939.87610586772 1
+90 2459.633306003153 36
+91 2476.9733547214432 54
+92 2602.3854441646417 92
+93 2641.4407053727327 16
+94 2490.116463139827 22
+95 2708.966592632696 50
+96 2712.70344859146 186
+97 2601.976556389392 122
+98 2696.738771182704 66
+99 2717.4488035655795 96
+100 2664.1535241047955 169
+101 2756.752618571351 135
+102 2690.486944774124 169
+103 2757.303936819443 122
+104 2788.0634856473407 455
+105 2799.2456126606685 442
+106 2824.3450568229086 140
+107 2810.7694676013543 170
+108 2763.8178304656767 344
+109 2737.670725269933 72
+110 2784.720632307665 251
+111 2785.9047004519016 159
+112 2875.0377388827437 260
+113 2811.6308434785674 181
+114 2870.785606763417 187
+115 2835.5659047181393 273
+116 2866.9461104108673 57
+117 2881.5004771819836 117
+118 2909.6427615774414 109
+119 2619.34037497993 258
+120 2939.450458844306 263
+121 2906.4929038275664 204
+122 2948.7314221542797 121
+123 2953.9094434325502 74
+124 2985.440838469254 88
+125 2975.29695996887 115
+126 2954.414493601059 126
+127 2896.9544352647317 185
+128 2949.3024599047144 314
+129 2910.3683615652503 209
+130 2979.817444072707 119
+131 3028.992076582572 177
+132 2808.3669632012125 177
+133 3073.615623333536 128
+134 3060.4393475447278 151
+135 3086.498663534459 133
+136 2561.4052783579564 312
+137 3126.1601046651467 227
+138 2979.8711381534604 198
+139 3155.1700429612347 90
+140 3151.571195451564 231
+141 3164.168927222439 154
+142 3129.1259162903625 232
+143 2847.00140498736 255
+144 3187.418077378617 219
+145 3138.7672739468912 97
+146 3195.2003067100504 89
+147 3194.6325297285757 158
+148 3195.955099809758 204
+149 3237.0166820700815 62
+150 3255.597180242052 127
+151 3254.93502239292 60
+152 3281.352160314403 73
+153 3279.3377380196753 125
+154 3255.6776560341473 152
+155 3271.2471627805808 279
+156 3247.845439672276 82
+157 3326.7739628655268 29
+158 2729.1090121136604 144
+159 3286.8233904485955 142
+160 3261.8871838247255 153
+161 3328.385344277312 44
+162 3046.827366294323 49
+163 3154.2986542177646 184
+164 3295.002427920198 83
+165 3357.799874918099 76
+166 3378.6307877600357 39
+167 3370.5692397575813 30
+168 3195.0414707793702 62
+169 2902.5402667318845 135
+170 3258.0098219618676 41
+171 3262.259952854769 26
+172 3422.502739224616 116
+173 3417.901402907931 66
+174 3439.523368142743 108
+175 3349.7058079777694 59
+176 3029.6093807618167 19
+177 3441.201679646225 19
+178 3409.1941863144143 224
+179 2929.2353951159334 19
+180 2900.2127508167396 26
+181 2828.9547539683276 18
+182 2834.0869781995048 17
+183 2761.479675825987 42
+184 2820.7943916563645 30
+191 3317.3273881243617 4
+192 3322.925969683947 1
+193 3360.9024085801716 3
+194 3392.7009888877624 14
+196 3491.7183448840774 98
+197 3560.370767209505 113
+199 3481.1977823731877 22
+200 3588.148547649609 76
+201 3452.222617387239 31
+202 3451.376536977674 53
+203 3230.5573822484566 111
+204 3113.641758455844 67
+205 2726.8489140397933 53
+206 2873.5044805950797 108
+207 3104.405418111494 43
+208 3249.613515481495 32
+209 3475.2795571004067 108
+210 3639.9057680110345 80
+211 3325.2151810070877 57
+212 2884.218091615126 33
+213 2920.3643608289703 13
+214 2906.8321244956683 2
+215 2813.3490718359144 27
+216 2939.36336644519 60
+217 3755.401043830073 69
+218 3585.3395933997663 86
+219 3758.964219036941 85
+220 3790.129945001886 122
+221 3810.992521640524 172
+222 3792.3610060225014 23
+223 3815.631009413777 26
+224 3795.2923471058193 16
+225 3826.4825884877614 69
+226 3831.510668130783 54
+227 3832.1638795855274 156
+228 3833.123269606653 120
+229 3859.508388383163 141
+230 3830.84912258366 86
+231 3783.793995449541 158
+232 3746.1261324199963 184
+233 2863.6000069842157 37
+236 2678.195287875774 32
+115 7887.098642720275 1
+116 9958.89833264704 1
+124 5461.392679527814 1
+126 8508.168428046074 1
+135 2830.58244889634 7
+137 4313.037096988617 1
+140 3420.4585949840116 1
+141 3163.25923692637 7
+142 3149.401212929213 43
+143 3176.547339486695 42
+144 3058.668010752393 23
+145 3126.600230282087 10
+146 3120.9783722416278 24
+147 3145.5927899205262 21
+148 3099.849351178215 16
+149 3050.773016794268 30
+150 2843.040801676965 15
+151 2624.4180307260503 19
+152 3056.6098867863398 125
+153 2468.258090232867 21
+154 2355.969651757 21
+155 2702.4124407647328 32
+156 2982.131452501717 24
+157 3142.504256162591 21
+158 3265.692269642074 28
+159 3282.8310952590905 6
+160 3261.3567728784287 5
+161 3274.0054978573266 11
+162 3332.2054258403696 15
+163 3361.016661666526 3
+164 3355.109089135553 14
+165 3373.971102425153 20
+166 3259.075482402947 7
+167 3265.9854561831717 10
+169 3411.079741079062 17
+170 3398.256611852613 37
+171 3409.9420816195693 33
+172 3456.7860795831725 82
+173 3373.551392820332 8
+174 3336.6609057559326 7
+175 3430.284244782056 15
+176 3437.843510109208 76
+177 3458.1489268104115 281
+178 3477.0933263287598 38
+179 3252.7631330916183 13
+180 3240.0327158842083 9
+181 3159.548227199579 60
+182 3040.0726964992136 77
+184 3227.447288492873 11
+186 3130.8784709726438 1
+187 3295.738460497131 2
+188 3356.7450007410453 26
+189 3489.3794577259723 114
+190 3467.191514756576 29
+191 3003.702382061179 30
+192 3060.0434637436115 34
+193 2897.203134058777 78
+194 2949.2990692705275 26
+195 3061.7589715717336 37
+196 3085.414720908682 39
+197 3373.3670123483453 109
+198 3590.769416155819 47
+199 3188.3251088933825 3
+200 3185.424932406978 15
+201 9721.27075026717 1
+225 2838.9635432671553 12
+228 9773.704773523703 1
+253 3503.388645297578 26
+254 3524.6476419636615 4
+124 5461.392679527814 1
+126 8508.168428046074 1
+141 6587.192725281385 1
+142 7665.473892721832 1
+149 9875.263895208067 1
+154 7029.594867415903 1
+157 7155.990497478319 1
+159 7350.145372712025 1
+172 5135.193180397404 1
+174 3431.546152975361 6
+175 3497.8764986774477 14
+176 3311.0096647397454 23
+177 3389.6768282536905 36
+178 3468.8963662813567 13
+179 3485.865889560297 37
+180 3490.3886603070437 113
+181 3515.5905620535505 98
+182 3499.1920496023076 15
+183 3419.91418020979 18
+184 3187.2913265028033 64
+185 3123.9286163419292 57
+195 3615.2654121101536 88
+196 3637.733635108541 62
+197 3628.236210612534 156
+198 3661.8806643581383 4
+199 3664.0061408245483 76
+200 3677.116261420082 12
+201 3637.3748775731106 146
+202 3685.892700554372 53
+203 3635.77777098656 56
+205 3716.556605246313 8
+206 3684.0620244507286 15
+207 3715.699799499416 30
+208 3468.918707609044 5
+209 3690.992549437075 16
+210 3509.5711988788603 26
+211 3555.6774319389547 26
+212 3433.242199437727 52
+213 3586.7179983935175 15
+214 3585.6561463698663 10
+215 3746.0168179013826 40
+216 3784.0027748404204 267
+217 3786.887112127849 70
+218 3689.460258628625 10
+219 3681.7335590724106 7
+220 3526.8682992139074 20
+221 3674.9727890149065 28
+222 3664.5142925086266 24
+223 3634.4388837893534 57
+224 3824.525199289449 80
+225 3828.2075440080307 63
+226 3813.7278350716115 126
+227 3816.3041021386125 227
+228 9773.704773523703 2
+229 3870.390807140798 48
+230 3793.3906996248093 27
+286 3049.217932519747 3
+287 2999.563301549077 1
+288 2918.4139528175233 2
+289 2928.8649678672455 1
+290 2923.3942601024582 1
+291 2926.6731966517887 1
+292 3092.7083599977545 12
+293 3092.7083599977545 53
+294 3101.7430583463874 28
+295 2832.8236796525125 40
+296 2666.3600657075553 5
+297 2658.3432810681165 14
+298 2840.7354681490497 2
+299 2840.7354681490497 9
+300 2840.7354681490497 2
+301 2840.7354681490497 4
+302 2840.7354681490497 63
+322 3771.882951524344 154
+323 3668.3926998073694 55
+324 3601.816902620121 2
+325 3550.685708423093 49
+195 9346.260482139367 1
+196 7269.414075425887 1
+251 3926.380139517823 7
+252 3995.193362028927 19
+253 3979.0382003695313 11
+254 2999.2432378851836 13
+256 3046.871510254412 16
+257 3030.115674359644 1
+195 9346.260482139367 1
+276 3564.1046561513876 2
+277 3713.46266980025 16
+278 3981.4374288691265 52
diff --git a/reversing_game_mechanics/zoomlevel/win.size.all.filtered b/reversing_game_mechanics/zoomlevel/win.size.all.filtered
new file mode 100644
index 0000000..b1abb5b
--- /dev/null
+++ b/reversing_game_mechanics/zoomlevel/win.size.all.filtered
@@ -0,0 +1,227 @@
+49 2120.5192760265113 245
+50 1969.829688069504 88
+51 1907.5106814904077 196
+56 2251.943604977709 106
+57 2004.575765592311 166
+58 2109.127781809343 362
+59 2126.4564420650613 137
+61 2173.5730951592127 58
+63 2232.8013794334684 39
+64 2133.3471353720192 71
+65 2239.5713875650404 100
+66 2259.5827048373335 868
+67 2240.224319125208 818
+68 2307.553032976707 443
+69 2226.5634956138124 579
+70 2356.425258734085 336
+71 2293.2538019155227 397
+72 2249.2098612623945 273
+73 2331.4519081465096 451
+74 2392.639755583778 300
+75 2404.2996901384818 277
+76 2403.8011565019265 161
+77 2357.5313359529287 262
+78 2377.4242364374095 157
+79 2339.8472172344927 145
+80 2438.0258407162137 228
+81 2488.3331770484433 301
+82 2489.7003835803216 352
+83 2499.0544211761376 291
+84 2455.6426857342253 304
+85 2498.9455776386967 154
+87 2474.5797623030867 702
+90 2515.553616999646 238
+91 2614.3163542310635 207
+92 2594.101771326638 66
+93 2544.643000501249 67
+94 2651.225377066235 95
+95 2681.96290056369 148
+96 2685.0344504307577 170
+97 2679.5546271722096 426
+98 2693.4938277263605 569
+99 2726.821592990638 54
+100 2696.036349903317 53
+101 2704.8558187082726 56
+102 2754.743726737571 88
+103 2719.114010114324 180
+104 2791.1581825471662 122
+105 2794.8023185907086 84
+106 2803.209767391659 133
+107 2805.824121358999 90
+108 2796.2517769328283 250
+109 2803.5384784232942 261
+110 2837.5144052497776 142
+111 2835.092414719492 107
+112 2846.737255174773 63
+113 2877.452345391666 63
+114 2851.5078467365297 53
+115 2780.2778638114573 113
+116 2891.1260781916794 242
+117 2870.733878296628 69
+118 2892.3390188565377 88
+122 2913.41998345587 61
+123 2955.795155283938 28
+125 3029.202205201891 67
+126 3015.162516349658 39
+127 3024.4353191959653 64
+128 3046.6238691377707 139
+129 3020.514029101669 40
+130 3015.3225366451265 112
+131 3021.6586504765887 92
+132 3040.8763539479864 73
+133 3065.0941257977706 58
+135 3026.624522467232 98
+136 3013.2540550043236 84
+140 3049.6730972351775 42
+148 3144.616033794905 7
+149 3228.400223020684 11
+150 3220.1863300125974 71
+151 3248.3468103021264 119
+152 3253.712494981694 66
+153 3238.846245192877 57
+154 3176.819950831334 43
+155 3301.4617974467005 36
+156 3296.8477368541 132
+157 3316.3596005258537 113
+158 3286.2537942161434 155
+92 2602.3854441646417 92
+93 2641.4407053727327 16
+95 2708.966592632696 50
+96 2712.70344859146 186
+97 2601.976556389392 122
+98 2696.738771182704 66
+99 2717.4488035655795 96
+100 2664.1535241047955 169
+101 2756.752618571351 135
+102 2690.486944774124 169
+103 2757.303936819443 122
+104 2788.0634856473407 455
+105 2799.2456126606685 442
+106 2824.3450568229086 140
+107 2810.7694676013543 170
+108 2763.8178304656767 344
+109 2737.670725269933 72
+110 2784.720632307665 251
+111 2785.9047004519016 159
+112 2875.0377388827437 260
+113 2811.6308434785674 181
+114 2870.785606763417 187
+115 2835.5659047181393 273
+116 2866.9461104108673 57
+117 2881.5004771819836 117
+118 2909.6427615774414 109
+120 2939.450458844306 263
+121 2906.4929038275664 204
+122 2948.7314221542797 121
+123 2953.9094434325502 74
+124 2985.440838469254 88
+125 2975.29695996887 115
+126 2954.414493601059 126
+128 2949.3024599047144 314
+130 2979.817444072707 119
+131 3028.992076582572 177
+133 3073.615623333536 128
+134 3060.4393475447278 151
+135 3086.498663534459 133
+137 3126.1601046651467 227
+139 3155.1700429612347 90
+140 3151.571195451564 231
+141 3164.168927222439 154
+142 3129.1259162903625 232
+144 3187.418077378617 219
+145 3138.7672739468912 97
+146 3195.2003067100504 89
+147 3194.6325297285757 158
+148 3195.955099809758 204
+149 3237.0166820700815 62
+150 3255.597180242052 127
+151 3254.93502239292 60
+152 3281.352160314403 73
+153 3279.3377380196753 125
+154 3255.6776560341473 152
+155 3271.2471627805808 279
+156 3247.845439672276 82
+157 3326.7739628655268 29
+159 3286.8233904485955 142
+160 3261.8871838247255 153
+161 3328.385344277312 44
+164 3295.002427920198 83
+165 3357.799874918099 76
+166 3378.6307877600357 39
+167 3370.5692397575813 30
+172 3422.502739224616 116
+173 3417.901402907931 66
+174 3439.523368142743 108
+177 3441.201679646225 19
+178 3409.1941863144143 224
+197 3560.370767209505 113
+200 3588.148547649609 76
+217 3755.401043830073 69
+219 3758.964219036941 85
+220 3790.129945001886 122
+221 3810.992521640524 172
+222 3792.3610060225014 23
+223 3815.631009413777 26
+224 3795.2923471058193 16
+225 3826.4825884877614 69
+226 3831.510668130783 54
+227 3832.1638795855274 156
+228 3833.123269606653 120
+229 3859.508388383163 141
+230 3830.84912258366 86
+141 3163.25923692637 7
+142 3149.401212929213 43
+143 3176.547339486695 42
+145 3126.600230282087 10
+146 3120.9783722416278 24
+147 3145.5927899205262 21
+158 3265.692269642074 28
+159 3282.8310952590905 6
+160 3261.3567728784287 5
+161 3274.0054978573266 11
+162 3332.2054258403696 15
+163 3361.016661666526 3
+164 3355.109089135553 14
+165 3373.971102425153 20
+169 3411.079741079062 17
+170 3398.256611852613 37
+171 3409.9420816195693 33
+172 3456.7860795831725 82
+173 3373.551392820332 8
+175 3430.284244782056 15
+176 3437.843510109208 76
+177 3458.1489268104115 281
+178 3477.0933263287598 38
+189 3489.3794577259723 114
+198 3590.769416155819 47
+174 3431.546152975361 6
+175 3497.8764986774477 14
+177 3389.6768282536905 36
+178 3468.8963662813567 13
+179 3485.865889560297 37
+180 3490.3886603070437 113
+181 3515.5905620535505 98
+182 3499.1920496023076 15
+195 3615.2654121101536 88
+196 3637.733635108541 62
+197 3628.236210612534 156
+198 3661.8806643581383 4
+199 3664.0061408245483 76
+200 3677.116261420082 12
+201 3637.3748775731106 146
+202 3685.892700554372 53
+203 3635.77777098656 56
+205 3716.556605246313 8
+206 3684.0620244507286 15
+207 3715.699799499416 30
+209 3690.992549437075 16
+215 3746.0168179013826 40
+216 3784.0027748404204 267
+217 3786.887112127849 70
+224 3824.525199289449 80
+225 3828.2075440080307 63
+226 3813.7278350716115 126
+227 3816.3041021386125 227
+229 3870.390807140798 48
+252 3995.193362028927 19
+253 3979.0382003695313 11
diff --git a/reversing_game_mechanics/zoomlevel/win.size.gnuplot b/reversing_game_mechanics/zoomlevel/win.size.gnuplot
new file mode 100644
index 0000000..aa5499c
--- /dev/null
+++ b/reversing_game_mechanics/zoomlevel/win.size.gnuplot
@@ -0,0 +1,19 @@
+min(a,b)=(a<b)?a:b
+max(a,b)=(a>b)?a:b
+
+f(x) = a* x**b
+g(x) = aa* x**bb + cc
+fit f(x) "win.size.all.filtered" using 1:2 via a,b
+fit g(x) "win.size.all.filtered" using 1:2 via aa,bb,cc
+
+plot "win.size.1" using 1:2:(min(1,$3/100)) lt rgb "red" pt 2 ps variable, \
+ "win.size.2" using 1:2:(min(1,$3/100)) lt rgb "blue" pt 2 ps variable, \
+ "win.size.3" using 1:2:(min(1,$3/100)) lt rgb "green" pt 2 ps variable, \
+ "win.size.4" using 1:2:(1) lt rgb "purple" pt 2 ps variable, \
+ "win.size.5" using 1:2:(1) lt rgb "cyan" pt 2 ps variable, \
+ "win.size.6" using 1:2:(1) lt rgb "orange" pt 2 ps variable, \
+ x**0.4*460+50 lt rgb "gray", \
+ x**0.407*460-400 lt rgb "gray", \
+ f(x) lt rgb "black", \
+ g(x) lt rgb "black"
+pause -1
diff --git a/stats.py b/stats.py
index f850339..b263f01 100644
--- a/stats.py
+++ b/stats.py
@@ -1,36 +1,475 @@
import time
+import math
+import random
+from collections import defaultdict
+import pickle
+from functools import reduce
+import mechanics
+import geometry
+#import numpy
+
+def fit_gaussian(l):
+ mean = sum(l) / len(l)
+ stddev = math.sqrt(sum(map(lambda v : (v-mean)**2, l)) / len(l))
+ return mean, stddev
+
+def flatten(l):
+ return reduce(lambda a,b:a+b, l)
+
+def quantile(values, q):
+ if isinstance(values, dict):
+ return quantile(flatten(map(lambda x : [x[0]]*x[1], sorted(values.items(),key=lambda x:x[0]))), q)
+ else:
+ try:
+ return sorted(values)[ int(len(values)*q) ]
+ except:
+ return 0
+
+def find_smallest_q_confidence_area(values, q):
+ """Calculates the (mid, delta) with the smallest delta, such that at least q * len(values)
+ lie within the interval [mid-delta, mid+delta]."""
+ try:
+ mid = min(values, key = lambda value : quantile(list(map(lambda x : abs(x-value), values)), q))
+ deviation = quantile(list(map(lambda x : abs(x-mid), values)),q)
+ #print(list(map(lambda x : abs(x-mid), values)))
+ return mid,deviation
+ except:
+ return 0,0
+
+def get_delta_confidence(values, mid, delta):
+ #"""Calculates which fraction of the values lie within [mid-delta, mid+delta]"""
+ #try:
+ return len(list(filter(lambda v : (mid-delta <= v and v <= mid+delta), values))) / len(values)
+ #except:
+ # raise
+ # return 0
+
+def avg(values):
+ if not isinstance(values, dict):
+ return sum(values)/len(values)
+ else:
+ return int(sum(map( lambda x : x[0]*x[1], values.items() )) / sum(map(lambda x : x[1], values.items())))
+
+def stddev(values):
+ a=avg(values)
+ return avg(list(map(lambda v : (v-a)**2, values)))
+
+def normalize(values):
+ a=avg(values)
+ return [x/a for x in values]
+
+class StatData():
+ pass
+
+def return_empty_list():
+ return []
+
+def return_defaultdict_with_empty_list():
+ return defaultdict(return_empty_list)
+
+def return_zero():
+ return 0
+
+def return_defaultdict_with_zeros():
+ return defaultdict(return_zero)
+
+class ReMerging:
+ def __init__(self, size1, size2, birth1, birth2, is_parent_child, begin_time):
+ self.size1 = size1
+ self.size2 = size2
+ self.birth1 = birth1
+ self.birth2 = birth2
+ self.is_parent_child = is_parent_child
+ self.begin_time = begin_time
+ self.end_time = None
class Stats:
- def __init__(self):
- self.min_mass = 0
- self.max_mass = 0
- self.current_mass = 0
+ def __init__(self,c,data=None):
+ self.c = c
+
+ self.countdown = 27*20
+
+ if data == None:
+ self.data = StatData()
+ self.data.version = 4
+
+ self.data.min_mass = 0
+ self.data.max_mass = 0
+ self.data.current_mass = 0
+
+ self.data.mass_history = []
+ self.data.pos_history = []
+ self.data.cell_aggressivity = {}
+ self.data.cell_split_frequency = {}
+ self.data.cell_defensiveness = {}
+
+ self.data.size_vs_speed = defaultdict(return_defaultdict_with_zeros)
+ self.data.size_vs_visible_window = defaultdict(return_defaultdict_with_empty_list)
+ self.data.mass_vs_visible_window = defaultdict(return_defaultdict_with_empty_list)
+
+ self.data.eject_distlogs = {"virus" : [], "split cell" : [], "ejected mass" : []}
+ self.data.eject_deviations = {"virus" : [], "virus2" : [], "virus3" : [], "split cell" : [], "ejected mass" : []}
+
+ self.data.observed_virus_sizes = defaultdict(return_zero)
+ self.data.remerging = {}
+ else:
+ self.data = data
- self.mass_history = []
- self.pos_history = []
- self.cell_aggressivity = {}
- self.cell_split_frequency = {}
- self.cell_defensiveness = {}
+ def save(self,filename):
+ pickle.dump(self.data, open(filename,"wb"))
+
+ def load(filename):
+ return Stats(None, pickle.load(open(filename,"rb")))
+
+ def merge(self, filename):
+ data2 = pickle.load(open(filename,"rb"))
+ self.data.min_mass = min(self.data.min_mass, data2.min_mass)
+ self.data.max_mass = max(self.data.max_mass, data2.max_mass)
+ for i in data2.size_vs_visible_window:
+ for j in data2.size_vs_visible_window[i]:
+ self.data.size_vs_visible_window[i][j] += data2.size_vs_visible_window[i][j]
+ for i in data2.mass_vs_visible_window:
+ for j in data2.mass_vs_visible_window[i]:
+ self.data.mass_vs_visible_window[i][j] += data2.mass_vs_visible_window[i][j]
+
+ for i in data2.size_vs_speed:
+ for j in data2.size_vs_speed[i]:
+ self.data.size_vs_speed[i][j] += data2.size_vs_speed[i][j]
+
+ for i in data2.eject_deviations:
+ self.data.eject_deviations[i] += data2.eject_deviations[i]
+
+ for i in data2.eject_distlogs:
+ self.data.eject_distlogs[i] += data2.eject_distlogs[i]
+
+ for i in self.data.observed_virus_sizes:
+ self.data.observed_virus_sizes[i] += data2.observed_virus_sizes[i]
+
+ self.data.remerging.update(data2.remerging)
+
def log_mass(self, mass):
- self.mass_history.append((time.time(), mass))
- self.current_mass = mass
- if mass > self.max_mass:
- self.max_mass = mass
- if mass < self.min_mass:
- self.min_mass = mass
+ self.data.mass_history.append((time.time(), mass))
+ self.data.current_mass = mass
+ if mass > self.data.max_mass:
+ self.data.max_mass = mass
+ if mass < self.data.min_mass:
+ self.data.min_mass = mass
def log_pos(self, pos):
- self.pos_history.append((time.time(), (pos[0], pos[1])))
+ self.data.pos_history.append((time.time(), (pos[0], pos[1])))
def update_cell_aggressivity(self, cell, value):
- self.cell_aggressivity[cell] = value
+ self.data.cell_aggressivity[cell] = value
def update_cell_split_frequency(self, cell, value):
- self.cell_split_frequency[cell] = value
+ self.data.cell_split_frequency[cell] = value
def update_cell_defensiveness(self, cell, value):
- self.cell_defensiveness[cell] = value
+ self.data.cell_defensiveness[cell] = value
def get_last_steps(self, list, steps = 10):
- return list[-steps:] \ No newline at end of file
+ return list[-steps:]
+
+ def process_frame(self):
+ self.countdown -= 1
+ if False and (self.countdown <= 0):
+ quick_followup = (random.random() < 0.1)
+
+ if quick_followup:
+ self.countdown = 7
+ else:
+ self.countdown = int(27* (random.random() * 15))
+
+ what_to_do = random.random()
+ if what_to_do < 0.2:
+ self.c.send_split()
+ else:
+ self.c.send_shoot()
+
+ self.log_pos(self.c.player.center)
+ self.log_mass(self.c.player.total_mass)
+
+ cells = self.c.world.cells.values()
+ own_cells = list(self.c.player.own_cells)
+
+ own_total_size = sum( map(lambda cell : cell.size, own_cells) )
+ own_total_mass = sum( map(lambda cell : cell.mass, own_cells) )
+ n_own_cells = len(own_cells)
+
+ n = 3
+ for cell in filter(lambda cell : not cell.is_food and not cell.is_virus and not cell.is_ejected_mass, cells):
+ if hasattr(cell,'poslog') and len(cell.poslog) > n+1:
+ cellspeed = 0
+ for i in range(1,n+1):
+ cellspeed += (cell.poslog[-i] - cell.poslog[-i-1]).len() / n
+
+ cellspeed = int(cellspeed*10)/10
+ self.data.size_vs_speed[cell.size][cellspeed] += 1
+
+ visible_width = max( map(lambda cell : cell.pos.x - cell.size, cells) ) - min( map(lambda cell : cell.pos.x + cell.size, cells) )
+ visible_height = max( map(lambda cell : cell.pos.y - cell.size, cells) ) - min( map(lambda cell : cell.pos.y + cell.size, cells) )
+
+ self.data.size_vs_visible_window[n_own_cells][own_total_size].append((visible_width,visible_height))
+ self.data.mass_vs_visible_window[n_own_cells][own_total_mass].append((visible_width,visible_height))
+
+
+ # log virus sizes
+ for cell in cells:
+ if cell.is_virus:
+ self.data.observed_virus_sizes[cell.size] += 1
+
+ # detect re-merging cells
+ for cell in own_cells:
+ for cell2 in own_cells:
+ if cell2 != cell:
+ dist = (cell.pos - cell2.pos).len()
+ expected_dist = cell.size + cell2.size
+ min_dist = max(cell.size, cell2.size)
+
+ if (dist < (0.9 * expected_dist + 0.1 * min_dist)):
+ is_parent_child = (cell == cell2.parent or cell2 == cell.parent)
+ print("cells seem to be merging! they are "+ ("" if is_parent_child else "NOT ") + "parent and child")
+ pair_id = (min(cell.cid,cell2.cid), max(cell.cid,cell2.cid))
+
+ if pair_id not in self.data.remerging:
+ self.data.remerging[pair_id] = ReMerging(cell.size, cell2.size, cell.spawntime, cell2.spawntime, is_parent_child, self.c.world.time)
+ else:
+ self.data.remerging[pair_id].end_time = self.c.world.time
+
+
+
+ # find ejected mass, split cells or viruses that have come to rest
+ for cell in cells:
+ if hasattr(cell,"parent") and cell.parent != None and not cell.calmed_down:
+ # we're only interested in cells with a parent set, because
+ # this also implies that we have tracked them since their
+ # creation.
+ # also, we're only interested in cells that are still flying
+ # as a result of being ejected/split.
+
+ if not cell.is_food and not cell.is_ejected_mass and not cell.is_virus:
+ expected_speed = mechanics.speed(cell.size)
+ celltype = "split cell"
+ elif cell.is_virus:
+ expected_speed = 1
+ celltype = "virus"
+ elif cell.is_ejected_mass:
+ expected_speed = 1
+ celltype = "ejected mass"
+
+
+ if cell.movement.len() < expected_speed * 1.1:
+ print(celltype+" has come to rest, nframes="+str(len(cell.poslog)))
+ cell.calmed_down = True
+ # TODO: speed log
+
+ # distance is calculated naively
+ distance = (cell.spawnpoint - cell.pos).len()
+
+ # distance2 is calculated along the cell's path (will differ if the flight was not colinear)
+ poslog = list(cell.poslog)
+ speeds = list(map(lambda vecs : (vecs[0]-vecs[1]).len(), zip(poslog, poslog[1:])))
+ distance2 = sum(speeds)
+
+ distance_from_parent = (cell.parentpos_when_spawned - cell.pos).len()
+
+ self.data.eject_distlogs[celltype] += [(distance, distance2, distance_from_parent, cell.parentsize_when_spawned, len(cell.poslog), speeds)]
+ print(" flown distance = %.2f / %.2f"%(distance,distance2))
+
+ if len(cell.poslog) == 5:
+ # calculate movement direction from the first 5 samples
+
+ # first check whether they're on a straight line
+ if geometry.is_colinear(cell.poslog) and cell.shoot_vec != None:
+ print(celltype+" direction available!")
+ fly_direction = cell.poslog[-1] - cell.poslog[0]
+ fly_angle = math.atan2(fly_direction.y, fly_direction.x)
+
+ shoot_angle = math.atan2(cell.shoot_vec.y, cell.shoot_vec.x)
+
+
+ deviation = (fly_angle - shoot_angle) % (2*math.pi)
+ if deviation > math.pi: deviation -= 2*math.pi
+ print(" deviation = "+str(deviation*180/math.pi))
+
+ self.data.eject_deviations[celltype] += [deviation]
+
+ if (celltype == 'virus'):
+ # FIXME so ugly
+ try:
+ shoot_angle = math.atan2(cell.shoot_vec2.y, cell.shoot_vec2.x)
+
+ deviation = (fly_angle - shoot_angle) % (2*math.pi)
+ if deviation > math.pi: deviation -= 2*math.pi
+ print(" deviation2= "+str(deviation*180/math.pi))
+
+ self.data.eject_deviations['virus2'] += [deviation]
+ except AttributeError:
+ print("virus2 not available, wtf?!")
+
+ try:
+ shoot_angle = math.atan2(cell.shoot_vec3.y, cell.shoot_vec3.x)
+
+ deviation = (fly_angle - shoot_angle) % (2*math.pi)
+ if deviation > math.pi: deviation -= 2*math.pi
+ print(" deviation3= "+str(deviation*180/math.pi))
+
+ self.data.eject_deviations['virus3'] += [deviation]
+ except AttributeError:
+ print("virus3 not available")
+
+ else:
+ print(celltype+" did NOT fly in a straight line, ignoring...")
+
+
+
+ def analyze_speed(self):
+ results=[]
+ for size, values in sorted(self.data.size_vs_speed.items(), key=lambda x : x[0]):
+ minimum = quantile(values, 0.2)
+ average = quantile(values, 0.5)
+ maximum = quantile(values, 0.8)
+
+ results += [(size,maximum,average,minimum,False,False,False,sum(values.values()))]
+
+ # mark outliers
+ for i in range(1, len(results)-1):
+ for j in range(1,4):
+ if abs(results[i][j] - results[i-1][j]) > 2 and abs(results[i][j] - results[i+1][j]) > 2:
+ tmp = list(results[i])
+ tmp[j+3] = True
+ results[i] = tuple(tmp)
+
+ coeff_vs_stddev = []
+ for coeff in [x/100 for x in range(10,100,1)]:
+ products = []
+ for size, maximum, average, minimum, maxoutlier, avgoutlier, minoutlier, ndata in results:
+ if not maxoutlier:
+ products += [size**coeff * maximum]
+
+ coeff_vs_stddev += [(coeff, avg(products), stddev(normalize(products)))]
+
+ best = min(coeff_vs_stddev, key=lambda v:v[2])
+
+ print("size\tcalc\tmax\tavg\tmin\t\tndata")
+ for size, maximum, average, minimum, maxoutlier, avgoutlier, minoutlier, ndata in results:
+ print(str(size) + ":\t" + "%.1f" % (best[1] / size**best[0]) + "\t" + ("*" if maxoutlier else "") + str(maximum) + "\t" + ("*" if avgoutlier else "") + str(average) + "\t" + ("*" if minoutlier else "") + str(minimum) + "\t\t" + str(ndata))
+
+ print("size**"+str(best[0])+" * speed = "+str(best[1]) )
+
+ def analyze_visible_window_helper(self, foo_vs_visible_window, verbose=False):
+ svw = {}
+ ratios = []
+ if verbose: print("size\tdiag")
+ for size, rects in sorted(foo_vs_visible_window.items(), key=lambda x:x[0]):
+ maxwidth = quantile(sorted(map(lambda x:x[0], rects)), 0.75)
+ maxheight = quantile(sorted(map(lambda x:x[1], rects)), 0.75)
+
+ if math.sqrt(maxwidth**2+maxheight**2) < 4000:
+ # TODO FIXME
+ svw[size] = (maxwidth,maxheight)
+ ratios += [maxwidth/maxheight]
+
+ if verbose: print(str(size)+"\t"+str(math.sqrt(maxwidth**2+maxheight**2))+"\t\t"+str(len(rects)))
+
+ print ("median ratio = "+str(quantile(sorted(ratios),0.5)))
+
+ coeff_vs_stddev=[]
+ for coeff in [x/100 for x in range(0,100,1)]:
+ quotients = []
+ for size, rect in svw.items():
+ if size != 0:
+ diag = math.sqrt(rect[0]**2+rect[1]**2)
+ quotients += [diag / size**coeff]
+
+ coeff_vs_stddev += [(coeff, avg(quotients), stddev(normalize(quotients)))]
+
+ best = min(coeff_vs_stddev, key=lambda v:v[2])
+
+ print("diag / size**"+str(best[0])+" = "+str(best[1]))
+
+ def analyze_visible_window(self, verbose=False):
+ for ncells in sorted(self.data.size_vs_visible_window.keys()):
+ if len(self.data.size_vs_visible_window[ncells]) > 0:
+ print("\nwith "+str(ncells)+" cells, depending on sum(size)")
+ try:
+ self.analyze_visible_window_helper(self.data.size_vs_visible_window[ncells], verbose)
+ except ZeroDivisionError:
+ print("\toops.")
+ for ncells in sorted(self.data.mass_vs_visible_window.keys()):
+ if len(self.data.mass_vs_visible_window[ncells]) > 0:
+ print("\nwith "+str(ncells)+" cells, depending on sum(mass)")
+ try:
+ self.analyze_visible_window_helper(self.data.mass_vs_visible_window[ncells], verbose)
+ except ZeroDivisionError:
+ print("\toops.")
+
+ def analyze_deviations(self, celltype):
+ ds = self.data.eject_deviations[celltype]
+
+ try:
+ mean, stddev = fit_gaussian(ds)
+ except:
+ mean, stddev = "???", "???"
+
+
+ quant = quantile(list(map(abs, ds)), 0.75)
+
+ print(celltype+" eject/split direction deviations: mean = "+str(mean)+", stddev="+str(stddev)+", ndata="+str(len(ds)))
+ print("\t75%% of the splits had a deviation smaller than %.2f rad = %.2f deg" % (quant, quant*180/math.pi))
+ print("")
+
+
+ #a,b = numpy.histogram(ds, bins=100)
+ #midpoints = map(lambda x : (x[0]+x[1])/2, zip(b, b[1:]))
+ #for n,x in zip(a,midpoints):
+ # print(str(n) + "\t" + str(x))
+
+ def analyze_distances(self, celltype):
+ ds = [v[0] for v in self.data.eject_distlogs[celltype]]
+ ns = [v[4] for v in self.data.eject_distlogs[celltype]]
+
+ try:
+ mean, stddev = fit_gaussian(ds)
+ meann, stddevn = fit_gaussian(ns)
+ except:
+ mean, stddev = "???", "???"
+ meann, stddevn = "???", "???"
+
+ print(celltype+" eject/split distances: mean = "+str(mean) +", stddev ="+str(stddev) +", ndata="+str(len(ds)))
+ print(celltype+" meann = "+str(meann)+", stddevn ="+str(stddevn))
+
+ #a,b = numpy.histogram(ds, bins=100)
+ #midpoints = list(map(lambda x : (x[0]+x[1])/2, zip(b, b[1:])))
+ #for n,x in zip(a,midpoints):
+ # print(str(n) + "\t" + str(x))
+
+ #maxidx = max(range(0,len(a)), key = lambda i : a[i])
+ #print("\tmaximum at "+str(midpoints[maxidx]))
+
+ #q = 75 if celltype == "ejected mass" else 75
+ #quant = quantile(list(map(lambda v : abs(v-midpoints[maxidx]), ds)), q/100)
+ #print("\t"+str(q)+"% of values lie have a distance of at most "+str(quant)+" from the maximum")
+
+ mid, delta = find_smallest_q_confidence_area(ds, 0.75)
+ print("\t75%% of the distances lie in the interval %.2f plusminus %.2f" % (mid,delta))
+ print("\t%2d%% of the distances lie in the interval %.2f plusminus %.2f" % (100*get_delta_confidence(ds, mid, delta*1.2), mid, delta*1.2) )
+ print("\tmax = %.2f" % (max(ds)))
+ mid, delta = find_smallest_q_confidence_area(ns, 0.75)
+ print("\t75%% of the flight lengths lie in the interval %.2f plusminus %.2f" % (mid,delta))
+ print("\t%2d%% of the flight lengths lie in the interval %.2f plusminus %.2f" % (100*get_delta_confidence(ns,mid,delta*1.2),mid,delta*1.2))
+ print("")
+
+ def analyze_virus_sizes(self):
+ print("\nI've seen the following %d virus sizes:" % len(self.data.observed_virus_sizes))
+ for size, ndata in sorted(self.data.observed_virus_sizes.items(), key=lambda x:x[0]):
+ print("\t%4d: %7d times" % (size, ndata))
+
+ def analyze_remerge(self):
+ relevant = list(filter(lambda r : r.is_parent_child, self.data.remerging.values()))
+ durations = list(map(lambda r : r.end_time - r.begin_time, relevant))
+ print("75%% of the remerge durations lie at %.2f plusminus %.2f frames" % find_smallest_q_confidence_area(durations,0.75))
+ waittimes = list(map(lambda r : r.begin_time - max(r.birth1, r.birth2), relevant))
+ print("75%% of the remerges were started after %.2f plusminus %.2f frames" % find_smallest_q_confidence_area(waittimes,0.75))
+
diff --git a/strategy.py b/strategy.py
index 0d2d8f0..3d5721a 100644
--- a/strategy.py
+++ b/strategy.py
@@ -1,15 +1,25 @@
import math
from interval_utils import *
-import gui
import random
+import nogui
+import mechanics
+
+friendly_players=["Windfisch","windfisch","Cyanide","cyanide"] +\
+ ["Midna","Nayru","Farore","Din","Ezelo","Navi","Zelda","Tetra","Link","Ciela","Linebeck","Salia","Epona","Shiek"] +\
+ ["Vaati","Ganon","Ganondorf","Ghirahim","Agahnim"]
class Strategy:
- def __init__(self, c):
+ def __init__(self, c, gui=None):
self.target = (0,0)
self.has_target = False
self.target_cell = None
self.color = (0,0,0)
self.c = c
+ self.do_approach_friends = True
+ if gui != None:
+ self.gui = gui
+ else:
+ self.gui = nogui
def get_my_smallest(self):
return sorted(self.c.player.own_cells, key = lambda x: x.mass)[0]
@@ -43,9 +53,12 @@ class Strategy:
def nonsplitkiller(self, cell):
return not cell.is_virus and not cell.is_food and 1.20*self.get_my_smallest().mass < cell.mass and cell.mass < 1.25*2*self.get_my_smallest().mass
- def quality(self, cell):
+ def quality(self, cell, myspeed):
dd_sq = max((cell.pos[0]-self.c.player.center[0])**2 + (cell.pos[1]-self.c.player.center[1])**2,0.001)
- sigma = 500
+ sigma = 500 * max(cell.mass,1) # TODO FIXME don't try to eat running away cells
+ if mechanics.speed(cell) - myspeed >= 0:
+ sigma = sigma / 3 / math.exp((mechanics.speed(cell)-myspeed)/10)
+
dist_score = -math.exp(-dd_sq/(2*sigma**2))
rivals = filter(lambda r : self.rival(r,cell), self.c.world.cells.values())
@@ -105,21 +118,123 @@ class Strategy:
def process_frame(self):
runaway = False
- my_smallest = min(map(lambda cell : cell.mass, self.c.player.own_cells))
- my_largest = max(map(lambda cell : cell.mass, self.c.player.own_cells))
+ my_smallest = min(self.c.player.own_cells, key=lambda cell : cell.mass)
+ my_largest = max(self.c.player.own_cells, key=lambda cell : cell.mass)
+
+ friendly_cells = list(filter(lambda c : c.is_virus or c.name in friendly_players, self.c.world.cells.values()))
+
+ if friendly_cells:
+ dist_to_friend = min(map(lambda c : (self.c.player.center-c.pos).len() - max(my_largest.size, c.size), friendly_cells))
+ else:
+ dist_to_friend = float('inf')
+
+ if dist_to_friend < 20 or my_largest.mass < 36:
+ if self.do_approach_friends: print("not approaching friends")
+ self.do_approach_friends = False
+ elif dist_to_friend > 200 and my_largest.mass > 36 + 10*16:
+ if not self.do_approach_friends: print("approaching friends")
+ self.do_approach_friends = True
+
+ if friendly_cells and self.do_approach_friends:
+ friend_to_feed = max(friendly_cells, key=lambda c:c.mass)
+ if friend_to_feed.mass < 1.25 * my_largest.mass:
+ print("friend too small")
+ friend_to_feed = None
+ if friend_to_feed:
+ self.gui.hilight_cell(friend_to_feed, (255,255,255),(255,127,127),30)
+
+ self.target_cell = friend_to_feed
+ self.has_target = True
+
+ if self.do_approach_friends:
+ for c in self.c.player.own_cells:
+ self.gui.hilight_cell(c, (255,255,255), (255,127,127), 20)
+
+ # can this cell feed that cell?
+ # "False" means "No, definitely not"
+ # "True" means "Maybe"
+ def can_feed(this, that):
+ if that.is_food or that.is_ejected_mass or that.size < 43: # too small cells cannot eat the ejected mass
+ return False
+
+ relpos = this.pos-that.pos
+ dist = relpos.len()
+ if dist == 0 or dist >= 700 + this.size + that.size:
+ return False
+
+ return check_cell_in_interval(this.pos, that, (this.movement_angle - 10*math.pi/180, this.movement_angle + 10*math.pi/180))
+
+
+ success_rate = 0
+ for my_cell in self.c.player.own_cells:
+ try:
+ my_cell.movement_angle
+ except AttributeError:
+ print("cannot calculate shoot angle, too few backlog")
+ continue
+ # check if ejecting mass would feed a friend
+ possibly_feedable_cells = list(filter(lambda c : can_feed(my_cell, c), self.c.world.cells.values()))
+ possibly_feedable_cells.sort(key = lambda c : (my_cell.pos - c.pos).len())
+ good_intervals = []
+ for feedable in possibly_feedable_cells:
+ self.gui.hilight_cell(feedable, (255,192,127), (127,127,255))
+ if feedable not in friendly_cells:
+ break
- # enemy/virus avoidance
+ good_intervals += canonicalize_angle_interval( interval_occupied_by_cell(my_cell.pos, feedable) )
+
+ good_intervals = merge_intervals(good_intervals)
+ area = interval_area( intersection(good_intervals, canonicalize_angle_interval((my_cell.movement_angle - mechanics.eject_delta*math.pi/180, my_cell.movement_angle + mechanics.eject_delta*math.pi/180))) )
+ success_rate += area / (2*mechanics.eject_delta*math.pi/180) / len(list(self.c.player.own_cells))
+
+
+ self.gui.draw_bar(((100,40),(500,24)), success_rate, thresh=.80, color=(0,0,127))
+ if success_rate >= 0.80:
+ self.c.send_shoot()
+
+
+
+ # enemy/virus/friend-we-would-kill avoidance
forbidden_intervals = []
for cell in self.c.world.cells.values():
relpos = ((cell.pos[0]-self.c.player.center[0]),(cell.pos[1]-self.c.player.center[1]))
dist = math.sqrt(relpos[0]**2+relpos[1]**2)
- if (not cell.is_virus and dist < ((500+2*cell.size) if cell.mass > 1.25*my_smallest*2 else (300+cell.size)) and cell.mass > 1.25 * my_smallest) or (cell.is_virus and dist < my_largest and cell.mass < my_largest):
- angle = math.atan2(relpos[1],relpos[0])
- corridor_halfwidth = math.asin(cell.size / dist)
- forbidden_intervals += canonicalize_angle_interval((angle-corridor_halfwidth, angle+corridor_halfwidth))
- runaway = True
+ # find out the allowed minimum distance
+ allowed_dist = None
+
+ if cell.is_virus:
+ if cell.mass < my_largest.mass:
+ allowed_dist = cell.size+2
+ else:
+ allowed_dist = "don't care"
+ elif cell in friendly_cells:
+ if 1.25 * my_largest.mass > cell.mass: # we're dangerous to our friends
+ allowed_dist = my_largest.size + 40
+ elif (cell not in self.c.player.own_cells and not cell.is_virus and not cell.is_ejected_mass and not cell.is_food) and cell.mass + 20 > 1.25 * my_smallest.mass: # our enemy is, or will be dangerous to us
+ if (cell.mass + 20) / 2 < 1.25 * my_smallest.mass:
+ # they can't splitkill us (soon)
+ allowed_dist = cell.size + 75
+ elif cell.mass / 15. < self.c.player.total_mass:
+ # they can and they will splitkill us
+ allowed_dist = 650 + cell.size
+ else:
+ # we're too small, not worth a splitkill. they have absolutely no
+ # chance to chase us
+ allowed_dist = cell.size + 10
+ else:
+ allowed_dist = "don't care"
+
+ if allowed_dist != "don't care" and dist < allowed_dist:
+ try:
+ angle = math.atan2(relpos[1],relpos[0])
+ corridor_halfwidth = math.asin(min(1, cell.size / dist))
+ forbidden_intervals += canonicalize_angle_interval((angle-corridor_halfwidth, angle+corridor_halfwidth))
+ runaway = True
+ except:
+ print("TODO FIXME: need to handle enemy cell which is in our centerpoint!")
+ print("dist=%.2f, allowed_dist=%.2f" % (dist, allowed_dist))
# wall avoidance
if self.c.player.center[0] < self.c.world.top_left[1]+(self.c.player.total_size*2):
@@ -138,8 +253,13 @@ class Strategy:
forbidden_intervals = merge_intervals(forbidden_intervals)
allowed_intervals = invert_angle_intervals(forbidden_intervals)
+
+ try:
+ (a,b) = find_largest_angle_interval(allowed_intervals)
+ except:
+ print("TODO FIXME: need to handle no runaway direction being available!")
+ (a,b) = (0,0)
- (a,b) = find_largest_angle_interval(allowed_intervals)
runaway_angle = (a+b)/2
runaway_x, runaway_y = (self.c.player.center[0]+int(100*math.cos(runaway_angle))), (self.c.player.center[1]+int(100*math.sin(runaway_angle)))
@@ -148,27 +268,25 @@ class Strategy:
self.target_cell = None
self.color = (255,0,0)
- print ("Running away: " + str((runaway_x-self.c.player.center[0], runaway_y-self.c.player.center[1])))
# a bit of debugging information
for i in forbidden_intervals:
- gui.draw_arc(self.c.player.center, self.c.player.total_size+10, i, (255,0,255))
+ self.gui.draw_arc(self.c.player.center, self.c.player.total_size+10, i, (255,0,255))
- # if however there's no enemy to avoid, chase food or jizz randomly around
- else:
+ # if however there's no enemy to avoid, try to feed a friend. or chase food or jizz randomly around
+ else:
if self.target_cell != None:
self.target = tuple(self.target_cell.pos)
- if self.target_cell not in self.c.world.cells.values() or not self.edible(self.target_cell):
+ if self.target_cell not in self.c.world.cells.values() or (not self.edible(self.target_cell) and not self.target_cell in friendly_cells):
self.target_cell = None
self.has_target = False
- print("target_cell does not exist any more")
elif self.target == tuple(self.c.player.center):
self.has_target = False
print("Reached random destination")
if not self.has_target:
food = list(filter(self.edible, self.c.world.cells.values()))
- food = sorted(food, key = self.quality)
+ food = sorted(food, key = lambda c : self.quality(c, mechanics.speed(my_largest)))
if len(food) > 0:
self.target = (food[0].pos[0], food[0].pos[1])
@@ -176,8 +294,6 @@ class Strategy:
self.has_target = True
self.color = (0,0,255)
- print("weight: ", self.weight_cell(self.target_cell))
- print("Found food at: " + str(food[0].pos))
else:
rx = self.c.player.center[0] + random.randrange(-400, 401)
ry = self.c.player.center[1] + random.randrange(-400, 401)
@@ -188,6 +304,6 @@ class Strategy:
# more debugging
- gui.draw_line(self.c.player.center, self.target, self.color)
+ self.gui.draw_line(self.c.player.center, self.target, self.color)
return self.target
diff --git a/subscriber.py b/subscriber.py
index a637e67..b89a6a9 100644
--- a/subscriber.py
+++ b/subscriber.py
@@ -1,5 +1,7 @@
from log import log
+from collections import deque
import sys
+import mechanics
class DummySubscriber:
def on_connect_error(self,s):
@@ -65,3 +67,191 @@ class DummySubscriber:
def on_debug_line(self,x,y):
log("debug line")
+class CellHistory:
+ def __init__(self):
+ self.poslog = deque(maxlen=300)
+ self.stale = False
+
+class OtherPlayer:
+ def __init__(self, playerid):
+ self.playerid = playerid
+ self.cells = set()
+
+class EnhancingSubscriber(DummySubscriber):
+ def __init__(self):
+ self.c = None
+ self.history = {}
+ self.time = 0
+ self.victims = {}
+
+ def set_client(self,c):
+ self.c = c
+
+ def cleanup_victims(self):
+ delete = []
+
+ for eater in self.victims:
+ self.victims[eater] = list(filter(lambda v : v[1] < self.time - 100, self.victims[eater]))
+ if len(self.victims[eater]) == 0:
+ delete += [eater]
+
+ for eater in delete:
+ del self.victims[eater]
+
+ def on_cell_eaten(self, eater_id, eaten_id):
+ if eater_id in self.c.world.cells and self.c.world.cells[eater_id].is_virus:
+ print("virus ate something!")
+
+ if eater_id not in self.victims:
+ self.victims[eater_id] = []
+
+ self.victims[eater_id] += [(self.c.world.cells[eaten_id], self.time)]
+
+ def on_world_update_post(self):
+ self.c.world.time = self.time
+ self.time += 1
+
+ if self.time % 100 == 0:
+ self.cleanup_victims()
+
+ # create and purge poslog history, movement and movement_angle
+ for cid in self.history:
+ self.history[cid].stale = True
+
+ for cid in self.c.world.cells:
+ if cid not in self.history:
+ self.history[cid] = CellHistory()
+
+ self.history[cid].poslog.append(self.c.world.cells[cid].pos.copy())
+ self.c.world.cells[cid].poslog = self.history[cid].poslog
+
+ self.history[cid].stale = False
+
+ self.history = {k: v for k, v in self.history.items() if v.stale == False}
+
+
+ for cid in self.c.world.cells:
+ cell = self.c.world.cells[cid]
+
+ if not hasattr(cell, "spawntime"):
+ cell.spawntime = self.c.world.time
+
+ try:
+ oldpos = cell.poslog[-3-1]
+ cell.movement = (cell.pos - oldpos)/3
+ cell.movement_angle = cell.movement.angle()
+ except (AttributeError, IndexError):
+ pass
+
+
+ # create OtherPlayer entries
+ otherplayers = {}
+ for cell in self.c.world.cells.values():
+ playerid = None
+ if not cell.is_food and not cell.is_ejected_mass and not cell.is_virus:
+ playerid = (cell.name, cell.color)
+ elif cell.is_virus:
+ playerid = "virus"
+ elif cell.is_food:
+ playerid = "food"
+ elif cell.is_ejected_mass:
+ playerid = "ejected mass"
+ else:
+ playerid = "???"
+
+ if playerid not in otherplayers:
+ otherplayers[playerid] = OtherPlayer(playerid)
+
+ cell.player = otherplayers[playerid]
+ cell.player.cells.add(cell)
+
+ # detect split cells and clean up obsolete parent references
+ for cell in self.c.world.cells.values():
+ # create attribute if not already there
+ try:
+ cell.parent = cell.parent
+ except:
+ cell.parent = None
+ cell.calmed_down = True
+
+ # clean up obsolete parent references
+ if cell.parent and cell.parent.cid not in self.c.world.cells:
+ cell.parent = None
+
+ # find split cells
+ is_split = False
+ if not cell.is_food and not cell.is_ejected_mass and not cell.is_virus:
+ try:
+ if cell.parent == None and cell.movement.len() > 2 * mechanics.speed(cell.size):
+ print("looks like a split!"+str(cell.movement.len() / mechanics.speed(cell.size)))
+ is_split = True
+ except AttributeError:
+ pass
+
+ if is_split:
+ history_len = len(cell.poslog)
+ cell.parent = min(cell.player.cells, key=lambda c : (c.poslog[-history_len] - cell.poslog[-history_len]).len() if c != cell and len(c.poslog) >= history_len else float('inf'))
+ try:
+ cell.shoot_vec = cell.parent.movement.copy()
+ except:
+ cell.shoot_vec = None
+ cell.calmed_down = False
+
+ elif cell.is_virus:
+ try:
+ if cell.parent == None and cell.movement.len() > 0:
+ print("split virus!")
+ is_split = True
+ except AttributeError:
+ pass
+
+ if is_split:
+ cell.parent = min(cell.player.cells, key=lambda c : (c.pos - cell.poslog[0]).len() if c != cell else float('inf'))
+ try:
+ last_feed = self.victims[cell.parent.cid][-1][0]
+ if not last_feed.is_ejected_mass:
+ print("wtf, last virus feed was not ejected mass?!")
+ raise KeyError
+ else:
+ cell.shoot_vec = cell.parent.pos - last_feed.poslog[0]
+ cell.shoot_vec2 = last_feed.poslog[-1] - last_feed.poslog[0]
+ try:
+ pos_when_shot = last_feed.parent.poslog[-len(last_feed.poslog)]
+ cell.shoot_vec3 = cell.parent.pos - pos_when_shot
+ except:
+ print("MOAAAHH")
+ cell.shoot_vec3 = None
+ except KeyError:
+ print("wtf, no last virus feed?!")
+ cell.shoot_vec = None
+ cell.shoot_vec2 = None
+ cell.shoot_vec3 = None
+
+ cell.calmed_down = False
+
+ elif cell.is_ejected_mass:
+ try:
+ if cell.parent == None and cell.movement.len() > 0:
+ print("ejected mass!")
+ is_split = True
+ except AttributeError:
+ pass
+
+ if is_split:
+ history_len = len(cell.poslog)
+ try:
+ cell.parent = min(filter(lambda c : not c.is_ejected_mass and not c.is_food and not c.is_virus and c.color == cell.color, self.c.world.cells.values()), key=lambda c : (c.poslog[-history_len] - cell.poslog[-history_len]).len() if len(c.poslog) >= history_len else float('inf'))
+ try:
+ cell.shoot_vec = cell.parent.movement.copy()
+ except:
+ cell.shoot_vec = None
+ cell.calmed_down = False
+ except ValueError:
+ # if no possible parents are found, min will raise a ValueError. ignore that.
+ pass
+
+ if is_split:
+ cell.spawnpoint = cell.pos.copy()
+ cell.parentsize_when_spawned = cell.parent.size if cell.parent != None else None
+ cell.parentpos_when_spawned = cell.parent.pos.copy() if cell.parent != None else None
+