Ejemplo n.º 1
0
class ClientCertificatePolicyForHTTPS(PClass):
    """
    ``ClientCertificatePolicyForHTTPS`` selects the correct client certificate
    and trust roots to use for interacting with the Kubernetes API server.

    :ivar credentials: All available client certificates.
    :ivar trust_roots: All available certificate authority certificates.
    """
    credentials = pmap_field(
        NetLocation,
        TLSCredentials,
    )

    trust_roots = pmap_field(
        NetLocation,
        pem.Certificate,
    )

    def creatorForNetloc(self, hostname, port):
        """
        Pick from amongst client certs and ca certs to create a proper TLS context
        factory.

        :see: ``twisted.web.iweb.IPolicyForHTTPS``
        """
        hostname = hostname.decode("ascii")

        netloc = NetLocation(host=hostname, port=port)
        client_cert, extra_cert_chain = pick_cert_for_twisted(
            netloc,
            self.credentials,
        )
        trust_root = pick_trust_for_twisted(netloc, self.trust_roots)

        return ssl.optionsForClientTLS(
            # It is not necessarily the case that the certificate presented
            # will use this name but it is common to encounter self-signed
            # certificates which do use this name.  There doesn't seem to be
            # anything in the configuration file which would tell us what the
            # proper name is.  We'll probably need to make this configurable
            # at some point, I guess.
            u"kubernetes",
            clientCertificate=client_cert,
            trustRoot=trust_root,
            extraCertificateOptions=dict(
                # optionsForClientTLS gets mad if extraCertChain is () and
                # client_cert is None even though `()` clearly represents no
                # extra certificate chain.  It wants them to both be None.
                # Make it so.
                extraCertChain=tuple(cert.original
                                     for cert in extra_cert_chain) or None, ),
        )
Ejemplo n.º 2
0
 class Record(PRecord):
     value = pmap_field(
         int, int,
         invariant=(
             lambda pmap: (len(pmap) == 1, "Exactly one item required.")
         )
     )
Ejemplo n.º 3
0
 def pclass_field_for_type(self, required):
     # XXX ignores the range's pyrsistent_invariant
     return pmap_field(
         key_type=unicode,
         value_type=self.value_type.python_types,
         optional=not required,
     )
Ejemplo n.º 4
0
class GameState(PClass):
    location_name = field(str)
    world = pmap_field(str, Location)
    inventory = pvector_field(Thing)

    @property
    def location(self):
        return self.world[self.location_name]
Ejemplo n.º 5
0
class _ParsedRoutePath(PRecord):
    """
    Parsed / derived components for matching a router path.
    """
    parts = pvector_field(unicode)
    params = pvector_field(unicode)
    constraints = pmap_field(unicode, unicode)
    priority = field(initial=0, type=int)
Ejemplo n.º 6
0
class WrittenMessage(PClass):
    """
    A L{Message} that has been logged.

    @ivar _logged_dict: The originally logged dictionary.
    """

    _logged_dict = pmap_field((str, unicode), object)

    @property
    def timestamp(self):
        """
        The Unix timestamp of when the message was logged.
        """
        return self._logged_dict[TIMESTAMP_FIELD]

    @property
    def task_uuid(self):
        """
        The UUID of the task in which the message was logged.
        """
        return self._logged_dict[TASK_UUID_FIELD]

    @property
    def task_level(self):
        """
        The L{TaskLevel} of this message appears within the task.
        """
        return TaskLevel(level=self._logged_dict[TASK_LEVEL_FIELD])

    @property
    def contents(self):
        """
        A C{PMap}, the message contents without Eliot metadata.
        """
        return (
            self._logged_dict.discard(TIMESTAMP_FIELD)
            .discard(TASK_UUID_FIELD)
            .discard(TASK_LEVEL_FIELD)
        )

    @classmethod
    def from_dict(cls, logged_dictionary):
        """
        Reconstruct a L{WrittenMessage} from a logged dictionary.

        @param logged_dictionary: A C{PMap} representing a parsed log entry.
        @return: A L{WrittenMessage} for that dictionary.
        """
        return cls(_logged_dict=logged_dictionary)

    def as_dict(self):
        """
        Return the dictionary that was used to write this message.

        @return: A C{dict}, as might be logged by Eliot.
        """
        return self._logged_dict
Ejemplo n.º 7
0
class Parser(PClass):
    """
    Parse serialized Eliot messages into L{Task} instances.

    @ivar _tasks: Map from UUID to corresponding L{Task}.
    """

    _tasks = pmap_field(unicode, Task)

    def add(self, message_dict):
        """
        Update the L{Parser} with a dictionary containing a serialized Eliot
        message.

        @param message_dict: Dictionary of serialized Eliot message.

        @return: Tuple of (list of completed L{Task} instances, updated
            L{Parser}).
        """
        uuid = message_dict[TASK_UUID_FIELD]
        if uuid in self._tasks:
            task = self._tasks[uuid]
        else:
            task = Task()
        task = task.add(message_dict)
        if task.is_complete():
            parser = self.transform(["_tasks", uuid], discard)
            return [task], parser
        else:
            parser = self.transform(["_tasks", uuid], task)
            return [], parser

    def incomplete_tasks(self):
        """
        @return: List of L{Task} that are not yet complete.
        """
        return list(self._tasks.values())

    @classmethod
    def parse_stream(cls, iterable):
        """
        Parse a stream of messages into a stream of L{Task} instances.

        :param iterable: An iterable of serialized Eliot message dictionaries.

        :return: An iterable of parsed L{Task} instances. Remaining
            incomplete L{Task} will be returned when the input stream is
            exhausted.
        """
        parser = Parser()
        for message_dict in iterable:
            completed, parser = parser.add(message_dict)
            for task in completed:
                yield task
        for task in parser.incomplete_tasks():
            yield task
