Exemplo n.º 1
0
class Simulator:
    world: World
    actors: List[Actor]

    def __init__(self):
        self.world = World(c.DIMENSIONS)
        self.actors = []

        for _ in range(c.FOOD_PLACES):
            self.world.sample().food = c.FOOD_MAX

        for i in c.HOME_RANGE:
            for j in c.HOME_RANGE:
                ant = Ant(Point(i, j), random.randint(0, 7))
                self.world[i, j].ant = ant
                self.world[i, j].home = True
                self.actors.append(Actor(self.world, ant))

    def iterate(self):
        for actor in self.actors:
            optimizer = Optimizer(actor.here, actor.nearby_places)

            if actor.foraging:
                action = optimizer.seek_food()
            else:
                action = optimizer.seek_home()

            if action == Action.DROP:
                actor.drop_food()
                actor.mark_food_trail()
                actor.turn(4)
            elif action == action.TAKE:
                actor.take_food()
                actor.mark_home_trail()
                actor.turn(4)
            elif action == Action.MOVE:
                actor.move()
            elif action == Action.LEFT:
                actor.turn(-1)
            elif action == Action.RIGHT:
                actor.turn(1)
            else:
                raise ValueError(f"{action} is not a valid action")
        self.evaporate()

    def evaporate(self):
        for cell, point in self.world.each():
            cell.home_pheromone *= c.EVAP_RATE
            if cell.home_pheromone <= 1e-2:
                cell.home_pheromone = 0.
            cell.food_pheromone *= c.EVAP_RATE
            if cell.food_pheromone <= 1e-2:
                cell.food_pheromone = 0.
Exemplo n.º 2
0
    def __init__(self):
        self.world = World(c.DIMENSIONS)
        self.actors = []

        for _ in range(c.FOOD_PLACES):
            self.world.sample().food = c.FOOD_MAX

        for i in c.HOME_RANGE:
            for j in c.HOME_RANGE:
                ant = Ant(Point(i, j), random.randint(0, 7))
                self.world[i, j].ant = ant
                self.world[i, j].home = True
                self.actors.append(Actor(self.world, ant))
Exemplo n.º 3
0
    def __init__(self,
                 rules_name,
                 player_names,
                 map_,
                 initial_zombies=0,
                 minimum_zombies=0,
                 docker_isolator=False,
                 debug=False,
                 isolator_port=8000,
                 use_basic_icons=False,
                 use_arduino=False,
                 arduino_device='/dev/ttyACM0',
                 arduino_bauds=9600):
        self.players = []

        self.rules_name = rules_name
        self.rules = get_creator('rules.' + rules_name)(self)
        self.map = map_
        self.minimum_zombies = minimum_zombies
        self.docker_isolator = docker_isolator
        self.isolator_port = isolator_port
        self.debug = debug
        self.use_basic_icons = use_basic_icons
        self.use_arduino = use_arduino
        self.arduino_device = arduino_device
        self.arduino_bauds = arduino_bauds

        self.world = World(self.map.size, debug=debug)

        for thing in self.map.things:
            self.world.spawn_thing(thing)

        if docker_isolator:
            from isolation.players_client import create_player_client
            self.players = [
                create_player_client(name, rules_name, self.map.objectives,
                                     self.isolator_port)
                for name in player_names
            ]
        else:
            self.players = [
                create_player(name, rules_name, self.map.objectives)
                for name in player_names
            ]

        self.spawn_players()
        self.spawn_zombies(initial_zombies)

        if self.use_arduino:
            self.initialize_arduino()
Exemplo n.º 4
0
    def __init__(self, rules_name, player_names, size, map_file=None,
                 player_spawns=None, zombie_spawns=None, objetives=None,
                 initial_zombies=0, minimum_zombies=0, docker_isolator=False,
                 debug=False, isolator_port=8000):
        self.players = []

        self.rules_name = rules_name
        self.rules = get_creator('rules.' + rules_name)(self)
        self.player_spawns = player_spawns
        self.zombie_spawns = zombie_spawns
        self.objetives = objetives
        self.minimum_zombies = minimum_zombies
        self.docker_isolator = docker_isolator
        self.isolator_port = isolator_port
        self.debug = debug

        self.world = World(size, debug=debug)

        if map_file is not None:
            self.import_map(map_file)

        if docker_isolator:
            from isolation.players_client import create_player_client
            self.players = [create_player_client(name, rules_name, objetives,
                                                 self.isolator_port)
                            for name in player_names]
        else:
            self.players = [create_player(name, rules_name, objetives)
                            for name in player_names]

        self.spawn_players()
        self.spawn_zombies(initial_zombies)
