exploration 0.7.5
Tool for reading, formatting, and manipulating exploration graphs, which are graphs (or graph-sequences) that describe exploration of a game space, principally designed with Metroidvania games in mind.
License: BSD 3-Clause License
Requires Python: >=3.10

# Exploration

## Overview

This program provides data types for representing the exploration of
spaces that can defined (or abstracted) in terms of discrete decisions,
such as videogame levels with multiple rooms and also other things like
conversation trees or a city block grid. It also has the beginnings of
code for representing open-world exploration in terms of districts,
nodes, paths, edges, and landmarks, although this code is not yet
complete.

The discrete code represents space using a `DecisionGraph`, which is a
multi-di-graph indicating the transition(s) between decisions, which can
include information about prerequisites for transitions as well as
effects a transition might have on the world. There is also a convention
for representing unexplored options using specially-tagged nodes of the
graph. A `DiscreteExploration` is a sequence of `DecisionGraph`s, along
with a sequence of exploration actions indicating what the explorer did
at each step, including which transition (if any) was taken. Exploration
`State` is tracked in terms of capabilities gained, tokens accumulated,
and also mechanism states throughout the graph.

These representations were developed with Metroidvania games in mind but
should apply to a variety of game genres and even real-life situations.

Core capabilities include:

- Representing exploration processes as a series of decisions including
    partial information about not-yet-explored decisions.
- Creating maps and explorations from various text formats, including
    exploration journal formats.
- Reasoning about reachability modulo transition requirements in terms of
    powers that must be possessed and/or tokens that must be spent for a
    transition. TODO
- The ability to represent fairly sophisticated game logic in the
    `DecisionGraph`, and even construct playable maps. Game logic that
    can't be captured this way can still be represented through making
    custom changes to maps between exploration steps.

## Dependencies:

- Python version 3.8+
- `networkx` For underlying graph structures.
- `pytest` for testing, install with `[test]` option to get it automatically.

## Installing

Just run `pip install exploration`. The `egtool` script should be
installed along with the module.

