RError.com

RError.com Logo RError.com Logo

RError.com Navigation

  • 主页

Mobile menu

Close
  • 主页
  • 系统&网络
    • 热门问题
    • 最新问题
    • 标签
  • Ubuntu
    • 热门问题
    • 最新问题
    • 标签
  • 帮助
主页 / 问题 / 1483990
Accepted
Максим Фисман
Максим Фисман
Asked:2023-01-06 16:55:43 +0000 UTC2023-01-06 16:55:43 +0000 UTC 2023-01-06 16:55:43 +0000 UTC

逻辑门序列软件架构

  • 772

逻辑教学应用:玩家可以从逻辑门组装电路(电路)。我希望他能收集到某种锁链,然后将它们合二为一。

例如,我们组装一个由两个门(AND 和 NOT)组成的电路,并将其组合成一个 NAND 门:

在此处输入图像描述

在此处输入图像描述

在这种情况下,每个阀门可以有一个或多个输入(input)和一个或多个输出(outputs)。

我想不通怎么写这样的门类。这是我的想法:

  1. 创建委托:'委托列表 GateFunction(List)'

  2. 门类包含一个“GateFunction func”字段,该字段定义将输入转换为输出的函数。

  3. 门类包含一个“List _inputs”数组,该数组包含对连接到此门的输入的先前门的输出的引用。

一个阀门可以有多个输出是非常重要的,一个阀门的不同输出可以转到不同的阀门。

我需要这样一种体系结构,该类的每个实例都存储一个对基本函数进行操作的 SPECIFIC func 函数。

最主要的是,在运行时,用户将构建并保存他自己的、新的、自定义的阀门,包括以前创建的(或基本的:和/或/不)。

我将不胜感激一般建议和代码。也许我错过了一些方便的明显模式......

