Skip to main content

Unlocks

Struct Unlocks 

Source
pub struct Unlocks<I, K> { /* private fields */ }
Expand description

A graph of things an entity can unlock.

The graph edges are never authored. One node grants a capability, another requires it, and that is the arrow. Change a requirement and the graph changes with it.

use std::collections::BTreeSet;
use plotline::{Requirement, Unlock, Unlocks};

#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord)]
enum Cap {
    Fusion,
    Warp,
}

let mut tree = Unlocks::new();
tree.insert("fusion-power", Unlock::free().granting([Cap::Fusion]));
tree.insert(
    "warp-drive",
    Unlock::new(Requirement::has(Cap::Fusion)).granting([Cap::Warp]),
);

// The arrow from fusion-power to warp-drive is derived, not declared.
assert_eq!(tree.dependencies(&"warp-drive"), vec![&"fusion-power"]);

let mut held = BTreeSet::new();
assert_eq!(tree.available(&held).collect::<Vec<_>>(), vec![&"fusion-power"]);

tree.take(&"fusion-power", &mut held);

// fusion-power stays available, because its requirement still holds. The
// host tracks which nodes it already took.
assert_eq!(
    tree.available(&held).collect::<Vec<_>>(),
    vec![&"fusion-power", &"warp-drive"],
);

Implementations§

Source§

impl<I, K> Unlocks<I, K>

Source

pub const fn new() -> Self

Creates an empty graph.

Source

pub fn len(&self) -> usize

Returns the number of nodes.

Source

pub fn is_empty(&self) -> bool

Returns whether the graph holds no nodes.

Source

pub fn ids(&self) -> Keys<'_, I, Unlock<K>> ⓘ

Iterates over the node identifiers in order.

Source§

impl<I: Ord, K> Unlocks<I, K>

Source

pub fn insert(&mut self, id: I, unlock: Unlock<K>) -> Option<Unlock<K>>

Adds a node and returns the node it replaced.

Source

pub fn get(&self, id: &I) -> Option<&Unlock<K>>

Returns a node.

Source§

impl<I: Ord, K: Ord> Unlocks<I, K>

Source

pub fn is_available(&self, id: &I, holder: &(impl Has<K> + ?Sized)) -> bool

Returns whether a node can be taken. A missing node never can.

The registry does not track which nodes a player already took, because two nodes may grant the same capability. Keep that record in the host and overlay it on this answer.

Source

pub fn status( &self, id: &I, holder: &(impl Has<K> + ?Sized), taken: &BTreeSet<I>, ) -> Option<NodeStatus>

Returns where a node stands, or None when it is missing.

taken is the host’s record of the nodes the entity took. A taken node reads NodeStatus::Unlocked even when its requirement no longer holds, because the record, not the rule, says what the entity took.

Source

pub fn available<'a, H: Has<K> + ?Sized>( &'a self, holder: &'a H, ) -> impl Iterator<Item = &'a I> + 'a

Iterates over the nodes that can be taken now.

Source

pub fn providers(&self, capability: &K) -> Vec<&I>

Returns every node that grants this capability.

Source

pub fn take<H>(&self, id: &I, holder: &mut H) -> bool
where H: Has<K> + Extend<K>, K: Clone,

Adds a node’s grants to the holder when the node is available.

Returns whether it applied. Taking a node twice is harmless.

The holder both answers the requirement and receives the grants. A set does both. A host type that computes some keys implements Extend to store the grants where its Has reads them.

Source§

impl<I: Ord, K: Ord + Clone> Unlocks<I, K>

Source

pub fn evaluate( &self, id: &I, holder: &(impl Has<K> + ?Sized), ) -> Option<Evaluation<K>>

Explains why a node is available or locked, or None when it is missing.

Source

pub fn dependencies(&self, id: &I) -> Vec<&I>

Returns the nodes that must be taken before this one.

A node is a dependency when it is the only other source of a key that the requirement demands outright. When several nodes grant that key, any one of them unlocks it, so they are optional dependencies instead. A node never depends on itself.

Source

pub fn optional_dependencies(&self, id: &I) -> Vec<&I>

Returns the nodes that could help unlock this one without being required.

These are the sources of the keys inside a choice, and the sources of a required key that more than one node grants. A node that Unlocks::dependencies returns is left out. Draw them differently.

Source

pub fn ranks(&self) -> BTreeMap<I, usize>
where I: Clone,

Returns the rank of every node that has one.