Exemplo n.º 5
0
 def make_world(self):
     world = World()
     world.num_UAVs = 2
     world.num_landmarks = 30  # 50
     world.UAVs = [UAV() for i in range(world.num_UAVs)]
     world.association = []
     world.probability_LoS = 1 / (1 + A)
     for i, uav in enumerate(world.UAVs):
         uav.name = 'UAV %d'.format(i)
         uav.id = i
         uav.size = 10
         uav.state.energy = Energy
     # # 列表形式的landmarks
     # world.landmarks = [Landmark() for i in range(world.num_landmarks)]
     # for i, landmark in enumerate(world.landmarks):
     #     landmark.name = 'landmark %d'.format(i)
     #     landmark.id = i
     #     landmark.size = 10
     # 字典形式的landmarks
     for i in range(world.num_landmarks):
         dic = {str(i): Landmark()}
         world.landmarks.update(dic)
     for i, landmark in enumerate(world.landmarks.values()):
         landmark.name = 'landmark %d'.format(i)
         landmark.id = i
         landmark.size = 5
     self.reset_world(
         world)  # 这里不reset会导致MultiUAVEnv中init中获得observation维度报错
     return world
Exemplo n.º 6
0
    def run_game(self):
        mainloop = True
        on_load  = True

        while mainloop:
            pygame.time.wait(Globals.WAIT_TIME)
            flags_n_key = Funcs.event_handler(pygame.event.get())

            if not flags_n_key[0] & Globals.K_NONE or on_load:

                if flags_n_key[0] & Globals.K_QUIT:
                    World.save_world(self.world)
                    pygame.quit()
                    sys.exit(0)

                if flags_n_key[0] & Globals.E_RESIZE:
                    self.screen = pygame.display.set_mode(flags_n_key[1], pygame.RESIZABLE)
                    self.render_map_x = self.screen.get_width()   // Globals.FONT_X // 2
                    self.render_map_y = self.screen.get_height()  // Globals.FONT_Y
                    self.screen.fill(Globals.BG_COLOR)

                if on_load:
                    self.objects.clear()
                    self.draw_queue.clear()
                    self.screen.fill(Globals.BG_COLOR)

                    if   self.link == 'main_menu':
                        #Main_menu.load_menu(self)
                        pass

                    if self.link == 'continue':
                        self.link = 'game'
                        self.world = World.load_world()
                        g.load_game(self)

                    elif self.link == 'game':
                        Main_menu.text_screen(self, 'generating...', (255, 255, 255))
                        pygame.display.flip()
                        self.world = World.World(Globals.FIELD_NUM_X, Globals.FIELD_NUM_Y)
                        g.load_game(self)
                    on_load = False

                self.link, on_load = self.scenes[self.link](self, flags_n_key)
                Graphics.draw_all(self)
                pygame.display.flip()
Exemplo n.º 7
0
 def make_world(self):
     world = World()
     # set any world properties first
     world.dim_c = 2
     num_good_agents = 1
     num_adversaries = 2
     num_agents = num_adversaries + num_good_agents
     # add agents
     world.agents = [Agent() for i in range(num_agents)]
     for i, agent in enumerate(world.agents):
         agent.name = 'agent %d' % i
         agent.survived = True
         # agent.game_time = 0
         agent.leader = True if i == 0 else False
         # agent.silent = True if i > 0 else False
         agent.silent = True
         agent.adversary = True if i < num_adversaries else False
         agent.accel = 1 if agent.adversary else 0.5
         agent.max_speed = 4 if agent.adversary else 2.5
     self.reset_world(world)
     return world
