Project · Unity companions

Unity interaction rules companion

A complete portable C# project with 17 independent key, focus, range and invalid-input checks; no UnityEngine dependency.

All C# source files below are loaded together in the playground. Files written by a run live in its virtual directory.

Program.cs

using System;
public static class Program
{
    public static void Main()
    {
        Console.WriteLine(InteractionChecks.Run() + " interaction checks passed");
    }
}

Domain/GateState.cs

using System;
namespace SharpRune.InteractionRules
{
public sealed class GateState
{
    public bool HasKey { get; private set; }
    public bool Opened { get; private set; }
    public void GrantKey() { if (!Opened) HasKey = true; }
    public bool TryOpen(bool focused, float distance, float maximum)
    {
        if (maximum <= 0f || float.IsNaN(maximum) || float.IsInfinity(maximum)) throw new ArgumentOutOfRangeException(nameof(maximum));
        if (distance < 0f || float.IsNaN(distance) || float.IsInfinity(distance)) throw new ArgumentOutOfRangeException(nameof(distance));
        if (Opened || !HasKey || !focused || distance > maximum) return false;
        HasKey = false; Opened = true; return true;
    }
}
}

Checks/InteractionChecks.cs

using System;
using SharpRune.InteractionRules;
public static class InteractionChecks
{
    public static int Run()
    {
        int passed = 0;
        void Check(bool actual, string name)
        {
            if (!actual) throw new InvalidOperationException(name);
            Console.WriteLine("PASS " + name); passed++;
        }
        bool Rejects(Action action)
        {
            try { action(); return false; }
            catch (ArgumentOutOfRangeException) { return true; }
        }
        var gate = new GateState();
        Check(!gate.HasKey && !gate.Opened, "fresh closed state");
        Check(!gate.TryOpen(true, 1f, 3f) && !gate.Opened, "key required");
        gate.GrantKey(); Check(gate.HasKey, "key grant");
        Check(!gate.TryOpen(false, 1f, 3f) && gate.HasKey, "focus rejection preserves key");
        Check(!gate.TryOpen(true, 3.01f, 3f) && gate.HasKey, "range rejection preserves key");
        Check(gate.TryOpen(true, 3f, 3f) && gate.Opened && !gate.HasKey, "exact boundary consumes once");
        Check(!gate.TryOpen(true, 1f, 3f), "opened state is idempotent");
        gate.GrantKey(); Check(!gate.HasKey, "opened gate cannot regain key");
        var other = new GateState(); other.GrantKey();
        Check(!other.Opened && other.HasKey && gate.Opened, "instances independent");
        Check(Rejects(() => other.TryOpen(true, -1f, 3f)) && other.HasKey && !other.Opened, "negative distance rejected unchanged");
        Check(Rejects(() => other.TryOpen(true, float.NaN, 3f)), "NaN distance rejected");
        Check(Rejects(() => other.TryOpen(true, float.PositiveInfinity, 3f)), "infinite distance rejected");
        Check(Rejects(() => other.TryOpen(true, 1f, 0f)), "zero maximum rejected");
        Check(Rejects(() => other.TryOpen(true, 1f, -1f)), "negative maximum rejected");
        Check(Rejects(() => other.TryOpen(true, 1f, float.NaN)), "NaN maximum rejected");
        Check(Rejects(() => other.TryOpen(true, 1f, float.PositiveInfinity)), "infinite maximum rejected");
        Check(other.TryOpen(true, 0f, 3f) && !other.HasKey, "zero distance accepted");
        return passed;
    }
}

InteractionCompanion.csproj

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>net9.0</TargetFramework>
    <LangVersion>9.0</LangVersion>
    <ImplicitUsings>disable</ImplicitUsings>
    <Nullable>enable</Nullable>
  </PropertyGroup>
</Project>