Ejemplo n.º 8
0
class ParserData(PClass):
    """
    A record to store sample input and output which can be used to test the
    PackerOutputParser.

    ;ivar FilePath input: A file containing sample ``packer build
         -machine-readable`` output which will be fed into the parser.
    :ivar pmap output: The expected dictionary of the regional AMI values after
        parsing ``input``.
    """
    input = field(type=FilePath, mandatory=True)
    output = pmap_field(key_type=unicode, value_type=unicode, optional=False)
Ejemplo n.º 9
0
class Dataset(PClass):
    """
    A dataset in the configuration.

    :attr UUID primary: The node where the dataset should manifest.
    :attr int|None maximum_size: Size of the dataset in bytes or ``None``
        if no particular size was requested.
    :attr UUID dataset_id: The UUID of the dataset.
    :attr metadata: A mapping between unicode keys and values.
    """
    dataset_id = field(type=UUID, mandatory=True)
    primary = field(type=UUID, mandatory=True)
    maximum_size = field(type=(int, NoneType), mandatory=True)
    metadata = pmap_field(unicode, unicode)
Ejemplo n.º 10
0
class Dataset(PClass):
    """
    A dataset in the configuration.

    :attr UUID primary: The node where the dataset should manifest.
    :attr int maximum_size: Size of new dataset, in bytes.
    :attr UUID dataset_id: The UUID of the dataset.
    :attr metadata: A mapping between unicode keys and values.
    :attr bool deleted: If true indicates this dataset should be deleted.
    """
    dataset_id = field(type=UUID, mandatory=True)
    primary = field(type=UUID, mandatory=True)
    maximum_size = field(type=int, mandatory=True)
    deleted = field(type=bool, mandatory=True, initial=False)
    metadata = pmap_field(unicode, unicode)
Ejemplo n.º 11
0
class DatasetsConfiguration(PClass):
    """
    Currently configured datasets.

    :ivar tag: The current version of the configuration, suitable for
        passing as conditional to operations like creation.
    :ivar datasets: The current ``Dataset`` on the server; maps ``UUID``
        to ``Dataset``.
    """
    tag = field(mandatory=True)
    datasets = pmap_field(UUID, Dataset)

    def __iter__(self):
        """
        :return: Iterator over ``Dataset`` instances.
        """
        return self.datasets.itervalues()
Ejemplo n.º 12
0
class PackerConfigure(PClass):
    """
    The attributes necessary to create a custom packer configuration file from
    the prototype files in ``admin/installer/packer``.

    :ivar build_region: The AWS region to build images in.
    :ivar publish_regions: The AWS regions to publish the build images to.
    :ivar template: The prototype configuration to use as a base. One of
        `docker` or `flocker`.
    :ivar configuration_directory: The directory containing prototype
        configuration templates.
    :ivar source_ami_map: The AMI map containing base images.
    """
    build_region = field(type=RegionConstant, mandatory=True)
    publish_regions = pvector_field(item_type=RegionConstant)
    template = field(type=unicode, mandatory=True)
    configuration_directory = field(type=FilePath, initial=PACKER_TEMPLATE_DIR)
    source_ami_map = pmap_field(key_type=RegionConstant, value_type=unicode)
Ejemplo n.º 13
0
class _Mismatch(PClass):
    """
    Immutable Mismatch that also stores the mismatched object.

    :ivar mismatched: The object that failed to match.
    """

    # XXX: No direct tests.

    # XXX: jml thinks the base testtools mismatch should be extended to
    # include the mismatched object.

    mismatched = field(object)
    _description = field((str, unicode))
    _details = pmap_field((str, unicode), Content)

    def describe(self):
        return self._description

    def get_details(self):
        return self._details
Ejemplo n.º 14
0
 class Record(PRecord):
     value = pmap_field((Something, Another), int)
     value2 = pmap_field(str, (int, float))
Ejemplo n.º 15
0
 class Record(PRecord):
     value = pmap_field(Something, Another)
     value2 = pmap_field(int, float)
Ejemplo n.º 16
0
 class Record(PRecord):
     value = pmap_field(int, int, optional=True)
Ejemplo n.º 17
0
 class Record(PRecord):
     value = pmap_field(int, int)
Ejemplo n.º 18
0
 class Record(PRecord):
     value = pmap_field(("record_test.Something", "record_test.Another"),
                        int)
     value2 = pmap_field(str,
                         ("record_test.Something", "record_test.Another"))