Exemplo n.º 8
0
    def __init__(self, rules_name, player_names, map_, initial_zombies=0,
                 minimum_zombies=0, docker_isolator=False, debug=False,
                 isolator_port=8000, use_basic_icons=False, use_arduino=False,
                 arduino_device='/dev/ttyACM0', arduino_bauds=9600):
        self.players = []

        self.rules_name = rules_name
        self.rules = get_creator('rules.' + rules_name)(self)
        self.map = map_
        self.minimum_zombies = minimum_zombies
        self.docker_isolator = docker_isolator
        self.isolator_port = isolator_port
        self.debug = debug
        self.use_basic_icons = use_basic_icons
        self.use_arduino = use_arduino
        self.arduino_device = arduino_device
        self.arduino_bauds = arduino_bauds

        self.world = World(self.map.size, debug=debug)

        for thing in self.map.things:
            self.world.spawn_thing(thing)

        if docker_isolator:
            from isolation.players_client import create_player_client
            self.players = [create_player_client(name, rules_name,
                                                 self.map.objetives,
                                                 self.isolator_port)
                            for name in player_names]
        else:
            self.players = [create_player(name, rules_name,
                                          self.map.objetives)
                            for name in player_names]

        self.spawn_players()
        self.spawn_zombies(initial_zombies)

        if self.use_arduino:
            self.arduino_serial = Serial(self.arduino_device,
                                         self.arduino_bauds)
