Skip to content

Commit

Permalink
Allow asking if a node name is a node group. Return an single item wh…
Browse files Browse the repository at this point in the history
…en querying for salient options given a non-group node name.
  • Loading branch information
desplesda committed Nov 27, 2024
1 parent 7970128 commit 91e73f5
Show file tree
Hide file tree
Showing 4 changed files with 115 additions and 0 deletions.
8 changes: 8 additions & 0 deletions YarnSpinner.Compiler/NodeGroupCompiler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,14 @@ internal IEnumerable<NodeCompilationResult> CompileNodeGroup()
Name = NodeGroupName,
};

// Mark that this node is a hub node, so that it can be queried for
// later
hubNode.Headers.Add(new Header
{
Key = Node.NodeIsHubNodeHeader,
Value = "1"
});

var hubNodeDebugInfo = new NodeDebugInfo("<generated>", NodeGroupName);
this.CurrentNode = hubNode;
this.CurrentNodeDebugInfo = hubNodeDebugInfo;
Expand Down
43 changes: 43 additions & 0 deletions YarnSpinner.Tests/SaliencyTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -278,5 +278,48 @@ This content is only seen once.
var availableContentAfterRun = this.dialogue.GetSaliencyOptionsForNodeGroup("Start");
availableContentAfterRun.Where(c => c.FailingConditionValueCount == 0).Should().HaveCount(0);
}

[Fact]
public void TestDialogueCanBeQueriedForNodeGroups()
{
// Given

string source = @"
title: Start
when: once
---
This content is only seen once.
===
title: Start
when: $a == 2
---
This content is only seen when a is 2.
===
title: NotAGroup
---
This node is not part of a node group.
===
";

CompileAndPrepareDialogue(source);

this.dialogue.NodeExists("DoesntExist").Should().BeFalse();

this.dialogue.NodeExists("Start").Should().BeTrue();
this.dialogue.IsNodeGroup("Start").Should().BeTrue();

this.dialogue.NodeExists("NotAGroup").Should().BeTrue();
this.dialogue.IsNodeGroup("NotAGroup").Should().BeFalse();

// We can always ask for saliency options given a valid node name,
// even if it's not a node group

var queryInvalidNodeName = () => { this.dialogue.GetSaliencyOptionsForNodeGroup("DoesntExist"); };
queryInvalidNodeName.Should().ThrowExactly<ArgumentException>().WithMessage("*not a valid node name*");

this.dialogue.GetSaliencyOptionsForNodeGroup("Start").Should().HaveCount(2);
this.dialogue.GetSaliencyOptionsForNodeGroup("NotAGroup").Should().HaveCount(1);

}
}
}
51 changes: 51 additions & 0 deletions YarnSpinner/Dialogue.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1076,11 +1076,62 @@ public bool TryGetSmartVariable<T>(string name, out T result)
/// cref="Saliency.ContentSaliencyOption"/> objects that may appear if
/// and when the node group <paramref name="nodeGroup"/> is run.
/// </returns>
/// <exception cref="ArgumentException">Thrown when <paramref
/// name="nodeGroup"/> is not a valid node name.</exception>
public IEnumerable<Saliency.ContentSaliencyOption> GetSaliencyOptionsForNodeGroup(string nodeGroup)
{
if (NodeExists(nodeGroup) == false)
{
// This node doesn't exist - it can't be a node OR a node group,
// and we've been asked for an invalid value.
throw new ArgumentException($"{nodeGroup} is not a valid node name");
}

if (IsNodeGroup(nodeGroup) == false)
{
// This is not a node group, it's a plain node. Return a single
// content saliency "option" that represents this node.
return new[] {
new Saliency.ContentSaliencyOption(nodeGroup) {
ComplexityScore = 0,
ContentType = Saliency.ContentSaliencyContentType.Node,
PassingConditionValueCount = 1,
FailingConditionValueCount = 0,
}
};
}

// This is a valid node group name. Ask the saliency system to
// produce the collection of options that could run.
return SmartVariableEvaluationVirtualMachine.GetSaliencyOptionsForNodeGroup(nodeGroup, this.VariableStorage, this.Library);
}

/// <summary>
/// Gets a value indicating whether <paramref name="nodeName"/> is the
/// name of a valid node group in the program.
/// </summary>
/// <param name="nodeName">The name of the node group to check.</param>
/// <returns><see langword="true"/> if <paramref name="nodeName"/> is
/// the name of a node group; <see langword="false"/>
/// otherwise.</returns>
/// <exception cref="InvalidOperationException">Thrown when <see
/// cref="Program"/> is null.</exception>
public bool IsNodeGroup(string nodeName)
{
if (this.Program == null)
{
throw new InvalidOperationException($"Can't determine if {nodeName} is a hub node, because no program has been set.");
}

if (this.Program.Nodes.TryGetValue(nodeName, out var node) == false)
{
// Not a valid node, so not a valid node group.
return false;
}

return node.IsNodeGroupHub;
}

// The standard, built-in library of functions and operators.
internal class StandardLibrary : Library
{
Expand Down
13 changes: 13 additions & 0 deletions YarnSpinner/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -570,6 +570,7 @@ public IEnumerable<string> Tags
internal const string TrackingVariableNameHeader = "$Yarn.Internal.TrackingVariable";
internal const string ContentSaliencyConditionVariablesHeader = "$Yarn.Internal.ContentSaliencyVariables";
internal const string ContentSaliencyConditionComplexityScoreHeader = "$Yarn.Internal.ContentSaliencyComplexity";
internal const string NodeIsHubNodeHeader = "$Yarn.Internal.NodeGroupHub";

/// <summary>
/// The name of the header that indicates a node's title.
Expand Down Expand Up @@ -612,6 +613,18 @@ public string? TrackingVariableName
}
}

/// <summary>
/// Gets a value indicating whether this node is the 'hub' node for a
/// node group.
/// </summary>
public bool IsNodeGroupHub
{
get
{
return GetHeaderValue(NodeIsHubNodeHeader) != null;
}
}

/// <summary>
/// Gets an enumerable containing the names of variables that must be
/// evaluated in order to determine whether this node can be selected as
Expand Down

0 comments on commit 91e73f5

Please sign in to comment.