using System;
using Amphora;
using System.Collections.Generic;
using System.Linq;
namespace Ariadne.UnitTests
{
///
/// ArcTests.TutorialArc written as a script: an intro pause, then passages Act2 and Act3, which both have a
/// "connect" step. Log records run-once code.
///
public partial class TutorialScript : Arc
{
[Scratch]
public readonly List Log = new List();
private void Act2(ArcTests.Gates gates)
{
Log.Add("act2 start");
build:
while (!gates.Open.Contains("build"))
{
Yield();
}
Log.Add("built");
connect:
while (!gates.Open.Contains("connect"))
{
Yield();
}
}
private void Act3(ArcTests.Gates gates)
{
Log.Add("act3 start");
connect:
while (!gates.Open.Contains("act3 connect"))
{
Yield();
}
}
protected override void Run(ArcTests.Gates gates)
{
Log.Add("start");
intro:
while (!gates.Open.Contains("intro"))
{
Yield();
}
Log.Add("after intro");
Act2(gates);
Act3(gates);
Log.Add("end");
}
}
public partial class RepeatedPassageScript : Arc
{
private void Briefing(ArcTests.Gates gates)
{
read:
while (!gates.Open.Contains("read"))
{
Yield();
}
gates.Open.Remove("read");
}
protected override void Run(ArcTests.Gates gates)
{
first:
Briefing(gates);
second:
Briefing(gates);
}
}
public partial class TwoPausesInOneStepScript : Arc
{
protected override void Run(ArcTests.Gates gates)
{
connect:
while (!gates.Open.Contains("breaker"))
{
Yield();
}
while (!gates.Open.Contains("load"))
{
Yield();
}
}
}
///
/// Calls a two-pause passage twice from one call site, so the second call can begin in the tick the first ends.
///
public partial class LoopedPassageScript : Arc
{
[Scratch]
public readonly List Log = new List();
private void Step(ArcTests.Gates gates)
{
Log.Add("step begins");
first:
while (!gates.Open.Contains("A"))
{
Yield();
}
Log.Add("A seen");
second:
while (!gates.Open.Contains("B"))
{
Yield();
}
Log.Add("step ends");
}
protected override void Run(ArcTests.Gates gates)
{
for (int i = 0; i < 2; i++)
{
Step(gates);
}
}
}
///
/// Calls a passage twice, which can return before its pause the second time.
///
public partial class LoopedEarlyReturnScript : Arc
{
[Scratch]
public readonly List Log = new List();
private void Step(ArcTests.Gates gates)
{
Log.Add("step begins");
if (gates.Open.Contains("skip"))
{
return;
}
wait:
while (!gates.Open.Contains("A"))
{
Yield();
}
Log.Add("step ends");
}
protected override void Run(ArcTests.Gates gates)
{
for (int i = 0; i < 2; i++)
{
Step(gates);
}
}
}
public partial class EarlyReturnScript : Arc
{
[Scratch]
public readonly List Log = new List();
protected override void Run(ArcTests.Gates gates)
{
if (gates.Open.Contains("skip"))
{
return;
}
wait:
while (!gates.Open.Contains("wait"))
{
Yield();
}
Log.Add("waited");
}
}
///
/// Counts the ticks spent waiting in a local, then hands a crate count to a passage that counts it down.
///
public partial class LocalsScript : Arc
{
[Scratch]
public readonly List Log = new List();
[Scratch]
public int ArgumentEvaluations;
private int Counted(int value)
{
ArgumentEvaluations++;
return value;
}
private void Deliver(ArcTests.Gates gates, int crates)
{
deliver:
while (crates > 0)
{
if (gates.Open.Remove("crate"))
{
crates--;
}
Yield();
}
Log.Add("delivered");
}
protected override void Run(ArcTests.Gates gates)
{
string greeting = "hello";
Log.Add(greeting);
int waits = 0;
wait:
while (!gates.Open.Contains("go"))
{
waits++;
Yield();
}
Log.Add("waited " + waits);
Deliver(gates, Counted(2));
}
}
public partial class CapturedLocalScript : Arc
{
protected override void Run(ArcTests.Gates gates)
{
string wanted = "go";
wait:
while (!gates.Open.Any(gate => gate == wanted))
{
Yield();
}
}
}
public struct Point
{
public double X;
public double Y;
public Point(double x, double y)
{
X = x;
Y = y;
}
}
public class Waypoint
{
public string Name;
public Point At;
}
///
/// Keeps a struct in a local across a pause, a class in a field, and a list behind an interface.
///
public partial class UserTypesScript : Arc
{
public Waypoint Destination;
public IList Visits = new List();
protected override void Run(ArcTests.Gates gates)
{
var start = new Point(1.5, -2);
wait:
while (!gates.Open.Contains("go"))
{
Yield();
}
Destination = new Waypoint { Name = "back to start", At = start };
}
}
///
/// A game's base class for its scripts, with state of its own to save.
///
public abstract partial class MissionArc : Arc
{
public int Reward = 5;
public string Title { get; set; } = "untitled";
[Scratch]
public int Cache = 1;
}
public partial class MissionScript : MissionArc
{
public int Stage;
protected override void Run(ArcTests.Gates gates)
{
wait:
while (!gates.Open.Contains("go"))
{
Stage++;
Yield();
}
}
}
public partial class ThrowingScript : Arc
{
protected override void Run(ArcTests.Gates gates)
{
explode:
while (!gates.Open.Contains("never"))
{
if (gates.Open.Contains("explode"))
{
throw new InvalidOperationException("boom");
}
Yield();
}
}
}
///
/// Pauses at the top level, then inside each branch of an else-if chain.
///
public partial class BranchingScript : Arc
{
[Scratch]
public readonly List Log = new List();
protected override void Run(ArcTests.Gates gates)
{
Log.Add("start");
first:
Yield();
Log.Add("after first");
if (gates.Open.Contains("left"))
{
Log.Add("left");
second:
Yield();
Log.Add("left done");
}
else if (gates.Open.Contains("middle"))
{
Log.Add("middle");
third:
Yield();
Log.Add("middle done");
}
else
{
Log.Add("right");
fourth:
Yield();
Log.Add("right done");
}
Log.Add("end");
}
}
///
/// Pauses inside an if and a plain block nested in it, with a pause after both.
///
public partial class NestedBlocksScript : Arc
{
[Scratch]
public readonly List Log = new List();
protected override void Run(ArcTests.Gates gates)
{
if (gates.Open.Contains("outer"))
{
Log.Add("outer");
{
Log.Add("inner");
deep:
Yield();
Log.Add("inner done");
}
Log.Add("outer done");
}
after:
Yield();
Log.Add("end");
}
}
///
/// Counts how often its condition runs, which a resume inside the if must not add to.
///
public partial class ConditionProbeScript : Arc
{
[Scratch]
public readonly List Log = new List();
[Scratch]
public int Probes;
private bool Probe(ArcTests.Gates gates)
{
Probes++;
return gates.Open.Contains("go");
}
protected override void Run(ArcTests.Gates gates)
{
if (Probe(gates))
{
inside:
Yield();
Log.Add("inside");
}
Log.Add("end");
}
}
///
/// Declares a local called total in each branch of an if, and keeps it across a pause.
///
public partial class BranchLocalsScript : Arc
{
[Scratch]
public readonly List Log = new List();
protected override void Run(ArcTests.Gates gates)
{
if (gates.Open.Contains("big"))
{
int total = 10;
big:
Yield();
total++;
Log.Add("big " + total);
}
else
{
int total = 1;
small:
Yield();
total++;
Log.Add("small " + total);
}
}
}
///
/// Assigns a local after a pause that it was declared before, without ever reading it across the pause.
///
public partial class AcrossScript : Arc
{
[Scratch]
public readonly List Log = new List();
protected override void Run(ArcTests.Gates gates)
{
if (gates.Open.Contains("go"))
{
int doubled;
mid:
Yield();
doubled = gates.Open.Count * 2;
Log.Add("doubled " + doubled);
}
}
}
///
/// Calls a passage from one branch and waits in a loop in the other.
///
public partial class NestedKindsScript : Arc
{
[Scratch]
public readonly List Log = new List();
private void Briefing(ArcTests.Gates gates)
{
read:
while (!gates.Open.Contains("read"))
{
Yield();
}
Log.Add("briefed");
}
protected override void Run(ArcTests.Gates gates)
{
if (gates.Open.Contains("brief"))
{
Briefing(gates);
Log.Add("after brief");
}
else
{
wait:
while (!gates.Open.Contains("go"))
{
Yield();
}
Log.Add("went");
}
}
}
///
/// Changes a local declared at the top of the passage on both sides of a pause inside an if.
///
public partial class SharedCounterScript : Arc
{
[Scratch]
public readonly List Log = new List();
protected override void Run(ArcTests.Gates gates)
{
int visits = 0;
if (gates.Open.Contains("go"))
{
visits++;
step:
Yield();
visits++;
}
Log.Add("visits " + visits);
}
}
///
/// Pauses in the middle of a while loop that can also continue and break before reaching the pause.
///
public partial class WhileMiddleScript : Arc
{
[Scratch]
public readonly List Log = new List();
protected override void Run(ArcTests.Gates gates)
{
int round = 0;
wait:
while (round < 4)
{
round++;
if (gates.Open.Contains("skip" + round))
{
continue;
}
if (gates.Open.Contains("stop" + round))
{
break;
}
Log.Add("before " + round);
Yield();
Log.Add("after " + round);
}
Log.Add("done " + round);
}
}
///
/// Pauses at the start of each trip around a for loop, whose counter has to survive a save.
///
public partial class ForPauseScript : Arc
{
[Scratch]
public readonly List Log = new List();
protected override void Run(ArcTests.Gates gates)
{
wait:
for (int i = 0; i < 3; i++)
{
Log.Add("i " + i);
Yield();
}
Log.Add("end");
}
}
public partial class DoPauseScript : Arc
{
[Scratch]
public readonly List Log = new List();
protected override void Run(ArcTests.Gates gates)
{
int count = 0;
wait:
do
{
Log.Add("do " + count);
count++;
Yield();
}
while (count < 3);
Log.Add("done");
}
}
///
/// Waits in a loop inside a for loop, so the pause sits two loops deep.
///
public partial class NestedLoopsScript : Arc
{
[Scratch]
public readonly List Log = new List();
protected override void Run(ArcTests.Gates gates)
{
row:
for (int j = 0; j < 2; j++)
{
int k = 0;
while (k < 2)
{
Log.Add("j" + j + "k" + k);
k++;
Yield();
}
Log.Add("row " + j);
}
}
}
///
/// Declares a local in a loop body and uses it after the pause, with a continue inside a switch.
///
public partial class LoopBodyLocalsScript : Arc
{
[Scratch]
public readonly List Log = new List();
protected override void Run(ArcTests.Gates gates)
{
int count = 0;
wait:
while (count < 3)
{
count++;
switch (count)
{
case 2:
continue;
}
int seen = gates.Open.Count;
Yield();
Log.Add("count " + count + " seen " + seen);
}
Log.Add("end");
}
}
///
/// Continues past one trip of a for loop, which has to run the loop's step before the next test.
///
public partial class ForContinueScript : Arc
{
[Scratch]
public readonly List Log = new List();
protected override void Run(ArcTests.Gates gates)
{
wait:
for (int i = 0; i < 4; i++)
{
if (i == 1)
{
continue;
}
Log.Add("i " + i);
Yield();
}
Log.Add("end");
}
}
///
/// Continues past one trip of a do loop, which has to test the loop's condition before the next trip.
///
public partial class DoContinueScript : Arc
{
[Scratch]
public readonly List Log = new List();
protected override void Run(ArcTests.Gates gates)
{
int n = 0;
wait:
do
{
n++;
if (n == 2)
{
continue;
}
Log.Add("n " + n);
Yield();
}
while (n < 3);
Log.Add("end");
}
}
///
/// Pauses in some sections of a switch and not in others, with two labels on one of them.
///
public partial class SwitchPauseScript : Arc
{
[Scratch]
public readonly List Log = new List();
protected override void Run(ArcTests.Gates gates)
{
int choice = 0;
if (gates.Open.Contains("a"))
{
choice = 1;
}
else if (gates.Open.Contains("b"))
{
choice = 2;
}
switch (choice)
{
case 1:
Log.Add("one");
first:
Yield();
Log.Add("one done");
break;
case 2:
case 3:
Log.Add("two or three");
second:
Yield();
Log.Add("two or three done");
break;
default:
Log.Add("other");
break;
}
Log.Add("end");
}
}
///
/// Continues from one section of a switch inside a loop and pauses in another.
///
public partial class SwitchLoopScript : Arc
{
[Scratch]
public readonly List Log = new List();
protected override void Run(ArcTests.Gates gates)
{
int n = 0;
wait:
while (n < 3)
{
n++;
switch (n)
{
case 2:
continue;
case 3:
Log.Add("three");
Yield();
break;
default:
Log.Add("other " + n);
break;
}
Log.Add("after " + n);
}
Log.Add("end");
}
}
public partial class GotoCaseScript : Arc
{
[Scratch]
public readonly List Log = new List();
protected override void Run(ArcTests.Gates gates)
{
int state = gates.Open.Count + 1;
switch (state)
{
case 1:
Log.Add("one");
goto case 2;
case 2:
Log.Add("two");
step:
Yield();
Log.Add("after");
break;
}
}
}
///
/// Parses a number in a guard clause and keeps the out variable across a pause.
///
public partial class GuardClauseScript : Arc
{
[Scratch]
public readonly List Log = new List();
[Scratch]
public string Text = "5";
protected override void Run(ArcTests.Gates gates)
{
if (!int.TryParse(Text, out int count))
{
Log.Add("bad");
return;
}
wait:
Yield();
Log.Add("count " + count);
}
}
///
/// Keeps a pattern variable from an if's condition across a pause inside the if.
///
public partial class PatternIfScript : Arc
{
[Scratch]
public readonly List Log = new List();
[Scratch]
public object Thing;
protected override void Run(ArcTests.Gates gates)
{
if (Thing is string text && text.Length > 0)
{
wait:
Yield();
Log.Add("text " + text);
}
else
{
Log.Add("none");
}
}
}
///
/// Takes an item from a loop condition's out variable and keeps it across a pause in the body.
///
public partial class OutLoopScript : Arc
{
[Scratch]
public readonly List Log = new List();
public int Remaining = 2;
private bool Next(out int item)
{
item = Remaining;
return Remaining-- > 0;
}
protected override void Run(ArcTests.Gates gates)
{
wait:
while (Next(out int item))
{
Yield();
Log.Add("item " + item);
}
Log.Add("end");
}
}
///
/// Picks a section with a pattern variable in the case label and keeps it across a pause in the section.
///
public partial class PatternCaseScript : Arc
{
[Scratch]
public readonly List Log = new List();
[Scratch]
public object Thing;
protected override void Run(ArcTests.Gates gates)
{
switch (Thing)
{
case string text when text.Length > 0:
Log.Add("string " + text);
found:
Yield();
Log.Add("done " + text);
break;
case int number:
Log.Add("int " + number);
break;
default:
Log.Add("other");
break;
}
}
}
///
/// Deconstructs a tuple into locals that are still needed after a pause.
///
public partial class DeconstructionScript : Arc
{
[Scratch]
public readonly List Log = new List();
[Scratch]
public (int, int) Bounds = (2, 9);
protected override void Run(ArcTests.Gates gates)
{
var (low, high) = Bounds;
var (spare, (inner, _)) = (low + 1, (high + 1, 0));
wait:
Yield();
Log.Add(low + "-" + high + " " + inner);
}
}
///
/// Waits in a passage that returns a number, then logs the number it was handed.
///
public partial class AskScript : Arc
{
[Scratch]
public readonly List Log = new List();
private int Ask(ArcTests.Gates gates)
{
int waited = 0;
wait:
while (!gates.Open.Contains("answer"))
{
waited++;
Yield();
}
return gates.Open.Count * 10 + waited;
}
protected override void Run(ArcTests.Gates gates)
{
first:
int choice = Ask(gates);
Log.Add("choice " + choice);
}
}
///
/// Hands a value up through two passages.
///
public partial class ValueChainScript : Arc
{
[Scratch]
public readonly List Log = new List();
private int Inner(ArcTests.Gates gates)
{
wait:
while (!gates.Open.Contains("inner"))
{
Yield();
}
return 7;
}
private int Outer(ArcTests.Gates gates)
{
return Inner(gates);
}
protected override void Run(ArcTests.Gates gates)
{
int result = Outer(gates);
Log.Add("result " + result);
}
}
///
/// Keeps the value a passage returned across a later pause.
///
public partial class KeptResultScript : Arc
{
[Scratch]
public readonly List Log = new List();
private int Ask(ArcTests.Gates gates)
{
int waited = 0;
wait:
while (!gates.Open.Contains("answer"))
{
waited++;
Yield();
}
return gates.Open.Count * 10 + waited;
}
protected override void Run(ArcTests.Gates gates)
{
int first = Ask(gates);
second:
Yield();
Log.Add("first " + first);
}
}
///
/// Takes passage values into a field, then a discard, each keyed by its own label.
///
public partial class AssignedValuesScript : Arc
{
[Scratch]
public readonly List Log = new List();
public int Total;
private int Ask(ArcTests.Gates gates)
{
int waited = 0;
wait:
while (!gates.Open.Contains("answer"))
{
waited++;
Yield();
}
return gates.Open.Count * 10 + waited;
}
protected override void Run(ArcTests.Gates gates)
{
one:
Total = Ask(gates);
two:
_ = Ask(gates);
Log.Add("total " + Total);
}
}
///
/// Returns from different branches of a passage, one of which pauses first.
///
public partial class BranchingValueScript : Arc
{
[Scratch]
public readonly List Log = new List();
private string Pick(ArcTests.Gates gates)
{
if (gates.Open.Contains("left"))
{
wait:
Yield();
return "left done";
}
return "right";
}
protected override void Run(ArcTests.Gates gates)
{
string picked = Pick(gates);
Log.Add(picked);
}
}
///
/// Reads a field before a call that pauses, so the field's old value is what gets added.
///
public partial class SpillOrderScript : Arc
{
[Scratch]
public readonly List Log = new List();
public int Offset = 100;
private int Ask(ArcTests.Gates gates)
{
int waited = 0;
wait:
while (!gates.Open.Contains("answer"))
{
waited++;
Yield();
}
return gates.Open.Count * 10 + waited;
}
protected override void Run(ArcTests.Gates gates)
{
int result = Offset + Ask(gates);
Log.Add("result " + result);
}
}
///
/// Pauses in one call, then another, in a single expression.
///
public partial class TwoCallsScript : Arc
{
[Scratch]
public readonly List Log = new List();
private int Ask(ArcTests.Gates gates)
{
int waited = 0;
wait:
while (!gates.Open.Contains("answer"))
{
waited++;
Yield();
}
return gates.Open.Count * 10 + waited;
}
private int AskLater(ArcTests.Gates gates)
{
later:
while (!gates.Open.Contains("later"))
{
Yield();
}
return 7;
}
protected override void Run(ArcTests.Gates gates)
{
int sum = Ask(gates) * 100 + AskLater(gates);
Log.Add("sum " + sum);
}
}
///
/// Returns more than a bare call, and uses a call as a receiver.
///
public partial class SpillReturnScript : Arc
{
[Scratch]
public readonly List Log = new List();
private int Ask(ArcTests.Gates gates)
{
int waited = 0;
wait:
while (!gates.Open.Contains("answer"))
{
waited++;
Yield();
}
return gates.Open.Count * 10 + waited;
}
private int Doubled(ArcTests.Gates gates)
{
return Ask(gates) * 2;
}
protected override void Run(ArcTests.Gates gates)
{
int doubled = Doubled(gates);
Log.Add("doubled " + doubled);
}
}
public partial class SpillReceiverScript : Arc
{
[Scratch]
public readonly List Log = new List();
private int Ask(ArcTests.Gates gates)
{
int waited = 0;
wait:
while (!gates.Open.Contains("answer"))
{
waited++;
Yield();
}
return gates.Open.Count * 10 + waited;
}
protected override void Run(ArcTests.Gates gates)
{
Log.Add("got " + Ask(gates).ToString());
}
}
///
/// Hands a value call's result to a passage that pauses with it.
///
public partial class RememberScript : Arc
{
[Scratch]
public readonly List Log = new List();
private int Ask(ArcTests.Gates gates)
{
int waited = 0;
wait:
while (!gates.Open.Contains("answer"))
{
waited++;
Yield();
}
return gates.Open.Count * 10 + waited;
}
private void Remember(ArcTests.Gates gates, int value)
{
wait:
while (!gates.Open.Contains("remember"))
{
Yield();
}
Log.Add("remembered " + value);
}
protected override void Run(ArcTests.Gates gates)
{
Remember(gates, Ask(gates));
}
}
///
/// Asks only when a flag is set, in the right side of an and.
///
public partial class AndAskScript : Arc
{
[Scratch]
public readonly List Log = new List();
private int Ask(ArcTests.Gates gates)
{
int waited = 0;
wait:
while (!gates.Open.Contains("answer"))
{
waited++;
Yield();
}
return gates.Open.Count * 10 + waited;
}
protected override void Run(ArcTests.Gates gates)
{
bool ok = gates.Open.Contains("flag") && Ask(gates) > 15;
Log.Add("ok " + ok);
}
}
///
/// Asks only when a flag is not set, in the right side of an or.
///
public partial class OrAskScript : Arc
{
[Scratch]
public readonly List Log = new List();
private int Ask(ArcTests.Gates gates)
{
int waited = 0;
wait:
while (!gates.Open.Contains("answer"))
{
waited++;
Yield();
}
return gates.Open.Count * 10 + waited;
}
protected override void Run(ArcTests.Gates gates)
{
bool ok = gates.Open.Contains("flag") || Ask(gates) > 15;
Log.Add("ok " + ok);
}
}
///
/// Uses a cached number if there is one, and asks if not.
///
public partial class CoalesceAskScript : Arc
{
[Scratch]
public readonly List Log = new List();
public int? Cached;
private int Ask(ArcTests.Gates gates)
{
int waited = 0;
wait:
while (!gates.Open.Contains("answer"))
{
waited++;
Yield();
}
return gates.Open.Count * 10 + waited;
}
protected override void Run(ArcTests.Gates gates)
{
int value = Cached ?? Ask(gates);
Log.Add("value " + value);
}
}
///
/// Picks between a literal and a passage.
///
public partial class TernaryAskScript : Arc
{
[Scratch]
public readonly List Log = new List();
private string Describe(ArcTests.Gates gates)
{
describe:
while (!gates.Open.Contains("describe"))
{
Yield();
}
return "slow";
}
protected override void Run(ArcTests.Gates gates)
{
string how = gates.Open.Contains("fast") ? "quick" : Describe(gates);
Log.Add("how " + how);
}
}
public class Recorder
{
public readonly List Seen = new List();
public void Record(int value)
{
Seen.Add(value);
}
}
///
/// Calls a method on an object that may not be there, with an argument that pauses.
///
public partial class MaybeRecordScript : Arc
{
public Recorder Helper;
private int Ask(ArcTests.Gates gates)
{
int waited = 0;
wait:
while (!gates.Open.Contains("answer"))
{
waited++;
Yield();
}
return gates.Open.Count * 10 + waited;
}
protected override void Run(ArcTests.Gates gates)
{
Helper?.Record(Ask(gates));
}
}
///
/// Reads a field ahead of a conditional that might pause, so its old value is what gets used.
///
public partial class OrderThroughBranchScript : Arc
{
[Scratch]
public readonly List Log = new List();
public int Offset = 100;
private int Ask(ArcTests.Gates gates)
{
int waited = 0;
wait:
while (!gates.Open.Contains("answer"))
{
waited++;
Yield();
}
return gates.Open.Count * 10 + waited;
}
protected override void Run(ArcTests.Gates gates)
{
int total = Offset + (gates.Open.Contains("flag") ? Ask(gates) : 0);
Log.Add("total " + total);
}
}
///
/// Branches on a passage call in an if condition.
///
public partial class IfAskScript : Arc
{
[Scratch]
public readonly List Log = new List();
private int Ask(ArcTests.Gates gates)
{
int waited = 0;
wait:
while (!gates.Open.Contains("answer"))
{
waited++;
Yield();
}
return gates.Open.Count * 10 + waited;
}
protected override void Run(ArcTests.Gates gates)
{
if (Ask(gates) > 15)
{
Log.Add("big");
}
else
{
Log.Add("small");
}
}
}
///
/// Asks again on every trip of a while loop.
///
public partial class WhileAskScript : Arc
{
[Scratch]
public readonly List Log = new List();
private int Ask(ArcTests.Gates gates)
{
int waited = 0;
wait:
while (!gates.Open.Contains("answer"))
{
waited++;
Yield();
}
return gates.Open.Count * 10 + waited;
}
protected override void Run(ArcTests.Gates gates)
{
int trips = 0;
while (Ask(gates) < 25)
{
trips++;
gates.Open.Add("trip" + trips);
}
Log.Add("trips " + trips);
}
}
///
/// Asks after each trip of a do loop.
///
public partial class DoAskScript : Arc
{
[Scratch]
public readonly List Log = new List();
private int Ask(ArcTests.Gates gates)
{
int waited = 0;
wait:
while (!gates.Open.Contains("answer"))
{
waited++;
Yield();
}
return gates.Open.Count * 10 + waited;
}
protected override void Run(ArcTests.Gates gates)
{
int trips = 0;
do
{
trips++;
gates.Open.Add("trip" + trips);
}
while (Ask(gates) < 25);
Log.Add("trips " + trips);
}
}
///
/// Starts a for loop at a value it asks for.
///
public partial class ForInitAskScript : Arc
{
[Scratch]
public readonly List Log = new List();
private int Ask(ArcTests.Gates gates)
{
int waited = 0;
wait:
while (!gates.Open.Contains("answer"))
{
waited++;
Yield();
}
return gates.Open.Count * 10 + waited;
}
protected override void Run(ArcTests.Gates gates)
{
for (int i = Ask(gates); i < 24; i++)
{
Log.Add("i " + i);
}
}
}
///
/// Asks on every test of a for loop.
///
public partial class ForConditionAskScript : Arc
{
[Scratch]
public readonly List Log = new List();
private int Ask(ArcTests.Gates gates)
{
int waited = 0;
wait:
while (!gates.Open.Contains("answer"))
{
waited++;
Yield();
}
return gates.Open.Count * 10 + waited;
}
protected override void Run(ArcTests.Gates gates)
{
for (int i = 0; i < Ask(gates) / 10; i++)
{
Log.Add("i " + i);
}
}
}
///
/// Asks in the step of a for loop.
///
public partial class ForStepAskScript : Arc
{
[Scratch]
public readonly List Log = new List();
private void Note(int value)
{
Log.Add("note " + value);
}
private int Ask(ArcTests.Gates gates)
{
int waited = 0;
wait:
while (!gates.Open.Contains("answer"))
{
waited++;
Yield();
}
return gates.Open.Count * 10 + waited;
}
protected override void Run(ArcTests.Gates gates)
{
for (int i = 0; i < 2; Note(Ask(gates)))
{
i++;
}
}
}
///
/// Switches on a passage call.
///
public partial class SwitchAskScript : Arc
{
[Scratch]
public readonly List Log = new List();
private int Ask(ArcTests.Gates gates)
{
int waited = 0;
wait:
while (!gates.Open.Contains("answer"))
{
waited++;
Yield();
}
return gates.Open.Count * 10 + waited;
}
protected override void Run(ArcTests.Gates gates)
{
switch (Ask(gates))
{
case 11:
Log.Add("eleven");
break;
default:
Log.Add("other");
break;
}
}
}
///
/// Declares two locals at once, the second from a passage that pauses and the first.
///
public partial class TwoDeclaratorsScript : Arc
{
[Scratch]
public readonly List Log = new List();
private int Wait(ArcTests.Gates gates, string name)
{
wait:
while (!gates.Open.Contains(name))
{
Yield();
}
return gates.Open.Count;
}
protected override void Run(ArcTests.Gates gates)
{
both:
int a = Wait(gates, "one"), b = Wait(gates, "two") + a;
Log.Add(a + "," + b);
}
}
///
/// Declares a local from a plain call, then another from a passage.
///
public partial class DeclaratorOrderScript : Arc
{
[Scratch]
public readonly List Log = new List();
public int Calls;
private int Next()
{
return ++Calls;
}
private int Wait(ArcTests.Gates gates, string name)
{
wait:
while (!gates.Open.Contains(name))
{
Yield();
}
return gates.Open.Count;
}
protected override void Run(ArcTests.Gates gates)
{
int first = Next(), second = Wait(gates, "one");
Log.Add(first + "," + second + "," + Calls);
}
}
///
/// Tests a passage's answer against patterns, and keeps the variable one declares across a later pause.
///
public partial class PatternAskScript : Arc
{
[Scratch]
public readonly List Log = new List();
private int Ask(ArcTests.Gates gates)
{
wait:
while (!gates.Open.Contains("answer"))
{
Yield();
}
return gates.Open.Count * 10;
}
protected override void Run(ArcTests.Gates gates)
{
if (Ask(gates) is int n && n > 15)
{
show:
Yield();
Log.Add("n " + n);
}
Log.Add("done");
}
}
///
/// Sorts a passage's answer with a switch expression and a relational pattern.
///
public partial class SwitchExpressionAskScript : Arc
{
[Scratch]
public readonly List Log = new List();
private int Ask(ArcTests.Gates gates)
{
wait:
while (!gates.Open.Contains("answer"))
{
Yield();
}
return gates.Open.Count * 10;
}
protected override void Run(ArcTests.Gates gates)
{
string size = Ask(gates) switch { > 15 => "big", _ => "small" };
Log.Add(size);
}
}
///
/// Picks what to do from a switch expression whose arms ask, one of them using a pattern variable.
///
public partial class ArmAskScript : Arc
{
[Scratch]
public readonly List Log = new List();
public int Calls;
private int Ask(ArcTests.Gates gates)
{
wait:
while (!gates.Open.Contains("answer"))
{
Yield();
}
return gates.Open.Count * 10;
}
private int Scale(ArcTests.Gates gates, int factor)
{
scale:
while (!gates.Open.Contains("scale"))
{
Yield();
}
return factor * 100;
}
private int Count()
{
return ++Calls;
}
protected override void Run(ArcTests.Gates gates)
{
string mode = gates.Open.Contains("fast") ? "fast" : gates.Open.Contains("slow") ? "slow" : "other";
int value = mode switch
{
"fast" => Count(),
"slow" => Ask(gates),
string text when text.Length > 3 => Scale(gates, text.Length),
_ => -1,
};
Log.Add(mode + " " + value + " " + Calls);
}
}
///
/// Adds to a field and a local, and fills an array slot, from passages that pause.
///
public partial class AssignAskScript : Arc
{
[Scratch]
public readonly List Log = new List();
public int Total = 5;
public int[] Numbers = new int[4];
public int Calls;
public string Name;
private int Ask(ArcTests.Gates gates)
{
wait:
while (!gates.Open.Contains("answer"))
{
Yield();
}
return gates.Open.Count * 10;
}
private string Describe(ArcTests.Gates gates)
{
describe:
while (!gates.Open.Contains("describe"))
{
Yield();
}
return "described";
}
private int Next()
{
return ++Calls;
}
protected override void Run(ArcTests.Gates gates)
{
field:
Total += Ask(gates);
local:
int sum = 1;
sum += Ask(gates);
slot:
Numbers[Next()] = Ask(gates);
name:
Name ??= Describe(gates);
Log.Add(Total + " " + sum + " " + Numbers[1] + " " + Calls + " " + Name);
}
}
///
/// Takes a pair from passages, into new locals and into existing ones.
///
public partial class PairAskScript : Arc
{
[Scratch]
public readonly List Log = new List();
public int Total;
private (int, int) Range(ArcTests.Gates gates)
{
range:
while (!gates.Open.Contains("answer"))
{
Yield();
}
return (gates.Open.Count, gates.Open.Count * 10);
}
protected override void Run(ArcTests.Gates gates)
{
fresh:
var (low, high) = Range(gates);
pause:
Yield();
Log.Add(low + "-" + high);
int other;
existing:
(Total, other) = Range(gates);
Log.Add(Total + "-" + other);
}
}
///
/// Calls passages that hand back values through out parameters, in each form a call can take.
///
public partial class OutParameterScript : Arc
{
[Scratch]
public readonly List Log = new List();
public int Stored;
private bool TryPick(ArcTests.Gates gates, out int choice)
{
choice = 1;
pick:
while (!gates.Open.Contains("answer"))
{
Yield();
}
choice = gates.Open.Count * 10;
return gates.Open.Contains("accept");
}
private void Fill(ArcTests.Gates gates, out int number, out string text)
{
number = 5;
text = "early";
if (gates.Open.Contains("skip"))
{
return;
}
fill:
while (!gates.Open.Contains("fill"))
{
Yield();
}
number++;
text = "filled";
}
private void Relay(ArcTests.Gates gates, out int number)
{
relay:
Fill(gates, out number, out string ignored);
}
protected override void Run(ArcTests.Gates gates)
{
header:
if (TryPick(gates, out int choice))
{
Log.Add("picked " + choice);
}
else
{
Log.Add("declined " + choice);
}
field:
TryPick(gates, out Stored);
Log.Add("stored " + Stored);
declared:
bool ok = TryPick(gates, out var again);
Log.Add("again " + again + " " + ok);
both:
Fill(gates, out int number, out string text);
Log.Add(number + " " + text);
relayed:
Relay(gates, out int relayedNumber);
Log.Add("relayed " + relayedNumber);
}
}
///
/// Passes a local and a field by reference to a passage that changes it before and after a pause.
///
public partial class RefParameterScript : Arc
{
[Scratch]
public readonly List Log = new List();
public int Total = 7;
private void Bump(ArcTests.Gates gates, ref int counter)
{
counter += 1;
bump:
while (!gates.Open.Contains("go"))
{
Yield();
}
counter *= 10;
}
private void Forward(ArcTests.Gates gates, ref int counter)
{
forward:
Bump(gates, ref counter);
counter += 5;
}
protected override void Run(ArcTests.Gates gates)
{
int local = 2;
call:
Bump(gates, ref local);
Log.Add("local " + local);
field:
Bump(gates, ref Total);
Log.Add("total " + Total);
relayed:
Forward(gates, ref local);
Log.Add("relayed " + local);
}
}
///
/// Passes arguments that have side effects to passages that pause, with and without using them afterward.
///
public partial class ArgumentEffectScript : Arc
{
[Scratch]
public readonly List Log = new List();
public int Calls;
private int Next()
{
return ++Calls;
}
private void Hold(ArcTests.Gates gates, int value)
{
hold:
while (!gates.Open.Contains("go"))
{
Yield();
}
}
private void Show(ArcTests.Gates gates, in int amount, string label = "amount")
{
show:
while (!gates.Open.Contains("show"))
{
Yield();
}
Log.Add(label + " " + amount);
}
private void Pass(ArcTests.Gates gates, in int amount)
{
pass:
Show(gates, in amount);
}
protected override void Run(ArcTests.Gates gates)
{
unused:
Hold(gates, Next());
shown:
Show(gates, Next());
named:
Show(gates, label: "named", amount: Next());
passed:
int local = Next();
Pass(gates, in local);
}
}
///
/// Reads a caller's field through a ref readonly parameter after a pause.
///
public partial class ReadonlyRefScript : Arc
{
[Scratch]
public readonly List Log = new List();
public int Level = 4;
private void Watch(ArcTests.Gates gates, ref readonly int level)
{
watch:
while (!gates.Open.Contains("go"))
{
Yield();
}
Log.Add("level " + level);
}
protected override void Run(ArcTests.Gates gates)
{
watching:
Watch(gates, in Level);
}
}
///
/// Pauses at a bare Yield() step, and jumps back to that step until a gate opens.
///
public partial class StepJumpScript : Arc
{
[Scratch]
public readonly List Log = new List();
protected override void Run(ArcTests.Gates gates)
{
Log.Add("start");
retry:
Yield();
Log.Add("woke");
if (!gates.Open.Contains("go"))
{
goto retry;
}
Log.Add("end");
}
}
///
/// Gives two sibling blocks a step with the same label, each jumping back to its own until a gate opens.
///
public partial class SiblingLabelScript : Arc
{
[Scratch]
public readonly List Log = new List();
protected override void Run(ArcTests.Gates gates)
{
if (gates.Open.Contains("left"))
{
again:
Yield();
Log.Add("left");
if (!gates.Open.Contains("stop"))
{
goto again;
}
}
else
{
again:
Yield();
Log.Add("right");
if (!gates.Open.Contains("stop"))
{
goto again;
}
}
Log.Add("end");
}
}
///
/// Gives a block that never pauses a label of the same name as a step in its sibling block.
///
public partial class CopiedLabelScript : Arc
{
[Scratch]
public readonly List Log = new List();
protected override void Run(ArcTests.Gates gates)
{
if (gates.Open.Contains("left"))
{
again:
Log.Add("copied");
if (Log.Count < 3)
{
goto again;
}
}
else
{
again:
Yield();
Log.Add("step");
}
Log.Add("end");
}
}
///
/// Jumps back to a label on an ordinary statement, ahead of the step that pauses.
///
public partial class PlainLabelJumpScript : Arc
{
[Scratch]
public readonly List Log = new List();
protected override void Run(ArcTests.Gates gates)
{
again:
Log.Add("body");
hold:
Yield();
if (Log.Count < 3)
{
goto again;
}
Log.Add("end");
}
}
///
/// Offers a passage for other arcs to run, which keeps a local across its pause and changes this arc's own state.
///
public partial class WorkerScript : Arc
{
[Scratch]
public readonly List Log = new List();
public int Total;
public int Add(ArcTests.Gates gates, int amount)
{
int seen = 0;
wait:
while (!gates.Open.Contains("go"))
{
seen++;
Yield();
}
Total += amount;
Log.Add("added " + amount + " after " + seen);
return Total;
}
protected override void Run(ArcTests.Gates gates)
{
}
}
///
/// Runs the passage of two other arcs of one class in turn.
///
public partial class ClientScript : Arc
{
[Scratch]
public readonly List Log = new List();
public WorkerScript First = new WorkerScript();
public WorkerScript Second = new WorkerScript();
protected override void Run(ArcTests.Gates gates)
{
int before = 3;
one:
int a = First.Add(gates, 5);
two:
int b = Second.Add(gates, 7);
Log.Add("a " + a + " b " + b + " before " + before);
}
}
///
/// Runs the passage of one other arc twice from one call.
///
public partial class RepeatingClientScript : Arc
{
public WorkerScript Worker = new WorkerScript();
protected override void Run(ArcTests.Gates gates)
{
for (int i = 0; i < 2; i++)
{
Worker.Add(gates, 10);
}
}
}
///
/// Calls a passage with params arguments, one of which has an effect.
///
public partial class ParamsScript : Arc
{
[Scratch]
public readonly List Log = new List();
public int Evaluations;
private string Next()
{
Evaluations++;
return "n" + Evaluations;
}
private void Say(ArcTests.Gates gates, params string[] lines)
{
wait:
while (!gates.Open.Contains("go"))
{
Yield();
}
Log.Add(string.Join(",", lines));
}
protected override void Run(ArcTests.Gates gates)
{
say:
Say(gates, "a", Next());
}
}
///
/// Holds a passage for the scripts that derive from it, which keeps a local across its pause.
///
public abstract partial class StepsArc : Arc
{
[Scratch]
public readonly List Log = new List();
protected void Hold(ArcTests.Gates gates, string name)
{
int seen = 0;
wait:
while (!gates.Open.Contains("go"))
{
seen++;
Yield();
}
Log.Add(name + " held " + seen);
}
}
///
/// Runs the passage of the class it derives from, twice.
///
public partial class HoldingScript : StepsArc
{
protected override void Run(ArcTests.Gates gates)
{
first:
Hold(gates, "first");
second:
Hold(gates, "second");
}
}
///
/// Runs the same inherited passage as HoldingScript.
///
public partial class OtherHoldingScript : StepsArc
{
protected override void Run(ArcTests.Gates gates)
{
only:
Hold(gates, "only");
}
}
///
/// Derives from a class that derives from the one with the passage.
///
public abstract partial class MoreStepsArc : StepsArc
{
public int Extra;
}
public partial class DeepHoldingScript : MoreStepsArc
{
protected override void Run(ArcTests.Gates gates)
{
deep:
Hold(gates, "deep");
}
}
///
/// Runs a passage of its own that runs the passage of another arc.
///
public partial class RelayScript : Arc
{
public WorkerScript Worker = new WorkerScript();
private void Relay(ArcTests.Gates gates)
{
int through = Worker.Add(gates, 1);
Worker.Log.Add("through " + through);
}
protected override void Run(ArcTests.Gates gates)
{
Relay(gates);
}
}
///
/// Pauses until its gate opens, then throws. Counts how often its code before the throw ran.
///
public partial class FaultingScript : Arc
{
public int Attempts;
protected override void Run(ArcTests.Gates gates)
{
hold:
while (!gates.Open.Contains("finish"))
{
if (gates.Open.Contains("explode"))
{
Attempts++;
throw new InvalidOperationException("boom");
}
Yield();
}
}
}
///
/// Throws like FaultingScript, and says which area it was in.
///
public partial class NotingScript : Arc
{
public string Area = "cellar";
protected override void AddFaultNotes(Dictionary notes)
{
notes["Area"] = Area;
}
protected override void Run(ArcTests.Gates gates)
{
hold:
while (!gates.Open.Contains("finish"))
{
if (gates.Open.Contains("explode"))
{
throw new InvalidOperationException("boom");
}
Yield();
}
}
}
///
/// Throws like FaultingScript, and then throws again when asked to say what it was doing.
///
public partial class BrokenNotingScript : Arc
{
protected override void AddFaultNotes(Dictionary notes)
{
notes["Before"] = "kept";
throw new InvalidOperationException("notes");
}
protected override void Run(ArcTests.Gates gates)
{
hold:
while (!gates.Open.Contains("finish"))
{
if (gates.Open.Contains("explode"))
{
throw new InvalidOperationException("boom");
}
Yield();
}
}
}
///
/// Records the times the latest tick gave it while it waits.
///
public partial class ClockScript : Arc
{
public long SeenDeltaTimeTicks;
public long SeenGameTicks;
protected override void Run(ArcTests.Gates gates)
{
hold:
while (!gates.Open.Contains("finish"))
{
SeenDeltaTimeTicks = DeltaTime.Ticks;
SeenGameTicks = GameTime.Ticks;
Yield();
}
}
}
///
/// Waits a second of game time, keeping its deadline in a local across the pause.
///
public partial class DeadlineScript : Arc
{
public bool Done;
protected override void Run(ArcTests.Gates gates)
{
Duration deadline = GameTime + Duration.FromSeconds(1);
wait:
while (GameTime < deadline)
{
Yield();
}
Done = true;
}
}
///
/// Offers a passage for other arcs to run, which records the times it sees once its gate opens.
///
public partial class TimeWorkerScript : Arc
{
public long SeenDeltaTimeTicks;
public long SeenGameTicks;
public void Note(ArcTests.Gates gates)
{
wait:
while (!gates.Open.Contains("go"))
{
Yield();
}
SeenDeltaTimeTicks = DeltaTime.Ticks;
SeenGameTicks = GameTime.Ticks;
}
protected override void Run(ArcTests.Gates gates)
{
}
}
///
/// Runs the passage of another arc, which is never ticked itself.
///
public partial class TimeClientScript : Arc
{
public TimeWorkerScript Worker = new TimeWorkerScript();
protected override void Run(ArcTests.Gates gates)
{
note:
Worker.Note(gates);
}
}
///
/// Ticks the arc it holds before or after its own script, which waits for that arc to finish.
///
public partial class ParentScript : Arc
{
[Scratch]
public readonly List Log = new List();
public FaultingScript Child = new FaultingScript();
public bool ChildFirst = true;
protected override bool TickSelf(ArcTests.Gates gates, TickContext tick)
{
if (ChildFirst)
{
Child.Tick(gates, tick);
Log.Add("child");
return base.TickSelf(gates, tick);
}
bool finished = base.TickSelf(gates, tick);
Child.Tick(gates, tick);
Log.Add("child");
return finished;
}
protected override void Run(ArcTests.Gates gates)
{
Log.Add("start");
wait:
while (!Child.Finished)
{
Log.Add("self");
Yield();
}
Log.Add("end");
}
}
///
/// Ticks the arc it holds instead of running its own script, and is done when that arc is.
///
public partial class SkippingScript : Arc
{
public FaultingScript Child = new FaultingScript();
public int Turns;
public bool Ran;
protected override bool TickSelf(ArcTests.Gates gates, TickContext tick)
{
++Turns;
Child.Tick(gates, tick);
return Child.Finished;
}
protected override void Run(ArcTests.Gates gates)
{
Ran = true;
}
}
///
/// Ticks the arc it holds and then throws.
///
public partial class ThrowingTurnScript : Arc
{
public FaultingScript Child = new FaultingScript();
public int Turns;
protected override bool TickSelf(ArcTests.Gates gates, TickContext tick)
{
++Turns;
Child.Tick(gates, tick);
throw new InvalidOperationException("turn");
}
protected override void Run(ArcTests.Gates gates)
{
}
}
///
/// Ticks itself.
///
public partial class SelfTickingScript : Arc
{
protected override bool TickSelf(ArcTests.Gates gates, TickContext tick)
{
Tick(gates, tick);
return false;
}
protected override void Run(ArcTests.Gates gates)
{
}
}
///
/// Ticks another arc of its own class, which can tick it back.
///
public partial class LoopingScript : Arc
{
public LoopingScript Other;
public int Turns;
protected override bool TickSelf(ArcTests.Gates gates, TickContext tick)
{
++Turns;
Other?.Tick(gates, tick);
return false;
}
protected override void Run(ArcTests.Gates gates)
{
}
}
}