Exemplo n.º 9
0
class Game(object):
    """An instance of game controls the flow of the game.

       This includes player and zombies spawning, game main loop, deciding when
       to stop, importing map data, drawing each update, etc.
    """
    def __init__(self,
                 rules_name,
                 player_names,
                 map_,
                 initial_zombies=0,
                 minimum_zombies=0,
                 docker_isolator=False,
                 debug=False,
                 isolator_port=8000,
                 use_basic_icons=False,
                 use_arduino=False,
                 arduino_device='/dev/ttyACM0',
                 arduino_bauds=9600):
        self.players = []

        self.rules_name = rules_name
        self.rules = get_creator('rules.' + rules_name)(self)
        self.map = map_
        self.minimum_zombies = minimum_zombies
        self.docker_isolator = docker_isolator
        self.isolator_port = isolator_port
        self.debug = debug
        self.use_basic_icons = use_basic_icons
        self.use_arduino = use_arduino
        self.arduino_device = arduino_device
        self.arduino_bauds = arduino_bauds

        self.world = World(self.map.size, debug=debug)

        for thing in self.map.things:
            self.world.spawn_thing(thing)

        if docker_isolator:
            from isolation.players_client import create_player_client
            self.players = [
                create_player_client(name, rules_name, self.map.objectives,
                                     self.isolator_port)
                for name in player_names
            ]
        else:
            self.players = [
                create_player(name, rules_name, self.map.objectives)
                for name in player_names
            ]

        self.spawn_players()
        self.spawn_zombies(initial_zombies)

        if self.use_arduino:
            self.initialize_arduino()

    def initialize_arduino(self):
        '''Initialize serial connection with arduino screen.'''
        from serial import Serial
        self.arduino_serial = Serial(self.arduino_device, self.arduino_bauds)

    def spawn_players(self):
        """Spawn players using the provided player create functions."""
        self.world.spawn_in_random(self.players, self.map.player_spawns)

    def spawn_zombies(self, count):
        """Spawn N zombies in the world."""
        zombies = [Zombie() for _ in range(count)]
        self.world.spawn_in_random(zombies,
                                   self.map.zombie_spawns,
                                   fail_if_cant=False)

    def position_draw(self, position):
        """Get the string to draw for a given position of the world."""
        # decorations first, then things over them
        thing = (self.world.things.get(position)
                 or self.world.decoration.get(position))

        if thing is not None:
            if self.use_basic_icons:
                icon = thing.icon_basic
            else:
                icon = thing.icon
            return colored(icon, thing.color)
        else:
            return u' '

    def play(self, frames_per_second=2.0):
        """Game main loop, ending in a game result with description."""
        while True:
            self.world.step()

            # maintain the flow of zombies if necessary
            zombies = [
                thing for thing in self.world.things.values()
                if isinstance(thing, Zombie)
            ]
            if len(zombies) < self.minimum_zombies:
                self.spawn_zombies(self.minimum_zombies - len(zombies))

            self.draw()

            if self.debug:
                if sys.version_info > (3, ):
                    input()
                else:
                    raw_input()
            else:
                time.sleep(1.0 / frames_per_second)

            if self.rules.game_ended():
                won, description = self.rules.game_won()

                if self.use_arduino:
                    if won:
                        self.arduino('g', True)  # "gwin!!"
                    else:
                        self.arduino('l', True)  # lose

                print('')
                if won:
                    print(colored(u'WIN! ', 'green'))
                else:
                    print(colored(u'GAME OVER ', 'red'))
                print(description)

                return won, description

    def arduino(self, data, add_end_chars=False):
        """Send an order to the arduino screen."""
        if add_end_chars:
            data = data + chr(1) * 2
        self.arduino_serial.write(data)

    def draw(self):
        """Draw the world."""
        screen = ''

        # print the world
        screen += '\n'.join(u''.join(
            self.position_draw((x, y)) for x in range(self.world.size[0]))
                            for y in range(self.world.size[1]))

        # game stats
        screen += '\nticks: %i deaths: %i' % (self.world.t, self.world.deaths)

        # print player stats
        players = sorted(self.players, key=lambda x: x.name)
        for player in players:
            try:
                weapon_name = player.weapon.name
            except:
                weapon_name = u'unarmed'

            if player.life > 0:
                # a small "health bar" with unicode chars, from 0 to 10 chars
                life_chars_count = int((10.0 / player.MAX_LIFE) * player.life)
                life_chars = life_chars_count * u'\u2588'
                no_life_chars = (10 - life_chars_count) * u'\u2591'
                life_bar = u'\u2665 %s%s' % (life_chars, no_life_chars)
            else:
                life_bar = u'\u2620 [dead]'

            player_stats = u'%s %s <%i %s %s>: %s' % (
                life_bar, player.name, player.life, str(
                    player.position), weapon_name, player.status or u'-')

            screen += '\n' + colored(player_stats, player.color)

        # print events (of last step) for debugging
        if self.debug:
            screen += u'\n'
            screen += u'\n'.join([
                colored(u'%s: %s' % (thing.name, event), thing.color)
                for t, thing, event in self.world.events if t == self.world.t
            ])
        os.system('clear')
        print(screen)

        # if using arduino screen, send data
        if self.use_arduino:
            for thing in self.world.things.values():
                if isinstance(thing, Player):
                    icon = 'p'
                else:
                    icon = thing.__class__.__name__[0].lower()

                self.arduino(icon + \
                                  chr(thing.position[0]) + \
                                  chr(thing.position[1]))
            self.arduino('r', True)