想法和截图来自 Sebastian Lague 视频 ( https://youtu.be/QZwneRb-zqA )。它完美地展示了我需要 4 到 5 分钟的时间。




UPD:解释示例。场上的玩家可以从可用的方块中收集左边的方案,点击“创建”按钮,得到右边对应的方块。同时可以看到,左边的所有功能都打开了,也就是可以连接到原来的联系人(屏幕左边第一个截图)。使用鼠标按钮打开和关闭这些联系人。也就是说,这些触点不包含在阀门本身中。因此,有必要能够告诉完成的、组装好的阀门其输入端连接到哪些触点。 在此处输入图像描述


UPD2:通过单击“创建”按钮,阀门应保存到菜单中,以便以后可以从那里获取和使用:

在此处输入图像描述

c#
  • 3 3 个回答
  • 64 Views

3 个回答

  • Voted
  1. Yaroslav
    2023-01-07T04:44:47Z2023-01-07T04:44:47Z
    public abstract class BoolChain
    {
        public abstract bool GetOutValue ();
    }
    

    public class TrueChain : BoolChain
    {
        public override bool GetOutValue () => true;
    
        public override string ToString () => "True";
    }
    

    public class NotChain : BoolChain
    {
        public BoolChain In;
    
        public NotChain (BoolChain inChain = null)
        {
            In = inChain;
        }
    
        public override bool GetOutValue () => In.GetOutValue() == false;
    
        public override string ToString () => $"Not({In})";
    }
    

    public class AndChain : BoolChain
    {
        public readonly BoolChain[] In;
    
        public AndChain (BoolChain[] inChain)
        {
            In = inChain;
        }
    
        public AndChain (int inCapacity = 2) : this(new BoolChain[inCapacity]) { }
    
        public override bool GetOutValue () 
        { 
            foreach (var chain in In)
                if (chain.GetOutValue() == false)
                    return false;
            return true;
        }
    
        public override string ToString () => $"And({string.Join(", ", In.Select(i => i.ToString()))})";
    }
    

    public class OrChain : BoolChain
    {
        public readonly BoolChain[] In;
    
        public OrChain (BoolChain[] inChain)
        {
            In = inChain;
        }
    
        public OrChain (int inCapacity = 2) : this(new BoolChain[inCapacity]) { }
    
        public override bool GetOutValue ()
        {
            foreach (var chain in In)
                if (chain.GetOutValue())
                    return true;
            return false;
        }
    
        public override string ToString () => $"Or({string.Join(", ", In.Select(i => i.ToString()))})";
    }
    

    你的BoolValve应该包含BoolChain可以是任何东西。例如,同一个[False]门将包含new NotChain(new TrueChain()) "Not(True)". 加一个prototype来复制整个链条Clone(),如果加任何flag IsSelected,就可以限制复制CloneSelected(),放到一个新的BoolValve,不管是什么。

    • 3
  2. Best Answer
    iiKuzmychov
    2023-01-07T04:56:46Z2023-01-07T04:56:46Z

    不是 Unity 程序员,但考虑到块必须支持从 GUI 更改的能力,在普通 C# 中可以这样实现。

    信号发生器:

    // генератор сигналов (ничего не принимает на вход)
    public interface ISignalGenerator
    {
        public bool GetOutput();
    }
    
    // генератор True-сигналов
    // реализует синглтон, так как более чем в одном таком генераторе смысла нет, он может даже не быть отдельным блоком в GUI
    public sealed class TrueGenerator : ISignalGenerator
    {
        private static TrueGenerator? s_instance;
    
        public static TrueGenerator Instance => s_instance ??= new();
    
        private TrueGenerator() { }
    
        public bool GetOutput() => true;
    }
    
    // генератор False-сигналов
    // реализует синглтон, так как более чем в одном таком генераторе смысла нет, он может даже не быть отдельным блоком в GUI
    public sealed class FalseGenerator : ISignalGenerator
    {
        private static FalseGenerator? s_instance;
    
        public static FalseGenerator Instance => s_instance ??= new();
    
        private FalseGenerator() { }
    
        public bool GetOutput() => false;
    }
    

    阀门:

    // вентиль (генерирует сигнал на основе входных сигналов)
    public interface ILogicGate : ISignalGenerator
    {
        // список генераторов для каждого из входов
        public IReadOnlyList<ISignalGenerator> InputGenerators { get; }
    
        // присоединить вход под номером inputIndex к генератору inputGenerator
        public void ConnectInputWithOutput(int inputIndex, ISignalGenerator inputGenerator);
    
        // отсоединить вход под номером inputIndex от генератора (по-сути, это тоже самое что и присоединить к False-генератору) 
        public void DisconnectInputGenerator(int inputIndex);
    }
    

    具有多个输入的阀门:

    public abstract class MultiInputGate : ILogicGate
    {
        private readonly ISignalGenerator[] _inputGenerators;
    
        public IReadOnlyList<ISignalGenerator> InputGenerators { get; }
    
        public MultiInputGate(int inputsCount)
        {
            if (inputsCount <= 1)
                throw new ArgumentOutOfRangeException(nameof(inputsCount));
    
            _inputGenerators = new ISignalGenerator[inputsCount];
            InputGenerators  = new ReadOnlyCollection<ISignalGenerator>(_inputGenerators);
    
            // подсоединяем все входы к False-генератору
            for (int i = 0; i < inputsCount; i++)
                _inputGenerators[i] = FalseGenerator.Instance;
        }
    
        public void DisconnectInputGenerator(int inputIndex)
        {
            if (inputIndex < 0 || inputIndex >= InputGenerators.Count)
                throw new ArgumentOutOfRangeException(nameof(inputIndex));
    
            ConnectInputWithOutput(inputIndex, FalseGenerator.Instance);
        }
    
        public virtual void ConnectInputWithOutput(int inputIndex, ISignalGenerator inputGenerator)
        {
            if (inputIndex < 0 || inputIndex >= InputGenerators.Count)
                throw new ArgumentOutOfRangeException(nameof(inputIndex));
    
            _inputGenerators[inputIndex] = inputGenerator ?? throw new ArgumentNullException(nameof(inputGenerator));
        }
    
        public abstract bool GetOutput();
    
        public override string ToString()
        {
            var inputs = string.Join(", ", InputGenerators.Select(generator => Convert.ToInt32(generator.GetOutput())));
            var output = Convert.ToInt32(GetOutput());
    
            return $"({inputs}) ---{GetType().Name}--> {output}";
        }
    }
    
    public sealed class AndGate : MultiInputGate
    {
        public AndGate() : base(2) { }
    
        public override bool GetOutput() => InputGenerators[0].GetOutput() & InputGenerators[1].GetOutput();
    }
    

    带 1 个入口的阀门:

    Класс SingleInputGate создан для удобного взаимодействия (например, позволяет писать gate.InputGenerator вместо gate.InputGenerators[0]).

    public abstract class SingleInputGate : ILogicGate
    {
        private readonly ISignalGenerator[] _inputGenerators;
    
        public ISignalGenerator InputGenerator
        {
            get => _inputGenerators[0];
            private set => _inputGenerators[0] = value;
        }
        IReadOnlyList<ISignalGenerator> ILogicGate.InputGenerators => _inputGenerators;
    
        public SingleInputGate()
        {
            _inputGenerators = new ISignalGenerator[] { FalseGenerator.Instance };
        }
    
        public void DisconnectInputGenerator() => ConnectInputWithOutput(FalseGenerator.Instance);
    
        public virtual void ConnectInputWithOutput(ISignalGenerator inputGenerator) =>
            InputGenerator = inputGenerator ?? throw new ArgumentNullException(nameof(inputGenerator));
    
        public abstract bool GetOutput();
    
        public override string ToString()
        {
            var input  = Convert.ToInt32(InputGenerator.GetOutput());
            var output = Convert.ToInt32(GetOutput());
    
            return $"({input}) ---{GetType().Name}--> {output}";
        }
    
        void ILogicGate.DisconnectInputGenerator(int inputIndex)
        {
            if (inputIndex != 0)
                throw new ArgumentOutOfRangeException(nameof(inputIndex));
    
            DisconnectInputGenerator();
        }
    
        void ILogicGate.ConnectInputWithOutput(int inputIndex, ISignalGenerator inputGenerator)
        {
            if (inputIndex != 0)
                throw new ArgumentOutOfRangeException(nameof(inputIndex));
    
            ConnectInputWithOutput(inputGenerator);
        }
    }
    
    public sealed class NotGate : SingleInputGate
    {
        public override bool GetOutput() => !InputGenerator.GetOutput();
    }
    
    public class DirectGate : SingleInputGate
    {
        public sealed override bool GetOutput() => InputGenerator.GetOutput();
    }
    

    Пользовательский вентиль:

    public sealed class UserGate : ILogicGate
    {
        private readonly DirectGate[] _inputGates;
    
        public DirectGate OutputGate { get; } = new();
        public IReadOnlyList<DirectGate> InputGates { get; }
        IReadOnlyList<ISignalGenerator> ILogicGate.InputGenerators =>
            InputGates.Select(gate => gate.InputGenerator).ToArray();
    
        public UserGate(int inputsCount)
        {
            if (inputsCount < 1)
                throw new ArgumentOutOfRangeException(nameof(inputsCount));
    
            _inputGates = new DirectGate[inputsCount];
            InputGates  = new ReadOnlyCollection<DirectGate>(_inputGates);
    
            for (int i = 0; i < inputsCount; i++)
                _inputGates[i] = new();
        }
    
        public bool GetOutput() => OutputGate.GetOutput();
    
        public override string ToString()
        {
            var inputs = string.Join(", ", InputGates.Select(gate => Convert.ToInt32(gate.GetOutput())));
            var output = Convert.ToInt32(GetOutput());
    
            return $"({inputs}) ---{GetType().Name}--> {output}";
        }
    
        void ILogicGate.DisconnectInputGenerator(int inputIndex)
        {
            if (inputIndex < 0 || inputIndex >= InputGates.Count)
                throw new ArgumentOutOfRangeException(nameof(inputIndex));
    
            InputGates[inputIndex].DisconnectInputGenerator();
        }
    
        void ILogicGate.ConnectInputWithOutput(int inputIndex, ISignalGenerator inputGenerator)
        {
            if (inputIndex < 0 || inputIndex >= InputGates.Count)
                throw new ArgumentOutOfRangeException(nameof(inputIndex));
    
            InputGates[inputIndex].ConnectInputWithOutput(inputGenerator);
        }
    }
    

    Тут сразу стоит пояснить, что весь сакральный смысл состоит в том, что каждый вход и выход пользовательского вентиля тоже является вентилем (DirectGate). Благодаря этому мы можем сделать связи как на картинке:

    生成器连接到 NAND Как видно из картинки, мы подключаем генератор (GEN) не напрямую к какому-то из внутренних вентелей (AND/NOT), а к входным вентилям (0/1), которые потом подключаются к внутренним (AND/NOT). Аналогично с выходом (R)

    Пример из картинки в виде кода:

    var and = new AndGate(); // создали блок AND
    var not = new NotGate(); // создали блок NOT
    not.ConnectInputWithOutput(and); // подключили вход NOT к выходу AND
    
    var nand = new UserGate(inputsCount: 2); // создали блок NAND с 2-мя входами
    nand.OutputGate.ConnectInputWithOutput(not); // поключили выход NAND к выходу NOT
    
    and.ConnectInputWithOutput(0, nand.InputGates[0]); // подключаем 0-й вход AND к 0-му входу NAND
    and.ConnectInputWithOutput(1, nand.InputGates[1]); // подключаем 1-й вход AND к 1-му входу NAND
    
    // подключаем входные сигналы NAND к генератору
    nand.InputGates[0].ConnectInputWithOutput(TrueGenerator.Instance);
    nand.InputGates[1].ConnectInputWithOutput(TrueGenerator.Instance);
    
    Console.WriteLine(nand); // (1, 1) ---UserGate--> 0
    
    // отключаем входные сигналы NAND от генератора
    //nand.InputGates[0].DisconnectInputGenerator();
    //nand.InputGates[1].DisconnectInputGenerator();
    
    //Console.WriteLine(nand); // (0, 0) ---UserGate--> 1
    

    Протестировать онлайн можно тут.


    СОХРАНЕНИЕ

    Я не реализовывал сериализацию, но я думаю она вполне возможна с моим кодом. Я это вижу так:

    1. 向每个生成器/门添加一个属性以Guid将其唯一 ID 存储在模式中
    2. 假设我们正在使用 System.Text.Json,那么对于类,ILogicGate我们需要制作一个转换器,将其所有输入转换为数组Guid,反之亦然。为了使反序列化工作,您需要将列表更改IReadOnlyList<ISignalGenerator> InputGenerators为可变的。理想情况下,您应该制作自己的列表,以便您可以安全地更改值并检查null.
    • 1
  3. Andrew_STOP_RU_AGRESSION_IN_UA
    2023-01-06T17:06:06Z2023-01-06T17:06:06Z

    我正在用伪代码编写。夏普已经忘记了)

    class Valve {
       private var type: ValveType
       var maxInputs: Int { type.MaxInputs() }
       var maxOutputs: Int { type.MaxOutputs() }
       var inputs: List<Point> = new List<Point>
       var outputs: List<Point> = new List<Point>
       
       func actionFunc() -> Void{
          switch type {
              case And: 
                 andFunc();
                 break;
              case Or:
                 orFunc()
                 break;
              default:
                 defaultFunc()
          }
       }
    }
    
    enum ValveType {
        case And
        case Or
        case Xor
        case Not
        case Nand
    }
    
    extension ValveType {
        func MaxInputs() -> Int {
            switch self {
                case Nand:
                   return 2;
                   break;
                default: 
                   return 2;
                   break;
            }
        }
    
        func MaxOutputs() -> Int {
            return 1
        }
    }
    
    



    好吧,或者给自己修一家工厂,该工厂将发布具有必要设置的阀门

    • 0

