Esempio n. 1
0
  def _unpack_(obj):
    """
    The inverse of `_pack_`; creates a Decision from a simple object.

    Note that the choice, option, and outcomes of this decision are new,
    disentangled objects, so this it isn't terribly memory efficient to pack
    and unpack Decision objects, and true linkage shouldn't be assumed.
    """
    return Decision(
      unpack(obj["choice"], choice.Choice),
      unpack(obj["option"], choice.Option),
      [ unpack(o, choice.Outcome) for o in obj["outcomes"] ],
      Decision.unpack_decision_model(obj["prospective_impressions"]),
      [
        Decision.unpack_decision_model(dm)
          for dm in obj["factored_decision_models"]
      ] if obj["factored_decision_models"] else None,
      {
        gn: unpack(obj["goal_relevance"][gn], Salience)
          for gn in obj["goal_relevance"]
      } if obj["goal_relevance"] else None,
      {
        gn: [
          unpack(o, perception.Retrospective)
            for o in obj["retrospective_impressions"][gn]
        ]
          for gn in obj["retrospective_impressions"]
      } if obj["retrospective_impressions"] else None
    )
Esempio n. 2
0
 def _unpack_(obj):
     """
 Delegates unpacking to the relevant subclass.
 """
     action = obj.split(' ')[0]
     if action == "set":
         return unpack(obj, SetValue)
     elif action == "add":
         return unpack(obj, IncrementValue)
     elif action == "invert":
         return unpack(obj, InvertValue)
Esempio n. 3
0
 def _unpack_(obj):
     """
 The inverse of `_pack_`; constructs an instance from a simple object (e.g.,
 one produced by json.loads).
 """
     return Outcome(
       obj["name"],
       obj["effects"],
       unpack(obj["salience"], Salience),
       unpack(obj["apparent_likelihood"], Certainty),
       unpack(obj["actual_likelihood"], Certainty) \
         if "actual_likelihood" in obj else None
     )
Esempio n. 4
0
File: test.py Progetto: solsword/pdm
  def test_packable():
    test_stuff = cls._pack_.__doc__.split("```")

    tinst = eval(utils.dedent(test_stuff[1]))
    tobj = eval(utils.dedent(test_stuff[2]))

    uinst = unpack(tobj, cls)
    pobj = pack(tinst)
    urec = unpack(pobj, cls)
    prec = pack(uinst)

    assert tinst == uinst, (
      (
        "Unpacked object doesn't match eval'd version:\n```\n{}\n```\n{}\n```"
        "\nDifferences:\n  {}"
      ).format(str(tinst), str(uinst), "\n  ".join(diff(tinst, uinst)))
    )

    assert pobj == tobj, (
      (
        "Packed object doesn't match given:\n```\n{}\n```\n{}\n```"
        "\nDifferences:\n  {}"
      ).format(
        str(tobj),
        str(pobj),
        "\n  ".join(diff(tobj, pobj))
      )
    )
    assert tinst == urec, (
      (
        "Pack/unpacked object doesn't match:\n```\n{}\n```\n{}\n```"
        "\nDifferences:\n  {}"
      ).format(
        str(tinst),
        str(urec),
        "\n  ".join(diff(tinst, urec))
      )
    )
    assert tobj == prec, (
      (
        "Unpack/packed object doesn't match:\n```\n{}\n```\n{}\n```"
        "\nDifferences:\n  {}"
      ).format(
        str(tobj),
        str(prec),
        "\n  ".join(diff(tobj, prec))
      )
    )

    return True
Esempio n. 5
0
 def _unpack_(obj):
   """
   The inverse of `_pack_`; creates a Percept from a simple object. This
   method handles unpacking for subtypes Prospective and Retrospective as
   well.
   """
   if obj["type"] == "Percept":
     return Percept(
       obj["goal"],
       obj["choice"],
       obj["option"],
       obj["outcome"],
       unpack(obj["valence"], Valence),
       unpack(obj["salience"], Salience),
     )
   elif obj["type"] == "Prospective":
     return Prospective(
       obj["goal"],
       obj["choice"],
       obj["option"],
       obj["outcome"],
       unpack(obj["valence"], Valence),
       unpack(obj["salience"], Salience),
       certainty=unpack(obj["certainty"], Certainty)
     )
   elif obj["type"] == "Retrospective":
     return Retrospective(
       obj["goal"],
       obj["choice"],
       obj["option"],
       obj["outcome"],
       unpack(obj["valence"], Valence),
       unpack(obj["salience"], Salience),
       prospective=unpack(obj["prospective"], Prospective)
     )