Ejemplo n.º 19
0
class SoccerState(GameState):
    ################################################################
    ## PUBLIC METHODS
    ################################################################

    # You may use these methods when implementing the MinimaxAgent.

    # For information on these methods, see _game.GameState

    @property
    def num_players(self):
        return len(self.players)

    @property
    def current_player(self):
        return self.current_player_id

    @property
    def is_terminal(self):
        return self.winner

    @property
    def actions(self):
        player = self.current_player_obj
        actions = []
        if player.has_ball:
            actions += [Action.KICK]
        # actions += [Action.CHANGE_STANCE]
        dx = 1 if player.team == Team.RED else -1
        actions += [
            Action.move(1, 0),
            Action.move(-1, 0),
            Action.move(0, 1),
            Action.move(0, -1)
        ]

        if player.x <= 1:
            dx = 1
        elif player.x >= self.pitch.width:
            dx = -1
        else:
            dx = 1 if player.team == Team.RED else -1

        actions += [Action.move(dx, 1), Action.move(dx, -1)]
        return actions

    def reward(self, player_id):
        if not self.is_terminal:
            return None
        return 10 if self.winner == self.players[player_id].team else -10

    def act(self, action):
        player = self.current_player_obj
        state = self

        state = state._action_is_valid(action)
        if not state:
            return None

        if action == Action.KICK:
            state = self._update_kick()
        elif action == Action.CHANGE_STANCE:
            state = state.transform(self._cpk('stance'),
                                    (player.stance + 1) % 2)
        elif isinstance(action, tuple) and action[0] == Action.MOVE:
            (_, dx, dy) = action
            state = self._update_move_to(player.x + dx, player.y + dy)
        else:
            return None

        if state:
            state = state.set(current_player_id=(self.current_player + 1) %
                              self.num_players)

        return state

    ################################################################
    ## INTERNAL
    ################################################################

    # These are 'internal' methods and variables to the SoccerState
    # class. You should not use any of these methods in the
    # MinimaxAgent implementation, but can use them in your soccer
    # evaluation function.

    ## Variables
    # Each of these fields can be directly accessed by calling
    # `state.<variable_name>`.

    # The ID of the current player.
    current_player_id = field(type=int)

    # A persistent list of players. Can be accessed like a normal
    # Python list: players[0] is the first player, etc.
    #
    # Each player object players[i] is a PMap (equivalent to a Python
    # dict) with the following structure:
    #
    # players[i] = {
    #   type="player",  # The type of object
    #   index=i,        # The index of the player in the players list
    #   agent=agent,    # The agent controlling the player
    #   team={Team.RED|Team.BLUE} # The player's team
    #   x=x_pos, y=y_pos, # The current (x,y) position of the player
    #   has_ball={True,False} # True if the player currently has the ball
    # }
    players = pvector_field(PMap)

    # Contains the players of each team. Keys are teams['red'] and
    # teams['blue']. Probably should switch to using the team enum in
    # the future.
    teams = pmap_field(str, PVector)

    # Information about the ball. Has the following information:
    #
    # ball = {
    #   type='ball',          # The type of object
    #   on_field={True,False} # True if nobody has the ball.
    #   x=x_pos, y=y_pos      # The (x,y) position of the ball
    # }
    #
    # Note that the position of the ball is updated even when a player
    # has the ball.
    ball = pmap_field(str, (bool, str, int))

    # The term 'field' was ambiguous, so this is just some
    # information about the soccer field:
    #
    # pitch = {
    #   width=pitch_width       # The width of the soccer field
    #   height=pitch_height     # The height of the soccer field
    #   goal_height=goal_height # The height of the goal (along the y-axis)
    # }
    pitch = pmap_field(str, int)

    # Indicates the winner of the round. When the game hasn't
    # finished, is None; when the game is complete, is the Team that
    # won the game.
    winner = field(type=(Team, type(None)))

    @property
    def objects(self):
        """Returns the list of 'objects' (players + the ball)"""
        return list(self.players) + [self.ball]

    def dist_to_goal(self, pos, team):
        """Returns the distance between an (x, y) position and a team's goal."""
        goal_pos = (self.pitch.width+0.5, self.pitch.height/2) if team == Team.RED \
                   else (0.5, self.pitch.height/2)
        return math.sqrt(
            sum([(p1 + 0.51 - p2)**2 for p1, p2 in zip(pos, goal_pos)]))

    def at(self, x, y):
        """Returns the object at position (x,y). If no object is there, return
        None.

        """
        for obj in self.objects:
            ## invariant: no two objects occupy the same space
            if obj.x == x and obj.y == y:
                return obj
        return None

    @property
    def current_player_obj(self):
        """Returns the info for the current player in a PMap object."""
        return self.players[self.current_player]

    @property
    def player_with_ball(self):
        return next((p for p in self.players if p.has_ball), [None])

    def goal_pos(self, team):
        """Returns the position of the `teams`'s goal."""
        return self.red_goal_pos if team == Team.RED \
            else self.blue_goal_pos

    @property
    def red_goal_pos(self):
        """The position of the red team's goal."""
        return (self.pitch.width + 1, int(self.pitch.height / 2) + 1)

    @property
    def blue_goal_pos(self):
        """The position of the blue team's goal."""
        return (0, int(self.pitch.height / 2) + 1)

    @property
    def goal_top(self):
        """Returns the y-position of the top of the goal."""
        return int(self.pitch.height + self.pitch.goal_height) / 2

    @property
    def goal_bottom(self):
        """Returns the y-position of the bottom of the goal."""
        return int(self.pitch.height - self.pitch.goal_height) / 2 + 1

    @property
    def ball_in_red_goal(self):
        """Returns true if the ball is currently in the Red goal."""
        return self.ball.x > self.pitch.width and\
            self.goal_bottom <= self.ball.y and self.ball.y <= self.goal_top

    @property
    def ball_in_blue_goal(self):
        """Returns true if the ball is currently in the Blue goal."""
        return self.ball.x < 1 and\
            self.goal_bottom <= self.ball.y and self.ball.y <= self.goal_top

    def player_in_red_penalty_area(self, player_id):
        """Returns true if the player_id is in the Red penalty area."""
        player = self.players[player_id]
        return player.x >= self.pitch.width-3 and\
            self.goal_bottom-1 <= player.y and player.y <= self.goal_top+1

    def player_in_blue_penalty_area(self, player_id):
        """Returns true if the player_id is in the Blue penalty area."""
        player = self.players[player_id]
        return player.x <= 3 and\
            self.goal_bottom-1 <= player.y and player.y <= self.goal_top+1

    def _cpk(self, *keys):
        """Internal method, returns a tuple for the current player that is
        used for updating the state.

        """
        return ('players', self.current_player, *keys)

    def _update_move_to(self, x, y):
        """State update: Current player moves to pos (x,y)."""
        player = self.current_player_obj
        state = self
        if player.has_ball:
            if y < 1 or y > state.pitch.height:  ## sidelines
                state = state._update_reset(prefer_side=player.team.inverse)
            elif x < 1 or x > self.pitch.width:
                if self.goal_bottom <= y and y <= self.goal_top:
                    if x < 1:
                        state = state.set(winner=Team.BLUE)
                    else:
                        state = state.set(winner=Team.RED)
                else:
                    if x < 1 and player.team == Team.RED \
                       or x > self.pitch.width and player.team == Team.BLUE:
                        state = state._update_corner_kick()
                    else:
                        state = state._update_reset(
                            prefer_side=player.team.inverse)
            else:
                ## deal with collision
                (state, do_move) = state._update_check_collide(x, y)
                if do_move:
                    state = state.transform(self._cpk('x'), x, self._cpk('y'),
                                            y, ('ball', 'x'), x, ('ball', 'y'),
                                            y)
                    state = state._update_check_goal()
        else:
            if x < 1 or x > self.pitch.width:
                return None
            elif y < 1 or y > self.pitch.height:
                return None
            else:
                ## deal with collision
                (state, do_move) = state._update_check_collide(x, y)
                if do_move:
                    state = state.transform(self._cpk('x'), x, self._cpk('y'),
                                            y)
        return state

    def _update_corner_kick(self):
        state = self
        p2 = self.player_with_ball

        if not p2:
            return state

        goal_pos = self.goal_pos(p2.team.inverse)
        p1 = self.players[self.teams[p2.team.inverse.name][0]]
        goal_pos_x = goal_pos[0]
        dx = -1 if goal_pos_x > 1 else 1

        state = state.transform(
            ('players', p1.index, 'x'), goal_pos_x + dx,
            ('players', p1.index, 'y'), 1, ('players', p1.index, 'has_ball'),
            True, ('players', p2.index, 'x'), goal_pos_x + 3 * dx,
            ('players', p2.index, 'y'), 3, ('players', p2.index, 'has_ball'),
            False)

        return state

    def is_goal(self, dist, angle):
        return 20 * abs(angle)**2 / (dist**2) > 3e-1

    def can_shoot_from(self, x, y, team):
        (x, y) = (x + 0.5, y - 0.5)
        goal_x = self.pitch.width + 2 if team == Team.RED else 0
        goal_y1 = int(self.pitch.height - self.pitch.goal_height) / 2
        goal_y2 = int(self.pitch.height + self.pitch.goal_height) / 2
        dx = goal_x - x
        dy1 = (goal_y1 - y)
        dy2 = (goal_y2 - y)
        norm1 = math.sqrt(dx**2 + dy1**2)
        norm2 = math.sqrt(dx**2 + dy2**2)
        dy = ((goal_y1 + goal_y2) / 2 - y)

        dist = math.sqrt(dx**2 + dy**2)
        angle = math.acos((dx**2 + dy1 * dy2) / (norm1 * norm2))

        return self.is_goal(dist, angle)

    def check_kick(self, player):
        (x, y) = (player.x + 0.5, player.y - 0.5)
        goal_x = self.pitch.width + 2 if player.team == Team.RED else 0
        goal_y1 = int(self.pitch.height - self.pitch.goal_height) / 2
        goal_y2 = int(self.pitch.height + self.pitch.goal_height) / 2
        dx = goal_x - x
        dy1 = (goal_y1 - y)
        dy2 = (goal_y2 - y)
        norm1 = math.sqrt(dx**2 + dy1**2)
        norm2 = math.sqrt(dx**2 + dy2**2)
        dy = ((goal_y1 + goal_y2) / 2 - y)

        f_y1 = lambda obj_x: y + dy1 * obj_x
        f_y2 = lambda obj_x: y + dy2 * obj_x

        dist = math.sqrt(dx**2 + dy**2)
        angle = math.acos((dx**2 + dy1 * dy2) / (norm1 * norm2))

        # Check for interceptions
        intercept = (None, float("inf"))
        for obj in self.players:
            if obj.index == player.index: continue
            (obj_x, obj_y) = (obj.x + 0.5, obj.y + 0.5)
            obj_x = (obj_x - x) / dx
            if obj_y >= f_y1(obj_x) and obj_y <= f_y2(obj_x):
                new_i = (obj, math.sqrt((x - obj_x)**2 + (y - obj_y)**2))
                intercept = min([intercept, new_i], key=lambda x: x[1])

        return (dist, angle, self.is_goal(dist, angle), intercept[0])

    def _update_kick(self):
        """State update: Current player kicks towards opponent goal."""
        player = self.current_player_obj
        state = self

        (_, _, is_goal, intercept_player) = self.check_kick(player)

        if intercept_player:
            state = state._update_switch_possession(player.index,
                                                    intercept_player.index)
        elif is_goal:
            goal_pos = self.goal_pos(player.team)
            state = state.transform(('ball', 'x'), goal_pos[0], ('ball', 'y'),
                                    goal_pos[1], ('ball', 'on_field'), True,
                                    self._cpk('has_ball'), False)
            state = state._update_check_goal()
        else:
            state = state._update_reset(prefer_side=player.team.inverse)
        return state

    def _update_switch_possession(self, player_a, player_b):
        """State update: Possession of ball switches between player_a and
        player_b

        """
        state = self
        if self.players[player_a].has_ball:
            p1 = self.players[player_a]
            p2 = self.players[player_b]
        else:
            p1 = self.players[player_b]
            p2 = self.players[player_a]
        goal_pos = self.goal_pos(p1.team.inverse)
        state = state.transform(('players', p1.index, 'has_ball'), False,
                                ('players', p2.index, 'has_ball'), True,
                                ('ball', 'x'), p2.x, ('ball', 'y'), p2.y)
        state = state._update_place_between(p1.index, p2.x, p2.y, goal_pos[0],
                                            goal_pos[1])
        return state

    def _update_place_between(self, player_id, x1, y1, x2, y2):
        """State update: player_id is placed in between position (x1, y1) and
        (x2, y2)

        """
        x = int((x1 + x2) / 2)
        y = int((y1 + y2) / 2)
        state = self
        if state.at(x, y):
            if not state.at(x + 1, y) and x + 1 <= self.pitch.width:
                x += 1
            elif not state.at(x - 1, y) and x - 1 >= 1:
                x -= 1
            elif not state.at(x, y + 1) and y + 1 <= self.pitch.height:
                y += 1
            elif not state.at(x, y - 1) and y - 1 >= 1:
                y -= 1
        state = state.transform(('players', player_id, 'x'), x,
                                ('players', player_id, 'y'), y)
        return state

    def _update_reset(self, prefer_side=None, random_pos=False):
        """State update: Players and ball are reset to original position.

        :param Team prefer_side: If set to a Team, that team will
            receive the ball when the game is reset.
        :param bool random_pos: If True, players will be randomly
            placed on their side of the field.
        """
        state = self
        mid_x = int(self.pitch.width / 2) + 1
        mid_y = int(self.pitch.height / 2) + 1
        ball_offset = 0 if not prefer_side \
                      else (-1 if prefer_side == 'red' else 1)
        if not prefer_side:
            state = state.transform(('ball', 'x'), mid_x + ball_offset,
                                    ('ball', 'y'), mid_y, ('ball', 'on_field'),
                                    True)
        else:
            state = state.transform(('ball', 'on_field'), False)
        for team_name in ('red', 'blue'):
            team = self.teams[team_name]
            i = 0
            for dx in range(4):
                done = False
                for dy in range(5):
                    if not random_pos:
                        x = mid_x + (-dx - 5 if team_name == 'red' else dx + 5)
                        y = mid_y + (2 * int(dy / 2) * (-1 if dy % 2 else 1))
                        state = state\
                                .transform(('players', team[i], 'x'), x)\
                                .transform(('players', team[i], 'y'), y)\
                                .transform(('players', team[i], 'has_ball'), False)
                        if i == 0 and prefer_side == self.players[
                                team[i]].team:
                            state = state.transform(
                                ('players', team[i], 'has_ball'), True,
                                ('ball', 'x'), x, ('ball', 'y'), y)
                        i += 1
                    else:
                        x_range = range(1,mid_x) if team_name == 'red' \
                                  else range(mid_x+1,self.pitch.width)
                        y_range = range(1, self.pitch.height)
                        state = state\
                                .transform(('players', team[i], 'x'), random.choice(x_range))\
                                .transform(('players', team[i], 'y'), random.choice(y_range))
                    if i >= len(team):
                        done = True
                        break
                if done:
                    break
        return state

    def _update_check_collide(self, x, y):
        """State update: Check if there will be a collision between the
        current player and whatever is at position (x,y).

        """
        state = self
        do_move = True
        obj = state.at(x, y)
        if not obj:
            do_move = True
        elif obj.type == 'ball':  ## If we collide with the ball...
            ## Pick up the ball
            state = state.transform(('ball', 'x'), x, ('ball', 'y'), y,
                                    ('ball', 'on_field'), False,
                                    self._cpk('has_ball'), True)
            do_move = True
        elif obj.type == 'player':
            # state = state.transform(
            #     ('ball', 'x'), x, ('ball', 'y'), y,
            #     self._cpk('has_ball'), False,
            #     ('players', obj.index, 'has_ball'), True
            # )
            if self.current_player_obj.has_ball or obj.has_ball:
                state = state._update_switch_possession(
                    self.current_player, obj.index)
            do_move = False
        else:
            state = None
            do_move = False
        return (state, do_move)

    def _update_check_goal(self):
        """State update: Check if the ball is in a goal, and, if so, update
        the winner of the game.

        """
        state = self
        if self.ball_in_red_goal:
            state = state.set(winner=Team.RED)
        elif self.ball_in_blue_goal:
            state = state.set(winner=Team.BLUE)
        return state

    def draw(self):
        """Internal method, draws the current game configuration."""
        BLOCK_SIZE = B = 32
        PITCH_COLOR = (0, 200, 0)
        PLAYER_RED_COLOR = (255, 0, 0)
        PLAYER_BLUE_COLOR = (0, 0, 255)
        BALL_COLOR = (255, 255, 255)
        surf = pygame.Surface(
            ((self.pitch.width + 2) * B, (self.pitch.height + 2) * B))

        (W, H) = (surf.get_width(), surf.get_height())
        (W_p, H_p) = (self.pitch.width, self.pitch.height)
        H_g = self.pitch.goal_height
        surf.fill(PITCH_COLOR)

        # Draw grid
        for i in range(self.pitch.width + 2):
            for j in range(self.pitch.height + 2):
                s1 = surf.subsurface(
                    (i * B, (self.pitch.height - j + 1) * B, B, B))
                if 1 <= i and i <= self.pitch.width and\
                   1 <= j and j <= self.pitch.height:
                    if self.can_shoot_from(i, j,
                                           Team.RED) or self.can_shoot_from(
                                               i, j, Team.BLUE):
                        s1.fill((100, 255, 100), (1, 1, B - 2, B - 2))
                    else:
                        s1.fill((0, 255, 0), (1, 1, B - 2, B - 2))

        # Draw field lines
        pygame.draw.lines(surf, (255, 255, 255), True, [(B, B), (W - B, B),
                                                        (W - B, H - B),
                                                        (B, H - B)], 3)
        pygame.draw.line(surf, (255, 255, 255), (int(W / 2), B),
                         (int(W / 2), H - B), 3)
        pygame.draw.line(surf, (255, 255, 255), (int(W / 2), B),
                         (int(W / 2), H - B), 3)
        pygame.draw.line(surf, (255, 255, 255), (int(W / 2), B),
                         (int(W / 2), H - B), 3)
        pygame.draw.circle(surf, (255, 255, 255), (int(W / 2), int(H / 2)),
                           int(B * 2.5), 3)
        pygame.draw.lines(surf, (255, 255, 255), False,
                          [(B, B * (int((H_p + H_g) / 2) + 2)),
                           (4 * B, B * (int((H_p + H_g) / 2) + 2)),
                           (4 * B, B * (int((H_p - H_g) / 2))),
                           (B, B * (int((H_p - H_g) / 2)))], 3)
        pygame.draw.lines(surf, (255, 255, 255), False,
                          [(W - B, B * (int((H_p + H_g) / 2) + 2)),
                           (W - 4 * B, B * (int((H_p + H_g) / 2) + 2)),
                           (W - 4 * B, B * (int((H_p - H_g) / 2))),
                           (W - B, B * (int((H_p - H_g) / 2)))], 3)
        # pygame.draw.circle(surf, (255, 255, 255), (W-B, int(H/2)), B*3, 3)
        # surf.fill(PITCH_COLOR, (0, 0, B-1, H))
        # surf.fill(PITCH_COLOR, (W-B+2, 0, B, H))
        font = pygame.font.SysFont("monospace", 18)
        font.set_bold(True)

        for i in range(self.pitch.width + 2):
            for j in range(self.pitch.height + 2):
                obj = self.at(i, j)
                s1 = surf.subsurface(
                    (i * B, (self.pitch.height - j + 1) * B, B, B))
                if obj and obj.type == 'ball' and obj.on_field:
                    s1.fill(BALL_COLOR, (6, 6, B - 12, B - 12))
                elif obj and obj.type == 'player':
                    c = PLAYER_RED_COLOR if obj.team == Team.RED else PLAYER_BLUE_COLOR
                    if obj.index == self.current_player:
                        s1.fill((250, 250, 0))
                    s1.fill(c, (2, 2, B - 4, B - 4))
                    if obj.has_ball:
                        pygame.draw.rect(s1, BALL_COLOR,
                                         (6, 6, B - 12, B - 12))
                    # label = font.render("R" if obj.stance == 0 else "B", 1, (0, 255, 0))
                    # s1.blit(label, (B/4, B/4))

                if (i < 1 or i > self.pitch.width) and \
                     (self.goal_bottom <= j and j <= self.goal_top):
                    for l in range(4, BLOCK_SIZE, 8):
                        pygame.draw.line(s1, (255, 255, 255), (0, l), (B, l))
                        pygame.draw.line(s1, (255, 255, 255), (l, 0), (l, B))

        GOAL_TOP = BLOCK_SIZE * (1 + int(
            (self.pitch.height + self.pitch.goal_height) / 2))
        GOAL_BOTTOM = BLOCK_SIZE * (1 + int(
            (self.pitch.height - self.pitch.goal_height) / 2))
        pygame.draw.lines(surf, (0, 0, 0), False, [(0, GOAL_TOP),
                                                   (B, GOAL_TOP),
                                                   (B, GOAL_BOTTOM),
                                                   (0, GOAL_BOTTOM)], 3)
        pygame.draw.lines(surf, (0, 0, 0), False, [(W, GOAL_TOP),
                                                   (W - B, GOAL_TOP),
                                                   (W - B, GOAL_BOTTOM),
                                                   (W, GOAL_BOTTOM)], 3)
        pygame.draw.rect(surf, PLAYER_RED_COLOR, (0, 0, 6, surf.get_height()))
        pygame.draw.rect(surf, PLAYER_BLUE_COLOR,
                         (surf.get_width() - 7, 0, 6, surf.get_height()))

        if self.is_terminal:
            winner_rect = ((1 + self.pitch.width / 2 - 3) * B,
                           (self.pitch.height / 2 + 2) * B, 6 * B, 2 * B)
            border_rect = (winner_rect[0] - 4, winner_rect[1] - 4,
                           winner_rect[2] + 8, winner_rect[3] + 8)
            label = None
            if self.winner == Team.RED:
                label = font.render("Team  RED wins!", 1, (255, 255, 255))
                pygame.draw.rect(surf, (200, 100, 100), border_rect)
                pygame.draw.rect(surf, (255, 0, 0), winner_rect)
            else:
                label = font.render("Team BLUE wins!", 1, (255, 255, 255))
                pygame.draw.rect(surf, (100, 100, 200), border_rect)
                pygame.draw.rect(surf, (0, 0, 255), winner_rect)
            surf.blit(label,
                      (winner_rect[0] + 7, winner_rect[1] + int(B / 2) + 4))

        return surf

    def __eq__(self, other):
        return hash(self) == hash(other)

    def __hash__(self):
        key = [self.current_player, (self.ball.x, self.ball.y)] \
              + [(p.x, p.y, p.stance, p.has_ball) for p in self.players]
        return hash(tuple(key))