A rank is the earliest round in which the entity can take a node, if it takes every node it can in each round. Only required keys count, and a key that no other node grants counts as held from the start. So a node ranks 0 when no other node grants a key it requires. Otherwise, for each required key, take the lowest rank among the nodes that grant it. The node ranks one more than the highest of those.

Use it as the column index in a tree layout. Every edge that Unlocks::dependencies returns points to a higher rank. A node has no rank when one of its required keys has other sources and none of them has a rank, as on a loop. It is left out, and Unlocks::validate reports it.

Source

pub fn view( &self, holder: &(impl Has<K> + ?Sized), taken: &BTreeSet<I>, ) -> Vec<NodeView<'_, I>>
where I: Clone,

Returns everything a tree view draws, one entry per node, in id order.

Each entry holds the node’s status, its rank, and both kinds of edge. taken is the host’s record of the nodes the entity took. Sort the entries by rank to lay them out in columns.

use std::collections::BTreeSet;
use plotline::{NodeStatus, Requirement, Unlock, Unlocks};

let mut tree = Unlocks::new();
tree.insert("fusion-power", Unlock::free().granting(["fusion"]));
tree.insert(
    "warp-drive",
    Unlock::new(Requirement::has("fusion")).granting(["warp"]),
);

let mut held = BTreeSet::new();
let mut taken = BTreeSet::new();
tree.take(&"fusion-power", &mut held);
taken.insert("fusion-power");

let view = tree.view(&held, &taken);
assert_eq!(view[0].status, NodeStatus::Unlocked);
assert_eq!(view[1].id, &"warp-drive");
assert_eq!(view[1].status, NodeStatus::Available);
assert_eq!(view[1].rank, Some(1));
assert_eq!(view[1].dependencies, vec![&"fusion-power"]);
Source§

impl<I: Ord + Clone, K: Ord + Clone> Unlocks<I, K>

Source

pub fn validate( &self, external: &(impl Has<K> + ?Sized), ) -> Vec<UnlockWarning<I, K>>

Reports authoring problems.

external holds the capabilities that other systems grant, such as a species trait or a starting bonus. Without it, every requirement that no node satisfies looks like dead content.

The checks read required keys only, as Unlocks::ranks does. They do not look inside Any or AtLeast, so a node that only a choice gates is not checked.

Trait Implementations§

Source§

impl<I: Clone, K: Clone> Clone for Unlocks<I, K>

Source§

fn clone(&self) -> Self

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl<I: Debug, K: Debug> Debug for Unlocks<I, K>

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl<I, K> Default for Unlocks<I, K>

Source§

fn default() -> Self

Returns the “default value” for a type. Read more
Source§

impl<'de, I, K> Deserialize<'de> for Unlocks<I, K>
where I: Ord + Deserialize<'de>, K: Ord + Deserialize<'de>,

Source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>
where __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
Source§

impl<I: Eq, K: Eq> Eq for Unlocks<I, K>

Source§

impl<I: PartialEq, K: PartialEq> PartialEq for Unlocks<I, K>

Source§

fn eq(&self, other: &Self) -> bool

Equality operator ==. Read more
1.0.0 (const: unstable) · Source§

fn ne(&self, other: &Rhs) -> bool

Inequality operator !=. Read more
Source§

impl<I, K> Serialize for Unlocks<I, K>
where I: Serialize, K: Serialize,

Source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>
where __S: Serializer,

Serialize this value into the given Serde serializer. Read more
Source§

impl<I: PartialEq, K: PartialEq> StructuralPartialEq for Unlocks<I, K>

Auto Trait Implementations§

§

impl<I, K> Freeze for Unlocks<I, K>
where BTreeMap<I, Unlock<K>>: Freeze,

§

impl<I, K> RefUnwindSafe for Unlocks<I, K>

§

impl<I, K> Send for Unlocks<I, K>
where BTreeMap<I, Unlock<K>>: Send,

§

impl<I, K> Sync for Unlocks<I, K>
where BTreeMap<I, Unlock<K>>: Sync,

§

impl<I, K> Unpin for Unlocks<I, K>
where BTreeMap<I, Unlock<K>>: Unpin,

§

impl<I, K> UnsafeUnpin for Unlocks<I, K>
where BTreeMap<I, Unlock<K>>: UnsafeUnpin,

§

impl<I, K> UnwindSafe for Unlocks<I, K>
where BTreeMap<I, Unlock<K>>: UnwindSafe,

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> DeserializeOwned for T
where T: for<'de> Deserialize<'de>,

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = !

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, !>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.