"""
``pyagar.controller``
=====================
Some very simple bots.
"""
# pylint: disable=I0011,C0103
from collections import namedtuple
import asyncio
from pyagar.log import logger
from pyagar.messages import Status, PlayerCell, ScreenAndCamera
Movement = namedtuple('Movement', ['x', 'y'])
[docs]class Controller:
"""
All bots should inherit from this class.
"""
def __init__(self, client):
self.client = client
self.messages = asyncio.Queue()
self.cells = {}
self.player_id = None
self.alive = False
self.screen = None
[docs] def get_name(self):
"""Returns the name of this bot."""
if not hasattr(self, 'name'):
return self.__class__.__name__
else:
return getattr(self, 'name')
[docs] def get_movement(self):
"""The method that subclasses must implement."""
raise NotImplementedError()
@property
def predators(self):
"""Cells that can eat me."""
p = self.player
if p:
return [c for c in self.opponents if c.size > p.size * 1.1]
else:
return []
@property
def edible(self):
"""You can eat cells 10% smaller than you."""
p = self.player
if p:
return [c for c in self.opponents if c.size * 1.1 < p.size]
else:
return []
@property
def player(self):
"""Returns the player main cell. None if not exists."""
return self.cells.get(self.player_id)
@property
def viruses(self):
"""Returns a list of visible viruses."""
return [c for c in self.cells.values() if c.is_virus]
@property
def opponents(self):
"""Return list of other cells."""
return [c for c in self.cells.values()
if c.id != self.player_id and not c.is_virus]
@asyncio.coroutine
[docs] def do_move(self):
"""Make a movement."""
m = self.get_movement()
if m is not None:
yield from self.client.move(m.x, m.y)
@asyncio.coroutine
[docs] def run(self):
"""The main loop of the bot."""
logger.info("Running bot '%s'", self.get_name())
while True:
data = yield from self.messages.get()
if isinstance(data, Status):
for cell in data.cells:
self.cells[cell.id] = cell
for cell in data.dissapears:
if cell.id in self.cells:
del self.cells[cell.id]
for eats in data.eat:
if eats.eatee == self.player_id:
self.alive = False
if eats.eatee in self.cells:
del self.cells[eats.eatee]
elif isinstance(data, PlayerCell):
self.player_id = data.cell.id
self.alive = True
elif isinstance(data, ScreenAndCamera):
self.screen = data.screen
else:
pass
if not self.alive:
yield from self.client.spawn()
yield from self.do_move()
[docs]class Closer(Controller):
"""Go to the closer "non-virus" cell, no matter the type."""
def get_movement(self):
p = self.player
o = self.opponents
if p and o:
closer = min(o, key=lambda c: abs(c.x-p.x)+abs(c.y-p.y))
return Movement(closer.x, closer.y)
else:
return None
[docs]class Greedy(Controller):
"""Only wants to eat."""
def get_movement(self):
o = self.edible
p = self.player
if o and p:
closer = min(o, key=lambda c: abs(c.x-p.x)+abs(c.y-p.y))
return Movement(closer.x, closer.y)
else:
return None
[docs]class Escape(Controller):
"""Escape from bigger opponents."""
@staticmethod
[docs] def escape_vector(player, cell):
"""Movement to escape from a cell."""
ox = (cell.x - player.x)
oy = (cell.y - player.y)
return Movement(player.x - ox,
player.y - oy)
[docs] def compound_escape_vector(self, vectors, cells):
"""Returns the sum of all ``vectors``."""
p = self.player
xs = [c.x - p.x for c in vectors]
ys = [c.y - p.y for c in vectors]
vs = [(x, y, c.size) for x, y, c in zip(xs, ys, cells)]
x = sum(s / 2 * x for x, _, s in vs if x != 0) * 300
y = sum(s / 2 * y for _, y, s in vs if y != 0) * 300
return Movement(x=p.x + x,
y=p.y + y)
def get_movement(self):
p = self.player
o = self.predators
if p and o:
vectors = [self.escape_vector(p, c) for c in o]
return self.compound_escape_vector(vectors, o)
elif p:
return Movement(x=p.x, y=p.y)
[docs]class Center(Controller):
"""Go to the center."""
def get_movement(self):
if self.player and self.screen:
return Movement(x=self.screen.x2 / 2, y=self.screen.y2 / 2)
[docs]class EatWhenNoPredators(Escape, Greedy, Center, Controller):
"""Only eats when all visible cells are smaller then itself."""
escape = Escape.get_movement
eat = Greedy.get_movement
nothing_to_do = Center.get_movement
def get_movement(self):
p = self.player
if p:
if not self.predators:
if self.edible:
return self.eat()
else:
return self.nothing_to_do()
else:
return self.escape()
else:
return None