Ejemplo n.º 20
0
class TaggedUnionInvariant(PClass):
    """
    An invariant that ensure the given object has an allowd tag attribute, and
    that all the other specified attributes are present if and only if the
    object has the appropriate tag. The tags must be
    :py:class:`NamedConstant`s.

    .. note:: Attributes that aren't specified by any tag are ignored.

    :param str tag_attribute: The attribute that contains the tag.
    :param dict attributes_for_tag: Dictionary mapping tags to the
        set of attributes allowed by that tag.
    """

    tag_attribute = field(str, mandatory=True)
    attributes_for_tag = pmap_field(
        key_type=NamedConstant,
        value_type=_AttributeSet,
        optional=True,
    )

    @property
    def _allowed_tags(self):
        """
        The set of all allowed tags.
        """
        return pset(self.attributes_for_tag.keys())

    @property
    def _all_attributes(self):
        """
        The set of all attributes controlled by the invariant.
        """
        return pset({
            attribute
            for attributes in self.attributes_for_tag.values()
            for attribute in attributes
        })

    def __call__(self, value):
        """
        Check that the invariant holds for the given value.

        :param value: Value to check invariant for.

        :returns: Pair of whether the invariant holds, and a message describing
            why it doesn't.
        :rtype: `tuple` of `bool` and `str`
        """
        tag = getattr(value, self.tag_attribute)
        if tag not in self._allowed_tags:
            return (False, "can only be in {tag_name}s {tags}.".format(
                tag_name=self.tag_attribute,
                tags=', '.join(map("`{0.name}`".format, self._allowed_tags)),
            ))
        for attribute in self.attributes_for_tag[tag]:
            if not hasattr(value, attribute):
                return (
                    False,
                    "`{attr}` must be specified in {tag_name} `{tag}`".format(
                        attr=attribute,
                        tag_name=self.tag_attribute,
                        tag=tag.name))
        for attribute in self._all_attributes - self.attributes_for_tag[tag]:
            if hasattr(value, attribute):
                return (
                    False,
                    "`{attr}` can't be specified in {tag_name} `{tag}`".format(
                        attr=attribute,
                        tag_name=self.tag_attribute,
                        tag=tag.name))
        return (True, "")