Esempio n. 6
0
 def _unpack_(obj):
     """
 Inverse of `_pack_`; creates an instance from a simple object.
 """
     return ModeOfEngagement(obj["name"],
                             [unpack(g, PlayerGoal) for g in obj["goals"]],
                             obj["priorities"])
Esempio n. 7
0
 def _unpack_(obj):
     """
 The inverse of `_pack_`; takes a simple object and returns a Choice
 instance.
 """
     opts = obj["options"]
     return Choice(obj["name"], [unpack(opts[k], Option) for k in opts])
Esempio n. 8
0
 def _unpack_(obj):
     """
 The inverse of `_pack_`; takes a simple object (e.g., from json.loads) and
 returns an Option instance.
 """
     return Option(obj["name"],
                   [unpack(o, Outcome) for o in obj["outcomes"]])
Esempio n. 9
0
 def _unpack_(obj):
     """
 Creates a StoryNode from a simple object (see packable.py). Note that the
 unpacked story node will not have a module finder.
 """
     return Story(
         obj["title"],
         obj["author"],
         obj["start"],
         {k: unpack(obj["nodes"][k], StoryNode)
          for k in obj["nodes"]},
         modules=obj["modules"] if "modules" in obj else None,
         setup=obj["setup"] if "setup" in obj else None)
Esempio n. 10
0
 def _unpack_(obj):
     """
 The inverse of `_pack_`; constructs an instance from a simple object (e.g.,
 one produced by json.loads).
 """
     return PlayerModel(
       obj["name"],
       unpack(obj["decision_method"], decision.DecisionMethod),
       {
         key: unpack(val, engagement.ModeOfEngagement)
           for (key, val) in obj["modes"].items()
       },
       unpack(obj["priority_method"], engagement.PriorityMethod) \
         if "priority_method" in obj else engagement.PriorityMethod.softpriority,
       mode_ranking=obj["mode_ranking"] if "mode_ranking" in obj else None,
       mode_adjustments=obj["mode_adjustments"] \
         if "mode_adjustments" in obj else None,
       goal_adjustments=obj["goal_adjustments"] \
         if "goal_adjustments" in obj else None,
       goal_overrides = obj["goal_overrides"] \
         if "goal_overrides" in obj else None
     )
Esempio n. 11
0
    def recall_story(self, title, author=None, is_module=False):
        """
    Looks up a story (or module) in the database and returns it, or None if no
    such story exists. If multiple stories match (when author is given as
    None), a list will be returned.
    """
        cached = self.find_cached(title, author, is_module)
        if cached:
            return cached

        table = "modules" if is_module else "stories"
        cur = self.connection.cursor()
        if author is None:
            cur.execute(
                "SELECT package FROM {} WHERE title = ?;".format(table),
                (title.title(), ))
        else:
            cur.execute(
                "SELECT package FROM {} WHERE title = ? AND author = ?;".
                format(table), (title.title(), author.title()))
        rows = cur.fetchall()
        if len(rows) < 1:
            return None

        if len(rows) > 1:  # Don't cache anything in this case
            result = [unpack(json.loads(r["package"]), Story) for r in rows]
            result.set_module_finder(self.find_module)
        else:
            result = unpack(json.loads(rows[0]["package"]), Story)
            result.set_module_finder(self.find_module)

            if is_module:
                self.cache_module(result)
            else:
                self.cache_story(result)

            return result
Esempio n. 12
0
 def unpack_decision_model(dm):
   """
   Helper method for _unpack_ that unpacks a decision model (a mapping from
   option names to mappings from goal names to lists of Prospective
   impressions).
   """
   return {
     optname: {
       goalname: [
         unpack(pri, perception.Prospective)
           for pri in dm[optname][goalname]
       ]
         for goalname in dm[optname]
     }
       for optname in dm
   } if dm else None