Exemplo n.º 10
0
class Game(object):
    """An instance of game controls the flow of the game.

       This includes player and zombies spawning, game main loop, deciding when
       to stop, importing map data, drawing each update, etc.
    """

    def __init__(
        self,
        rules_name,
        player_names,
        size,
        map_file=None,
        player_spawns=None,
        zombie_spawns=None,
        objetives=None,
        initial_zombies=0,
        minimum_zombies=0,
        docker_isolator=False,
        debug=False,
        isolator_port=8000,
    ):
        self.players = []

        self.rules_name = rules_name
        self.rules = get_creator("rules." + rules_name)(self)
        self.player_spawns = player_spawns
        self.zombie_spawns = zombie_spawns
        self.objetives = objetives
        self.minimum_zombies = minimum_zombies
        self.docker_isolator = docker_isolator
        self.isolator_port = isolator_port
        self.debug = debug

        self.world = World(size, debug=debug)

        if map_file is not None:
            self.import_map(map_file)

        if docker_isolator:
            from isolation.players_client import create_player_client

            self.players = [
                create_player_client(name, rules_name, objetives, self.isolator_port) for name in player_names
            ]
        else:
            self.players = [create_player(name, rules_name, objetives) for name in player_names]

        self.spawn_players()
        self.spawn_zombies(initial_zombies)

    def spawn_players(self):
        """Spawn players using the provided player create functinons."""
        self.world.spawn_in_random(self.players, self.player_spawns)

    def spawn_zombies(self, count):
        """Spawn N zombies in the world."""
        zombies = [Zombie() for i in range(count)]
        self.world.spawn_in_random(zombies, self.zombie_spawns, fail_if_cant=False)

    def position_draw(self, position):
        """Get the string to draw for a given position of the world."""
        # decorations first, then things over them
        thing = self.world.things.get(position)
        decoration = self.world.decoration.get(position)

        if thing is not None:
            return thing.draw()
        elif decoration is not None:
            return decoration.draw()
        else:
            return u" "

    def play(self, frames_per_second=2.0):
        """Game main loop, ending in a game result with description."""
        while True:
            self.world.step()

            # mantain the flow of zombies if necessary
            zombies = [thing for thing in self.world.things.values() if isinstance(thing, Zombie)]
            if len(zombies) < self.minimum_zombies:
                self.spawn_zombies(self.minimum_zombies - len(zombies))

            self.draw()

            if self.debug:
                if sys.version_info > (3,):
                    input()
                else:
                    raw_input()
            else:
                time.sleep(1.0 / frames_per_second)

            if self.rules.game_ended():
                return self.rules.game_won()

    def draw(self):
        """Draw the world."""
        os.system("clear")

        # print the world
        print(
            "\n".join(
                u"".join(self.position_draw((x, y)) for x in range(self.world.size[0]))
                for y in range(self.world.size[1])
            )
        )

        # print player stats
        players = sorted(self.players, key=lambda x: x.name)
        for player in players:
            try:
                weapon_name = player.weapon.name
            except:
                weapon_name = u"unarmed"

            if player.life > 0:
                # a small "health bar" with unicode chars, from 0 to 10 chars
                life_chars_count = int((10.0 / player.MAX_LIFE) * player.life)
                life = u"\u2665 %s%s %i" % (
                    life_chars_count * u"\u2588",
                    (10 - life_chars_count) * u"\u2591",
                    player.life,
                )
            else:
                life = u"\u2620 [dead]"

            player_stats = u"%s %s (%s): %s" % (life, player.name, weapon_name, player.status or u"-")

            print(colored(player_stats, player.color))

        # print events (of last step) for debugging
        if self.debug:
            print(
                u"\n".join(
                    [
                        colored(u"%s: %s" % (thing.name, event), thing.color)
                        for t, thing, event in self.world.events
                        if t == self.world.t
                    ]
                )
            )

    def import_map(self, file_path):
        """Import things from a utf-8 map file."""
        zombie_spawns = []
        player_spawns = []
        objetives = []

        max_row = 0
        max_col = 0

        # read the file
        encoding = "utf-8"
        if sys.version_info > (3,):
            with open(file_path, encoding=encoding) as map_file:
                lines = map_file.read().split("\n")
        else:
            with open(file_path) as map_file:
                lines = map_file.read().decode(encoding).split("\n")

        # for each char, create the corresponding object
        for row_index, line in enumerate(lines):
            max_row = row_index

            for col_index, char in enumerate(line):
                if char:
                    max_col = max(col_index, max_col)

                position = (col_index, row_index)
                if char in (Box.ICON, "b", "B"):
                    self.world.spawn_thing(Box(position))
                elif char in (Wall.ICON, "w", "W"):
                    self.world.spawn_thing(Wall(position))
                elif char.lower() == "p":
                    player_spawns.append(position)
                elif char.lower() == "z":
                    zombie_spawns.append(position)
                elif char.lower() == "o":
                    objetives.append(position)
                    self.world.spawn_thing(ObjetiveLocation(position), decoration=True)

        # if had any info, update spawns and objetives
        if player_spawns:
            self.player_spawns = player_spawns
        if zombie_spawns:
            self.zombie_spawns = zombie_spawns
        if objetives:
            self.objetives = objetives

        # be sure everything in the map gets into the world size
        if max_row > self.world.size[1] or max_col > self.world.size[0]:
            message = "This map is bigger than the choosen size. Needs at least a %ix%i size"
            raise Exception(message % (max_col + 1, max_row + 1))
