public static Blueprint Generate(SpriteShifterConfiguration configuration)
        {
            var signalCount = configuration.SignalCount ?? ScreenUtil.PixelSignals.Count;

            const int maxFilters       = 20;
            const int shifterCount     = 32;
            const int inputSignalCount = 32;

            var inputSignals = ComputerSignals.OrderedSignals.Take(inputSignalCount).ToList();
            var offsetSignal = VirtualSignalNames.Dot;

            var entities  = new List <Entity>();
            var inputMaps = new Entity[inputSignalCount];
            var shifters  = new Shifter[shifterCount];

            // Input maps
            for (var processorIndex = 0; processorIndex < inputSignalCount; processorIndex++)
            {
                var inputSignal = inputSignals[processorIndex];
                var y           = processorIndex * 4 + 3;

                if (y % 18 == 8)
                {
                    y--;
                }
                else if (y % 18 == 9)
                {
                    y++;
                }

                var inputMap = new Entity
                {
                    Name     = ItemNames.ConstantCombinator,
                    Position = new Position
                    {
                        X = 1,
                        Y = y
                    },
                    Direction        = Direction.Right,
                    Control_behavior = new ControlBehavior
                    {
                        Filters = new List <Filter> {
                            Filter.Create(offsetSignal, processorIndex + 1)
                        }
                    }
                };
                inputMaps[processorIndex] = inputMap;
                entities.Add(inputMap);
            }

            // Shifters
            for (var shifterIndex = 0; shifterIndex < shifterCount; shifterIndex++)
            {
                var shifterX = shifterIndex * 2 + shifterIndex / 8 * 2 + 2;

                var outputLink = new Entity
                {
                    Name     = ItemNames.BigElectricPole,
                    Position = new Position
                    {
                        X = 0.5 + shifterX,
                        Y = -1.5
                    }
                };
                entities.Add(outputLink);

                var inputSquared = new Entity
                {
                    Name     = ItemNames.ArithmeticCombinator,
                    Position = new Position
                    {
                        X = shifterX,
                        Y = 0.5
                    },
                    Direction        = Direction.Down,
                    Control_behavior = new ControlBehavior
                    {
                        Arithmetic_conditions = new ArithmeticConditions
                        {
                            First_signal    = SignalID.Create(VirtualSignalNames.Each),
                            Second_constant = 2,
                            Operation       = ArithmeticOperations.Exponentiation,
                            Output_signal   = SignalID.Create(VirtualSignalNames.Each)
                        }
                    }
                };
                entities.Add(inputSquared);

                var bufferedInput = new Entity
                {
                    Name     = ItemNames.ArithmeticCombinator,
                    Position = new Position
                    {
                        X = 0.5 + shifterX,
                        Y = 2
                    },
                    Direction        = Direction.Right,
                    Control_behavior = new ControlBehavior
                    {
                        Arithmetic_conditions = new ArithmeticConditions
                        {
                            First_signal    = SignalID.Create(VirtualSignalNames.Each),
                            Second_constant = 1,
                            Operation       = ArithmeticOperations.Multiplication,
                            Output_signal   = SignalID.Create(VirtualSignalNames.Each)
                        }
                    }
                };
                entities.Add(bufferedInput);

                var negativeInputSquared = new Entity
                {
                    Name     = ItemNames.ArithmeticCombinator,
                    Position = new Position
                    {
                        X = 1 + shifterX,
                        Y = 0.5
                    },
                    Direction        = Direction.Up,
                    Control_behavior = new ControlBehavior
                    {
                        Arithmetic_conditions = new ArithmeticConditions
                        {
                            First_signal    = SignalID.Create(VirtualSignalNames.Each),
                            Second_constant = -1,
                            Operation       = ArithmeticOperations.Multiplication,
                            Output_signal   = SignalID.Create(VirtualSignalNames.Each)
                        }
                    }
                };
                entities.Add(negativeInputSquared);

                // Input signal processors
                var signalProcessors = new SignalProcessor[inputSignalCount];
                for (var processorIndex = 0; processorIndex < inputSignalCount; processorIndex++)
                {
                    var inputSignal = inputSignals[processorIndex];
                    var y           = processorIndex * 4 + 3;

                    var inputChecker = new Entity
                    {
                        Name     = ItemNames.DeciderCombinator,
                        Position = new Position
                        {
                            X = 0.5 + shifterX,
                            Y = y
                        },
                        Direction        = Direction.Right,
                        Control_behavior = new ControlBehavior
                        {
                            Decider_conditions = new DeciderConditions
                            {
                                First_signal          = SignalID.Create(VirtualSignalNames.Each),
                                Second_signal         = SignalID.Create(offsetSignal),
                                Comparator            = Comparators.IsEqual,
                                Output_signal         = SignalID.Create(VirtualSignalNames.Each),
                                Copy_count_from_input = false
                            }
                        }
                    };
                    entities.Add(inputChecker);

                    var inputBuffer = new Entity
                    {
                        Name     = ItemNames.ArithmeticCombinator,
                        Position = new Position
                        {
                            X = 0.5 + shifterX,
                            Y = 1 + y
                        },
                        Direction        = Direction.Right,
                        Control_behavior = new ControlBehavior
                        {
                            Arithmetic_conditions = new ArithmeticConditions
                            {
                                First_signal    = SignalID.Create(inputSignal),
                                Second_constant = 1,
                                Operation       = ArithmeticOperations.Multiplication,
                                Output_signal   = SignalID.Create(inputSignal)
                            }
                        }
                    };
                    entities.Add(inputBuffer);

                    var outputGenerator = new Entity
                    {
                        Name     = ItemNames.ArithmeticCombinator,
                        Position = new Position
                        {
                            X = 0.5 + shifterX,
                            Y = 2 + y
                        },
                        Direction        = Direction.Right,
                        Control_behavior = new ControlBehavior
                        {
                            Arithmetic_conditions = new ArithmeticConditions
                            {
                                First_signal  = SignalID.Create(VirtualSignalNames.Each),
                                Second_signal = SignalID.Create(inputSignal),
                                Operation     = ArithmeticOperations.Multiplication,
                                Output_signal = SignalID.Create(VirtualSignalNames.Each)
                            }
                        }
                    };
                    entities.Add(outputGenerator);

                    var outputCleaner = new Entity
                    {
                        Name     = ItemNames.ArithmeticCombinator,
                        Position = new Position
                        {
                            X = 0.5 + shifterX,
                            Y = 3 + y
                        },
                        Direction        = Direction.Right,
                        Control_behavior = new ControlBehavior
                        {
                            Arithmetic_conditions = new ArithmeticConditions
                            {
                                First_signal    = SignalID.Create(inputSignal),
                                Second_constant = -1,
                                Operation       = ArithmeticOperations.Multiplication,
                                Output_signal   = SignalID.Create(offsetSignal)
                            }
                        }
                    };
                    entities.Add(outputCleaner);

                    signalProcessors[processorIndex] = new SignalProcessor
                    {
                        InputChecker    = inputChecker,
                        InputBuffer     = inputBuffer,
                        OutputGenerator = outputGenerator,
                        OutputCleaner   = outputCleaner
                    };
                }

                // Output signal maps
                var outputMaps = new Entity[(signalCount + maxFilters - 1) / maxFilters];
                for (var index = 0; index < outputMaps.Length; index++)
                {
                    var outputSignalMap = new Entity
                    {
                        Name     = ItemNames.ConstantCombinator,
                        Position = new Position
                        {
                            X = index % 2 + shifterX,
                            Y = index / 2 + inputSignalCount * 4 + 3
                        },
                        Direction        = Direction.Right,
                        Control_behavior = new ControlBehavior
                        {
                            Filters = ScreenUtil.PixelSignals.Skip(index * maxFilters).Take(Math.Min(maxFilters, signalCount - index * maxFilters)).Select((signal, signalIndex) => new Filter
                            {
                                Signal = SignalID.Create(signal),
                                Count  = index * maxFilters + signalIndex + 1
                            }).ToList()
                        }
                    };
                    outputMaps[index] = outputSignalMap;
                    entities.Add(outputSignalMap);
                }

                var offsetBuffer = new Entity
                {
                    Name     = ItemNames.ArithmeticCombinator,
                    Position = new Position
                    {
                        X = 0.5 + shifterX,
                        Y = outputMaps[^ 1].Position.Y + 1
        public static Blueprint Generate(SpriteMemoryConfiguration configuration)
        {
            var spriteCount = configuration.SpriteCount ?? 16;
            var baseAddress = configuration.BaseAddress ?? 1;

            var rowSignals  = ComputerSignals.OrderedSignals.Take(36).ToList();
            var inputSignal = VirtualSignalNames.LetterOrDigit('0');

            var entities   = new List <Entity>();
            var sprites    = new Sprite[spriteCount];
            var rowFilters = new RowFilter[rowSignals.Count + 1];

            for (var index = 0; index < spriteCount; index++)
            {
                var reader = new Entity
                {
                    Name     = ItemNames.DeciderCombinator,
                    Position = new Position
                    {
                        X = 0.5,
                        Y = index
                    },
                    Direction        = Direction.Left,
                    Control_behavior = new ControlBehavior
                    {
                        Decider_conditions = new DeciderConditions
                        {
                            First_signal          = SignalID.Create(VirtualSignalNames.Check),
                            Constant              = 0,
                            Comparator            = Comparators.IsEqual,
                            Output_signal         = SignalID.Create(VirtualSignalNames.Everything),
                            Copy_count_from_input = true
                        }
                    }
                };
                entities.Add(reader);

                var drawSelector = new Entity
                {
                    Name     = ItemNames.DeciderCombinator,
                    Position = new Position
                    {
                        X = 2.5,
                        Y = index
                    },
                    Direction        = Direction.Left,
                    Control_behavior = new ControlBehavior
                    {
                        Decider_conditions = new DeciderConditions
                        {
                            First_signal          = SignalID.Create(VirtualSignalNames.Dot),
                            Constant              = index + 1,
                            Comparator            = Comparators.IsEqual,
                            Output_signal         = SignalID.Create(VirtualSignalNames.Everything),
                            Copy_count_from_input = true
                        }
                    }
                };
                entities.Add(drawSelector);

                var memory = new Entity
                {
                    Name     = ItemNames.DeciderCombinator,
                    Position = new Position
                    {
                        X = 4.5,
                        Y = index
                    },
                    Direction        = Direction.Left,
                    Control_behavior = new ControlBehavior
                    {
                        Decider_conditions = new DeciderConditions
                        {
                            First_signal          = SignalID.Create(VirtualSignalNames.Info),
                            Constant              = 0,
                            Comparator            = Comparators.IsEqual,
                            Output_signal         = SignalID.Create(VirtualSignalNames.Everything),
                            Copy_count_from_input = true
                        }
                    }
                };
                entities.Add(memory);

                var writer = new Entity
                {
                    Name     = ItemNames.DeciderCombinator,
                    Position = new Position
                    {
                        X = 6.5,
                        Y = index
                    },
                    Direction        = Direction.Left,
                    Control_behavior = new ControlBehavior
                    {
                        Decider_conditions = new DeciderConditions
                        {
                            First_signal          = SignalID.Create(VirtualSignalNames.Check),
                            Constant              = 0,
                            Comparator            = Comparators.IsEqual,
                            Output_signal         = SignalID.Create(VirtualSignalNames.Everything),
                            Copy_count_from_input = true
                        }
                    }
                };
                entities.Add(writer);

                var spriteSelector = new Entity
                {
                    Name     = ItemNames.DeciderCombinator,
                    Position = new Position
                    {
                        X = 8.5,
                        Y = index
                    },
                    Direction        = Direction.Left,
                    Control_behavior = new ControlBehavior
                    {
                        Decider_conditions = new DeciderConditions
                        {
                            First_signal          = SignalID.Create(VirtualSignalNames.Info),
                            Constant              = index + 1,
                            Comparator            = Comparators.IsNotEqual,
                            Output_signal         = SignalID.Create(VirtualSignalNames.Check),
                            Copy_count_from_input = false
                        }
                    }
                };
                entities.Add(spriteSelector);

                sprites[index] = new Sprite
                {
                    Reader         = reader,
                    DrawSelector   = drawSelector,
                    Memory         = memory,
                    Writer         = writer,
                    SpriteSelector = spriteSelector
                };
            }

            for (var index = 0; index < rowFilters.Length; index++)
            {
                var renamer = new Entity
                {
                    Name     = index == 0 ? ItemNames.DeciderCombinator : ItemNames.ArithmeticCombinator,
                    Position = new Position
                    {
                        X = 10.5,
                        Y = index
                    },
                    Direction        = Direction.Left,
                    Control_behavior = index == 0
                        ? new ControlBehavior
                    {
                        Decider_conditions = new DeciderConditions
                        {
                            First_signal          = SignalID.Create(inputSignal),
                            Constant              = 1,
                            Comparator            = Comparators.IsEqual,
                            Output_signal         = SignalID.Create(VirtualSignalNames.Info),
                            Copy_count_from_input = false
                        }
                    }
                        : new ControlBehavior
                    {
                        Arithmetic_conditions = new ArithmeticConditions
                        {
                            First_signal    = SignalID.Create(inputSignal),
                            Second_constant = 1,
                            Operation       = ArithmeticOperations.Multiplication,
                            Output_signal   = SignalID.Create(rowSignals[index - 1])
                        }
                    }
                };
                entities.Add(renamer);

                var addressMatcher = new Entity
                {
                    Name     = ItemNames.DeciderCombinator,
                    Position = new Position
                    {
                        X = 12.5,
                        Y = index
                    },
                    Direction        = Direction.Left,
                    Control_behavior = new ControlBehavior
                    {
                        Decider_conditions = new DeciderConditions
                        {
                            First_signal          = SignalID.Create(VirtualSignalNames.Check),
                            Constant              = baseAddress + index,
                            Comparator            = Comparators.IsEqual,
                            Output_signal         = SignalID.Create(inputSignal),
                            Copy_count_from_input = true
                        }
                    }
                };
                entities.Add(addressMatcher);

                rowFilters[index] = new RowFilter
                {
                    Renamer        = renamer,
                    AddressMatcher = addressMatcher
                };
            }

            BlueprintUtil.PopulateEntityNumbers(entities);

            AddConnection(CircuitColor.Green, sprites[0].Writer, CircuitId.Input, rowFilters[0].Renamer, CircuitId.Output);

            for (var index = 0; index < spriteCount; index++)
            {
                var sprite = sprites[index];

                AddConnection(CircuitColor.Green, sprite.Reader, CircuitId.Input, sprite.DrawSelector, CircuitId.Input);
                AddConnection(CircuitColor.Green, sprite.DrawSelector, CircuitId.Input, sprite.Memory, CircuitId.Output);
                AddConnection(CircuitColor.Green, sprite.Memory, CircuitId.Output, sprite.Memory, CircuitId.Input);
                AddConnection(CircuitColor.Red, sprite.Memory, CircuitId.Input, sprite.Writer, CircuitId.Output);
                AddConnection(CircuitColor.Red, sprite.Reader, CircuitId.Input, sprite.Writer, CircuitId.Input);
                AddConnection(CircuitColor.Red, sprite.Writer, CircuitId.Input, sprite.SpriteSelector, CircuitId.Output);

                if (index > 0)
                {
                    var adjacentSprite = sprites[index - 1];

                    AddConnection(CircuitColor.Green, sprite.Reader, CircuitId.Output, adjacentSprite.Reader, CircuitId.Output);
                    AddConnection(CircuitColor.Green, sprite.DrawSelector, CircuitId.Output, adjacentSprite.DrawSelector, CircuitId.Output);
                    AddConnection(CircuitColor.Red, sprite.DrawSelector, CircuitId.Input, adjacentSprite.DrawSelector, CircuitId.Input);
                    AddConnection(CircuitColor.Green, sprite.Writer, CircuitId.Input, adjacentSprite.Writer, CircuitId.Input);
                    AddConnection(CircuitColor.Green, sprite.SpriteSelector, CircuitId.Input, adjacentSprite.SpriteSelector, CircuitId.Input);
                }
            }

            for (var index = 0; index < rowFilters.Length; index++)
            {
                var rowFilter = rowFilters[index];

                AddConnection(CircuitColor.Green, rowFilter.Renamer, CircuitId.Input, rowFilter.AddressMatcher, CircuitId.Output);

                if (index > 0)
                {
                    var adjacentRowFilter = rowFilters[index - 1];

                    AddConnection(CircuitColor.Green, rowFilter.Renamer, CircuitId.Output, adjacentRowFilter.Renamer, CircuitId.Output);
                    AddConnection(CircuitColor.Green, rowFilter.AddressMatcher, CircuitId.Input, adjacentRowFilter.AddressMatcher, CircuitId.Input);
                    AddConnection(CircuitColor.Red, rowFilter.AddressMatcher, CircuitId.Input, adjacentRowFilter.AddressMatcher, CircuitId.Input);
                }
            }

            return(new Blueprint
            {
                Label = $"Sprite Memory",
                Icons = new List <Icon>
                {
                    Icon.Create(ItemNames.DeciderCombinator),
                    Icon.Create(ItemNames.Lamp)
                },
                Entities = entities,
                Item = ItemNames.Blueprint,
                Version = BlueprintVersions.CurrentVersion
            });
        }
        public static Blueprint Generate(SpeakerConfiguration configuration)
        {
            const int numberOfInstruments = 12;

            var speakersPerConfiguration = configuration.SpeakersPerConfiguration ?? 6;
            var volumeLevels             = configuration.VolumeLevels ?? 1;
            var minVolume       = configuration.MinVolume ?? 0;
            var maxVolume       = configuration.MaxVolume ?? 1;
            var baseAddress     = configuration.BaseAddress ?? 0;
            var reverseAddressX = configuration.ReverseAddressX ?? false;
            var reverseAddressY = configuration.ReverseAddressY ?? false;
            var signal          = configuration.Signal ?? '0';
            var includePower    = configuration.IncludePower ?? true;

            var speakersPerVolumeLevel = speakersPerConfiguration * numberOfInstruments;
            var speakerCount           = speakersPerVolumeLevel * volumeLevels;
            var width  = configuration.Width ?? speakerCount / (configuration.Height ?? 16);
            var height = configuration.Height ?? speakerCount / width;

            const int entitiesPerCell = 6;
            const int cellHeight      = 11;

            const int writerEntityOffset  = -1;
            const int clearerEntityOffset = -2;
            const int readerEntityOffset  = 1;
            const int playerEntityOffset  = 2;
            const int speakerEntityOffset = 3;

            var gridWidth  = width + (includePower ? ((width + 7) / 16 + 1) * 2 : 0);
            var gridHeight = height * cellHeight;
            var xOffset    = -gridWidth / 2;
            var yOffset    = -gridHeight / 2;

            var entities = new List <Entity>();

            for (int row = 0; row < height; row++)
            {
                for (int column = 0; column < width; column++)
                {
                    var relativeAddress        = (reverseAddressY ? (height - row - 1) : row) * width + (reverseAddressX ? (width - column - 1) : column);
                    var address                = relativeAddress + baseAddress + 1;
                    var memoryCellEntityNumber = (row * width + column) * entitiesPerCell + 3;
                    var memoryCellX            = column + (includePower ? (column / 16 + 1) * 2 : 0) + xOffset;
                    var memoryCellY            = (height - row - 1) * cellHeight + 4.5 + yOffset;

                    var adjacentMemoryCells = new List <int> {
                        -1, 1
                    }
                    .Where(offset => column + offset >= 0 && column + offset < width)
                    .Select(offset => memoryCellEntityNumber + offset * entitiesPerCell)
                    .Concat(new List <int> {
                        -1, 1
                    }
                            .Where(offset => row + offset >= 0 && row + offset < height && column == 0)
                            .Select(offset => memoryCellEntityNumber + offset * width * entitiesPerCell)
                            )
                    .ToList();

                    // Memory cell
                    entities.Add(new Entity
                    {
                        Entity_number = memoryCellEntityNumber,
                        Name          = ItemNames.DeciderCombinator,
                        Position      = new Position
                        {
                            X = memoryCellX,
                            Y = memoryCellY
                        },
                        Direction        = Direction.Down,
                        Control_behavior = new ControlBehavior
                        {
                            Decider_conditions = new DeciderConditions
                            {
                                First_signal          = SignalID.CreateVirtual(VirtualSignalNames.Check),
                                Constant              = address,
                                Comparator            = Comparators.IsNotEqual,
                                Output_signal         = SignalID.CreateLetterOrDigit(signal),
                                Copy_count_from_input = true
                            }
                        },
                        Connections = CreateConnections(new ConnectionPoint
                        {
                            Red = new List <ConnectionData>
                            {
                                // Connection to writer input (address line)
                                new ConnectionData
                                {
                                    Entity_id  = memoryCellEntityNumber + writerEntityOffset,
                                    Circuit_id = CircuitId.Input
                                },
                                // Connection to reader input (address line)
                                new ConnectionData
                                {
                                    Entity_id  = memoryCellEntityNumber + readerEntityOffset,
                                    Circuit_id = CircuitId.Input
                                }
                            },
                            Green = new List <ConnectionData>
                            {
                                // Connection to own output (data feedback)
                                new ConnectionData
                                {
                                    Entity_id  = memoryCellEntityNumber,
                                    Circuit_id = CircuitId.Output
                                },
                                // Connection to writer output (data in)
                                new ConnectionData
                                {
                                    Entity_id  = memoryCellEntityNumber + writerEntityOffset,
                                    Circuit_id = CircuitId.Output
                                }
                            }
                        }, new ConnectionPoint
                        {
                            Green = new List <ConnectionData>
                            {
                                // Connection to own input (data feedback)
                                new ConnectionData
                                {
                                    Entity_id  = memoryCellEntityNumber,
                                    Circuit_id = CircuitId.Input
                                },
                                // Connection to reader input (data out)
                                new ConnectionData
                                {
                                    Entity_id  = memoryCellEntityNumber + readerEntityOffset,
                                    Circuit_id = CircuitId.Input
                                }
                            }
                        })
                    });

                    // Memory cell writer
                    entities.Add(new Entity
                    {
                        Entity_number = memoryCellEntityNumber + writerEntityOffset,
                        Name          = ItemNames.DeciderCombinator,
                        Position      = new Position
                        {
                            X = memoryCellX,
                            Y = memoryCellY - 2
                        },
                        Direction        = Direction.Down,
                        Control_behavior = new ControlBehavior
                        {
                            Decider_conditions = new DeciderConditions
                            {
                                First_signal          = SignalID.CreateVirtual(VirtualSignalNames.Check),
                                Constant              = address,
                                Comparator            = Comparators.IsEqual,
                                Output_signal         = SignalID.CreateLetterOrDigit(signal),
                                Copy_count_from_input = true
                            }
                        },
                        Connections = CreateConnections(new ConnectionPoint
                        {
                            // Connection to adjacent writer input (address line)
                            Red = adjacentMemoryCells.Select(entityNumber => new ConnectionData
                            {
                                Entity_id  = entityNumber + writerEntityOffset,
                                Circuit_id = CircuitId.Input
                            }).Concat(new List <ConnectionData>
                            {
                                // Connection to memory cell input (address line)
                                new ConnectionData
                                {
                                    Entity_id  = memoryCellEntityNumber,
                                    Circuit_id = CircuitId.Input
                                }
                            }).ToList(),
                            // Connection to adjacent writer input (data in)
                            Green = adjacentMemoryCells.Select(entityNumber => new ConnectionData
                            {
                                Entity_id  = entityNumber + writerEntityOffset,
                                Circuit_id = CircuitId.Input
                            }).ToList()
                        }, new ConnectionPoint
                        {
                            Green = new List <ConnectionData>
                            {
                                // Connection to memory cell input (data out)
                                new ConnectionData
                                {
                                    Entity_id  = memoryCellEntityNumber,
                                    Circuit_id = CircuitId.Input
                                },
                                // Connection to memory cell clearer output (data in/out)
                                new ConnectionData
                                {
                                    Entity_id  = memoryCellEntityNumber + clearerEntityOffset,
                                    Circuit_id = CircuitId.Output
                                }
                            }
                        })
                    });

                    // Memory cell clearer
                    entities.Add(new Entity
                    {
                        Entity_number = memoryCellEntityNumber + clearerEntityOffset,
                        Name          = ItemNames.ArithmeticCombinator,
                        Position      = new Position
                        {
                            X = memoryCellX,
                            Y = memoryCellY - 4
                        },
                        Direction        = Direction.Down,
                        Control_behavior = new ControlBehavior
                        {
                            Arithmetic_conditions = new ArithmeticConditions
                            {
                                First_signal  = SignalID.CreateLetterOrDigit(signal),
                                Second_signal = SignalID.CreateVirtual(VirtualSignalNames.Dot),
                                Operation     = ArithmeticOperations.Multiplication,
                                Output_signal = SignalID.CreateLetterOrDigit(signal)
                            }
                        },
                        Connections = CreateConnections(new ConnectionPoint
                        {
                            // Connection to adjacent clearer input (clear signal)
                            Red = adjacentMemoryCells.Select(entityNumber => new ConnectionData
                            {
                                Entity_id  = entityNumber + clearerEntityOffset,
                                Circuit_id = CircuitId.Input
                            }).ToList(),
                            Green = new List <ConnectionData>
                            {
                                // Connection to own output (data in)
                                new ConnectionData
                                {
                                    Entity_id  = memoryCellEntityNumber + clearerEntityOffset,
                                    Circuit_id = CircuitId.Output
                                }
                            }
                        }, new ConnectionPoint
                        {
                            Green = new List <ConnectionData>
                            {
                                // Connection to own input (data in)
                                new ConnectionData
                                {
                                    Entity_id  = memoryCellEntityNumber + clearerEntityOffset,
                                    Circuit_id = CircuitId.Input
                                },
                                // Connection to memory cell writer output (data in/out)
                                new ConnectionData
                                {
                                    Entity_id  = memoryCellEntityNumber + writerEntityOffset,
                                    Circuit_id = CircuitId.Output
                                }
                            }
                        })
                    });

                    // Memory cell reader
                    entities.Add(new Entity
                    {
                        Entity_number = memoryCellEntityNumber + readerEntityOffset,
                        Name          = ItemNames.DeciderCombinator,
                        Position      = new Position
                        {
                            X = memoryCellX,
                            Y = memoryCellY + 2
                        },
                        Direction        = Direction.Down,
                        Control_behavior = new ControlBehavior
                        {
                            Decider_conditions = new DeciderConditions
                            {
                                First_signal          = SignalID.CreateVirtual(VirtualSignalNames.Info),
                                Constant              = address,
                                Comparator            = Comparators.IsEqual,
                                Output_signal         = SignalID.CreateLetterOrDigit(signal),
                                Copy_count_from_input = true
                            }
                        },
                        Connections = CreateConnections(new ConnectionPoint
                        {
                            Red = new List <ConnectionData>
                            {
                                // Connection to memory cell input (address line)
                                new ConnectionData
                                {
                                    Entity_id  = memoryCellEntityNumber,
                                    Circuit_id = CircuitId.Input
                                }
                            },
                            Green = new List <ConnectionData>
                            {
                                // Connection to memory cell output (data out)
                                new ConnectionData
                                {
                                    Entity_id  = memoryCellEntityNumber,
                                    Circuit_id = CircuitId.Output
                                },
                                // Connection to player input (data out)
                                new ConnectionData
                                {
                                    Entity_id  = memoryCellEntityNumber + playerEntityOffset,
                                    Circuit_id = CircuitId.Input
                                }
                            }
                        }, new ConnectionPoint
                        {
                            // Connection to adjacent reader output (data out)
                            Green = adjacentMemoryCells.Select(entityNumber => new ConnectionData
                            {
                                Entity_id  = entityNumber + readerEntityOffset,
                                Circuit_id = CircuitId.Output
                            }).ToList()
                        })
                    });

                    // Player
                    entities.Add(new Entity
                    {
                        Entity_number = memoryCellEntityNumber + playerEntityOffset,
                        Name          = ItemNames.DeciderCombinator,
                        Position      = new Position
                        {
                            X = memoryCellX,
                            Y = memoryCellY + 4
                        },
                        Direction        = Direction.Down,
                        Control_behavior = new ControlBehavior
                        {
                            Decider_conditions = new DeciderConditions
                            {
                                First_signal          = SignalID.CreateVirtual(VirtualSignalNames.Dot),
                                Constant              = 0,
                                Comparator            = Comparators.GreaterThan,
                                Output_signal         = SignalID.CreateLetterOrDigit(signal),
                                Copy_count_from_input = true
                            }
                        },
                        Connections = CreateConnections(new ConnectionPoint
                        {
                            // Connection to adjacent player input (play signal)
                            Red = adjacentMemoryCells.Select(entityNumber => new ConnectionData
                            {
                                Entity_id  = entityNumber + playerEntityOffset,
                                Circuit_id = CircuitId.Input
                            }).ToList(),
                            Green = new List <ConnectionData>
                            {
                                // Connection to reader input (data in)
                                new ConnectionData
                                {
                                    Entity_id  = memoryCellEntityNumber + readerEntityOffset,
                                    Circuit_id = CircuitId.Input
                                }
                            }
                        }, new ConnectionPoint
                        {
                            Green = new List <ConnectionData>
                            {
                                // Connection to speaker (data out)
                                new ConnectionData
                                {
                                    Entity_id = memoryCellEntityNumber + speakerEntityOffset
                                }
                            }
                        })
                    });

                    // Speaker
                    entities.Add(new Entity
                    {
                        Entity_number = memoryCellEntityNumber + speakerEntityOffset,
                        Name          = ItemNames.ProgrammableSpeaker,
                        Position      = new Position
                        {
                            X = memoryCellX,
                            Y = memoryCellY + 5.5
                        },
                        Control_behavior = new ControlBehavior
                        {
                            Circuit_condition = new CircuitCondition
                            {
                                First_signal = SignalID.CreateLetterOrDigit(signal)
                            },
                            Circuit_parameters = new CircuitParameters
                            {
                                Signal_value_is_pitch = true,
                                Instrument_id         = relativeAddress % speakersPerVolumeLevel / speakersPerConfiguration
                            }
                        },
                        Connections = CreateConnections(new ConnectionPoint
                        {
                            Green = new List <ConnectionData>
                            {
                                // Connection to player output (data in)
                                new ConnectionData
                                {
                                    Entity_id  = memoryCellEntityNumber + playerEntityOffset,
                                    Circuit_id = CircuitId.Output
                                }
                            }
                        }),
                        Parameters = new SpeakerParameter
                        {
                            Playback_volume   = maxVolume - (double)(relativeAddress / speakersPerVolumeLevel) / (volumeLevels - 1) * (maxVolume - minVolume),
                            Playback_globally = true,
                            Allow_polyphony   = true
                        },
                        Alert_parameters = new SpeakerAlertParameter
                        {
                            Show_alert = false
                        }
                    });
                }
            }

            if (includePower)
            {
                var substationWidth  = (width + 7) / 16 + 1;
                var substationHeight = (gridHeight + 3) / 18 + 1;

                entities.AddRange(CreateSubstations(substationWidth, substationHeight, xOffset, yOffset + 2, width * height * entitiesPerCell + 1));
            }

            return(new Blueprint
            {
                Label = $"{width}x{height} Speaker",
                Icons = new List <Icon>
                {
                    new Icon
                    {
                        Signal = SignalID.Create(ItemNames.ProgrammableSpeaker)
                    }
                },
                Entities = entities,
                Item = ItemNames.Blueprint,
                Version = BlueprintVersions.CurrentVersion
            });
        }
Beispiel #4
0
        public static Blueprint Generate(RamConfiguration configuration)
        {
            var width        = configuration.Width ?? 16;
            var height       = configuration.Height ?? 16;
            var baseAddress  = configuration.BaseAddress ?? 0;
            var signal       = configuration.Signal ?? '0';
            var includePower = configuration.IncludePower ?? true;

            const int entitiesPerCell = 3;
            const int cellHeight      = 6;

            const int writerEntityOffset = -1;
            const int readerEntityOffset = 1;

            var gridWidth  = width + (includePower ? ((width + 7) / 16 + 1) * 2 : 0);
            var gridHeight = height * cellHeight;
            var xOffset    = -gridWidth / 2;
            var yOffset    = -gridHeight / 2;

            var entities = new List <Entity>();

            for (int row = 0; row < height; row++)
            {
                for (int column = 0; column < width; column++)
                {
                    var address = row * width + column + baseAddress + 1;
                    var memoryCellEntityNumber = (row * width + column) * entitiesPerCell + 2;
                    var memoryCellX            = column + (includePower ? (column / 16 + 1) * 2 : 0) + xOffset;
                    var memoryCellY            = (height - row - 1) * cellHeight + 2.5 + yOffset;

                    var adjacentMemoryCells = new List <int> {
                        -1, 1
                    }
                    .Where(offset => column + offset >= 0 && column + offset < width)
                    .Select(offset => memoryCellEntityNumber + offset * entitiesPerCell)
                    .Concat(new List <int> {
                        -1, 1
                    }
                            .Where(offset => row + offset >= 0 && row + offset < height && column == 0)
                            .Select(offset => memoryCellEntityNumber + offset * width * entitiesPerCell)
                            )
                    .ToList();

                    // Memory cell
                    entities.Add(new Entity
                    {
                        Entity_number = memoryCellEntityNumber,
                        Name          = ItemNames.DeciderCombinator,
                        Position      = new Position
                        {
                            X = memoryCellX,
                            Y = memoryCellY
                        },
                        Direction        = Direction.Down,
                        Control_behavior = new ControlBehavior
                        {
                            Decider_conditions = new DeciderConditions
                            {
                                First_signal          = SignalID.CreateVirtual(VirtualSignalNames.Check),
                                Constant              = address,
                                Comparator            = Comparators.IsNotEqual,
                                Output_signal         = SignalID.CreateLetterOrDigit(signal),
                                Copy_count_from_input = true
                            }
                        },
                        Connections = CreateConnections(new ConnectionPoint
                        {
                            Red = new List <ConnectionData>
                            {
                                // Connection to writer input (address line)
                                new ConnectionData
                                {
                                    Entity_id  = memoryCellEntityNumber + writerEntityOffset,
                                    Circuit_id = CircuitId.Input
                                },
                                // Connection to reader input (address line)
                                new ConnectionData
                                {
                                    Entity_id  = memoryCellEntityNumber + readerEntityOffset,
                                    Circuit_id = CircuitId.Input
                                }
                            },
                            Green = new List <ConnectionData>
                            {
                                // Connection to own output (data feedback)
                                new ConnectionData
                                {
                                    Entity_id  = memoryCellEntityNumber,
                                    Circuit_id = CircuitId.Output
                                },
                                // Connection to writer output (data in)
                                new ConnectionData
                                {
                                    Entity_id  = memoryCellEntityNumber + writerEntityOffset,
                                    Circuit_id = CircuitId.Output
                                }
                            }
                        }, new ConnectionPoint
                        {
                            Green = new List <ConnectionData>
                            {
                                // Connection to own input (data feedback)
                                new ConnectionData
                                {
                                    Entity_id  = memoryCellEntityNumber,
                                    Circuit_id = CircuitId.Input
                                },
                                // Connection to reader input (data out)
                                new ConnectionData
                                {
                                    Entity_id  = memoryCellEntityNumber + readerEntityOffset,
                                    Circuit_id = CircuitId.Input
                                }
                            }
                        })
                    });

                    // Memory cell writer
                    entities.Add(new Entity
                    {
                        Entity_number = memoryCellEntityNumber + writerEntityOffset,
                        Name          = ItemNames.DeciderCombinator,
                        Position      = new Position
                        {
                            X = memoryCellX,
                            Y = memoryCellY - 2
                        },
                        Direction        = Direction.Down,
                        Control_behavior = new ControlBehavior
                        {
                            Decider_conditions = new DeciderConditions
                            {
                                First_signal          = SignalID.CreateVirtual(VirtualSignalNames.Check),
                                Constant              = address,
                                Comparator            = Comparators.IsEqual,
                                Output_signal         = SignalID.CreateLetterOrDigit(signal),
                                Copy_count_from_input = true
                            }
                        },
                        Connections = CreateConnections(new ConnectionPoint
                        {
                            // Connection to adjacent writer input (address line)
                            Red = adjacentMemoryCells.Select(entityNumber => new ConnectionData
                            {
                                Entity_id  = entityNumber + writerEntityOffset,
                                Circuit_id = CircuitId.Input
                            }).Concat(new List <ConnectionData>
                            {
                                // Connection to memory cell input (address line)
                                new ConnectionData
                                {
                                    Entity_id  = memoryCellEntityNumber,
                                    Circuit_id = CircuitId.Input
                                }
                            }).ToList(),
                            // Connection to adjacent writer input (data in)
                            Green = adjacentMemoryCells.Select(entityNumber => new ConnectionData
                            {
                                Entity_id  = entityNumber + writerEntityOffset,
                                Circuit_id = CircuitId.Input
                            }).ToList()
                        }, new ConnectionPoint
                        {
                            Green = new List <ConnectionData>
                            {
                                // Connection to memory cell input (data out)
                                new ConnectionData
                                {
                                    Entity_id  = memoryCellEntityNumber,
                                    Circuit_id = CircuitId.Input
                                }
                            }
                        })
                    });

                    // Memory cell reader
                    entities.Add(new Entity
                    {
                        Entity_number = memoryCellEntityNumber + readerEntityOffset,
                        Name          = ItemNames.DeciderCombinator,
                        Position      = new Position
                        {
                            X = memoryCellX,
                            Y = memoryCellY + 2
                        },
                        Direction        = Direction.Down,
                        Control_behavior = new ControlBehavior
                        {
                            Decider_conditions = new DeciderConditions
                            {
                                First_signal          = SignalID.CreateVirtual(VirtualSignalNames.Info),
                                Constant              = address,
                                Comparator            = Comparators.IsEqual,
                                Output_signal         = SignalID.CreateLetterOrDigit(signal),
                                Copy_count_from_input = true
                            }
                        },
                        Connections = CreateConnections(new ConnectionPoint
                        {
                            Red = new List <ConnectionData>
                            {
                                // Connection to memory cell input (address line)
                                new ConnectionData
                                {
                                    Entity_id  = memoryCellEntityNumber,
                                    Circuit_id = CircuitId.Input
                                }
                            },
                            Green = new List <ConnectionData>
                            {
                                // Connection to memory cell output (data out)
                                new ConnectionData
                                {
                                    Entity_id  = memoryCellEntityNumber,
                                    Circuit_id = CircuitId.Output
                                }
                            }
                        }, new ConnectionPoint
                        {
                            // Connection to adjacent reader output (data out)
                            Green = adjacentMemoryCells.Select(entityNumber => new ConnectionData
                            {
                                Entity_id  = entityNumber + readerEntityOffset,
                                Circuit_id = CircuitId.Output
                            }).ToList()
                        })
                    });
                }
            }

            if (includePower)
            {
                var substationWidth  = (width + 7) / 16 + 1;
                var substationHeight = (gridHeight + 3) / 18 + 1;

                entities.AddRange(CreateSubstations(substationWidth, substationHeight, xOffset, gridHeight % 18 - 4 + yOffset, width * height * entitiesPerCell + 1));
            }

            return(new Blueprint
            {
                Label = $"{width}x{height} RAM",
                Icons = new List <Icon>
                {
                    new Icon
                    {
                        Signal = SignalID.Create(ItemNames.AdvancedCircuit)
                    }
                },
                Entities = entities,
                Item = ItemNames.Blueprint,
                Version = BlueprintVersions.CurrentVersion
            });
        }
        public static Blueprint Generate(VideoMemoryConfiguration configuration, List <bool[, ]> frames = null)
        {
            var width       = configuration.Width ?? 32;
            var height      = configuration.Height ?? 2;
            var baseAddress = configuration.BaseAddress ?? 1;

            var frameHeight = frames?.ElementAtOrDefault(0)?.GetLength(0) ?? 0;

            const int framesPerRow = 32;
            const int maxFilters   = 20;

            var entities   = new List <Entity>();
            var memoryRows = new MemoryRow[height];

            var metadata = new Entity
            {
                Name     = ItemNames.ConstantCombinator,
                Position = new Position
                {
                    X = 2,
                    Y = 0
                },
                Direction        = Direction.Down,
                Control_behavior = new ControlBehavior
                {
                    Filters = new List <Filter>
                    {
                        Filter.Create(VirtualSignalNames.LetterOrDigit('Z'), frames?.Count ?? 0)
                    }
                }
            };

            entities.Add(metadata);

            for (var row = 0; row < height; row++)
            {
                var cellY     = row * 9 - (row % 2 == 1 ? 1 : 0) + 2;
                var rowFrames = frames?.Skip(row * framesPerRow).Take(framesPerRow).ToList();

                var addressMatcher = new Entity
                {
                    Name     = ItemNames.DeciderCombinator,
                    Position = new Position
                    {
                        X = 1,
                        Y = cellY + 0.5
                    },
                    Direction        = Direction.Down,
                    Control_behavior = new ControlBehavior
                    {
                        Decider_conditions = new DeciderConditions
                        {
                            First_signal          = SignalID.Create(VirtualSignalNames.Info),
                            Constant              = row + baseAddress,
                            Comparator            = Comparators.IsNotEqual,
                            Output_signal         = SignalID.Create(VirtualSignalNames.Check),
                            Copy_count_from_input = false
                        }
                    }
                };
                entities.Add(addressMatcher);

                var memoryCells = new MemoryCell[width];

                for (var column = 0; column < width; column++)
                {
                    var cellX = column + 2;

                    var enabler = new Entity
                    {
                        Name     = ItemNames.DeciderCombinator,
                        Position = new Position
                        {
                            X = cellX,
                            Y = cellY + 0.5
                        },
                        Direction        = Direction.Up,
                        Control_behavior = new ControlBehavior
                        {
                            Decider_conditions = new DeciderConditions
                            {
                                First_signal          = SignalID.Create(VirtualSignalNames.Check),
                                Constant              = 0,
                                Comparator            = Comparators.IsEqual,
                                Output_signal         = SignalID.Create(VirtualSignalNames.Everything),
                                Copy_count_from_input = true
                            }
                        }
                    };
                    entities.Add(enabler);

                    var pixelFilters = Enumerable.Range(0, frameHeight)
                                       .Select(frameRow =>
                    {
                        var pixel = rowFrames
                                    .Select((frame, frameOffset) => frame[frameRow, column] ? 1 << frameOffset : 0)
                                    .Sum();

                        return(Filter.Create(ScreenUtil.PixelSignals[frameRow], pixel));
                    })
                                       .Where(pixelFilter => pixelFilter.Count != 0)
                                       .ToList();

                    var subCellCount = column % 18 >= 16 ? 6 : 7;
                    var subCells     = new Entity[subCellCount];

                    for (var subCellIndex = 0; subCellIndex < subCellCount; subCellIndex++)
                    {
                        var subCell = new Entity
                        {
                            Name     = ItemNames.ConstantCombinator,
                            Position = new Position
                            {
                                X = cellX,
                                Y = subCellIndex < 6 || row % 2 == 1 ? cellY + subCellIndex + 2 : cellY - 1
                            },
                            Direction        = Direction.Down,
                            Control_behavior = new ControlBehavior
                            {
                                Filters = pixelFilters.Skip(subCellIndex * maxFilters).Take(maxFilters).ToList()
                            }
                        };
                        entities.Add(subCell);
                        subCells[subCellIndex] = subCell;
                    }

                    memoryCells[column] = new MemoryCell
                    {
                        Enabler  = enabler,
                        SubCells = subCells
                    };
                }

                memoryRows[row] = new MemoryRow
                {
                    AddressMatcher = addressMatcher,
                    Cells          = memoryCells
                };
            }

            BlueprintUtil.PopulateEntityNumbers(entities);

            var substationWidth  = (width + 9) / 18 + 1;
            var substationHeight = height / 2 + 1;

            entities.AddRange(CreateSubstations(substationWidth, substationHeight, 0, 0, entities.Count + 1));

            for (var row = 0; row < height; row++)
            {
                var memoryRow = memoryRows[row];

                AddConnection(CircuitColor.Red, memoryRow.AddressMatcher, CircuitId.Output, memoryRow.Cells[0].Enabler, CircuitId.Input);

                var adjacentRow = row - 1;
                if (adjacentRow >= 0)
                {
                    var adjacentMemoryRow = memoryRows[adjacentRow];

                    AddConnection(CircuitColor.Green, memoryRow.AddressMatcher, CircuitId.Input, adjacentMemoryRow.AddressMatcher, CircuitId.Input);

                    for (var column = 0; column < width; column++)
                    {
                        var memoryCell         = memoryRow.Cells[column];
                        var adjacentMemoryCell = adjacentMemoryRow.Cells[column];

                        AddConnection(CircuitColor.Green, memoryCell.Enabler, CircuitId.Output, adjacentMemoryCell.Enabler, CircuitId.Output);
                    }
                }

                for (var column = 0; column < width; column++)
                {
                    var memoryCell = memoryRow.Cells[column];

                    AddConnection(CircuitColor.Green, memoryCell.SubCells[0], null, memoryCell.Enabler, CircuitId.Input);

                    for (var subCellIndex = 1; subCellIndex < memoryCell.SubCells.Length; subCellIndex++)
                    {
                        var subCell         = memoryCell.SubCells[subCellIndex];
                        var adjacentSubCell = memoryCell.SubCells[subCellIndex < 6 || row % 2 == 1 ? subCellIndex - 1 : 0];

                        AddConnection(CircuitColor.Green, subCell, null, adjacentSubCell, null);
                    }

                    var adjacentColumn = column - 1;
                    if (adjacentColumn >= 0)
                    {
                        var adjacentMemoryCell = memoryRow.Cells[adjacentColumn];

                        AddConnection(CircuitColor.Red, memoryCell.Enabler, CircuitId.Input, adjacentMemoryCell.Enabler, CircuitId.Input);
                    }
                }
            }

            return(new Blueprint
            {
                Label = $"Video ROM",
                Icons = new List <Icon>
                {
                    Icon.Create(ItemNames.Lamp),
                    Icon.Create(ItemNames.ConstantCombinator)
                },
                Entities = entities,
                Item = ItemNames.Blueprint,
                Version = BlueprintVersions.CurrentVersion
            });
        }
        public static Blueprint Generate(ScreenConfiguration configuration)
        {
            var width  = configuration.Width ?? 18;
            var height = configuration.Height ?? 18;

            const int cycle         = 2;
            const int parallelCycle = 32;

            var entities          = new List <Entity>();
            var pixels            = new Entity[height, width];
            var columnControllers = new Controller[width];
            var rowControllers    = new Controller[height];

            // Pixels
            for (var row = 0; row < height; row++)
            {
                for (var column = 0; column < width; column++)
                {
                    var relativeRow    = row % 18;
                    var relativeColumn = column % 18;

                    // Don't place lights that intersect the substations
                    if (relativeRow > 15 && relativeColumn > 15)
                    {
                        continue;
                    }

                    var pixel = new Entity
                    {
                        Name     = ItemNames.Lamp,
                        Position = new Position
                        {
                            X = column + 2,
                            Y = row + 2
                        },
                        Control_behavior = new ControlBehavior
                        {
                            Circuit_condition = new CircuitCondition
                            {
                                First_signal = SignalID.Create(ScreenUtil.PixelSignals[row]),
                                Comparator   = Comparators.GreaterThan,
                                Constant     = 0
                            },
                            Use_colors = true
                        }
                    };
                    pixels[row, column] = pixel;
                    entities.Add(pixel);
                }
            }

            // Column controllers
            for (var column = 0; column < width; column++)
            {
                var controllerX = column + 2;
                var controllerY = height + 4;

                var memory = new Entity
                {
                    Name     = ItemNames.DeciderCombinator,
                    Position = new Position
                    {
                        X = controllerX,
                        Y = controllerY + 0.5
                    },
                    Direction        = Direction.Up,
                    Control_behavior = new ControlBehavior
                    {
                        Decider_conditions = new DeciderConditions
                        {
                            First_signal          = SignalID.Create(VirtualSignalNames.Each),
                            Constant              = 0,
                            Comparator            = Comparators.GreaterThan,
                            Output_signal         = SignalID.Create(VirtualSignalNames.Each),
                            Copy_count_from_input = false
                        }
                    }
                };
                entities.Add(memory);

                var writer = new Entity
                {
                    Name     = ItemNames.DeciderCombinator,
                    Position = new Position
                    {
                        X = controllerX,
                        Y = controllerY + 2.5
                    },
                    Direction        = Direction.Up,
                    Control_behavior = new ControlBehavior
                    {
                        Decider_conditions = new DeciderConditions
                        {
                            First_signal          = SignalID.Create(VirtualSignalNames.Check),
                            Constant              = 0,
                            Comparator            = Comparators.IsEqual,
                            Output_signal         = SignalID.Create(VirtualSignalNames.Everything),
                            Copy_count_from_input = true
                        }
                    }
                };
                entities.Add(writer);

                var addressMatcher = new Entity
                {
                    Name     = ItemNames.DeciderCombinator,
                    Position = new Position
                    {
                        X = controllerX,
                        Y = controllerY + 4.5
                    },
                    Direction        = Direction.Up,
                    Control_behavior = new ControlBehavior
                    {
                        Decider_conditions = new DeciderConditions
                        {
                            First_signal          = SignalID.Create(VirtualSignalNames.Info),
                            Constant              = column + 1,
                            Comparator            = Comparators.IsNotEqual,
                            Output_signal         = SignalID.Create(VirtualSignalNames.Check),
                            Copy_count_from_input = false
                        }
                    }
                };
                entities.Add(addressMatcher);

                var cyclicWriter = new Entity
                {
                    Name     = ItemNames.DeciderCombinator,
                    Position = new Position
                    {
                        X = controllerX,
                        Y = controllerY + 6.5
                    },
                    Direction        = Direction.Up,
                    Control_behavior = new ControlBehavior
                    {
                        Decider_conditions = new DeciderConditions
                        {
                            First_signal          = SignalID.Create(VirtualSignalNames.Check),
                            Constant              = 0,
                            Comparator            = Comparators.IsEqual,
                            Output_signal         = SignalID.Create(VirtualSignalNames.Everything),
                            Copy_count_from_input = true
                        }
                    }
                };
                entities.Add(cyclicWriter);

                var cyclicMatcher = new Entity
                {
                    Name     = ItemNames.DeciderCombinator,
                    Position = new Position
                    {
                        X = controllerX,
                        Y = controllerY + 8.5
                    },
                    Direction        = Direction.Up,
                    Control_behavior = new ControlBehavior
                    {
                        Decider_conditions = new DeciderConditions
                        {
                            First_signal          = SignalID.Create(VirtualSignalNames.Info),
                            Constant              = column % cycle + 1,
                            Comparator            = Comparators.IsNotEqual,
                            Output_signal         = SignalID.Create(VirtualSignalNames.Check),
                            Copy_count_from_input = false
                        }
                    }
                };
                entities.Add(cyclicMatcher);

                var parallelWriter = new Entity
                {
                    Name     = ItemNames.DeciderCombinator,
                    Position = new Position
                    {
                        X = controllerX,
                        Y = controllerY + 14.5
                    },
                    Direction        = Direction.Up,
                    Control_behavior = new ControlBehavior
                    {
                        Decider_conditions = new DeciderConditions
                        {
                            First_signal          = SignalID.Create(VirtualSignalNames.Check),
                            Constant              = 0,
                            Comparator            = Comparators.IsEqual,
                            Output_signal         = SignalID.Create(VirtualSignalNames.Everything),
                            Copy_count_from_input = true
                        }
                    }
                };
                entities.Add(parallelWriter);

                var parallelAddressRangeLow = new Entity
                {
                    Name     = ItemNames.DeciderCombinator,
                    Position = new Position
                    {
                        X = controllerX,
                        Y = controllerY + 10.5
                    },
                    Direction        = Direction.Up,
                    Control_behavior = new ControlBehavior
                    {
                        Decider_conditions = new DeciderConditions
                        {
                            First_signal          = SignalID.Create(VirtualSignalNames.Info),
                            Constant              = column + 1,
                            Comparator            = Comparators.LessThan,
                            Output_signal         = SignalID.Create(VirtualSignalNames.Check),
                            Copy_count_from_input = false
                        }
                    }
                };
                entities.Add(parallelAddressRangeLow);

                var parallelAddressRangeHigh = new Entity
                {
                    Name     = ItemNames.DeciderCombinator,
                    Position = new Position
                    {
                        X = controllerX,
                        Y = controllerY + 12.5
                    },
                    Direction        = Direction.Up,
                    Control_behavior = new ControlBehavior
                    {
                        Decider_conditions = new DeciderConditions
                        {
                            First_signal          = SignalID.Create(VirtualSignalNames.Info),
                            Constant              = column + parallelCycle + 1,
                            Comparator            = Comparators.GreaterThanOrEqualTo,
                            Output_signal         = SignalID.Create(VirtualSignalNames.Check),
                            Copy_count_from_input = false
                        }
                    }
                };
                entities.Add(parallelAddressRangeHigh);

                var isOdd = column % 2 == 1;

                var parallelHorizontalLink1 = new Entity
                {
                    Name     = column % 18 == 16 ? ItemNames.Substation : ItemNames.BigElectricPole,
                    Position = new Position
                    {
                        X = controllerX + 0.5,
                        Y = controllerY + 16.5 + (isOdd ? 2 : 0)
                    }
                };
                entities.Add(parallelHorizontalLink1);

                var parallelHorizontalLink2 = new Entity
                {
                    Name     = ItemNames.BigElectricPole,
                    Position = new Position
                    {
                        X = controllerX + 0.5,
                        Y = controllerY + 20.5 + (isOdd ? 2 : 0)
                    }
                };
                entities.Add(parallelHorizontalLink2);

                var videoEnabler = new Entity
                {
                    Name     = ItemNames.DeciderCombinator,
                    Position = new Position
                    {
                        X = controllerX,
                        Y = controllerY + 24.5
                    },
                    Direction        = Direction.Up,
                    Control_behavior = new ControlBehavior
                    {
                        Decider_conditions = new DeciderConditions
                        {
                            First_signal          = SignalID.Create(VirtualSignalNames.Check),
                            Constant              = 0,
                            Comparator            = Comparators.IsEqual,
                            Output_signal         = SignalID.Create(VirtualSignalNames.Everything),
                            Copy_count_from_input = true
                        }
                    }
                };
                entities.Add(videoEnabler);

                var videoReferenceSignalSubtractor = new Entity
                {
                    Name     = ItemNames.ArithmeticCombinator,
                    Position = new Position
                    {
                        X = controllerX,
                        Y = controllerY + 26.5
                    },
                    Direction        = Direction.Up,
                    Control_behavior = new ControlBehavior
                    {
                        Arithmetic_conditions = new ArithmeticConditions
                        {
                            First_signal    = SignalID.Create(VirtualSignalNames.Each),
                            Second_constant = -3,
                            Operation       = ArithmeticOperations.Multiplication,
                            Output_signal   = SignalID.Create(VirtualSignalNames.Each)
                        }
                    }
                };
                entities.Add(videoReferenceSignalSubtractor);

                var videoValueSpreader = new Entity
                {
                    Name     = ItemNames.ArithmeticCombinator,
                    Position = new Position
                    {
                        X = controllerX,
                        Y = controllerY + 28.5
                    },
                    Direction        = Direction.Up,
                    Control_behavior = new ControlBehavior
                    {
                        Arithmetic_conditions = new ArithmeticConditions
                        {
                            First_signal    = SignalID.Create(VirtualSignalNames.Each),
                            Second_constant = 2,
                            Operation       = ArithmeticOperations.Multiplication,
                            Output_signal   = SignalID.Create(VirtualSignalNames.Each)
                        }
                    }
                };
                entities.Add(videoValueSpreader);

                var videoBitIsolator = new Entity
                {
                    Name     = ItemNames.ArithmeticCombinator,
                    Position = new Position
                    {
                        X = controllerX,
                        Y = controllerY + 30.5
                    },
                    Direction        = Direction.Up,
                    Control_behavior = new ControlBehavior
                    {
                        Arithmetic_conditions = new ArithmeticConditions
                        {
                            First_signal    = SignalID.Create(VirtualSignalNames.Each),
                            Second_constant = 1,
                            Operation       = ArithmeticOperations.And,
                            Output_signal   = SignalID.Create(VirtualSignalNames.Each)
                        }
                    }
                };
                entities.Add(videoBitIsolator);

                var videoBitShifter = new Entity
                {
                    Name     = ItemNames.ArithmeticCombinator,
                    Position = new Position
                    {
                        X = controllerX,
                        Y = controllerY + 32.5
                    },
                    Direction        = Direction.Up,
                    Control_behavior = new ControlBehavior
                    {
                        Arithmetic_conditions = new ArithmeticConditions
                        {
                            First_signal  = SignalID.Create(VirtualSignalNames.Each),
                            Second_signal = SignalID.CreateLetterOrDigit('W'),
                            Operation     = ArithmeticOperations.RightShift,
                            Output_signal = SignalID.Create(VirtualSignalNames.Each)
                        }
                    }
                };
                entities.Add(videoBitShifter);

                columnControllers[column] = new Controller
                {
                    Memory         = memory,
                    Writer         = writer,
                    AddressMatcher = addressMatcher,
                    Cyclic         = new CyclicInput
                    {
                        Writer  = cyclicWriter,
                        Matcher = cyclicMatcher
                    },
                    Parallel = new ParallelInput
                    {
                        Writer           = parallelWriter,
                        AddressRangeLow  = parallelAddressRangeLow,
                        AddressRangeHigh = parallelAddressRangeHigh,
                        HorizontalLink1  = parallelHorizontalLink1,
                        HorizontalLink2  = parallelHorizontalLink2
                    },
                    Video = new VideoInput
                    {
                        Enabler = videoEnabler,
                        ReferenceSignalSubtractor = videoReferenceSignalSubtractor,
                        ValueSpreader             = videoValueSpreader,
                        BitIsolator = videoBitIsolator,
                        BitShifter  = videoBitShifter
                    }
                };
            }

            // Row controllers
            for (var row = 0; row < height; row++)
            {
                var controllerY = row + 2;

                var memory = new Entity
                {
                    Name     = ItemNames.DeciderCombinator,
                    Position = new Position
                    {
                        X = -1.5,
                        Y = controllerY
                    },
                    Direction        = Direction.Right,
                    Control_behavior = new ControlBehavior
                    {
                        Decider_conditions = new DeciderConditions
                        {
                            First_signal          = SignalID.Create(VirtualSignalNames.Each),
                            Constant              = 0,
                            Comparator            = Comparators.LessThan,
                            Output_signal         = SignalID.Create(VirtualSignalNames.Each),
                            Copy_count_from_input = true
                        }
                    }
                };
                entities.Add(memory);

                var writer = new Entity
                {
                    Name     = ItemNames.DeciderCombinator,
                    Position = new Position
                    {
                        X = -3.5,
                        Y = controllerY
                    },
                    Direction        = Direction.Right,
                    Control_behavior = new ControlBehavior
                    {
                        Decider_conditions = new DeciderConditions
                        {
                            First_signal          = SignalID.Create(VirtualSignalNames.Check),
                            Constant              = 0,
                            Comparator            = Comparators.IsEqual,
                            Output_signal         = SignalID.Create(VirtualSignalNames.Everything),
                            Copy_count_from_input = true
                        }
                    }
                };
                entities.Add(writer);

                var addressMatcher = new Entity
                {
                    Name     = ItemNames.DeciderCombinator,
                    Position = new Position
                    {
                        X = -5.5,
                        Y = controllerY
                    },
                    Direction        = Direction.Right,
                    Control_behavior = new ControlBehavior
                    {
                        Decider_conditions = new DeciderConditions
                        {
                            First_signal          = SignalID.Create(VirtualSignalNames.Info),
                            Constant              = row + 1,
                            Comparator            = Comparators.IsNotEqual,
                            Output_signal         = SignalID.Create(VirtualSignalNames.Check),
                            Copy_count_from_input = false
                        }
                    }
                };
                entities.Add(addressMatcher);

                var cyclicWriter = new Entity
                {
                    Name     = ItemNames.DeciderCombinator,
                    Position = new Position
                    {
                        X = -7.5,
                        Y = controllerY
                    },
                    Direction        = Direction.Right,
                    Control_behavior = new ControlBehavior
                    {
                        Decider_conditions = new DeciderConditions
                        {
                            First_signal          = SignalID.Create(VirtualSignalNames.Check),
                            Constant              = 0,
                            Comparator            = Comparators.IsEqual,
                            Output_signal         = SignalID.Create(VirtualSignalNames.Everything),
                            Copy_count_from_input = true
                        }
                    }
                };
                entities.Add(cyclicWriter);

                var cyclicMatcher = new Entity
                {
                    Name     = ItemNames.DeciderCombinator,
                    Position = new Position
                    {
                        X = -9.5,
                        Y = controllerY
                    },
                    Direction        = Direction.Right,
                    Control_behavior = new ControlBehavior
                    {
                        Decider_conditions = new DeciderConditions
                        {
                            First_signal          = SignalID.Create(VirtualSignalNames.Info),
                            Constant              = row % cycle + 1,
                            Comparator            = Comparators.IsNotEqual,
                            Output_signal         = SignalID.Create(VirtualSignalNames.Check),
                            Copy_count_from_input = false
                        }
                    }
                };
                entities.Add(cyclicMatcher);

                rowControllers[row] = new Controller
                {
                    Memory         = memory,
                    Writer         = writer,
                    AddressMatcher = addressMatcher,
                    Cyclic         = new CyclicInput
                    {
                        Writer  = cyclicWriter,
                        Matcher = cyclicMatcher
                    }
                };
            }

            BlueprintUtil.PopulateEntityNumbers(entities);

            var substationWidth  = (width + 8) / 18 + 1;
            var substationHeight = (height + 8) / 18 + 1;
            var substations      = CreateSubstations(substationWidth, substationHeight, 0, 0, entities.Count + 1, GridConnectivity.Top | GridConnectivity.Vertical);

            entities.AddRange(substations);
            var substations2 = CreateSubstations(substationWidth, 1, 0, height + 38, entities.Count + 1);

            entities.AddRange(substations2);

            // Pixel connections
            for (var row = 0; row < height; row++)
            {
                for (var column = 0; column < width; column++)
                {
                    var pixel = pixels[row, column];
                    if (pixel == null)
                    {
                        continue;
                    }
        public static Blueprint Generate(FontConfiguration configuration)
        {
            var fontImageFile      = configuration.FontImage;
            var combinatorsPerRow  = configuration.CombinatorsPerRow ?? 5;
            var useOneSignalPerRow = configuration.UseOneSignalPerRow ?? false;
            var inputSignal        = configuration.InputSignal ?? VirtualSignalNames.Dot;
            var widthSignal        = configuration.WidthSignal;
            var heightSignal       = configuration.HeightSignal;
            var signals            = configuration.Signals.Contains(',') ? configuration.Signals.Split(',').ToList() : configuration.Signals.Select(signal => VirtualSignalNames.LetterOrDigit(signal)).ToList();

            const int maxFilters = 20;

            var font       = FontUtil.ReadFont(fontImageFile);
            var characters = font.Characters;

            var entities          = new List <Entity>();
            var characterEntities = new List <(Entity Matcher, List <Entity> Glyph)>();

            if (heightSignal != null)
            {
                characters.Add(new FontUtil.Character
                {
                    CharacterCode = '\n',
                    GlyphPixels   = new bool[font.Height, 0]
                });
            }

            var combinatorX = 0;

            for (var characterIndex = 0; characterIndex < characters.Count; characterIndex++)
            {
                var character   = characters[characterIndex];
                var glyphPixels = character.GlyphPixels;
                var height      = glyphPixels.GetLength(0);
                var width       = glyphPixels.GetLength(1);

                if (characterIndex % combinatorsPerRow == 0)
                {
                    combinatorX = 0;
                }

                var glyphFilters = new List <Filter>();

                if (widthSignal != null)
                {
                    glyphFilters.Add(Filter.Create(widthSignal, width));
                }

                if (heightSignal != null)
                {
                    glyphFilters.Add(Filter.Create(heightSignal, height));
                }

                for (int y = 0; y < height; y++)
                {
                    if (useOneSignalPerRow)
                    {
                        var rowSignal = 0;

                        for (int x = 0; x < width; x++)
                        {
                            if (glyphPixels[y, x])
                            {
                                rowSignal |= 1 << x;
                            }
                        }

                        glyphFilters.Add(Filter.Create(signals[y], rowSignal));
                    }
                    else
                    {
                        for (int x = 0; x < width; x++)
                        {
                            if (glyphPixels[y, x])
                            {
                                glyphFilters.Add(Filter.Create(signals[y * width + x]));
                            }
                        }
                    }
                }

                var combinatorY = characterIndex / combinatorsPerRow;

                var matcher = new Entity
                {
                    Name     = ItemNames.DeciderCombinator,
                    Position = new Position
                    {
                        X = (characterIndex % combinatorsPerRow - combinatorsPerRow) * 2 + 0.5,
                        Y = combinatorY
                    },
                    Direction        = Direction.Left,
                    Control_behavior = new ControlBehavior
                    {
                        Decider_conditions = new DeciderConditions
                        {
                            First_signal          = SignalID.Create(inputSignal),
                            Constant              = character.CharacterCode,
                            Comparator            = Comparators.IsEqual,
                            Output_signal         = SignalID.Create(VirtualSignalNames.Everything),
                            Copy_count_from_input = true
                        }
                    }
                };
                entities.Add(matcher);

                var glyph = new List <Entity>();

                for (int index = 0; index < (glyphFilters.Count + maxFilters - 1) / maxFilters; index++)
                {
                    var glyphPart = new Entity
                    {
                        Name     = ItemNames.ConstantCombinator,
                        Position = new Position
                        {
                            X = combinatorX++,
                            Y = combinatorY
                        },
                        Direction        = Direction.Down,
                        Control_behavior = new ControlBehavior
                        {
                            Filters = glyphFilters.Skip(index * maxFilters).Take(maxFilters).ToList()
                        }
                    };
                    glyph.Add(glyphPart);
                    entities.Add(glyphPart);
                }

                characterEntities.Add((matcher, glyph));
            }

            BlueprintUtil.PopulateEntityNumbers(entities);

            for (int characterIndex = 0; characterIndex < characterEntities.Count; characterIndex++)
            {
                var(matcher, glyph) = characterEntities[characterIndex];

                AddConnection(CircuitColor.Green, matcher, CircuitId.Input, glyph[0], null); // Connect to constant combinators holding glyphs

                var adjacentCharacterIndex = characterIndex - (characterIndex / combinatorsPerRow == 0 ? 1 : combinatorsPerRow);
                if (adjacentCharacterIndex >= 0)
                {
                    var adjacentMatcher = characterEntities[adjacentCharacterIndex].Matcher;

                    AddConnection(CircuitColor.Red, matcher, CircuitId.Input, adjacentMatcher, CircuitId.Input);     // Connect inputs together
                    AddConnection(CircuitColor.Green, matcher, CircuitId.Output, adjacentMatcher, CircuitId.Output); // Connect outputs together
                }

                // Connections between glyph parts
                for (int index = 1; index < glyph.Count; index++)
                {
                    AddConnection(CircuitColor.Green, glyph[index], null, glyph[index - 1], null);
                }
            }

            return(new Blueprint
            {
                Label = $"{font.Width}x{font.Height} Font",
                Icons = new List <Icon>
                {
                    Icon.Create(ItemNames.ConstantCombinator),
                    Icon.Create(VirtualSignalNames.LetterOrDigit('A')),
                    Icon.Create(VirtualSignalNames.LetterOrDigit('B')),
                    Icon.Create(VirtualSignalNames.LetterOrDigit('C'))
                },
                Entities = entities,
                Item = ItemNames.Blueprint,
                Version = BlueprintVersions.CurrentVersion
            });
        }
Beispiel #8
0
        public static Blueprint Generate(PixelSignalsConfiguration configuration)
        {
            var signalCount = configuration.SignalCount ?? ScreenUtil.PixelSignals.Count;

            const int maxFilters = 20;

            var entities        = new List <Entity>();
            var signalConstants = new Entity[(signalCount + maxFilters - 1) / maxFilters];

            // Signal constants
            for (var index = 0; index < signalConstants.Length; index++)
            {
                var outputSignalMap = new Entity
                {
                    Name     = ItemNames.ConstantCombinator,
                    Position = new Position
                    {
                        X = index + 1,
                        Y = 0
                    },
                    Direction        = Direction.Right,
                    Control_behavior = new ControlBehavior
                    {
                        Filters = ScreenUtil.PixelSignals.Skip(index * maxFilters).Take(Math.Min(maxFilters, signalCount - index * maxFilters)).Select((signal, signalIndex) => new Filter
                        {
                            Signal = SignalID.Create(signal),
                            Count  = 1
                        }).ToList()
                    }
                };
                signalConstants[index] = outputSignalMap;
                entities.Add(outputSignalMap);
            }

            BlueprintUtil.PopulateEntityNumbers(entities);

            // Signal constant connections
            for (var index = 1; index < signalConstants.Length; index++)
            {
                var outputSignalMap         = signalConstants[index];
                var adjacentOutputSignalMap = signalConstants[index - 1];

                AddConnection(CircuitColor.Red, outputSignalMap, null, adjacentOutputSignalMap, null);
            }

            return(new Blueprint
            {
                Label = $"Pixel signals",
                Icons = new List <Icon>
                {
                    new Icon
                    {
                        Signal = SignalID.Create(ItemNames.Lamp)
                    },
                    new Icon
                    {
                        Signal = SignalID.Create(ItemNames.ConstantCombinator)
                    }
                },
                Entities = entities,
                Item = ItemNames.Blueprint,
                Version = BlueprintVersions.CurrentVersion
            });
        }
        public static Blueprint Generate(DemuxConfiguration configuration)
        {
            var signalCount   = configuration.SignalCount ?? ComputerSignals.OrderedSignals.Count;
            var width         = configuration.Width ?? 1;
            var addressSignal = configuration.AddressSignal ?? VirtualSignalNames.Dot;
            var outputSignal  = configuration.OutputSignal ?? VirtualSignalNames.LetterOrDigit('A');

            var entities      = new List <Entity>();
            var signalFilters = new SignalFilter[signalCount];

            for (int index = 0; index < signalCount; index++)
            {
                var filterX = index % width * 4;
                var filterY = index / width;

                var addressChecker = new Entity
                {
                    Name     = ItemNames.DeciderCombinator,
                    Position = new Position
                    {
                        X = 0.5 + filterX,
                        Y = filterY
                    },
                    Direction        = Direction.Right,
                    Control_behavior = new ControlBehavior
                    {
                        Decider_conditions = new DeciderConditions
                        {
                            First_signal          = SignalID.Create(addressSignal),
                            Constant              = index + 1,
                            Comparator            = Comparators.IsEqual,
                            Output_signal         = SignalID.Create(VirtualSignalNames.Dot),
                            Copy_count_from_input = false
                        }
                    }
                };
                entities.Add(addressChecker);

                var signalRenamer = new Entity
                {
                    Name     = ItemNames.ArithmeticCombinator,
                    Position = new Position
                    {
                        X = 2.5 + filterX,
                        Y = filterY
                    },
                    Direction        = Direction.Right,
                    Control_behavior = new ControlBehavior
                    {
                        Arithmetic_conditions = new ArithmeticConditions
                        {
                            First_signal  = SignalID.Create(ComputerSignals.OrderedSignals[index]),
                            Second_signal = SignalID.Create(VirtualSignalNames.Dot),
                            Operation     = ArithmeticOperations.Multiplication,
                            Output_signal = SignalID.Create(outputSignal)
                        }
                    }
                };
                entities.Add(signalRenamer);

                signalFilters[index] = new SignalFilter
                {
                    AddressChecker = addressChecker,
                    SignalRenamer  = signalRenamer
                };
            }

            BlueprintUtil.PopulateEntityNumbers(entities);

            for (var index = 0; index < signalCount; index++)
            {
                var signalFilter = signalFilters[index];

                AddConnection(CircuitColor.Red, signalFilter.AddressChecker, CircuitId.Output, signalFilter.SignalRenamer, CircuitId.Input);

                if (index > 0)
                {
                    var adjacentSignalFilterIndex = index / width == 0 ? index - 1 : index - width;
                    var adjacentSignalFilter      = signalFilters[adjacentSignalFilterIndex];

                    AddConnection(CircuitColor.Red, signalFilter.AddressChecker, CircuitId.Input, adjacentSignalFilter.AddressChecker, CircuitId.Input);
                    AddConnection(CircuitColor.Green, signalFilter.SignalRenamer, CircuitId.Input, adjacentSignalFilter.SignalRenamer, CircuitId.Input);
                    AddConnection(CircuitColor.Red, signalFilter.SignalRenamer, CircuitId.Output, adjacentSignalFilter.SignalRenamer, CircuitId.Output);
                }
            }

            return(new Blueprint
            {
                Label = $"Demultiplexer",
                Icons = new List <Icon>
                {
                    Icon.Create(ItemNames.DeciderCombinator),
                    Icon.Create(ItemNames.ArithmeticCombinator)
                },
                Entities = entities,
                Item = ItemNames.Blueprint,
                Version = BlueprintVersions.CurrentVersion
            });
        }
Beispiel #10
0
        public Blueprint Generate(IConfigurationRoot configuration)
        {
            var registerCount = int.TryParse(configuration["RegisterCount"], out var registerCountValue) ? registerCountValue : 16;

            const char memorySignal       = 'A';
            const char writeSignal        = '1';
            const char leftOperandSignal  = '2';
            const char rightOperandSignal = '3';
            const char conditionSignal    = '4';

            const int writerEntityOffset        = -1;
            const int autoIncrementEntityOffset = -2;
            const int leftOperandEntityOffset   = 1;
            const int rightOperandEntityOffset  = 2;
            const int conditionEntityOffset     = 3;

            const int entitiesPerRegister = 6;
            const int cellWidth           = 8;
            const int cellHeight          = 2;

            var gridHeight = registerCount * 2;
            var xOffset    = -cellWidth / 2;
            var yOffset    = -gridHeight / 2;

            var entities = new List <Entity>();

            for (int row = 0; row < registerCount; row++)
            {
                var address = row + 1;
                var memoryCellEntityNumber = row * entitiesPerRegister + 3;
                var memoryCellX            = 2.5 + xOffset;
                var memoryCellY            = row * cellHeight + 1 + yOffset;

                List <int> GetAdjacentMemoryCells(params int[] offsets) => offsets
                .Where(offset => row + offset >= 0 && row + offset < registerCount)
                .Select(offset => memoryCellEntityNumber + offset * entitiesPerRegister)
                .ToList();

                var previousMemoryCell  = GetAdjacentMemoryCells(-1);
                var nextMemoryCell      = GetAdjacentMemoryCells(1);
                var adjacentMemoryCells = GetAdjacentMemoryCells(-1, 1);

                // Memory cell
                entities.Add(new Entity
                {
                    Entity_number = memoryCellEntityNumber,
                    Name          = ItemNames.DeciderCombinator,
                    Position      = new Position
                    {
                        X = memoryCellX,
                        Y = memoryCellY
                    },
                    Direction        = Direction.Right,
                    Control_behavior = new ControlBehavior
                    {
                        Decider_conditions = new DeciderConditions
                        {
                            First_signal          = SignalID.CreateLetterOrDigit(writeSignal),
                            Constant              = address,
                            Comparator            = Comparators.IsNotEqual,
                            Output_signal         = SignalID.CreateLetterOrDigit(memorySignal),
                            Copy_count_from_input = true
                        }
                    },
                    Connections = CreateConnections(new ConnectionPoint
                    {
                        Red = new List <ConnectionData>
                        {
                            // Connection to own output (data feedback)
                            new ConnectionData
                            {
                                Entity_id  = memoryCellEntityNumber,
                                Circuit_id = CircuitId.Output
                            },
                            // Connection to auto-increment output (data in)
                            new ConnectionData
                            {
                                Entity_id  = memoryCellEntityNumber + autoIncrementEntityOffset,
                                Circuit_id = CircuitId.Output
                            }
                        },
                        // Connection to next writer input (address line)
                        Green = nextMemoryCell.Select(entityNumber => new ConnectionData
                        {
                            Entity_id  = entityNumber + writerEntityOffset,
                            Circuit_id = CircuitId.Input
                        }).Concat(new List <ConnectionData>
                        {
                            // Connection to writer input (address line)
                            new ConnectionData
                            {
                                Entity_id  = memoryCellEntityNumber + writerEntityOffset,
                                Circuit_id = CircuitId.Input
                            }
                        }).ToList()
                    }, new ConnectionPoint
                    {
                        Red = new List <ConnectionData>
                        {
                            // Connection to own input (data feedback)
                            new ConnectionData
                            {
                                Entity_id  = memoryCellEntityNumber,
                                Circuit_id = CircuitId.Input
                            },
                            // Connection to writer output (data in)
                            new ConnectionData
                            {
                                Entity_id  = memoryCellEntityNumber + writerEntityOffset,
                                Circuit_id = CircuitId.Output
                            },
                            // Connection to right operand input (data out)
                            new ConnectionData
                            {
                                Entity_id  = memoryCellEntityNumber + rightOperandEntityOffset,
                                Circuit_id = CircuitId.Input
                            }
                        }
                    })
                });

                // Memory cell writer
                entities.Add(new Entity
                {
                    Entity_number = memoryCellEntityNumber + writerEntityOffset,
                    Name          = ItemNames.DeciderCombinator,
                    Position      = new Position
                    {
                        X = memoryCellX,
                        Y = memoryCellY - 1
                    },
                    Direction        = Direction.Right,
                    Control_behavior = new ControlBehavior
                    {
                        Decider_conditions = new DeciderConditions
                        {
                            First_signal          = SignalID.CreateLetterOrDigit(writeSignal),
                            Constant              = address,
                            Comparator            = Comparators.IsEqual,
                            Output_signal         = SignalID.CreateLetterOrDigit(memorySignal),
                            Copy_count_from_input = true
                        }
                    },
                    Connections = CreateConnections(new ConnectionPoint
                    {
                        // Connection to adjacent writer input (data in)
                        Red = adjacentMemoryCells.Select(entityNumber => new ConnectionData
                        {
                            Entity_id  = entityNumber + writerEntityOffset,
                            Circuit_id = CircuitId.Input
                        }).ToList(),
                        // Connection to previous memory cell input (address line)
                        Green = previousMemoryCell.Select(entityNumber => new ConnectionData
                        {
                            Entity_id  = entityNumber,
                            Circuit_id = CircuitId.Input
                        }).Concat(new List <ConnectionData>
                        {
                            // Connection to memory cell input (address line)
                            new ConnectionData
                            {
                                Entity_id  = memoryCellEntityNumber,
                                Circuit_id = CircuitId.Input
                            }
                        }).ToList(),
                    }, new ConnectionPoint
                    {
                        Red = new List <ConnectionData>
                        {
                            // Connection to memory cell output (data out)
                            new ConnectionData
                            {
                                Entity_id  = memoryCellEntityNumber,
                                Circuit_id = CircuitId.Output
                            }
                        }
                    })
                });

                // Auto-increment writer
                entities.Add(new Entity
                {
                    Entity_number = memoryCellEntityNumber + autoIncrementEntityOffset,
                    Name          = ItemNames.DeciderCombinator,
                    Position      = new Position
                    {
                        X = memoryCellX - 2,
                        Y = memoryCellY - 1
                    },
                    Direction        = Direction.Right,
                    Control_behavior = new ControlBehavior
                    {
                        Decider_conditions = new DeciderConditions
                        {
                            First_signal          = SignalID.CreateLetterOrDigit(leftOperandSignal),
                            Constant              = address,
                            Comparator            = Comparators.IsEqual,
                            Output_signal         = SignalID.CreateLetterOrDigit(memorySignal),
                            Copy_count_from_input = true
                        }
                    },
                    Connections = CreateConnections(new ConnectionPoint
                    {
                        // Connection to adjacent auto-increment input (address line/data in)
                        Green = adjacentMemoryCells.Select(entityNumber => new ConnectionData
                        {
                            Entity_id  = entityNumber + autoIncrementEntityOffset,
                            Circuit_id = CircuitId.Input
                        }).ToList(),
                    }, new ConnectionPoint
                    {
                        Red = new List <ConnectionData>
                        {
                            // Connection to memory cell input (data out)
                            new ConnectionData
                            {
                                Entity_id  = memoryCellEntityNumber,
                                Circuit_id = CircuitId.Input
                            }
                        }
                    })
                });

                // Left operand reader
                entities.Add(new Entity
                {
                    Entity_number = memoryCellEntityNumber + leftOperandEntityOffset,
                    Name          = ItemNames.DeciderCombinator,
                    Position      = new Position
                    {
                        X = memoryCellX + 2,
                        Y = memoryCellY - 1
                    },
                    Direction        = Direction.Right,
                    Control_behavior = new ControlBehavior
                    {
                        Decider_conditions = new DeciderConditions
                        {
                            First_signal          = SignalID.CreateLetterOrDigit(leftOperandSignal),
                            Constant              = address,
                            Comparator            = Comparators.IsEqual,
                            Output_signal         = SignalID.CreateLetterOrDigit(memorySignal),
                            Copy_count_from_input = true
                        }
                    },
                    Connections = CreateConnections(new ConnectionPoint
                    {
                        Red = new List <ConnectionData>
                        {
                            // Connection to right operand input (data out)
                            new ConnectionData
                            {
                                Entity_id  = memoryCellEntityNumber + rightOperandEntityOffset,
                                Circuit_id = CircuitId.Input
                            },
                            // Connection to condition input (data out)
                            new ConnectionData
                            {
                                Entity_id  = memoryCellEntityNumber + conditionEntityOffset,
                                Circuit_id = CircuitId.Input
                            }
                        },
                        // Connection to previous right operand input (address line)
                        Green = previousMemoryCell.Select(entityNumber => new ConnectionData
                        {
                            Entity_id  = entityNumber + rightOperandEntityOffset,
                            Circuit_id = CircuitId.Input
                        }).Concat(new List <ConnectionData>
                        {
                            // Connection to right operand input (address line)
                            new ConnectionData
                            {
                                Entity_id  = memoryCellEntityNumber + rightOperandEntityOffset,
                                Circuit_id = CircuitId.Input
                            },
                            // Connection to condition input (address line)
                            new ConnectionData
                            {
                                Entity_id  = memoryCellEntityNumber + conditionEntityOffset,
                                Circuit_id = CircuitId.Input
                            }
                        }).ToList()
                    }, new ConnectionPoint
                    {
                        // Connection to adjacent left operand output (data out)
                        Red = adjacentMemoryCells.Select(entityNumber => new ConnectionData
                        {
                            Entity_id  = entityNumber + leftOperandEntityOffset,
                            Circuit_id = CircuitId.Output
                        }).ToList()
                    })
                });

                // Right operand reader
                entities.Add(new Entity
                {
                    Entity_number = memoryCellEntityNumber + rightOperandEntityOffset,
                    Name          = ItemNames.DeciderCombinator,
                    Position      = new Position
                    {
                        X = memoryCellX + 2,
                        Y = memoryCellY
                    },
                    Direction        = Direction.Right,
                    Control_behavior = new ControlBehavior
                    {
                        Decider_conditions = new DeciderConditions
                        {
                            First_signal          = SignalID.CreateLetterOrDigit(rightOperandSignal),
                            Constant              = address,
                            Comparator            = Comparators.IsEqual,
                            Output_signal         = SignalID.CreateLetterOrDigit(memorySignal),
                            Copy_count_from_input = true
                        }
                    },
                    Connections = CreateConnections(new ConnectionPoint
                    {
                        Red = new List <ConnectionData>
                        {
                            // Connection to memory cell output (data out)
                            new ConnectionData
                            {
                                Entity_id  = memoryCellEntityNumber,
                                Circuit_id = CircuitId.Output
                            }
                        },
                        // Connection to next left operand input (address line)
                        Green = nextMemoryCell.Select(entityNumber => new ConnectionData
                        {
                            Entity_id  = entityNumber + leftOperandEntityOffset,
                            Circuit_id = CircuitId.Input
                        }).Concat(new List <ConnectionData>
                        {
                            // Connection to left operand input (address line)
                            new ConnectionData
                            {
                                Entity_id  = memoryCellEntityNumber + leftOperandEntityOffset,
                                Circuit_id = CircuitId.Input
                            }
                        }).ToList()
                    }, new ConnectionPoint
                    {
                        // Connection to adjacent right operand output (data out)
                        Red = adjacentMemoryCells.Select(entityNumber => new ConnectionData
                        {
                            Entity_id  = entityNumber + rightOperandEntityOffset,
                            Circuit_id = CircuitId.Output
                        }).ToList()
                    })
                });

                // Condition reader
                entities.Add(new Entity
                {
                    Entity_number = memoryCellEntityNumber + conditionEntityOffset,
                    Name          = ItemNames.DeciderCombinator,
                    Position      = new Position
                    {
                        X = memoryCellX + 4,
                        Y = memoryCellY - 1
                    },
                    Direction        = Direction.Right,
                    Control_behavior = new ControlBehavior
                    {
                        Decider_conditions = new DeciderConditions
                        {
                            First_signal          = SignalID.CreateLetterOrDigit(conditionSignal),
                            Constant              = address,
                            Comparator            = Comparators.IsEqual,
                            Output_signal         = SignalID.CreateLetterOrDigit(memorySignal),
                            Copy_count_from_input = true
                        }
                    },
                    Connections = CreateConnections(new ConnectionPoint
                    {
                        Red = new List <ConnectionData>
                        {
                            // Connection to left operand input (data out)
                            new ConnectionData
                            {
                                Entity_id  = memoryCellEntityNumber + leftOperandEntityOffset,
                                Circuit_id = CircuitId.Input
                            }
                        },
                        Green = new List <ConnectionData>
                        {
                            // Connection to left operand input (address line)
                            new ConnectionData
                            {
                                Entity_id  = memoryCellEntityNumber + leftOperandEntityOffset,
                                Circuit_id = CircuitId.Input
                            }
                        }
                    }, new ConnectionPoint
                    {
                        // Connection to adjacent condition output (data out)
                        Red = adjacentMemoryCells.Select(entityNumber => new ConnectionData
                        {
                            Entity_id  = entityNumber + conditionEntityOffset,
                            Circuit_id = CircuitId.Output
                        }).ToList()
                    })
                });
            }

            return(new Blueprint
            {
                Label = $"{registerCount} Registers",
                Icons = new List <Icon>
                {
                    new Icon
                    {
                        Signal = SignalID.Create(ItemNames.ProcessingUnit)
                    }
                },
                Entities = entities,
                Item = ItemNames.Blueprint,
                Version = BlueprintVersions.CurrentVersion
            });
        }