Esempio n. 13
0
def load_story_from_file(filename, fmt="auto"):
    """
  Loads a Story object from a filename. Prints a warning and does nothing if
  the file cannot be loaded. The fmt argument decides which format to load,
  "json" for JSON format, and "story" for Markdown-like format, or "auto" to
  choose based on the start of the file (just checks whether the first two
  non-whitespace characters are '{' and '"', in which case it uses JSON). 
  """
    with open(filename, 'r') as fin:
        try:
            raw = fin.read()
            fmtstr = fmt
            if fmt == "auto":
                if raw[:1024].translate({ord(c): None
                                         for c in ' \t\n\r'}).startswith('{"'):
                    fmt = "json"
                    fmtstr = "json (detected)"
                else:
                    fmt = "story"
                    fmtstr = "story (default)"

            if fmt == "json":
                st = unpack(json.loads(raw), story.Story)
            elif fmt == "story":
                st = parse.parse_story(raw)
            else:
                raise ValueError("Invalid story format '{}'.".format(fmtstr))
        except Exception as e:
            print(
                "Warning: file '{}' could not be read as a Story in format '{}'."
                .format(filename, fmtstr))
            if config.DEBUG:
                print(e, file=sys.stderr)
                traceback.print_tb(e.__traceback__, file=sys.stderr)
            return None

    return st
Esempio n. 14
0
def package(story):
    with open("story_engine.js", 'r') as fin:
        ejs = fin.read()

    return TEMPLATE.format(title=story.name.title(),
                           story_content=pack(story),
                           engine=ejs)


if __name__ == "__main__":
    import sys, os
    if len(sys.argv) < 2:
        print("Error: Missing argument 'target stories'", file=sys.stderr)
    for tf in sys.argv[1:]:
        with open(tf, 'r') as fin:
            html = package(unpack(json.load(fin), Story))
            basename = os.path.basename(tf)
            output = "{}.html".format(basename)
            i = 0
            while os.path.exists(output):
                old = output
                output = "{}.{}.html".format(basename, i)
                print(
                    "Warning: target file '{}' already exists, using '{}' instead..."
                    .format(old, output),
                    file=sys.stderr)
                i += 1

            with open(output, 'w') as fout:
                fout.write(html)