Exemplo n.º 11
0
            pygame.draw.rect(surface, color,
                             [j * c.C2P, i * c.C2P, c.C2P, c.C2P])

    pygame.draw.rect(surface,
                     BLACK, [
                         c.HOME_START * c.C2P, c.HOME_START * c.C2P,
                         c.NANTS_SQRT * c.C2P, c.NANTS_SQRT * c.C2P
                     ],
                     width=2)

    for ant in world.ants:
        draw_ant(ant, surface)


world = World(c.DIMENSIONS)
# Define some colors
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
GREEN = (0, 255, 0)
RED = (255, 0, 0)

pygame.init()

# Set the width and height of the screen [width, height]
size = (c.RESOLUTION, c.RESOLUTION)
screen = pygame.display.set_mode(size)

pygame.display.set_caption("AInts")

# Loop until the user clicks the close button.
Exemplo n.º 12
0
from __future__ import annotations

import matplotlib.pyplot as plt
from matplotlib import animation

from core import Ant, World
import constants as c
from visualize import show_picture
from tqdm import trange, tqdm

ITERS = 10000

world = World(c.DIMENSIONS)

fig, ax = plt.subplots()
pbar = tqdm(total=ITERS)


# animation function.  This is called sequentially
def animate(i):
    ax.cla()
    img = show_picture(world)
    world.step()

    pbar.update(1)
    im = ax.imshow(img)
    return im,


