diff --git a/src/nobilis/game_data.py b/src/nobilis/game_data.py
--- a/src/nobilis/game_data.py
+++ b/src/nobilis/game_data.py
@@ -244,18 +244,18 @@
SPECIAL_CONTACT_TEMPLATES += GeneralTemplate('Mystery Cult', ()),
-AFFLIATION_TEMPLATES = ()
-AFFLIATION_TEMPLATES += GeneralTemplate(
+AFFILIATION_TEMPLATES = ()
+AFFILIATION_TEMPLATES += GeneralTemplate(
'(The Song of) Heaven', (1, 2, 3)),
-AFFLIATION_TEMPLATES += GeneralTemplate(
+AFFILIATION_TEMPLATES += GeneralTemplate(
'(The Song of) Hell', (4, 5, 6)),
-AFFLIATION_TEMPLATES += GeneralTemplate(
+AFFILIATION_TEMPLATES += GeneralTemplate(
'(The Song of) The Light', (7, 8)),
-AFFLIATION_TEMPLATES += GeneralTemplate(
+AFFILIATION_TEMPLATES += GeneralTemplate(
'(The Song of) The Dark', (9, 10)),
-AFFLIATION_TEMPLATES += GeneralTemplate(
+AFFILIATION_TEMPLATES += GeneralTemplate(
'(The Song of) The Wild', (11, 12, 13)),
-AFFLIATION_TEMPLATES += GeneralTemplate(
+AFFILIATION_TEMPLATES += GeneralTemplate(
'An Independent Song', (14, 15, 16)),
diff --git a/src/nobilis/lifepath.py b/src/nobilis/lifepath.py
--- a/src/nobilis/lifepath.py
+++ b/src/nobilis/lifepath.py
@@ -12,6 +12,7 @@
def key(item):
+ """Return a type-based key for the given item."""
return type(item), item
@@ -19,6 +20,7 @@
# now. Basically, it needs to be used as a mixin AHEAD OF a namedtuple class
# with a first field of "hash".
class QuickHash:
+ """Base class for namedtuples that store their hash in the first field."""
__slots__ = ()
@@ -28,6 +30,7 @@
@classmethod
def _pre_hash(cls, *args, **kwargs):
+ """Return the hash the instance will use."""
args = tuple(args)
fields = getattr(cls, '_fields', ('hash',))[1:]
keywords = fields[len(args):]
@@ -42,10 +45,12 @@
class Child(Business):
+ """Purely structural child visitor."""
__slots__ = ()
def default_visit(self, node):
+ """Yield zero times."""
yield from ()
@@ -53,6 +58,7 @@
class Tree(QH, nt('Tree', 'hash root reverse_mapping')):
+ """Purely structural tree structure, for determining parents."""
__slots__ = ()
@@ -71,77 +77,64 @@
return super().__new__(cls, root, mapping.Map(reverse_mapping))
def parent(self, node):
+ """Return the parent of the given node."""
return self.reverse_mapping.get(key(node))
@Business.mark
class Choice(QH, nt('Choice', 'hash template')):
-
- __slots__ = ()
-
-
[email protected]
-class ChoiceDisplay(QH, nt('ChoiceDisplay', 'hash choice')):
+ """An underlying choice for tracking state."""
__slots__ = ()
+# First to test in rewrite.
@Business.mark
-class ChoiceTaken(QH, nt('ChoiceTaken', 'hash choice')):
+class Tab(QH, nt('Tab', 'hash name node')):
__slots__ = ()
[email protected]
-class ChoiceListed(QH, nt('ChoiceListed', 'hash choice')):
-
- __slots__ = ()
-
-
+# First to test in rewrite.
@Business.mark
-class ChoiceList(QH, nt('ChoiceList', 'hash choices initial_min initial_max')):
-
- __slots__ = ()
-
-
[email protected]
-class DisplayList(QH, nt('DisplayList', 'hash factory choices_ sections_')):
+class TabList(QH, nt('TabList', 'hash tabs')):
__slots__ = ()
@property
- def choices(self):
- return map_tuple.MapTuple(self.factory, self.choices_)
-
- @property
- def sections(self):
- return map_tuple.MapTuple(
- functools.partial(Section, self), range(len(self.sections_)))
+ def enumerated_names(self):
+ return tuple(enumerate(tab.name for tab in self.tabs))
+# Second to test in rewrite, may need rename.
@Business.mark
-class Section(QH, nt('Section', 'hash display_list index')):
+class Table(QH, nt('Table', 'hash heading node')):
+
+ __slots__ = ()
+
+
+# Second to test in rewrite, may need rename.
[email protected]
+class TableList(QH, nt('TableList', 'hash items')):
__slots__ = ()
- @property
- def _data(self):
- return self.display_list.sections_[self.index]
- @property
- def name(self):
- return self._data[0]
-
- @property
- def predicate(self):
- return self._data[1]
[email protected](Tab)
+def Child(self, node, next_visitor):
+ yield node.node
[email protected](ChoiceList)
[email protected](TabList)
def Child(self, node, next_visitor):
- yield from node.choices
+ yield from node.tabs
[email protected](DisplayList)
[email protected](Table)
def Child(self, node, next_visitor):
- yield from node.choices
+ yield node.node
+
+
[email protected](TableList)
+def Child(self, node, next_visitor):
+ yield from node.items
diff --git a/tests/nobilis/test_lifepath.py b/tests/nobilis/test_lifepath.py
--- a/tests/nobilis/test_lifepath.py
+++ b/tests/nobilis/test_lifepath.py
@@ -7,7 +7,7 @@
def test_key(lifepath):
assert lifepath.key(lifepath.Choice(None)) != lifepath.key(
- lifepath.ChoiceDisplay(None))
+ lifepath.TabList(None))
def test_hash_amortization(lifepath):
@@ -61,55 +61,133 @@
def test_children(lifepath):
child = lifepath.Child()
- leaf = lifepath.Choice(None)
+ tab = lifepath.Tab('name', None)
- assert tuple(child.visit(leaf)) == ()
+ tab_list = lifepath.TabList((tab,))
+
+ assert tuple(child.visit(tab_list)) == (tab,)
+ assert tuple(child.visit(tab)) == (None,)
- for wrapper in (lifepath.ChoiceDisplay, lifepath.ChoiceTaken,
- lifepath.ChoiceListed):
- assert tuple(child.visit(wrapper(leaf))) == ()
+ item = lifepath.Table('heading', None)
- assert tuple(child.visit(lifepath.ChoiceList((leaf,), 0, None))) == (leaf,)
+ item_list = lifepath.TableList((item,))
- display_list = lifepath.DisplayList(
- lifepath.ChoiceDisplay, (leaf,), (('name', None),))
-
- assert tuple(child.visit(display_list)) == (lifepath.ChoiceDisplay(leaf),)
+ assert tuple(child.visit(item_list)) == (item,)
+ assert tuple(child.visit(item)) == (None,)
def test_tree(lifepath):
- leaf = lifepath.Choice(None)
- root = lifepath.ChoiceList((leaf,), 0, None)
+ leaf = lifepath.Tab('test1', None)
+ root = lifepath.TabList((leaf,))
tree = lifepath.Tree(root)
assert tree.parent(leaf) == root
- rooter = lifepath.ChoiceList((root,), 0, None)
+ rooter = lifepath.TabList((root,))
treeer = lifepath.Tree(rooter)
assert treeer.parent(root) == rooter
assert treeer.parent(leaf) == root
- dag = lifepath.ChoiceList((leaf, leaf), 0, None)
+ dag = lifepath.TabList((leaf, leaf))
with pytest.raises(ValueError):
print(lifepath.Tree(dag))
-def test_section(lifepath):
- leaf = lifepath.Choice(None)
- sentinel = object()
-
- display_list = lifepath.DisplayList(
- lifepath.ChoiceDisplay, (leaf,), (('name', sentinel),))
-
- section, = display_list.sections
-
- assert section.name == 'name'
- assert section.predicate is sentinel
-
-
def test_state(lifepath):
# Okay, so, let's think how this has to go.
# Ideally, I wouldn't be relying on mutable state, so...
# state = lifepath.State()
pass
+
+
+# Okay, let's start over. At the current way of thinking of things, a character
+# sheet is going to have multiple tabs.
+
+def test_power_tabs(lifepath):
+ placeholder = object()
+
+ # Must be selected before the others.
+ key_tab = lifepath.Tab('Keys', placeholder)
+ # No additional dependencies.
+ foundation_tab = lifepath.Tab('Foundation', placeholder)
+ # No additional dependencies.
+ estate_tab = lifepath.Tab('Estate', placeholder)
+ # Internal dependencies.
+ origins_tab = lifepath.Tab('Origins', placeholder)
+ # Some interaction with Origins.
+ contacts_tab = lifepath.Tab('Contacts', placeholder)
+ # No additional dependencies.
+ affiliation_tab = lifepath.Tab('Affiliation', placeholder)
+ # No additional dependencies.
+ background_tab = lifepath.Tab('Background', placeholder)
+
+ tab_list = lifepath.TabList((
+ key_tab,
+ foundation_tab,
+ estate_tab,
+ origins_tab,
+ contacts_tab,
+ affiliation_tab,
+ background_tab))
+
+ # Now that we have the tab_list, we need to reason about it...
+ # Would be good to get an enumerated list of names.
+
+ assert tab_list.enumerated_names == (
+ (0, 'Keys'),
+ (1, 'Foundation'),
+ (2, 'Estate'),
+ (3, 'Origins'),
+ (4, 'Contacts'),
+ (5, 'Affiliation'),
+ (6, 'Background'),)
+
+
+# Actually, what if I do both kinds of character sheet?
+
+def test_deceiver_tabs(lifepath):
+ placeholder = object()
+
+ # Must be selected before the others.
+ key_tab = lifepath.Tab('Keys', placeholder)
+ # The Foundation tab seems the most mix-and-matchable.
+ # No additional dependencies.
+ foundation_tab = lifepath.Tab('Foundation', placeholder)
+ # No additional dependencies.
+ p_state_tab = lifepath.Tab('pState', placeholder)
+ # No internal dependencies.
+ breakthrough_tab = lifepath.Tab('Breakthrough', placeholder)
+ # May end up with 2 plan tabs
+ # No internal dependencies.
+ plans_tab = lifepath.Tab('Plans', placeholder)
+ # Contacts divided into typical and rare, both sorted, which implies some
+ # way of marking them outside of a section label.
+ # Of note: no dependency on other sections. Just pick "up to three"
+ # Not sure if that REQUIRES at least 1, it's probably a good idea though.
+ # No internal dependencies.
+ contacts_tab = lifepath.Tab('Contacts', placeholder)
+ # No internal dependencies.
+ justification_tab = lifepath.Tab('Justification', placeholder)
+ # No internal dependencies.
+ territory_tab = lifepath.Tab('Territory', placeholder)
+
+ tab_list = lifepath.TabList((
+ key_tab,
+ foundation_tab,
+ p_state_tab,
+ breakthrough_tab,
+ plans_tab,
+ contacts_tab,
+ justification_tab,
+ territory_tab))
+
+ assert tab_list.enumerated_names == (
+ (0, 'Keys'),
+ (1, 'Foundation'),
+ (2, 'pState'),
+ (3, 'Breakthrough'),
+ (4, 'Plans'),
+ (5, 'Contacts'),
+ (6, 'Justification'),
+ (7, 'Territory'),)
Comments