You can then run `python -m exploration.tests` to run tests (use `pip
install exploration[test]` if this doesn't work out-of-the-box).

## Getting Started:

The `egtool` script provides a command-line interface to core
functionality. The `exploration.main` module provides equivalent entry
points from Python. `exploration.core` provides the main types and
explains how they fit together.

## Plans

- Better support for open-world games, where decisions are not as closely
    linked to virtual space structure.

## Changelog

- v0.7.5 adds features to support the new explorationViewer module, which
    uses pyodide to run exploration analyses from Javascript which is
    also doing visualization stuff. It also changes the datatype
    specification strings in the .exp JSON format to shave some bytes off
    of the bloated exploration JSON... More work needs to be done there.
    JSON format will probably NOT be backwards-compatible in the future,
    and is not backwards compatible right now.
- v0.7.4 fixes some minor issues:
    * Fixes a bug with mechanism search where relevant transitions were not
      being used as the search basis in some cases.
- v0.7.3 adds a bit more:
    * Journal support for tagging and annotating zones.
    * `ReqTag` requirements for requiring the presence of a certain tag
      (or a certain tag value) on some active decision or zone.
    * `ReqLevel` requirements for requiring a minimum skill level.
    * Removes the restriction on entering relative mode in an
      empty/unstarted graph.
    * Respects current domain when creating new nodes via warps or when
      there's no current decision.
    * Adds 'inventory', 'equivalences', and 'mechanisms' debug options.
- v0.7.2 offers a few more improvements and more API changes (including
  breaking ones):
    * `replaceUnexplored` is now `replaceUnconfirmed`. `unvisited` tag is
      now `unconfirmed`. `core.DecisionGraph.hasBeenVisited` is now
      `core.DecisionGraph.isConfirmed`. Finally a good naming distinction
      between graph confirmation & exploration visited status.
    * Adds 'inspect' mode to the `egtool.py` script and an `inspect`
      entry point in `main.py`, allowing you to interactively examine an
      exploration on the command line.
    * Fixes an issue with 'unvisited' tags on endings.
    * Adds zone inference to a lot more code paths, such that things like
      revisits and obviates no longer need to re-specify the current
      zone.
    * Adds decision type specifiers for journal entries, and
      changes + expands the available decision types.
    * Adds special behavior for the 'trigger' tag when applied to action
      transitions: When `core.DiscreteExploration.advanceSituation` is
      called, each trigger action at an active decision whose
      requirements are satisfied will have its consequences applied.
    * Prevents using 'a'/'action' to take an existing action, requiring
      the use of 't'/'retrace' with the 'actionPart' target instead
      (typically 'ta'). This helps catch issues with action name
      misspellings, and location confusion, among others.
    * Endings are now warps by default instead of transitions, use
      'actionPart' (e.g., 'Ea') to mark a voluntary ending.
- v0.7.1 fixes major bugs with v0.7 and adds a few more nice/important
  things:
    * Transitions may include outcome specifiers, so that in a journal
      you can annotate taking the same transition multiple times and
      getting different results each time for challenges.
    * A new 'save' effect copies game state, which can be restored (in
      whole or in part) via a new 'revertTo' / 'revert'
      `ExplorationAction` / journal entry. Custom combinations indicating
      which state parts to revert can be stored in an exploration for
      later re-use.
    * Fixed up some fuzziness around bounce/follow effects, and added a
      `primaryDecision` slot to the `State` structure, so there's always
      a per-situation idea of a single primary decision (except when it's
      `None`).
    * Warnings for 'at:' annotations, along with 'active:', 'has:',
      'level:', 'can:', 'state:', and 'exists:' so these can be used to
      help with journal debugging.
    * Auto-slots for journal aliases, for example `__here__` and
      `__transition__`.
    * `base.Requirement.walk`, to support auto-creation of mentioned
      mechanisms in journals.
    * `AnyEffectValue` union type.
    * Substantially cleaned up handling of exploration statues, and
      introduced the new 'status' journal entry type to set them, which
      can also disable the new automatic update-to-'explored' feature
      which normally triggers when leaving a decision.
    * Many more contexts allow you to omit zone information, although
      there are still some that require it.
    * More consistency with when to use 'x' vs. 'r': 'r' is only used
      when the exploration status of a node is 'exploring' or higher,
      regardless of its 'unvisited' tag status.
- v0.7 is a MAJOR overhaul, and breaks compatibility with previous
  versions. It's a solid alpha version, and may soon be upgraded to a 1.0
  beta, but it needs a bit more use with longer journals first. It adds:
    * The first nascent support for open-world exploration in
      `geographic.py`, but this is still far from complete.
    * Zone prefixing is gone, and instead `Decision`s have IDs and may
      freely share names. `DecisionSpecifier`s may identify a decision by
      domain, zone, and/or name.
    * Domains with different focalization modes, focal contexts, and
      focal points. In most cases these can be safely ignored, but they
      are useful for:
        + Representing multiple concurrent active decisions (e.g., an
          omnipresent menu option that affects exploration in the main
          game world) via multiple `Domain`s.
        + Representing control over multiple units, via
          `FocalPointSpecifier`s and 'plural'-focalized domains.
        + Representing control that can swap between different
          factions/protagonists, via `FocalContext`s.
        + Representing domains in which a transition at any discovered
          decision point can be taken, via 'spreading'-focalized domains.
      Note that unfortunately, this complicates the view of position,
      since there may be multiple active decisions or even no active
      decisions at a particular step.
    * `Consequence`s replace `Effect`s, retaining the latter as one
      sub-type. `Challenge` and `Condition` nodes and containment can be
      used to represent very complex outcomes from a transition,
      including outcomes that depend on `Skill`s. An entire new syntax
      for writing out complex `Consequence`s has been introduced (see the
      `parsing.ParseFormat.parseConsequence` examples).
    * Parsing code has been stripped out from much of the core libraries
      to simplify their code, and put into a new `parsing.py`. Sadly,
      this does mean that in a few contexts where before you could use a
      compound decision name with a zone part you must now explicitly use
      something like a `DecisionSpecifier` rather than just a string that
      will be parsed into one.
    * In addition to `Capability`s and `Token`s, mechanisms now allow for
      specifying game state that is attached to decisions rather than
      being part of the player's focal context. This mainly makes naming
      easier, as mechanisms have `MechanismID`s and can share names, but
      requirements look them up according to nearby decisions allowing
      (in most cases) for a name like 'lever' to be re-used across many
      different decisions.
    * ALL volatile game state that can be modified by effects (other than
      'edit') is now stored in the `State`, including taken-counts for
      transitions to enforce delay and charges restrictions and a
      deactivated-map for 'deactivate' effects. This will help in
      contexts like search where you can maintain a single
      `DecisionGraph` and search through `State` possibilities. It also
      helps with from a final exploration `Situation` to approximate what
      the full form of an initial game graph would have been.
    * Steps in a `DiscreteExploration` are now categorized as
      `ExplorationAction`s, with more clarity in representing things
      like warps.
    * In journals, zone information as part of an exploration or
      observation should now be included as part of the destination
      specifier, rather than taking up an extra argument slot which had
      been awkwardly positioned.
    * Several `DiscreteExploration` methods have been eliminated in favor
      of centralizing `getSituation` and accessing the various sub-parts
      of the situation. Several other -AtStep methods have been
      eliminated, in favor of adding default `step` arguments to base
      fetch methods.
- v0.6.1 fixes egtool.py script to include new optional command-line args
    instead of mandatory args.
- v0.6.0 adds Graphviz Dot format support (both import and export) and
    fixes up some bugs with the JSON import/export. Also changes tags so
    that they have values instead of just being part of a set. Analysis
    tools have expanded a bit and a system of metafunctions for
    automatically applying smallest-unit analysis tools to larger units
    has been added so we don't have to re-write "apply this to each
    situation" a bunch of times. Analysis tools are set up to run every
    tool on a file and write results to a CSV file (see `main.py` for
    analysis tool configuration). The `egtool.py` script now accepts
    command-line options but falls back to interactive prompting.
    Finally finished describeProgress and overhauled analysis tests so
    that we don't have any xfails right now. Adds 'extinguish' and
    'complicate' journal entry types to better support mistaken
    impressions.
- v0.5.1 reorganizes some testing code out of the import-module run path.
- v0.5 includes a few bugfixes over 0.4, and most notably a stable syntax
    for entering relative mode at the current location, as well as an 'F'
    entry type for "fulfills" to note power equivalence. It also
    introduces the first real analysis tool, which just counts the number
    of unexplored options at each step of the graph.
- v0.4 introduces the 'actionPart' target type so that you can do 'oa' to
    observe an action without taking it. It also introduces long-form
    entry types and targets: you can just write out the full name of an
    entry type, possibly followed by an @ and either a full or
    abbreviated target type. Hopefully this helps make things more
    accessible for beginners. There are also now a few built-in debug
    commands available for printing relvant stuff. More may be added as
    they become popular. It also introduces equivalences, stored in the
    `DecisionGraph`, which allow powers (but not tokens) to count as
    being obtained when one of a set of other requirements is met
    instead. The 'fulfills' journal entry can add these.
- v0.3 is a pretty big overhaul. Changes journal format a bit (zones are
    now easier to deal with). Adds command-line interface (`__main__.py`
    via API in `main.py`). Adds interactive script `egtool`. Adds JSON
    serialization and `__eq__` methods for `DecisionGraph` and
    `Exploration` types. Fills out 'edit' effect type with a new
    `Command` syntax which is it's own tiny DSL for editing
    graphs/explorations. Also adds aliases in the journal format so that
    it's easier to store/recall complex but repeated patterns. Extends
    testing code quite a bit and fixes a fair number of bugs found via
    those tests, although coverage is still incomplete. This version
    pushes module compatibility up to 3.10 since the type checking code
    is to tightly intertwined with the actual runtime code in places to
    be easily separable and it uses 3.10 features. One final big change:
    the default journal format has changed so that 'g' is for tag, 't'
    (which used to be tag) is for 'retrace', and 'r' (which used to be
    retrace) is for 'return'). 'R' is no longer used by the default
    format. Note that until 1.0, there may continue to be some
    instability in the default journal format.
- v0.2 Journal functionality (`journal.py`) is working at a basic level,
    with a few things still to-do (e.g., edit effects). Design has
    changed since previous versions. The zone system now works, although
    needs more testing. Design will be iterated on so the API and
    particularly the journal format is not 100% stable yet. Changed
    version support from 3.7+ to 3.8+ because of needing typing.Literal.
- v0.1.2 Core functionality (`core.py`) is working & tested, with the
    exception of the zones system. Journals are not working, and tests
    for those have been disabled for now. Could be used for
    representation purposes, but is not yet complete. This version is
    effectively the first alpha release since I'm demoing at the PCG
    workshop. Note most of the 'core capabilities' are still TODO.
- v0.1.1 Still pre-alpha as it's in the process of being re-architected a
    bit, but some core functionality is present if rough (e.g.,
    `core.DecisionGraph` and `core.Exploration`).
- v0.1 Initial pre-alpha upload.