anim = animation.FuncAnimation(fig,
                               animate,
Exemplo n.º 13
0
def main():
    DEBUG = '--debug' in sys.argv

    size = height, width = 480, 480
    world = World(height, width)
    screen = pygame.display.set_mode(size)
    screen.fill((0, 0, 0))

    home = Home(position=(0, 0))
    home2 = Home(position=(465, 465))

    ant = Ant(position=home.position, speed=3)
    ant2 = Ant(position=home2.position, speed=3)
    ant.setHome(home)
    ant2.setHome(home2)

    world.add_entity(ant)
    world.add_entity(ant2)
    world.add_entity(home)
    world.add_entity(home2)

    for i in range(100):
        plant = Plant(position=(random.randint(10, 450),
                                random.randint(10, 450)))
        world.add_entity(plant)

    running = True
    update = False

    while running:
        for event in pygame.event.get():
            if event.type == pygame.QUIT or pygame.key.get_pressed()[
                    pygame.K_ESCAPE]:
                running = False

            if DEBUG and event.type in (pygame.KEYDOWN, pygame.KEYUP):
                update = pygame.key.get_pressed()[pygame.K_SPACE]

        if not DEBUG or update:
            world.update()
            world.render(screen)
            sleep(1 / 25)
Exemplo n.º 14
0
class Game(object):
    '''An instance of game controls the flow of the game.

       This includes player and zombies spawning, game main loop, deciding when
       to stop, importing map data, drawing each update, etc.
    '''
    def __init__(self, rules_name, player_names, map_, initial_zombies=0,
                 minimum_zombies=0, docker_isolator=False, debug=False,
                 isolator_port=8000, use_basic_icons=False, use_arduino=False,
                 arduino_device='/dev/ttyACM0', arduino_bauds=9600):
        self.players = []

        self.rules_name = rules_name
        self.rules = get_creator('rules.' + rules_name)(self)
        self.map = map_
        self.minimum_zombies = minimum_zombies
        self.docker_isolator = docker_isolator
        self.isolator_port = isolator_port
        self.debug = debug
        self.use_basic_icons = use_basic_icons
        self.use_arduino = use_arduino
        self.arduino_device = arduino_device
        self.arduino_bauds = arduino_bauds

        self.world = World(self.map.size, debug=debug)

        for thing in self.map.things:
            self.world.spawn_thing(thing)

        if docker_isolator:
            from isolation.players_client import create_player_client
            self.players = [create_player_client(name, rules_name,
                                                 self.map.objetives,
                                                 self.isolator_port)
                            for name in player_names]
        else:
            self.players = [create_player(name, rules_name,
                                          self.map.objetives)
                            for name in player_names]

        self.spawn_players()
        self.spawn_zombies(initial_zombies)

        if self.use_arduino:
            self.arduino_serial = Serial(self.arduino_device,
                                         self.arduino_bauds)

    def spawn_players(self):
        '''Spawn players using the provided player create functinons.'''
        self.world.spawn_in_random(self.players, self.map.player_spawns)

    def spawn_zombies(self, count):
        '''Spawn N zombies in the world.'''
        zombies = [Zombie() for i in range(count)]
        self.world.spawn_in_random(zombies,
                                   self.map.zombie_spawns,
                                   fail_if_cant=False)

    def position_draw(self, position):
        '''Get the string to draw for a given position of the world.'''
        # decorations first, then things over them
        thing = self.world.things.get(position) or \
                self.world.decoration.get(position)

        if thing is not None:
            if self.use_basic_icons:
                icon = thing.icon_basic
            else:
                icon = thing.icon
            return colored(icon, thing.color)
        else:
            return u' '

    def play(self, frames_per_second=2.0):
        '''Game main loop, ending in a game result with description.'''
        while True:
            self.world.step()

            # mantain the flow of zombies if necessary
            zombies = [thing for thing in self.world.things.values()
                       if isinstance(thing, Zombie)]
            if len(zombies) < self.minimum_zombies:
                self.spawn_zombies(self.minimum_zombies - len(zombies))

            self.draw()

            if self.debug:
                if sys.version_info > (3,):
                    input()
                else:
                    raw_input()
            else:
                time.sleep(1.0 / frames_per_second)

            if self.rules.game_ended():
                won, description = self.rules.game_won()

                if self.use_arduino:
                    if won:
                        self.arduino('g', True)  # "gwin!!"
                    else:
                        self.arduino('l', True)  # lose

                print('')
                if won:
                    print(colored(u'WIN! ', 'green'))
                else:
                    print(colored(u'GAME OVER ', 'red'))
                print(description)

                return won, description

    def arduino(self, data, add_end_chars=False):
        '''Send an order to the arduino screen.'''
        if add_end_chars:
            data = data + chr(1) * 2
        self.arduino_serial.write(data)

    def draw(self):
        '''Draw the world.'''
        screen = ''

        # print the world
        screen += '\n'.join(u''.join(self.position_draw((x, y))
                                     for x in range(self.world.size[0]))
                            for y in range(self.world.size[1]))

        # game stats
        screen += '\nticks: %i deaths: %i' % (self.world.t, self.world.deaths)

        # print player stats
        players = sorted(self.players, key=lambda x: x.name)
        for player in players:
            try:
                weapon_name = player.weapon.name
            except:
                weapon_name = u'unarmed'

            if player.life > 0:
                # a small "health bar" with unicode chars, from 0 to 10 chars
                life_chars_count = int((10.0 / player.MAX_LIFE) * player.life)
                life_chars = life_chars_count * u'\u2588'
                no_life_chars = (10 - life_chars_count) * u'\u2591'
                life_bar = u'\u2665 %s%s' % (life_chars, no_life_chars)
            else:
                life_bar = u'\u2620 [dead]'

            player_stats = u'%s %s <%i %s %s>: %s' % (life_bar,
                                                      player.name,
                                                      player.life,
                                                      str(player.position),
                                                      weapon_name,
                                                      player.status or u'-')

            screen += '\n' + colored(player_stats, player.color)

        # print events (of last step) for debugging
        if self.debug:
            screen += u'\n'
            screen += u'\n'.join([colored(u'%s: %s' % (thing.name, event),
                                          thing.color)
                                  for t, thing, event in self.world.events
                                  if t == self.world.t])
        os.system('clear')
        print(screen)

        # if using arduino screen, send data
        if self.use_arduino:
            for thing in self.world.things.values():
                if isinstance(thing, Player):
                    icon = 'p'
                else:
                    icon = thing.__class__.__name__[0].lower()

                self.arduino(icon + \
                                  chr(thing.position[0]) + \
                                  chr(thing.position[1]))
            self.arduino('r', True)