Ejemplo n.º 21
0
 class MyClass1(PClass):
     f = pmap_field(key_type=Foo, value_type=int)
Ejemplo n.º 22
0
class TypedContainerObj(PClass):
    map = pmap_field(str, str)
    set = pset_field(str)
    vec = pvector_field(str)
Ejemplo n.º 23
0
class WrittenAction(PClass):
    """
    An Action that has been logged.

    This class is intended to provide a definition within Eliot of what an
    action actually is, and a means of constructing actions that are known to
    be valid.

    @ivar WrittenMessage start_message: A start message whose task UUID and
        level match this action, or C{None} if it is not yet set on the
        action.
    @ivar WrittenMessage end_message: An end message hose task UUID and
        level match this action. Can be C{None} if the action is
        unfinished.
    @ivar TaskLevel task_level: The action's task level, e.g. if start
        message has level C{[2, 3, 1]} it will be
        C{TaskLevel(level=[2, 3])}.
    @ivar UUID task_uuid: The UUID of the task to which this action belongs.
    @ivar _children: A L{pmap} from L{TaskLevel} to the L{WrittenAction} and
        L{WrittenMessage} objects that make up this action.
    """

    start_message = field(type=optional(WrittenMessage),
                          mandatory=True,
                          initial=None)
    end_message = field(type=optional(WrittenMessage),
                        mandatory=True,
                        initial=None)
    task_level = field(type=TaskLevel, mandatory=True)
    task_uuid = field(type=unicode, mandatory=True, factory=unicode)
    # Pyrsistent doesn't support pmap_field with recursive types.
    _children = pmap_field(TaskLevel, object)

    @classmethod
    def from_messages(cls,
                      start_message=None,
                      children=pvector(),
                      end_message=None):
        """
        Create a C{WrittenAction} from C{WrittenMessage}s and other
        C{WrittenAction}s.

        @param WrittenMessage start_message: A message that has
            C{ACTION_STATUS_FIELD}, C{ACTION_TYPE_FIELD}, and a C{task_level}
            that ends in C{1}, or C{None} if unavailable.
        @param children: An iterable of C{WrittenMessage} and C{WrittenAction}
        @param WrittenMessage end_message: A message that has the same
            C{action_type} as this action.

        @raise WrongTask: If C{end_message} has a C{task_uuid} that differs
            from C{start_message.task_uuid}.
        @raise WrongTaskLevel: If any child message or C{end_message} has a
            C{task_level} that means it is not a direct child.
        @raise WrongActionType: If C{end_message} has an C{ACTION_TYPE_FIELD}
            that differs from the C{ACTION_TYPE_FIELD} of C{start_message}.
        @raise InvalidStatus: If C{end_message} doesn't have an
            C{action_status}, or has one that is not C{SUCCEEDED_STATUS} or
            C{FAILED_STATUS}.
        @raise InvalidStartMessage: If C{start_message} does not have a
            C{ACTION_STATUS_FIELD} of C{STARTED_STATUS}, or if it has a
            C{task_level} indicating that it is not the first message of an
            action.

        @return: A new C{WrittenAction}.
        """
        actual_message = [
            message
            for message in [start_message, end_message] + list(children)
            if message
        ][0]
        action = cls(
            task_level=actual_message.task_level.parent(),
            task_uuid=actual_message.task_uuid,
        )
        if start_message:
            action = action._start(start_message)
        for child in children:
            if action._children.get(child.task_level, child) != child:
                raise DuplicateChild(action, child)
            action = action._add_child(child)
        if end_message:
            action = action._end(end_message)
        return action

    @property
    def action_type(self):
        """
        The type of this action, e.g. C{"yourapp:subsystem:dosomething"}.
        """
        if self.start_message:
            return self.start_message.contents[ACTION_TYPE_FIELD]
        elif self.end_message:
            return self.end_message.contents[ACTION_TYPE_FIELD]
        else:
            return None

    @property
    def status(self):
        """
        One of C{STARTED_STATUS}, C{SUCCEEDED_STATUS}, C{FAILED_STATUS} or
        C{None}.
        """
        message = self.end_message if self.end_message else self.start_message
        if message:
            return message.contents[ACTION_STATUS_FIELD]
        else:
            return None

    @property
    def start_time(self):
        """
        The Unix timestamp of when the action started, or C{None} if there has
        been no start message added so far.
        """
        if self.start_message:
            return self.start_message.timestamp

    @property
    def end_time(self):
        """
        The Unix timestamp of when the action ended, or C{None} if there has been
        no end message.
        """
        if self.end_message:
            return self.end_message.timestamp

    @property
    def exception(self):
        """
        If the action failed, the name of the exception that was raised to cause
        it to fail. If the action succeeded, or hasn't finished yet, then
        C{None}.
        """
        if self.end_message:
            return self.end_message.contents.get(EXCEPTION_FIELD, None)

    @property
    def reason(self):
        """
        The reason the action failed. If the action succeeded, or hasn't finished
        yet, then C{None}.
        """
        if self.end_message:
            return self.end_message.contents.get(REASON_FIELD, None)

    @property
    def children(self):
        """
        The list of child messages and actions sorted by task level, excluding the
        start and end messages.
        """
        return pvector(
            sorted(self._children.values(), key=lambda m: m.task_level))

    def _validate_message(self, message):
        """
        Is C{message} a valid direct child of this action?

        @param message: Either a C{WrittenAction} or a C{WrittenMessage}.

        @raise WrongTask: If C{message} has a C{task_uuid} that differs from the
            action's C{task_uuid}.
        @raise WrongTaskLevel: If C{message} has a C{task_level} that means
            it's not a direct child.
        """
        if message.task_uuid != self.task_uuid:
            raise WrongTask(self, message)
        if not message.task_level.parent() == self.task_level:
            raise WrongTaskLevel(self, message)

    def _add_child(self, message):
        """
        Return a new action with C{message} added as a child.

        Assumes C{message} is not an end message.

        @param message: Either a C{WrittenAction} or a C{WrittenMessage}.

        @raise WrongTask: If C{message} has a C{task_uuid} that differs from the
            action's C{task_uuid}.
        @raise WrongTaskLevel: If C{message} has a C{task_level} that means
            it's not a direct child.

        @return: A new C{WrittenAction}.
        """
        self._validate_message(message)
        level = message.task_level
        return self.transform(("_children", level), message)

    def _start(self, start_message):
        """
        Start this action given its start message.

        @param WrittenMessage start_message: A start message that has the
            same level as this action.

        @raise InvalidStartMessage: If C{start_message} does not have a
            C{ACTION_STATUS_FIELD} of C{STARTED_STATUS}, or if it has a
            C{task_level} indicating that it is not the first message of an
            action.
        """
        if start_message.contents.get(ACTION_STATUS_FIELD,
                                      None) != STARTED_STATUS:
            raise InvalidStartMessage.wrong_status(start_message)
        if start_message.task_level.level[-1] != 1:
            raise InvalidStartMessage.wrong_task_level(start_message)
        return self.set(start_message=start_message)

    def _end(self, end_message):
        """
        End this action with C{end_message}.

        Assumes that the action has not already been ended.

        @param WrittenMessage end_message: An end message that has the
            same level as this action.

        @raise WrongTask: If C{end_message} has a C{task_uuid} that differs
            from the action's C{task_uuid}.
        @raise WrongTaskLevel: If C{end_message} has a C{task_level} that means
            it's not a direct child.
        @raise InvalidStatus: If C{end_message} doesn't have an
            C{action_status}, or has one that is not C{SUCCEEDED_STATUS} or
            C{FAILED_STATUS}.

        @return: A new, completed C{WrittenAction}.
        """
        action_type = end_message.contents.get(ACTION_TYPE_FIELD, None)
        if self.action_type not in (None, action_type):
            raise WrongActionType(self, end_message)
        self._validate_message(end_message)
        status = end_message.contents.get(ACTION_STATUS_FIELD, None)
        if status not in (FAILED_STATUS, SUCCEEDED_STATUS):
            raise InvalidStatus(self, end_message)
        return self.set(end_message=end_message)
