public static void Run(IConfigurationRoot configuration) { var outputBlueprintFile = configuration["OutputBlueprint"]; var outputJsonFile = configuration["OutputJson"]; var memoryType = configuration["MemoryType"]; var generatorType = typeof(MemoryInitializer).Assembly.GetTypes() .FirstOrDefault(type => type.IsAssignableTo(typeof(IBlueprintGenerator)) && type.Name.Equals($"{memoryType}Generator", StringComparison.CurrentCultureIgnoreCase)); if (generatorType == null) { throw new Exception($"Unsupported memory type: {memoryType}"); } var generator = (IBlueprintGenerator)Activator.CreateInstance(generatorType); var blueprint = generator.Generate(configuration); BlueprintUtil.PopulateIndices(blueprint); var blueprintWrapper = new BlueprintWrapper { Blueprint = blueprint }; BlueprintUtil.WriteOutBlueprint(outputBlueprintFile, blueprintWrapper); BlueprintUtil.WriteOutJson(outputJsonFile, blueprintWrapper); }
public static void Run(AssemblerConfiguration configuration) { var inputProgramFile = configuration.InputProgram; var outputBlueprintFile = configuration.OutputBlueprint; var outputJsonFile = configuration.OutputJson; var outputInstructionsFile = configuration.OutputInstructions; var snapToGrid = configuration.SnapToGrid; var x = configuration.X; var y = configuration.Y; var width = configuration.Width; var height = configuration.Height; using var instructionsWriter = new StreamWriter(outputInstructionsFile); var compiledProgram = AssembleCode(inputProgramFile, instructionsWriter); if (compiledProgram != null) { var blueprint = ProgramRomGenerator.CreateBlueprintFromCompiledProgram(compiledProgram, snapToGrid, x, y, width, height, instructionsWriter); BlueprintUtil.PopulateIndices(blueprint); var blueprintWrapper = new BlueprintWrapper { Blueprint = blueprint }; BlueprintUtil.WriteOutBlueprint(outputBlueprintFile, blueprintWrapper); BlueprintUtil.WriteOutJson(outputJsonFile, blueprintWrapper); } }
public static void Run(IConfigurationRoot configuration) { var inputBlueprintFile = configuration["InputBlueprint"]; var outputJsonFile = configuration["OutputJson"]; var json = BlueprintUtil.ReadBlueprintFileAsJson(inputBlueprintFile); var jsonObj = JsonSerializer.Deserialize <object>(json); BlueprintUtil.WriteOutJson(outputJsonFile, jsonObj); }
public static void Run(IConfigurationRoot configuration) { var inputBlueprintFile = configuration["InputBlueprint"]; var outputBlueprintFile = configuration["OutputBlueprint"]; var outputJsonFile = configuration["OutputJson"]; var outputUpdatedJsonFile = configuration["OutputUpdatedJson"]; var signalToFind = configuration["SignalToFind"]; var replacementSignal = configuration["ReplacementSignal"]; var json = BlueprintUtil.ReadBlueprintFileAsJson(inputBlueprintFile); var jsonObj = JsonSerializer.Deserialize <object>(json); var blueprintWrapper = BlueprintUtil.DeserializeBlueprintWrapper(json); BlueprintUtil.WriteOutJson(outputJsonFile, jsonObj); foreach (var entity in blueprintWrapper.Blueprint.Entities) { var signals = new List <SignalID> { entity.Control_behavior?.Circuit_condition?.First_signal, entity.Control_behavior?.Circuit_condition?.Second_signal, entity.Control_behavior?.Arithmetic_conditions?.First_signal, entity.Control_behavior?.Arithmetic_conditions?.Second_signal, entity.Control_behavior?.Arithmetic_conditions?.Output_signal, entity.Control_behavior?.Decider_conditions?.First_signal, entity.Control_behavior?.Decider_conditions?.Second_signal, entity.Control_behavior?.Decider_conditions?.Output_signal } .Concat(entity.Control_behavior?.Filters?.Select(filter => filter.Signal) ?? new List <SignalID>()) .Where(signal => signal != null); foreach (var signal in signals) { if (signal.Name == signalToFind) { signal.Name = replacementSignal; } } } BlueprintUtil.WriteOutBlueprint(outputBlueprintFile, blueprintWrapper); BlueprintUtil.WriteOutJson(outputUpdatedJsonFile, blueprintWrapper); }
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(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 void Run(VideoConfiguration configuration) { var videoFile = configuration.VideoFile; var outputBlueprintFile = configuration.OutputBlueprint; var outputJsonFile = configuration.OutputJson; var frameWidth = configuration.FrameWidth ?? 32; var frameHeight = configuration.FrameHeight ?? 32; var colorMode = configuration.ColorMode ?? ColorMode.Monochrome; var ditheringMode = configuration.DitheringMode ?? DitheringMode.None; var romHeight = configuration.RomHeight ?? 2; var maxFrames = romHeight * 32; var videoBuffer = new MemoryStream(); using (var videoStream = File.OpenRead(videoFile)) { videoStream.CopyTo(videoBuffer); } var pixelSize = colorMode switch { ColorMode.Monochrome => 1, ColorMode.RedGreenBlue => 2, ColorMode.RedGreenBlueWhite => 2, _ => throw new Exception($"Unexpected color mode: {colorMode}") }; var palette = colorMode switch { ColorMode.Monochrome => GeneratePalette(new HdrColor[] { HdrColor.FromRgb(1, 1, 1) }), ColorMode.RedGreenBlue => GeneratePalette(new HdrColor[] { HdrColor.FromRgb(1, 0, 0), HdrColor.FromRgb(0, 1, 0), HdrColor.FromRgb(0, 0, 1) }), ColorMode.RedGreenBlueWhite => GeneratePalette(new HdrColor[] { HdrColor.FromRgb(0.8, 0, 0), HdrColor.FromRgb(0, 0.8, 0), HdrColor.FromRgb(0, 0, 0.8), HdrColor.FromRgb(0.8, 0.8, 0.8) }), _ => throw new Exception($"Unexpected color mode: {colorMode}") }; // Information on dithering: https://cmitja.files.wordpress.com/2015/01/hellandtanner_imagedithering11algorithms.pdf var ditheringWeights = ditheringMode switch { DitheringMode.None => Array.Empty <DitheringWeight>(), DitheringMode.Sierra => new DitheringWeight[] { new DitheringWeight(5 / 32d, 1, 0), new DitheringWeight(3 / 32d, 2, 0), new DitheringWeight(2 / 32d, -2, 1), new DitheringWeight(4 / 32d, -1, 1), new DitheringWeight(5 / 32d, 0, 1), new DitheringWeight(4 / 32d, 1, 1), new DitheringWeight(2 / 32d, 2, 1), new DitheringWeight(2 / 32d, -1, 2), new DitheringWeight(3 / 32d, 0, 2), new DitheringWeight(2 / 32d, 1, 2), }, DitheringMode.SierraLite => new DitheringWeight[] { new DitheringWeight(2 / 4d, 1, 0), new DitheringWeight(1 / 4d, -1, 1), new DitheringWeight(1 / 4d, 0, 1) }, DitheringMode.Temporal => new DitheringWeight[] { new DitheringWeight(2 / 6d, 1, 0, 0), new DitheringWeight(1 / 6d, -1, 1, 0), new DitheringWeight(1 / 6d, 0, 1, 0), new DitheringWeight(2 / 6d, 0, 0, 1) }, _ => throw new Exception($"Unexpected dithering mode: {ditheringMode}") }; var rawFrameWidth = frameWidth / pixelSize; var rawFrameHeight = frameHeight / pixelSize; var video = new Video(videoBuffer, rawFrameWidth, rawFrameHeight); var rawFrame = new int[rawFrameWidth * rawFrameHeight]; var frames = new List <bool[, ]>(); var ditheringFrames = ditheringWeights.Max(ditheringWeight => ditheringWeight.Frame) + 1; var colorErrors = new HdrColor[ditheringFrames][, ]; for (var index = 0; index < ditheringFrames; index++) { colorErrors[index] = new HdrColor[frameHeight, frameWidth]; } while (video.AdvanceFrame(rawFrame) && frames.Count < maxFrames) { var frame = new bool[frameHeight, frameWidth]; frames.Add(frame); var totalBrightness = 0d; for (var rawY = 0; rawY < rawFrameHeight; rawY++) { for (var rawX = 0; rawX < rawFrameWidth; rawX++) { var color = System.Drawing.Color.FromArgb(rawFrame[rawX + rawY * rawFrameWidth]); totalBrightness += color.GetBrightness(); } } var averageBrightness = totalBrightness / (rawFrameWidth * rawFrameHeight); const double gammaMultiplier = 2; var gamma = gammaMultiplier * (averageBrightness is > 0.1 and < 0.9 ? Math.Log(0.5, averageBrightness) : 1); for (var rawY = 0; rawY < rawFrameHeight; rawY++) { for (var rawX = 0; rawX < rawFrameWidth; rawX++) { var x = rawX * pixelSize; var y = rawY * pixelSize; var rawColor = HdrColor.FromArgb(rawFrame[rawX + rawY * rawFrameWidth]); var gammaCorrectedColor = rawColor.Pow(gamma); var ditheredColor = gammaCorrectedColor + colorErrors[0][rawY, rawX]; var closestPaletteEntry = GetClosestPaletteEntry(palette, ditheredColor); var newColorError = ditheredColor - closestPaletteEntry.Color; var outputColor = closestPaletteEntry.OutputColor; for (var subPixelY = 0; subPixelY < pixelSize; subPixelY++) { for (var subPixelX = 0; subPixelX < pixelSize; subPixelX++) { var subPixelIndex = subPixelX + subPixelY * pixelSize; if (subPixelIndex < outputColor.Length) { frame[y + subPixelY, x + subPixelX] = outputColor[subPixelIndex]; } } } for (var index = 0; index < ditheringWeights.Length; index++) { var ditheringWeight = ditheringWeights[index]; var errorX = rawX + ditheringWeight.X; var errorY = rawY + ditheringWeight.Y; if (errorX >= 0 && errorX < frameWidth && errorY < frameHeight) { colorErrors[ditheringWeight.Frame][errorY, errorX] += newColorError * ditheringWeight.Weight; } } } } for (var index = 0; index < ditheringFrames - 1; index++) { colorErrors[index] = colorErrors[index + 1]; } colorErrors[ditheringFrames - 1] = new HdrColor[frameHeight, frameWidth]; Console.Write('.'); } Console.WriteLine($"Frames: {frames.Count}"); var blueprint = VideoRomGenerator.Generate(new VideoMemoryConfiguration { SnapToGrid = configuration.SnapToGrid, X = configuration.X, Y = configuration.Y, Width = frameWidth, Height = romHeight, BaseAddress = configuration.BaseAddress }, frames); BlueprintUtil.PopulateIndices(blueprint); var blueprintWrapper = new BlueprintWrapper { Blueprint = blueprint }; BlueprintUtil.WriteOutBlueprint(outputBlueprintFile, blueprintWrapper); BlueprintUtil.WriteOutJson(outputJsonFile, blueprintWrapper); }
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 void Run(VideoConfiguration configuration) { var videoFile = configuration.VideoFile; var outputBlueprintFile = configuration.OutputBlueprint; var outputJsonFile = configuration.OutputJson; var frameWidth = configuration.FrameWidth ?? 32; var frameHeight = configuration.FrameHeight ?? 32; var colorMode = configuration.ColorMode ?? ColorMode.Monochrome; var ditherSize = configuration.DitherSize ?? 1; var useEdgeDetection = configuration.UseEdgeDetection ?? false; var romHeight = configuration.RomHeight ?? 2; var maxFrames = romHeight * 32; var videoBuffer = new MemoryStream(); using (var videoStream = File.OpenRead(videoFile)) { videoStream.CopyTo(videoBuffer); } var basePixelSize = colorMode switch { ColorMode.Monochrome => 1, ColorMode.RedGreenBlue => 2, ColorMode.RedGreenBlueWhite => 2, _ => throw new Exception($"Unexpected color mode: {colorMode}") }; var pixelSize = basePixelSize * ditherSize; var rawFrameWidth = frameWidth / pixelSize; var rawFrameHeight = frameHeight / pixelSize; var video = new Video(videoBuffer, rawFrameWidth, rawFrameHeight); var rawFrame = new int[rawFrameWidth * rawFrameHeight]; var frames = new List <bool[, ]>(); while (video.AdvanceFrame(rawFrame) && frames.Count < maxFrames) { var frame = new bool[frameHeight, frameWidth]; frames.Add(frame); var brightnessCutoff = 0d; if (ditherSize == 1) { var totalBrightness = 0d; for (var rawY = 0; rawY < rawFrameHeight; rawY++) { for (var rawX = 0; rawX < rawFrameWidth; rawX++) { var color = System.Drawing.Color.FromArgb(rawFrame[rawX + rawY * rawFrameWidth]); totalBrightness += color.GetBrightness(); } } brightnessCutoff = totalBrightness / (rawFrameWidth * rawFrameHeight); } for (var rawY = 0; rawY < rawFrameHeight; rawY++) { for (var rawX = 0; rawX < rawFrameWidth; rawX++) { var x = rawX * pixelSize; var y = rawY * pixelSize; var color = SysColor.FromArgb(rawFrame[rawX + rawY * rawFrameWidth]); var levels = ditherSize * ditherSize + 1; switch (colorMode) { case ColorMode.Monochrome: { var brightness = color.GetBrightness(); for (var ditherY = 0; ditherY < ditherSize; ditherY++) { for (var ditherX = 0; ditherX < ditherSize; ditherX++) { var currentBrightnessCutoff = ditherSize == 1 ? brightnessCutoff : (double)(ditherX + ditherY * ditherSize + 1) / levels; frame[y + ditherY, x + ditherX] = brightness > currentBrightnessCutoff; } } } break; case ColorMode.RedGreenBlue: for (var ditherY = 0; ditherY < ditherSize; ditherY++) { for (var ditherX = 0; ditherX < ditherSize; ditherX++) { var currentBrightnessCutoff = ditherSize == 1 ? brightnessCutoff : (double)(ditherX + ditherY * ditherSize + 1) / levels; var cutoff = (byte)(currentBrightnessCutoff * 255); var red = color.R >= cutoff; var green = color.G >= cutoff; var blue = color.B >= cutoff; var baseX = x + ditherX * 2; var baseY = y + ditherY * 2; frame[baseY, baseX] = red; frame[baseY, baseX + 1] = green; frame[baseY + 1, baseX] = blue; } } break; case ColorMode.RedGreenBlueWhite: for (var ditherY = 0; ditherY < ditherSize; ditherY++) { for (var ditherX = 0; ditherX < ditherSize; ditherX++) { var currentBrightnessCutoff = ditherSize == 1 ? brightnessCutoff : (double)(ditherX + ditherY * ditherSize + 1) / levels; var cutoff = (byte)(currentBrightnessCutoff * 255); var red = color.R >= cutoff; var green = color.G >= cutoff; var blue = color.B >= cutoff; bool white = useEdgeDetection ? DetectEdge(color, rawFrame, rawX, rawY, rawFrameWidth, -1, 0) || DetectEdge(color, rawFrame, rawX, rawY, rawFrameWidth, 0, -1) || DetectEdge(color, rawFrame, rawX, rawY, rawFrameWidth, -1, -1) : red && green && blue && color.GetBrightness() >= (currentBrightnessCutoff + 1) / 2; var baseX = x + ditherX * 2; var baseY = y + ditherY * 2; frame[baseY, baseX] = red; frame[baseY, baseX + 1] = green; frame[baseY + 1, baseX] = blue; frame[baseY + 1, baseX + 1] = white; } } break; } } } } Console.WriteLine($"Frames: {frames.Count}"); var blueprint = VideoRomGenerator.Generate(new VideoMemoryConfiguration { SnapToGrid = configuration.SnapToGrid, X = configuration.X, Y = configuration.Y, Width = frameWidth, Height = romHeight, BaseAddress = configuration.BaseAddress }, frames); BlueprintUtil.PopulateIndices(blueprint); var blueprintWrapper = new BlueprintWrapper { Blueprint = blueprint }; BlueprintUtil.WriteOutBlueprint(outputBlueprintFile, blueprintWrapper); BlueprintUtil.WriteOutJson(outputJsonFile, blueprintWrapper); }
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 }); }
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 }); }