Esempio n. 15
0
def parse_first_node(src):
    """
  Takes a chunk of story source and parses the first node from it (which should
  start immediately at the top of the source modulo comments and empty space).

  Returns (None, src) if there isn't a node definition present, or the parsed
  node plus the remaining source if there is.

  Example:

  ```
  StoryNode(
    "at_the_beach",
    "You walk along the beach, watching seabirds dance with the waves. The sea calls to you, but you should go home.",
    {
      "The sea": ["wading_out", "(set: mood | desolate)"],
      "go home": ["back_home", "(set: mood | warm)"]
    }
  )
  ```
  # at_the_beach

  You walk along the beach, watching seabirds dance with the waves. [[The
  sea|wading_out|(set: mood | desolate)]] calls to you, but you should [[go
  home|back_home|(set: mood | warm)]].
  ```

  """
    src = src.strip()
    first = NODE_START.search(src)
    # No node found:
    if not first or first.start() != 0:
        return (None, src)

    node = {}

    # Search for the next node, or else use the end of the source:
    second = NODE_START.search(src, pos=first.end())
    if second:
        node_end = second.start()
    else:
        node_end = len(src)

    # Grab the name and content:
    node["name"] = first.group(1)
    content = reflow(src[first.end():node_end])
    leftovers = src[node_end:]

    # Find and transform all links in the text, replacing them with their display
    # text and creating link entries for each. Note that if the display text is
    # not unique, all instances within the node will be highlighted.
    link_extents = []
    i = 0
    while i < len(content) - 1:
        i += 1  # we intentionally skip i = 0 since we match the second '['
        if content[i - 1:i + 1] == "[[":
            try:
                close = utils.matching_brace(content, i, '[', ']')
            except utils.UnmatchedError:
                # TODO: Print a warning here?
                continue

            # Push to create reverse ordering so link replacement doesn't change
            # indices of earlier link_extents.
            link_extents.insert(0, (i + 1, close))

            # jump ahead to the end of this link
            i = close

    node["successors"] = {}
    for start, end in link_extents:
        # Ordering is last-to-first, so that replacement here doesn't affect
        # indices of earlier links.
        contents = content[start:end]
        contents = contents.strip()
        # TODO: Error if this strip results in an empty string?

        # First, figure out display text:
        if contents[0] == '"':
            end, val = utils.string_literal(contents, 0, '"')
            if '|' in contents[end:]:
                display_end = contents.index('|', end)
                display = val + contents[end:display_end]
            else:
                display_end = len(contents)
                display = val + contents[end:]
        else:
            try:
                display_end = contents.index('|')
                display = contents[:display_end]
            except ValueError:
                display_end = len(contents)
                display = contents

        # Next figure out the link destination:
        if display_end < len(contents):
            try:
                dest_end = contents.index('|', display_end + 1)
                destination = contents[display_end + 1:dest_end].strip()
                if destination == "":
                    destination = display
            except ValueError:
                dest_end = len(contents)
                destination = contents[display_end + 1:].strip()
        else:
            destination = display
            dest_end = len(contents)

        # Finally, grab the transition text:
        transition = contents[dest_end + 1:].strip()

        # Revise the source to replace the link literal with its display text:
        content = content[:start - 2] + display + content[end + 2:]

        # Add to our links information:
        if transition:
            node["successors"][display] = [destination, transition]
        else:
            node["successors"][display] = [destination, ""]

    # Put the revised content into our node:
    node["content"] = content

    return (unpack(node, StoryNode), leftovers)
Esempio n. 16
0
 def _unpack_(obj):
     bits = obj.split(' ')
     if bits[0] != "set":
         raise ValueError(
             "Can't unpack '{}' as a SetValue change.".format(obj))
     return SetValue(bits[1], unpack(json.loads(' '.join(bits[2:]))))
Esempio n. 17
0
def main(models_str, choices_str, trace_strings):
    """
  Takes JSON strings specifying models and choices and a list of JSON strings
  specifying traces and runs analyze_trace on each trace before summarizing
  results.
  """
    packed_models = json.loads(models_str)
    models = []
    for i, obj in enumerate(packed_models):
        try:
            models.append(unpack(obj, player.PlayerModel))
        except Exception as e:
            raise ValueError(
                "Failed to unpack player model #{}".format(i)) from e

    packed_choices = json.loads(choices_str)
    choices = []
    for i, obj in enumerate(packed_choices):
        try:
            choices.append(unpack(obj, choice.Choice))
        except Exception as e:
            raise ValueError("Failed to unpack choice #{}".format(i)) from e

    traces = {fn: json.loads(trace_strings[fn]) for fn in trace_strings}

    results = {tr: analyze_trace(models, choices, traces[tr]) for tr in traces}

    all_decisions = {}
    for ch in choices:
        cn = ch.name
        if cn not in all_decisions:
            all_decisions[cn] = {}
    for tr in traces:
        for tch in traces[tr]:
            add_to = all_decisions[tch[0]]
            if tch[1] in add_to:
                add_to[tch[1]] += 1
            else:
                add_to[tch[1]] = 1

    overall_per_choice = combine_per_choice(
        *[res[2] for res in results.values()])

    # TODO: More formatting here
    for tn in results:
        res = results[tn]
        print("Averages for {}:".format(tn))
        for model in models:
            pmn = model.name
            print("  {:.3g} {}".format(res[0][pmn], pmn))
        print('-' * 80)

    for cn in overall_per_choice:
        times, agreements = overall_per_choice[cn]
        print("Choice: '{}' (seen {} times)".format(cn, times))
        print("Decisions:")
        for dc in all_decisions[cn]:
            print("  {}: {}".format(dc, all_decisions[cn][dc]))
        print("Model agreement:")
        for pmn in agreements:
            print("  {}: {:.3g}".format(pmn, agreements[pmn]))
        print('-' * 80)