exploration.overview
exploration
library Overview
This page explains an overview of the most important concepts that the
exploration
library deals with.
What is it for?
The exploration
library is designed to support formal analysis of
exploration processes in videogame spaces which can be approximated as a
graph of discrete and repeatable decisions. It's heavily inspired
by the Boss Keys YouTube
series
It could also be applied in some non-game contexts as well. Adventure games, 2D RPGs, action-adventure games, and most centrally, Metroidvania games (roughly, side-scrolling 2D exploration-adventure games) are all genres where it should be applicable. The kinds of questions it might help answer include questions like:
- "Which areas are accessible via which other areas, with which items?"
- "How many untaken branches has the player passed at this point?"
- "What percentage of players get item X before item Y?"
Along with more qualitative analysis, it might also help support research into other questions, like "what level structures make players feel claustrophobic?" The main goal is to have a data format that can be used to write down exploration processes, which is abstract enough to enable comparisons between different games and/or different players playing the same game (though there are lots of issues with the latter application).
It does rely on human judgement to figure out what should be classified as a "decision," and it also does not work well for certain types of games, like open-world games, where you never really make the same decision twice (code for dealing with these types of games is in the planning stages).
The DTER Model
We model the exploration process in games using a highly abstract model
that includes Decisions, Transitions, Effects, and Requirements as part
of a decision graph (represented by the core.DecisionGraph
class).
Many aspects of the game, such as how difficult the enemies are, what the
story, graphics, and audio are like, and how a player specifically
navigates the geometry of individual rooms are by default not
represented, although some of that information could be added as
annotations. The four key components of the model are:
Decisions. These represent a position or small region where the player is forced to make a decision about where to go next. See the section on "Decisions are Fractal" below for more details about what qualifies as a decision, but for one example, we can imagine each room might be a decision, where the options are the doors of the room. Each decision is assigned an ID number, and is also given a name (which does not have to be unique).
Transitions. These are the links between decisions, and represent the available options. For each move the player could make (e.g., for each door going out of a room), we add a transition and connect it to another decision representing where the player would be once they take that transition. Each transition has a name, which must be unique among all transitions outgoing from a particular decision (but which is usually NOT unique across the entire graph). Something like "west" can be used to indicate "the west door of this room." Each transition starts from a specific decision and ends at a specific decision. Transitions are allowed to start and end at the same decision, we call these "actions" and they represent things like pulling a lever where the options you're faced with don't directly change, but some kind of effect might be applied which indirectly changes things.
Effects. Each transition has the immediate effect of changing the player's current decision, and thus the options available to them. Transitions may also apply one or more secondary effects, which interact with the player state composed of capabilities and tokens, plus the world state composed of mechanism states. A secondary effect can be something like gaining a new key, gaining a certain number of coins, or setting a mechanism in the world to a particular state. By creating standardized data structures for player & world states, we enable deeper comparisons between different games, although in some cases we then need to abstract some of the details of those games. See the "Effects and Requirements" section below for more details.
Requirements. Using the same abstract player & world state data that effects manipulate, each transition may have a requirement to represent situations like a locked door requiring a certain key, or a bridge where a toll must be paid at each crossing (a toll would also involve an effect to represent losing the money paid). When figuring out the options available to the player, the system can automatically disregard transitions at the current decision whose requirements are not met. A requirement can be a series of specific capabilities, token counts, and/or mechanism states which are combined using AND (
&
), OR (|
), and/or NOT (!
) operators. For example, the requirementfly | (climb & stamina*3)
represents a situation where the player must either have the "fly" capability, or they must have the "climb" capability, along with at least three "stamina" tokens. As with effects, sometimes expressing the exact requirements imposed by a particular game engine is difficult, and some abstraction must be used, but the format of requirements has been designed and tested to cover most situations that arise in the videogame genres we've studied. See the "Effects and Requirements" section below for more details.
Putting together Decisions, Transitions, Effects, and Requirements, we
create a "decision graph" (represented by the core.DecisionGraph
class). This details every decision, each transition at that decision,
and what the effects of those transitions are along with the requirements
necessary for taking them. These decision graphs are rich enough models
of game spaces to allow for lots of analysis, and because they're
abstract, they allow for comparison between different games (although
they remain fundamentally subjective).
Decisions are Fractal
When creating decision graphs, the player needs to choose what level of decision they wish to capture. Since the actions used to carry out most decisions can be decomposed into a series of more-local decisions, we can view decisions in games as having a fractal structure. For example, the actions taken as a result of a decision to "visit the water temple" probably involve many specific navigation moves, like "go left at the crossroads" or "cut across the field towards the river," and each of these is the result of a decision that's made in support of the higher-level goal of reaching the water temple. Likewise, "go left at the crossroads" requires deciding to move the character to the left by a certain amount, and that lower-level decision could even be broken down into individual decisions to press certain buttons required to make that happen. In theory, a decision map could be constructed at the level of button presses, but it would be excruciatingly hard to do so manually, and analysis of such a map would probably be very difficult. Instead, analyzing at the "go left at the crossroads" level is probably more productive, since at that level, we can see that many similar game states result in very similar sets of choices (e.g., you always have the option to go left or right at the crossroads, and those two choices always lead to similar subsequent decisions). One could of course analyze decisions at the level of "go to the water temple" instead, and here there might still be enough similarity in decisions for this to be useful (e.g., when at the water temple, you always have the same few options of what to visit next).
The exploration
library does not require you to use any particular
level of decision, and through the use of Zone
s it does allow grouping
base-level decisions into larger groups to capture multiple levels of
decision structure. Zones can also be stacked within other zones to
represent complex multi-layer groupings. Support for deriving a
higher-level decision graph from zones of a lower-level graph is a
planned feature.
Effects and Requirements
To model game state in a manner that can be abstracted across multiple games, we use the following kinds of state:
- Capabilities: Each capability is identified by a unique string, and represents some specific capability that affects which transitions a player can take. Examples include movement abilities (e.g., "doubleJump") and re-usable key items (e.g., "yellowKeyCard"). At each step of an exploration, a player either has or does not have each capability, which is represented by a set of capability strings that they player has. The 'gain' effect adds a capability to this set, while the 'lose' effect removes one (in both cases nothing happens if the capability is already present/absent). A requirement may stipulate that a player must have or must not have a certain capability in order to traverse a transition.
- Tokens: using capabilities alone, it's difficult to represent situations where items can be accumulated (like holding multiple keys that get used up when unlocking doors). So we also maintain a mapping from token names to integers, and the 'gain' and 'lose' effects can specify a token name plus number to add or remove that many tokens of the specified type. Currently, minimum or maximum token limits are not supported, but we plan to add these in a future release. For tokens, a 'set' effect is also available that sets the token count to a specific amount regardless of the previous amount. A token requirement means "at-least-this-many" and negating it thus means "fewer-than-this-many". Using 'and', 'or', and/or 'not' more specific requirements can be created. By combining a token requirement with a 'lose' effect on the same transition, we can create transitions that have costs.
- Mechanisms: in many games there are things like levers that open nearby doors. These could be represented by creating unique capability names for each lever, but that becomes tedious and error-prone, since if you re-use the same name by accident, the library will assume that the capability for one location applies equally to the other. So we include *mechanisms, which are tied to a particular decision point, and their name only needs to be unique at-that-decision. When referring to a mechanism by name alone, the system will assume that we mean a mechanism at the current decision point, or if no such mechanism exists, one with that name in the current zone, searching higher-level zones if necessary and finally the entire decision map. Each mechanism can have any number of specific states, each identified by a string. The associated effect is 'set' with a mechanism name + state string, which changes the mechanism's current state to the specified one (each mechanism starts out as not-in-any-state and once given a state, can only be in one state at once). Requirements can be that a mechanism is in a specific state, or that it's not in a specific state. There is also a 'toggle' effect which cycles the mechanism through a specific sequence of states on each activation. When a mechanism requirement exists on a transition, we start looking for the named mechanism at both ends of the transition first. A mechanism may also be identified by a decision ID plus mechanism name to avoid the search process. In the most common case where, for example, multiple levers open different doors, but each lever is adjacent to the door it opens and no two levers are at adjacent decisions, each lever could use the same mechanism name, and each door could simply require that that mechanism be in the 'open' state, and the search process would match up levers with doors correctly. Using names like "leftLever" and "rightLever" might be necessary when multiple levers are in the same or adjacent locations, but that's often natural in any case.
Whereas capabilities and tokens are stored as part of the player state, mechanism states are stored as part of a separate global decision-graph state This helps in situations where, for example, the map state gets reset while the player state stays the same, or vice versa. Although we won't get into it here, there's also support for maintaining multiple separate player states for games where the player switches between different avatars with different capabilities.
Besides the 'gain', 'lose', 'set', and 'toggle' effects mentioned above, a transition can also have a 'deactivate' effect which disables that transition once taken; this is useful for things like chests that can only be opened once.
Advanced Effects can Challenges
The base effects system triggers each effect whenever a transition is
taken, and this can cover probably 90% of cases. However, in some
situations, things are more complex than that. The effects of a
transition are actually represented as a Conesquence
, which is a list
that can contain Effect
s as well as Condition
s or Challenge
s, and
the latter two options can contain their own sub-Consequence
s. This
forms a tree structure of consequence and sub-consequence lists, which
can represent quite complicated situations, including conditional and
random effects. The three types that can be present in a Consequence
list are:
Effect
s are as described above, and are applied in-order. When a consequence list is applied, each effect in the list is applied to the current state.Condition
s specify aRequirement
, and trigger their sub-Consequence
list of additional effects if that requirement is met. This is different fromRequirement
s applied to transitions, which prevent the player from taking that transition. Even if aCondition
requirement is not met, the associated transition can still be taken, but the sub-effects of that specific condition will not be applied. We can use this to represent situations like "if you don't have the master key you will use up a small key" (with "master key or one small key" as a requirement to take the transition).Challenge
s are used to represent random outcomes, and they have two sub-Consequence
lists: one for 'success' and the other for 'failure' (although these need not represent exactly those two things). Multi-outcome processes can be represented by nesting challenges inside each other. By default, each option has a 50/50 chance of happening, although in practice, the player usually knows which outcome actually did happen during a particular traversal. There is aSkill
s system to indicate how player skills and/or upgrades might affect probabilities of different outcomes (see theChallenge
documentation), and in theory challenges allow for a model to be playable, although we have not yet implemented an interface for this. Although challenges only offer a rough approximation of potentially very complex game systems, they do allow for comparison between games when players are able to estimate the likelihood of different outcomes and the effects of skills on those likelihoods. In theory, we hope that this will enable things like checking whether players who lose to a 'difficult' boss are more likely to decide to explore other areas compared to players who lose to a boss they think they can beat in a few more tries.
Because Challenge
s can be placed inside of Condition
s and vice-versa,
relatively complex game logic can be attached to a particular transition.
There are a few more complex effects that can be used to control
traversal of decision graphs when used conditionally:
- 'bounce' is an effect which cancels the movement associated with a transition. Each transition has a destination and normally the player's next decision will be made using the options at that destination (we call this decision the 'primary decision'). If a 'bounce' effect triggers when taking a transition, the player's primary decision will stay the same, even though other effects of that transition may also apply. This can model situations where the player can attempt to take a transition, but without the proper requirements they will be unable to actually complete the transition. It can also model challenges which must be passed in order to progress, where a 'bounce' effect is included in the challenge failure outcome.
- 'goto' is an effect which sets the player's primary decision to a particular decision. Normally unnecessary since the transition has a destination, but it could be used to model a movement challenge where the player falls down to another decision point if they fail, for example.
- 'follow' is an effect which names a transition, and forces the player
to immediately take the named transition once they arrive at their
initial destination. Again most useful with a
Condition
orChallenge
, it can be used in situations where without meeting certain requirements, a player is shunted off to a different destination, or it could force the player to trigger an action at the destination of a transition. Effects of the secondary transition are applied as normal after all effects of the initial transition, so a chain of 'follow' effects might occur.
These more complex effects are rarely needed, but help model games more faithfully for certain key design patterns, such as the "broken bridge" scenario where the player's initial attempt to reach a goal unexpectedly redirects them elsewhere.
Additionally, transitions can be tagged as 'trigger' transitions, and whenever the player faces a decision, any 'trigger' transitions at that decision will apply their effects before the player actually gets to make a choice. This can approximate things like environmental hazards that apply passive damage over time.
Saving, Loading, and Endings
The effects described above can model or at least approximate most game
logic (although note that token values being integers restricts things a
bit). However, dying and restoring from a save point is a common
occurrence in games that isn't well modeled by anything described above.
For this specific mechanic, we have a 'save' effect which snapshots the
current game state, and a 'load' action can be taken by the player to
restore state to that snapshot. 'save' as an Effect
can be applied to
any transition, whereas 'load' is not a transition effect but rather a
type of action the player can take regardless of which other options are
available to them based on the current decision.
There are options to control exactly which parts of the game state are restored when loading a saved state, since in many games only some state reverts upon death. Multiple save slots with different names can be used.
To represent endings, including death but also various ways of beating the game, we use a special domain. Domains are parallel decision spaces, where when making a decision the player can chose any transition from an active decision in any of their current domains. However, the 'endings' domain is special: When any ending decision point is active, the only legitimate action is to load a previous state. So by activating a 'death' decision point in the 'endings' domain, we can represent the fact that the player has died, and their only option remaining is to load a saved game.
Usually, we can annotate the possibility of death as a 'goto' effect of a challenge failure, or when dying accidentally even when a challenge is not significant enough to be annotated, a special 'warp' action can be used to represent movement without taking a listed transition.
More Details
More details of the API not mentioned here can be found in the
documentation for various API functions and core classes. The
exploration.base
and exploration.core
modules contain most of the
relevant definitions, particularly the methods of the
exploration.core.DecisionGraph
and
exploration.core.DiscreteExploration
classes, plus the Requirement
,
Effect
, Challenge
, Condition
, and Consequence
definitions in the
exploration.base
module.
1""" 2# `exploration` library Overview 3 4This page explains an overview of the most important concepts that the 5`exploration` library deals with. 6 7## What is it for? 8 9The `exploration` library is designed to support **formal** analysis of 10exploration processes in videogame spaces which can be approximated as a 11graph of **discrete** and **repeatable** decisions. It's heavily inspired 12by [the Boss Keys YouTube 13series](https://www.youtube.com/playlist?list=PLc38fcMFcV_ul4D6OChdWhsNsYY3NA5B2) 14 15It could also be applied in some non-game contexts as well. Adventure 16games, 2D RPGs, action-adventure games, and most centrally, Metroidvania 17games (roughly, side-scrolling 2D exploration-adventure games) are all 18genres where it should be applicable. The kinds of questions it might 19help answer include questions like: 20 21- "Which areas are accessible via which other areas, with which items?" 22- "How many untaken branches has the player passed at this point?" 23- "What percentage of players get item X before item Y?" 24 25Along with more qualitative analysis, it might also help support research 26into other questions, like "what level structures make players feel 27claustrophobic?" The main goal is to have a data format that can be used 28to write down exploration processes, which is abstract enough to enable 29comparisons between different games and/or different players playing the 30same game (though there are lots of issues with the latter application). 31 32It does rely on human judgement to figure out what should be classified 33as a "decision," and it also does not work well for certain types of 34games, like open-world games, where you never really make the same 35decision twice (code for dealing with these types of games is in the 36planning stages). 37 38 39## The DTER Model 40 41We model the exploration process in games using a highly abstract model 42that includes Decisions, Transitions, Effects, and Requirements as part 43of a **decision graph** (represented by the `core.DecisionGraph` class). 44Many aspects of the game, such as how difficult the enemies are, what the 45story, graphics, and audio are like, and how a player specifically 46navigates the geometry of individual rooms are by default not 47represented, although some of that information could be added as 48annotations. The four key components of the model are: 49 501. Decisions. These represent a position or small region where the player 51is forced to make a decision about where to go next. See the section on 52"Decisions are Fractal" below for more details about what qualifies as a 53decision, but for one example, we can imagine each room might be a 54decision, where the options are the doors of the room. Each decision is 55assigned an ID number, and is also given a name (which does not have to 56be unique). 57 582. Transitions. These are the links between decisions, and represent the 59available options. For each move the player could make (e.g., for each 60door going out of a room), we add a transition and connect it to another 61decision representing where the player would be once they take that 62transition. Each transition has a name, which must be unique among all 63transitions outgoing from a particular decision (but which is usually NOT 64unique across the entire graph). Something like "west" can be used to 65indicate "the west door of this room." Each transition starts from a 66specific decision and ends at a specific decision. Transitions are 67allowed to start and end at the same decision, we call these "actions" 68and they represent things like pulling a lever where the options you're 69faced with don't directly change, but some kind of effect might be 70applied which indirectly changes things. 71 723. Effects. Each transition has the immediate effect of changing the 73player's current decision, and thus the options available to them. 74Transitions may also apply one or more secondary effects, which interact 75with the player state composed of **capabilities** and **tokens**, plus 76the world state composed of **mechanism states**. A secondary effect can 77be something like gaining a new key, gaining a certain number of coins, 78or setting a mechanism in the world to a particular state. By creating 79standardized data structures for player & world states, we enable deeper 80comparisons between different games, although in some cases we then need 81to abstract some of the details of those games. See the "Effects and 82Requirements" section below for more details. 83 844. Requirements. Using the same abstract player & world state data that 85effects manipulate, each transition may have a requirement to represent 86situations like a locked door requiring a certain key, or a bridge where 87a toll must be paid at each crossing (a toll would also involve an effect 88to represent losing the money paid). When figuring out the options 89available to the player, the system can automatically disregard 90transitions at the current decision whose requirements are not met. A 91requirement can be a series of specific capabilities, token counts, 92and/or mechanism states which are combined using AND (`&`), OR (`|`), 93and/or NOT (`!`) operators. For example, the requirement 94`fly | (climb & stamina*3)` represents a situation where the player must 95either have the "fly" capability, or they must have the "climb" 96capability, along with *at least* three "stamina" tokens. As with 97effects, sometimes expressing the exact requirements imposed by a 98particular game engine is difficult, and some abstraction must be used, 99but the format of requirements has been designed and tested to cover most 100situations that arise in the videogame genres we've studied. See the 101"Effects and Requirements" section below for more details. 102 103Putting together Decisions, Transitions, Effects, and Requirements, we 104create a "decision graph" (represented by the `core.DecisionGraph` 105class). This details every decision, each transition at that decision, 106and what the effects of those transitions are along with the requirements 107necessary for taking them. These decision graphs are rich enough models 108of game spaces to allow for lots of analysis, and because they're 109abstract, they allow for comparison between different games (although 110they remain fundamentally subjective). 111 112 113## Decisions are Fractal 114 115When creating decision graphs, the player needs to choose what level of 116decision they wish to capture. Since the actions used to carry out most 117decisions can be decomposed into a series of more-local decisions, we can 118view decisions in games as having a fractal structure. For example, the 119actions taken as a result of a decision to "visit the water temple" 120probably involve many specific navigation moves, like "go left at the 121crossroads" or "cut across the field towards the river," and each of 122these is the result of a decision that's made in support of the 123higher-level goal of reaching the water temple. Likewise, "go left at the 124crossroads" requires deciding to move the character to the left by a 125certain amount, and that lower-level decision could even be broken down 126into individual decisions to press certain buttons required to make that 127happen. In theory, a decision map could be constructed at the level of 128button presses, but it would be excruciatingly hard to do so manually, 129and analysis of such a map would probably be very difficult. Instead, 130analyzing at the "go left at the crossroads" level is probably more 131productive, since at that level, we can see that many similar game states 132result in very similar sets of choices (e.g., you always have the option 133to go left or right at the crossroads, and those two choices always lead 134to similar subsequent decisions). One could of course analyze decisions 135at the level of "go to the water temple" instead, and here there might 136still be enough similarity in decisions for this to be useful (e.g., when 137at the water temple, you always have the same few options of what to 138visit next). 139 140The `exploration` library does not require you to use any particular 141level of decision, and through the use of `Zone`s it does allow grouping 142base-level decisions into larger groups to capture multiple levels of 143decision structure. Zones can also be stacked within other zones to 144represent complex multi-layer groupings. Support for deriving a 145higher-level decision graph from zones of a lower-level graph is a 146planned feature. 147 148 149## Effects and Requirements 150 151To model game state in a manner that can be abstracted across multiple 152games, we use the following kinds of state: 153 1541. **Capabilities**: Each capability is identified by a unique string, 155 and represents some specific capability that affects which 156 transitions a player can take. Examples include movement abilities 157 (e.g., "doubleJump") and re-usable key items (e.g., "yellowKeyCard"). 158 At each step of an exploration, a player either has or does not have 159 each capability, which is represented by a set of capability strings 160 that they player has. The 'gain' effect adds a capability to this 161 set, while the 'lose' effect removes one (in both cases nothing 162 happens if the capability is already present/absent). A requirement 163 may stipulate that a player must have or must not have a certain 164 capability in order to traverse a transition. 1652. **Tokens**: using capabilities alone, it's difficult to represent 166 situations where items can be accumulated (like holding multiple keys 167 that get used up when unlocking doors). So we also maintain a mapping 168 from token names to integers, and the 'gain' and 'lose' effects can 169 specify a token name plus number to add or remove that many tokens of 170 the specified type. Currently, minimum or maximum token limits are 171 not supported, but we plan to add these in a future release. For 172 tokens, a 'set' effect is also available that sets the token count to 173 a specific amount regardless of the previous amount. A token 174 requirement means "at-least-this-many" and negating it thus means 175 "fewer-than-this-many". Using 'and', 'or', and/or 'not' more specific 176 requirements can be created. By combining a token requirement with a 177 'lose' effect on the same transition, we can create transitions that 178 have costs. 1793. **Mechanisms*: in many games there are things like levers that open 180 nearby doors. These could be represented by creating unique 181 capability names for each lever, but that becomes tedious and 182 error-prone, since if you re-use the same name by accident, the 183 library will assume that the capability for one location applies 184 equally to the other. So we include **mechanisms**, which are tied to 185 a particular decision point, and their name only needs to be unique 186 at-that-decision. When referring to a mechanism by name alone, the 187 system will assume that we mean a mechanism at the current decision 188 point, or if no such mechanism exists, one with that name in the 189 current zone, searching higher-level zones if necessary and finally 190 the entire decision map. Each mechanism can have any number of 191 specific states, each identified by a string. The associated effect 192 is 'set' with a mechanism name + state string, which changes the 193 mechanism's current state to the specified one (each mechanism starts 194 out as not-in-any-state and once given a state, can only be in one 195 state at once). Requirements can be that a mechanism is in a specific 196 state, or that it's not in a specific state. There is also a 'toggle' 197 effect which cycles the mechanism through a specific sequence of 198 states on each activation. When a mechanism requirement exists on a 199 transition, we start looking for the named mechanism at both ends of 200 the transition first. A mechanism may also be identified by a 201 decision ID plus mechanism name to avoid the search process. In the 202 most common case where, for example, multiple levers open different 203 doors, but each lever is adjacent to the door it opens and no two 204 levers are at adjacent decisions, each lever could use the same 205 mechanism name, and each door could simply require that that 206 mechanism be in the 'open' state, and the search process would match 207 up levers with doors correctly. Using names like "leftLever" and 208 "rightLever" might be necessary when multiple levers are in the same 209 or adjacent locations, but that's often natural in any case. 210 211Whereas capabilities and tokens are stored as part of the player state, 212mechanism states are stored as part of a separate global decision-graph 213state This helps in situations where, for example, the map state gets 214reset while the player state stays the same, or vice versa. Although we 215won't get into it here, there's also support for maintaining multiple 216separate player states for games where the player switches between 217different avatars with different capabilities. 218 219Besides the 'gain', 'lose', 'set', and 'toggle' effects mentioned above, 220a transition can also have a 'deactivate' effect which disables that 221transition once taken; this is useful for things like chests that can 222only be opened once. 223 224 225## Advanced Effects can Challenges 226 227The base effects system triggers each effect whenever a transition is 228taken, and this can cover probably 90% of cases. However, in some 229situations, things are more complex than that. The effects of a 230transition are actually represented as a `Conesquence`, which is a list 231that can contain `Effect`s as well as `Condition`s or `Challenge`s, and 232the latter two options can contain their own sub-`Consequence`s. This 233forms a tree structure of consequence and sub-consequence lists, which 234can represent quite complicated situations, including conditional and 235random effects. The three types that can be present in a `Consequence` 236list are: 237 2381. `Effect`s are as described above, and are applied in-order. When a 239 consequence list is applied, each effect in the list is applied to 240 the current state. 2412. `Condition`s specify a `Requirement`, and trigger their 242 sub-`Consequence` list of additional effects if that requirement is 243 met. This is different from `Requirement`s applied to transitions, 244 which prevent the player from taking that transition. Even if a 245 `Condition` requirement is not met, the associated transition can 246 still be taken, but the sub-effects of that specific condition will 247 not be applied. We can use this to represent situations like "if you 248 don't have the master key you will use up a small key" (with "master 249 key or one small key" as a requirement to take the transition). 2503. `Challenge`s are used to represent random outcomes, and they have two 251 sub-`Consequence` lists: one for 'success' and the other for 252 'failure' (although these need not represent exactly those two 253 things). Multi-outcome processes can be represented by nesting 254 challenges inside each other. By default, each option has a 50/50 255 chance of happening, although in practice, the player usually knows 256 which outcome actually did happen during a particular traversal. 257 There is a `Skill`s system to indicate how player skills and/or 258 upgrades might affect probabilities of different outcomes (see the 259 `Challenge` documentation), and in theory challenges allow for a 260 model to be playable, although we have not yet implemented an 261 interface for this. Although challenges only offer a rough 262 approximation of potentially very complex game systems, they do allow 263 for comparison between games when players are able to estimate the 264 likelihood of different outcomes and the effects of skills on those 265 likelihoods. In theory, we hope that this will enable things like 266 checking whether players who lose to a 'difficult' boss are more 267 likely to decide to explore other areas compared to players who lose 268 to a boss they think they can beat in a few more tries. 269 270Because `Challenge`s can be placed inside of `Condition`s and vice-versa, 271relatively complex game logic can be attached to a particular transition. 272There are a few more complex effects that can be used to control 273traversal of decision graphs when used conditionally: 274 275- 'bounce' is an effect which cancels the movement associated with a 276 transition. Each transition has a destination and normally the 277 player's next decision will be made using the options at that 278 destination (we call this decision the 'primary decision'). 279 If a 'bounce' effect triggers when taking a transition, the player's 280 primary decision will stay the same, even though other effects of 281 that transition may also apply. This can model situations where the 282 player can attempt to take a transition, but without the proper 283 requirements they will be unable to actually complete the transition. 284 It can also model challenges which must be passed in order to 285 progress, where a 'bounce' effect is included in the challenge 286 failure outcome. 287- 'goto' is an effect which sets the player's primary decision to a 288 particular decision. Normally unnecessary since the transition has a 289 destination, but it could be used to model a movement challenge where 290 the player falls down to another decision point if they fail, for 291 example. 292- 'follow' is an effect which names a transition, and forces the player 293 to immediately take the named transition once they arrive at their 294 initial destination. Again most useful with a `Condition` or 295 `Challenge`, it can be used in situations where without meeting 296 certain requirements, a player is shunted off to a different 297 destination, or it could force the player to trigger an action at the 298 destination of a transition. Effects of the secondary transition are 299 applied as normal after all effects of the initial transition, so a 300 chain of 'follow' effects might occur. 301 302These more complex effects are rarely needed, but help model games more 303faithfully for certain key design patterns, such as the "broken bridge" 304scenario where the player's initial attempt to reach a goal unexpectedly 305redirects them elsewhere. 306 307Additionally, transitions can be tagged as 'trigger' transitions, and 308whenever the player faces a decision, any 'trigger' transitions at that 309decision will apply their effects before the player actually gets to make 310a choice. This can approximate things like environmental hazards that 311apply passive damage over time. 312 313 314## Saving, Loading, and Endings 315 316The effects described above can model or at least approximate most game 317logic (although note that token values being integers restricts things a 318bit). However, dying and restoring from a save point is a common 319occurrence in games that isn't well modeled by anything described above. 320For this specific mechanic, we have a 'save' effect which snapshots the 321current game state, and a 'load' action can be taken by the player to 322restore state to that snapshot. 'save' as an `Effect` can be applied to 323any transition, whereas 'load' is not a transition effect but rather a 324type of action the player can take regardless of which other options are 325available to them based on the current decision. 326 327There are options to control exactly which parts of the game state are 328restored when loading a saved state, since in many games only some state 329reverts upon death. Multiple save slots with different names can be used. 330 331To represent endings, including death but also various ways of beating 332the game, we use a special domain. Domains are parallel decision spaces, 333where when making a decision the player can chose any transition from an 334active decision in any of their current domains. However, the 'endings' 335domain is special: When any ending decision point is active, the only 336legitimate action is to load a previous state. So by activating a 'death' 337decision point in the 'endings' domain, we can represent the fact that 338the player has died, and their only option remaining is to load a saved 339game. 340 341Usually, we can annotate the possibility of death as a 'goto' effect of a 342challenge failure, or when dying accidentally even when a challenge is 343not significant enough to be annotated, a special 'warp' action can be 344used to represent movement without taking a listed transition. 345 346 347## More Details 348 349More details of the API not mentioned here can be found in the 350documentation for various API functions and core classes. The 351`exploration.base` and `exploration.core` modules contain most of the 352relevant definitions, particularly the methods of the 353`exploration.core.DecisionGraph` and 354`exploration.core.DiscreteExploration` classes, plus the `Requirement`, 355`Effect`, `Challenge`, `Condition`, and `Consequence` definitions in the 356`exploration.base` module. 357"""