相关问题

  • 使用嵌套类导出 xml 文件

  • 分层数据模板 [WPF]

  • 如何在 WPF 中为 ListView 手动创建列?

  • 在 2D 空间中,Collider 2D 挂在玩家身上,它对敌人的重量相同,我需要它这样当它们碰撞时,它们不会飞向不同的方向。统一

  • 如何在 c# 中使用 python 神经网络来创建语音合成?

  • 如何知道类中的方法是否属于接口?

Sidebar

Stats

  • 问题 10021
  • Answers 30001
  • 最佳答案 8000
  • 用户 6900
  • 常问
  • 回答
  • Marko Smith

    我看不懂措辞

    • 1 个回答
  • Marko Smith

    请求的模块“del”不提供名为“default”的导出

    • 3 个回答
  • Marko Smith

    "!+tab" 在 HTML 的 vs 代码中不起作用

    • 5 个回答
  • Marko Smith

    我正在尝试解决“猜词”的问题。Python

    • 2 个回答
  • Marko Smith

    可以使用哪些命令将当前指针移动到指定的提交而不更改工作目录中的文件?

    • 1 个回答
  • Marko Smith

    Python解析野莓

    • 1 个回答
  • Marko Smith

    问题:“警告:检查最新版本的 pip 时出错。”

    • 2 个回答
  • Marko Smith

    帮助编写一个用值填充变量的循环。解决这个问题

    • 2 个回答
  • Marko Smith

    尽管依赖数组为空,但在渲染上调用了 2 次 useEffect

    • 2 个回答
  • Marko Smith

    数据不通过 Telegram.WebApp.sendData 发送

    • 1 个回答
  • Martin Hope
    Alexandr_TT 2020年新年大赛! 2020-12-20 18:20:21 +0000 UTC
  • Martin Hope
    Alexandr_TT 圣诞树动画 2020-12-23 00:38:08 +0000 UTC
  • Martin Hope
    Air 究竟是什么标识了网站访问者? 2020-11-03 15:49:20 +0000 UTC
  • Martin Hope
    Qwertiy 号码显示 9223372036854775807 2020-07-11 18:16:49 +0000 UTC
  • Martin Hope
    user216109 如何为黑客设下陷阱,或充分击退攻击? 2020-05-10 02:22:52 +0000 UTC
  • Martin Hope
    Qwertiy 并变成3个无穷大 2020-11-06 07:15:57 +0000 UTC
  • Martin Hope
    koks_rs 什么是样板代码? 2020-10-27 15:43:19 +0000 UTC
  • Martin Hope
    Sirop4ik 向 git 提交发布的正确方法是什么? 2020-10-05 00:02:00 +0000 UTC
  • Martin Hope
    faoxis 为什么在这么多示例中函数都称为 foo? 2020-08-15 04:42:49 +0000 UTC
  • Martin Hope
    Pavel Mayorov 如何从事件或回调函数中返回值?或者至少等他们完成。 2020-08-11 16:49:28 +0000 UTC

热门标签

javascript python java php c# c++ html android jquery mysql

Explore

  • 主页
  • 问题
    • 热门问题
    • 最新问题
  • 标签
  • 帮助

Footer

RError.com

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

帮助

© 2023 RError.com All Rights Reserve   沪ICP备12040472号-5