Ejemplo n.º 24
0
 class Bar(PRecord):
     bar = pmap_field(str, Foo)
Ejemplo n.º 25
0
class Task(PClass):
    """
    A tree of actions with the same task UUID.
    """
    _nodes = pmap_field(TaskLevel, (WrittenAction, WrittenMessage))
    _completed = pset_field(TaskLevel)
    _root_level = TaskLevel(level=[])

    def root(self):
        """
        @return: The root L{WrittenAction}.
        """
        return self._nodes[self._root_level]

    def is_complete(self):
        """
        @return bool: True only if all messages in the task tree have been
        added to it.
        """
        return self._root_level in self._completed

    def _insert_action(self, node):
        """
        Add a L{WrittenAction} to the tree.

        Parent actions will be created as necessary.

        @param child: A L{WrittenAction} to add to the tree.

        @return: Updated L{Task}.
        """
        task = self
        if (
            node.end_message and node.start_message and
            (len(node.children) == node.end_message.task_level.level[-1] - 2)):
            # Possibly this action is complete, make sure all sub-actions
            # are complete:
            completed = True
            for child in node.children:
                if (
                    isinstance(child, WrittenAction)
                    and child.task_level not in self._completed):
                    completed = False
                    break
            if completed:
                task = task.transform(["_completed"],
                                      lambda s: s.add(node.task_level))
        task = task.transform(["_nodes", node.task_level], node)
        return task._ensure_node_parents(node)

    def _ensure_node_parents(self, child):
        """
        Ensure the node (WrittenAction/WrittenMessage) is referenced by parent
        nodes.

        Parent actions will be created as necessary.

        @param child: A L{WrittenMessage} or L{WrittenAction} which is
            being added to the tree.

        @return: Updated L{Task}.
        """
        task_level = child.task_level
        if task_level.parent() is None:
            return self

        parent = self._nodes.get(task_level.parent())
        if parent is None:
            parent = WrittenAction(
                task_level=task_level.parent(), task_uuid=child.task_uuid)
        parent = parent._add_child(child)
        return self._insert_action(parent)

    def add(self, message_dict):
        """
        Update the L{Task} with a dictionary containing a serialized Eliot
        message.

        @param message_dict: Dictionary whose task UUID matches this one.

        @return: Updated L{Task}.
        """
        is_action = message_dict.get(ACTION_TYPE_FIELD) is not None
        written_message = WrittenMessage.from_dict(message_dict)
        if is_action:
            action_level = written_message.task_level.parent()
            action = self._nodes.get(action_level)
            if action is None:
                action = WrittenAction(
                    task_level=action_level,
                    task_uuid=message_dict[TASK_UUID_FIELD])
            if message_dict[ACTION_STATUS_FIELD] == STARTED_STATUS:
                # Either newly created MissingAction, or one created by
                # previously added descendant of the action.
                action = action._start(written_message)
            else:
                action = action._end(written_message)
            return self._insert_action(action)
        else:
            # Special case where there is no action:
            if written_message.task_level.level == [1]:
                return self.transform([
                    "_nodes", self._root_level], written_message, [
                        "_completed"], lambda s: s.add(self._root_level))
            else:
                return self._ensure_node_parents(written_message)
Ejemplo n.º 26
0
class RecordContainingContainers(PRecord):
    map = pmap_field(str, str)
    vec = pvector_field(str)
    set = pset_field(str)
Ejemplo n.º 27
0
 class Record(PRecord):
     value = pmap_field((int, str), type(None))
Ejemplo n.º 28
0
 class Record(PRecord):
     value = pmap_field(int, (str, type(None)))
Ejemplo n.º 29
0
 class MyClass2(PClass):
     f = pmap_field(key_type=(Foo, ), value_type=int)
Ejemplo n.º 30
0
 class Record(PRecord):
     value = pmap_field("record_test.Something", "record_test.Another")