sandboxels/mods/circuitcore.js

1915 lines
51 KiB
JavaScript
Raw Permalink Normal View History

2025-06-30 20:38:23 -04:00
// CircuitCore.js: adds circuits (logicGates.js is required)
// Other mods
ensureModEnabled("mods/betterSettings.js");
ensureModEnabled("mods/logicgates.js");
const isLightmapEnabled = enabledMods.includes("mods/lightmap.js") || enabledMods.includes("mods/fast_lightmap.js");
// Define settings
const cc_settingsTab = new SettingsTab("CircuitCore");
const cc_heat_emit_setting = new Setting("Make circuits emit heat", "heatEmit", settingType.BOOLEAN, false, true);
const cc_stable_tick_setting = new Setting("More consistent logicgates.js tick (page refresh required)", "stableTick", settingType.BOOLEAN, false, true);
cc_settingsTab.registerSettings("Realism", cc_heat_emit_setting);
cc_settingsTab.registerSettings("Tick", cc_stable_tick_setting);
2024-08-10 23:13:02 -04:00
settingsManager.registerTab(cc_settingsTab);
2024-07-21 00:56:17 -04:00
2025-06-30 20:38:23 -04:00
// Constants
const DIGIT_SEGMENT_PATTERNS = [
"111101101101111", // 0
"001001001001001", // 1
"111001111100111", // 2
"111001111001111", // 3
"101101111001001", // 4
"111100111001111", // 5
"111100111101111", // 6
"111001001001001", // 7
"111101111101111", // 8
"111101111001111", // 9
"111101111101101", // A
"100100111101111", // B
"111100100100111", // C
"001001111101111", // D
"111100111100111", // E
"111100111100100" // F
];
let colorPalette_4bit = [
2025-02-22 05:05:00 -05:00
"#101820", "#37175F", "#5F1717", "#6F175F",
"#005F00", "#1563BF", "#7F401A", "#525252",
"#8F8F8F", "#EE8822", "#FF3027", "#FF47FF",
"#58E618", "#27FFDF", "#FFFF27", "#FFFFFF"
2024-07-21 00:56:17 -04:00
];
2025-06-30 20:38:23 -04:00
function ensureModEnabled(modName) {
if (enabledMods.includes(modName))
return;
2025-02-22 05:05:00 -05:00
2025-06-30 20:38:23 -04:00
enabledMods.unshift(modName);
localStorage.setItem("enabledMods", JSON.stringify(enabledMods));
window.location.reload();
2024-08-10 23:13:02 -04:00
}
function cc_arrayToRgbString(rgbArray) {
2025-02-22 05:05:00 -05:00
return `rgb(${rgbArray.join(', ')})`;
2024-08-10 23:13:02 -04:00
}
function cc_scaleList(numbers, scale) {
return numbers.map(number => number * scale);
}
2024-07-21 00:56:17 -04:00
function binaryArrayToNumber(binaryArray) {
2025-06-30 20:38:23 -04:00
return binaryArray.reduce((number, bit) => (number << 1) | bit, 0);
2024-07-21 00:56:17 -04:00
}
2025-06-30 20:38:23 -04:00
function validateHexDigit(digit) {
if (!Number.isInteger(digit) || digit < 0 || digit >= DIGIT_SEGMENT_PATTERNS.length) {
throw new RangeError(`Hex digit must be between 0 and 15, got ${digit}`);
2024-07-21 00:56:17 -04:00
}
2025-06-30 20:38:23 -04:00
}
function parseBitTriplet(triplet) {
return triplet.split('').map(c => c === '1' ? 1 : 0);
}
function createSevenSegmentGrid(digit) {
validateHexDigit(digit);
2024-07-21 00:56:17 -04:00
2025-06-30 20:38:23 -04:00
const pattern = DIGIT_SEGMENT_PATTERNS[digit];
const grid = [];
for (let i = 0; i < 15; i += 3) {
grid.push(parseBitTriplet(pattern.slice(i, i + 3)));
2024-07-21 00:56:17 -04:00
}
2025-06-30 20:38:23 -04:00
return grid;
2024-07-21 00:56:17 -04:00
}
2025-06-30 20:38:23 -04:00
function rotateCoordinateAroundOrigin(x, y, angle) {
let radians = angle * (Math.PI / 180);
let cos = Math.cos(radians);
let sin = Math.sin(radians);
2024-07-21 00:56:17 -04:00
2025-06-30 20:38:23 -04:00
let nx = Math.round(x * cos - y * sin);
let ny = Math.round(x * sin + y * cos);
2024-07-21 00:56:17 -04:00
return [nx, ny];
}
// Initialize the circuit with optional rotation
2025-06-30 20:38:23 -04:00
function initializeCircuit(pixel, pins, w, h, center = true, rotation = circuitRotation, callback = () => {
}) {
if (pixel.hasGenerated) {
return;
}
2024-07-21 00:56:17 -04:00
2025-06-30 20:38:23 -04:00
if (!center) {
rotation = 0;
} // non-centered circuits don't support rotation yet
2024-07-21 00:56:17 -04:00
pixel.circuitRotation = rotation;
createCircuitFrame(pixel, w, h, center, rotation);
createPins(pixel, pins, rotation);
callback(pixel, pins, w, h);
pixel.hasGenerated = true;
}
2025-06-30 20:38:23 -04:00
function createCircuitFrame(pixel, width_, height_, center = true, rotation = 0) {
let halfHeight = Math.floor(height_ / 2);
let halfWidth = Math.floor(width_ / 2);
2024-07-21 00:56:17 -04:00
2025-06-30 20:38:23 -04:00
let a = -halfHeight;
let b = halfHeight;
let c = -halfWidth;
let d = halfWidth;
2024-07-21 00:56:17 -04:00
if (!center) {
a = 0;
b = height_ - 1;
c = 0;
d = width_ - 1;
}
2025-06-30 20:38:23 -04:00
for (let y = a; y <= b; y++) {
for (let x = c; x <= d; x++) {
let [rx, ry] = rotateCoordinateAroundOrigin(x, y, rotation);
let px = pixel.x + rx;
let py = pixel.y + ry;
2025-06-30 20:38:23 -04:00
if (!(0 <= px && px < width && 0 <= py && py < height)) {
continue;
}
2024-07-21 00:56:17 -04:00
// Create the pixel
2024-07-21 00:56:17 -04:00
if (!pixelMap[px] || pixelMap[px][py] === undefined) {
createPixel("circuit_material", px, py);
}
// Set the core position property
if (pixelMap[px] && pixelMap[px][py] && pixelMap[px][py].element === "circuit_material") {
2025-06-30 20:38:23 -04:00
pixelMap[px][py].corePosition = {x: pixel.x, y: pixel.y};
}
2024-07-21 00:56:17 -04:00
}
}
}
2025-06-30 20:38:23 -04:00
function createPins(pixel, pins, rotation = 0) {
for (let i = 0; i < pins.length; i++) {
let [rx, ry] = rotateCoordinateAroundOrigin(pins[i][0], pins[i][1], rotation);
let px = pixel.x + rx;
let py = pixel.y + ry;
if (!(0 <= px && px < width && 0 <= py && py < height)) {
continue;
}
2025-06-30 20:38:23 -04:00
if (!pixelMap[px] || pixelMap[px][py] === undefined) {
let pinType = pins[i][2] ? "input_pin" : "output_pin";
2024-07-21 00:56:17 -04:00
createPixel(pinType, px, py);
}
}
}
2025-06-30 20:38:23 -04:00
function getRotatedPinPixel(pixel, pins, index, rotation = pixel.circuitRotation) {
let [rx, ry] = rotateCoordinateAroundOrigin(pins[index][0], pins[index][1], rotation);
let px = pixel.x + rx;
let py = pixel.y + ry;
if (!(0 <= px && px < width && 0 <= py && py < height)) {
return null;
}
return pixelMap[px][py];
2024-07-21 00:56:17 -04:00
}
2025-06-30 20:38:23 -04:00
function checkPin(pixel, pins, index, rotation = pixel.circuitRotation) {
const target = getRotatedPinPixel(pixel, pins, index, rotation);
return target && target.active;
}
2024-07-21 00:56:17 -04:00
2025-06-30 20:38:23 -04:00
function setPin(pixel, pins, index, value, rotation = pixel.circuitRotation) {
const target = getRotatedPinPixel(pixel, pins, index, rotation);
if (target && target.element === "output_pin") {
target.active = value;
2024-07-21 00:56:17 -04:00
}
}
// Circuits
2025-06-30 20:38:23 -04:00
function general_reverser(inputBits) {
return function (pixel) {
let pins = [];
let outputCount = inputBits;
let circuitWidth = (inputBits * 2) + 1;
let circuitHeight = 3;
// Define input pins
for (let i = 0; i < inputBits; i++) {
pins.push([Math.floor(circuitWidth / 2) - 1 - (2 * i), -2, true]);
}
// Define output pins
for (let i = 0; i < outputCount; i++) {
pins.push([Math.floor(circuitWidth / 2) - 1 - (2 * i), 2, false]); // Right outputs
}
initializeCircuit(pixel, pins, circuitWidth, circuitHeight);
// Read input values
let input = [];
for (let i = 0; i < inputBits; i++) {
input.push(checkPin(pixel, pins, i));
}
// Set output values
for (let i = 0; i < outputCount; i++) {
setPin(pixel, pins, inputBits + i, input[input.length - 1 - i]);
}
};
}
elements.two_bit_reverser_circuit = {
cc_stableTick: general_reverser(2)
};
let lastSelectedReverserCircuitBits;
elements.reverser_circuit = {
onSelect: function () {
// Prompt user for the bit count
lastSelectedReverserCircuitBits = parseInt(prompt("How many bits wide chip?", "4"));
},
cc_stableTick: function (pixel) {
if (!pixel.bits) {
pixel.bits = lastSelectedReverserCircuitBits;
}
let circuitFunction = general_reverser(pixel.bits);
circuitFunction(pixel);
}
};
2024-08-23 05:15:43 -04:00
elements.four_bit_selector_circuit = {
2025-06-30 20:38:23 -04:00
cc_stableTick: function (pixel) {
let pins = [
2025-02-22 05:05:00 -05:00
// First 4-bit input (A)
[-7, -2, true], // A0
[-5, -2, true], // A1
[-3, -2, true], // A2
[-1, -2, true], // A3
// Second 4-bit input (B)
[1, -2, true], // B0
[3, -2, true], // B1
[5, -2, true], // B2
[7, -2, true], // B3
// Selection pin (Sel)
[9, 0, true], // Selection (Sel)
// Output (O)
[-3, 2, false], // O0 (centered)
[-1, 2, false], // O1 (centered)
[1, 2, false], // O2 (centered)
[3, 2, false], // O3 (centered)
];
initializeCircuit(pixel, pins, 17, 3);
// Read inputs
2025-06-30 20:38:23 -04:00
let A = [
2025-02-22 05:05:00 -05:00
checkPin(pixel, pins, 0),
checkPin(pixel, pins, 1),
checkPin(pixel, pins, 2),
checkPin(pixel, pins, 3)
];
2025-06-30 20:38:23 -04:00
let B = [
2025-02-22 05:05:00 -05:00
checkPin(pixel, pins, 4),
checkPin(pixel, pins, 5),
checkPin(pixel, pins, 6),
checkPin(pixel, pins, 7)
];
2025-06-30 20:38:23 -04:00
let Sel = checkPin(pixel, pins, 8); // Selection pin
2025-02-22 05:05:00 -05:00
// Select between A and B based on Sel
2025-06-30 20:38:23 -04:00
let output = Sel ? B : A;
2025-02-22 05:05:00 -05:00
// Output the selected 4-bit value
setPin(pixel, pins, 9, output[0]); // O0
setPin(pixel, pins, 10, output[1]); // O1
setPin(pixel, pins, 11, output[2]); // O2
setPin(pixel, pins, 12, output[3]); // O3
}
2024-08-23 05:15:43 -04:00
};
2024-07-21 00:56:17 -04:00
elements.four_bit_enabler_circuit = {
centered: true,
2025-06-30 20:38:23 -04:00
cc_stableTick: function (pixel) {
let pins = [
2024-07-21 00:56:17 -04:00
// Data inputs (D0-D3)
[-3, -2, true], // D0
[-1, -2, true], // D1
[1, -2, true], // D2
[3, -2, true], // D3
// Enable input (E)
[5, 0, true], // Enable (E)
// Enable mirror (E2)
[-5, 0, false],
// Outputs (Q0-Q3)
[-3, 2, false], // Q0
[-1, 2, false], // Q1
[1, 2, false], // Q2
[3, 2, false] // Q3
];
2025-06-30 20:38:23 -04:00
let elementData = elements[pixel.element];
2024-07-21 00:56:17 -04:00
initializeCircuit(pixel, pins, 9, 3, elementData.centered);
// Read inputs
2025-06-30 20:38:23 -04:00
let D = [
2024-07-21 00:56:17 -04:00
checkPin(pixel, pins, 0),
checkPin(pixel, pins, 1),
checkPin(pixel, pins, 2),
checkPin(pixel, pins, 3)
];
2025-06-30 20:38:23 -04:00
let C = checkPin(pixel, pins, 4); // Control input
2024-07-21 00:56:17 -04:00
setPin(pixel, pins, 5, C);
// Previous state initialization
if (pixel._state === undefined) {
pixel._state = [false, false, false, false];
}
// Update latch state based on control input
if (C) {
pixel._state = [D[0], D[1], D[2], D[3]]; // Update latch state with data inputs
} else {
pixel._state = [false, false, false, false];
}
// Output the latch state
setPin(pixel, pins, 6, pixel._state[0]); // Q0
setPin(pixel, pins, 7, pixel._state[1]); // Q1
setPin(pixel, pins, 8, pixel._state[2]); // Q2
setPin(pixel, pins, 9, pixel._state[3]); // Q3
}
};
elements.randomizer = {
color: "#FFCCFF",
2025-06-30 20:38:23 -04:00
cc_stableTick: function (pixel) {
for (let i = 0; i < adjacentCoords.length; i++) {
let coord = adjacentCoords[i];
let x = pixel.x + coord[0];
let y = pixel.y + coord[1];
2024-07-21 00:56:17 -04:00
if (!isEmpty(x, y, true)) {
2025-06-30 20:38:23 -04:00
if (pixelMap[x][y].element === "logic_wire") {
if (Math.random() < 0.5) {
2024-07-21 00:56:17 -04:00
pixelMap[x][y].lstate = 2
pixelMap[x][y].color = pixelColorPick(pixelMap[x][y], "#ffe49c")
} else {
pixelMap[x][y].lstate = -2
pixelMap[x][y].color = pixelColorPick(pixelMap[x][y], "#3d4d2c")
}
}
}
}
},
}
elements.four_bit_randomizer_circuit = {
2025-06-30 20:38:23 -04:00
cc_stableTick: function (pixel) {
let pins = [
2024-07-21 00:56:17 -04:00
// Clock input
[0, -2, true], // Clock
// Outputs (Q0-Q3)
[-3, 2, false], // Q0
[-1, 2, false], // Q1
[1, 2, false], // Q2
[3, 2, false] // Q3
];
initializeCircuit(pixel, pins, 9, 3);
// Read clock input
2025-06-30 20:38:23 -04:00
let clock = checkPin(pixel, pins, 0);
2024-07-21 00:56:17 -04:00
// Initialize the state if not already done
if (pixel._state === undefined) {
pixel._state = [false, false, false, false];
pixel.prevClock = false;
}
// Detect the positive edge on the clock pin
if (clock && !pixel.prevClock) {
// Generate a new 4-bit random number
2025-06-30 20:38:23 -04:00
let randomValue = Math.floor(Math.random() * 16);
2024-07-21 00:56:17 -04:00
// Update the state with the new random value
pixel._state = [
(randomValue & 1) !== 0,
(randomValue & 2) !== 0,
(randomValue & 4) !== 0,
(randomValue & 8) !== 0
];
}
// Output the current state
setPin(pixel, pins, 1, pixel._state[0]); // Q0
setPin(pixel, pins, 2, pixel._state[1]); // Q1
setPin(pixel, pins, 3, pixel._state[2]); // Q2
setPin(pixel, pins, 4, pixel._state[3]); // Q3
// Update previous state of clock input
pixel.prevClock = clock;
}
};
function general_encoder(inputBits) {
2025-06-30 20:38:23 -04:00
return function (pixel) {
let pins = [];
let outputBits = Math.ceil(Math.log2(inputBits));
let circuitWidth = (inputBits * 2) + 1;
let circuitHeight = (outputBits * 2) + 1;
2024-07-21 00:56:17 -04:00
// Define input pins
2025-06-30 20:38:23 -04:00
for (let i = 0; i < inputBits; i++) {
2024-08-10 23:13:02 -04:00
pins.push([Math.floor(circuitWidth / 2) - 1 - (2 * i), outputBits + 1, true]);
2024-07-21 00:56:17 -04:00
}
// Define output pins
2025-06-30 20:38:23 -04:00
for (let i = 0; i < outputBits; i++) {
2024-08-10 23:13:02 -04:00
pins.push([Math.floor(circuitWidth / 2) + 1, outputBits - 1 - (2 * i), false]); // Right outputs
2024-07-21 00:56:17 -04:00
}
2024-08-10 23:13:02 -04:00
// Mirrored outputs
2025-06-30 20:38:23 -04:00
for (let i = 0; i < outputBits; i++) {
2024-08-10 23:13:02 -04:00
pins.push([-Math.floor(circuitWidth / 2) - 1, outputBits - 1 - (2 * i), false]); // Left outputs
2024-07-21 00:56:17 -04:00
}
initializeCircuit(pixel, pins, circuitWidth, circuitHeight);
// Determine which input is active (priority encoder)
2025-06-30 20:38:23 -04:00
let activeInput = -1;
for (let i = inputBits - 1; i >= 0; i--) {
2024-07-21 00:56:17 -04:00
if (checkPin(pixel, pins, i)) {
activeInput = i;
break;
}
}
// Set output values based on active input
2025-06-30 20:38:23 -04:00
for (let i = 0; i < outputBits; i++) {
let outputValue = activeInput >= 0 ? ((activeInput >> i) & 1) : false;
2024-07-21 00:56:17 -04:00
setPin(pixel, pins, inputBits + i, outputValue); // Right outputs
setPin(pixel, pins, inputBits + outputBits + i, outputValue); // Left outputs
}
};
}
// Define a 2-to-1 encoder using the general_encoder function
elements.two_to_one_encoder_circuit = {
cc_stableTick: general_encoder(2)
2024-07-21 00:56:17 -04:00
};
// Define a 4-to-2 encoder using the general_encoder function
elements.four_to_two_encoder_circuit = {
cc_stableTick: general_encoder(4)
2024-07-21 00:56:17 -04:00
};
// Define an 8-to-3 encoder using the general_encoder function
elements.eight_to_three_encoder_circuit = {
cc_stableTick: general_encoder(8)
2024-07-21 00:56:17 -04:00
};
// Define a 16-to-4 encoder using the general_encoder function
elements.sixteen_to_four_encoder_circuit = {
cc_stableTick: general_encoder(16)
2024-07-21 00:56:17 -04:00
};
function general_demultiplexer(selectorBits) {
2025-06-30 20:38:23 -04:00
return function (pixel) {
let pins = [];
let outputCount = Math.pow(2, selectorBits);
let circuitWidth = 3;
let circuitHeight = (outputCount * 2) + 1;
2024-07-21 00:56:17 -04:00
// Define the input pin
pins.push([0, Math.floor(circuitHeight / 2) + 1, true]);
// Define selector pins
2025-06-30 20:38:23 -04:00
for (let i = 0; i < selectorBits; i++) {
2024-07-21 00:56:17 -04:00
pins.push([-2, (Math.floor(circuitHeight / 2) - 1) - (2 * i), true]);
}
// Define output pins
2025-06-30 20:38:23 -04:00
for (let i = 0; i < outputCount; i++) {
2024-08-10 23:13:02 -04:00
pins.push([Math.floor(circuitWidth / 2) + 1, Math.floor(circuitHeight / 2) - 1 - (2 * i), false]);
2024-07-21 00:56:17 -04:00
}
initializeCircuit(pixel, pins, circuitWidth, circuitHeight);
// Read input and selector values
2025-06-30 20:38:23 -04:00
let input = checkPin(pixel, pins, 0);
let selector = 0;
for (let i = 0; i < selectorBits; i++) {
2024-07-21 00:56:17 -04:00
if (checkPin(pixel, pins, 1 + i)) {
selector += Math.pow(2, i);
}
}
// Set output values based on selector
2025-06-30 20:38:23 -04:00
for (let i = 0; i < outputCount; i++) {
2024-07-21 00:56:17 -04:00
setPin(pixel, pins, 1 + selectorBits + i, i === selector ? input : false);
}
};
}
// Define a 1-to-2 demultiplexer using the general_demultiplexer function
elements.one_to_two_demultiplexer_circuit = {
cc_stableTick: general_demultiplexer(1)
2024-07-21 00:56:17 -04:00
};
// Define a 1-to-4 demultiplexer using the general_demultiplexer function
elements.one_to_four_demultiplexer_circuit = {
cc_stableTick: general_demultiplexer(2)
2024-07-21 00:56:17 -04:00
};
// Define a 1-to-8 demultiplexer using the general_demultiplexer function
elements.one_to_eight_demultiplexer_circuit = {
cc_stableTick: general_demultiplexer(3)
2024-07-21 00:56:17 -04:00
};
// Define a 1-to-16 demultiplexer using the general_demultiplexer function
elements.one_to_sixteen_demultiplexer_circuit = {
cc_stableTick: general_demultiplexer(4)
2024-07-21 00:56:17 -04:00
};
function general_decoder(inputBits) {
2025-06-30 20:38:23 -04:00
return function (pixel) {
let pins = [];
let outputCount = Math.pow(2, inputBits);
let circuitWidth = (inputBits * 2) + 1;
let circuitHeight = (outputCount * 2) + 1;
2024-07-21 00:56:17 -04:00
// Define input pins
2025-06-30 20:38:23 -04:00
for (let i = 0; i < inputBits; i++) {
2024-08-10 23:13:02 -04:00
pins.push([Math.floor(circuitWidth / 2) - 1 - (2 * i), outputCount + 1, true]);
2024-07-21 00:56:17 -04:00
}
// Define output pins
2025-06-30 20:38:23 -04:00
for (let i = 0; i < outputCount; i++) {
2024-08-10 23:13:02 -04:00
pins.push([Math.floor(circuitWidth / 2) + 1, outputCount - 1 - (2 * i), false]); // Right outputs
2024-07-21 00:56:17 -04:00
}
2025-06-30 20:38:23 -04:00
for (let i = 0; i < outputCount; i++) {
2024-08-10 23:13:02 -04:00
pins.push([-Math.floor(circuitWidth / 2) - 1, outputCount - 1 - (2 * i), false]); // Left outputs
2024-07-21 00:56:17 -04:00
}
initializeCircuit(pixel, pins, circuitWidth, circuitHeight);
// Read input values
2025-06-30 20:38:23 -04:00
let input = 0;
for (let i = 0; i < inputBits; i++) {
2024-07-21 00:56:17 -04:00
if (checkPin(pixel, pins, i)) {
input += Math.pow(2, i);
}
}
// Set output values
2025-06-30 20:38:23 -04:00
for (let i = 0; i < outputCount; i++) {
let outputValue = (i === input);
2024-07-21 00:56:17 -04:00
setPin(pixel, pins, inputBits + i, outputValue); // Right outputs
setPin(pixel, pins, inputBits + outputCount + i, outputValue); // Left outputs
}
};
}
elements.one_to_two_decoder_circuit = {
cc_stableTick: general_decoder(1)
2024-07-21 00:56:17 -04:00
};
elements.two_to_four_decoder_circuit = {
cc_stableTick: general_decoder(2)
2024-07-21 00:56:17 -04:00
};
elements.three_to_eight_decoder_circuit = {
cc_stableTick: general_decoder(3)
2024-07-21 00:56:17 -04:00
};
elements.four_to_sixteen_decoder_circuit = {
cc_stableTick: general_decoder(4)
2024-07-21 00:56:17 -04:00
};
function general_multiplexer(inputLines) {
2025-06-30 20:38:23 -04:00
return function (pixel) {
let pins = [];
let selectorBits = Math.ceil(Math.log2(inputLines));
let circuitWidth = (selectorBits * 2) + 1;
let circuitHeight = (inputLines * 2) + 1;
2024-07-21 00:56:17 -04:00
// Define selector pins
2025-06-30 20:38:23 -04:00
for (let i = 0; i < selectorBits; i++) {
2024-08-10 23:13:02 -04:00
pins.push([Math.floor(circuitWidth / 2) - 1 - (2 * i), inputLines + 1, true]);
2024-07-21 00:56:17 -04:00
}
// Define input data pins
2025-06-30 20:38:23 -04:00
for (let i = 0; i < inputLines; i++) {
2024-08-10 23:13:02 -04:00
pins.push([-Math.floor(circuitWidth / 2) - 1, inputLines - 1 - (2 * i), true]);
2024-07-21 00:56:17 -04:00
}
// Define output pin
pins.push([Math.floor(circuitWidth / 2) + 1, 0, false]);
initializeCircuit(pixel, pins, circuitWidth, circuitHeight);
// Read selector input
2025-06-30 20:38:23 -04:00
let selector = 0;
for (let i = 0; i < selectorBits; i++) {
2024-07-21 00:56:17 -04:00
if (checkPin(pixel, pins, i)) {
selector += Math.pow(2, i);
}
}
setPin(pixel, pins, selectorBits + inputLines, checkPin(pixel, pins, selector + selectorBits)); // Output pin
};
}
// Define a 2-input multiplexer using the general_multiplexer function
elements.two_to_one_multiplexer_circuit = {
cc_stableTick: general_multiplexer(2)
2024-07-21 00:56:17 -04:00
};
// Define a 4-input multiplexer using the general_multiplexer function
elements.four_to_one_multiplexer_circuit = {
cc_stableTick: general_multiplexer(4)
2024-07-21 00:56:17 -04:00
};
// Define an 8-input multiplexer using the general_multiplexer function
elements.eight_to_one_multiplexer_circuit = {
cc_stableTick: general_multiplexer(8)
2024-07-21 00:56:17 -04:00
};
// Define an 8-input multiplexer using the general_multiplexer function
elements.sixteen_to_one_multiplexer_circuit = {
cc_stableTick: general_multiplexer(16)
2024-07-21 00:56:17 -04:00
};
elements.four_bit_PISO_shift_register_circuit = {
2025-06-30 20:38:23 -04:00
cc_stableTick: function (pixel) {
let pins = [
2024-07-21 00:56:17 -04:00
// Data inputs (D0-D3)
[3, -3, true], // D3
2024-08-10 23:13:02 -04:00
[1, -3, true], // D2
[-1, -3, true], // D1
[-3, -3, true], // D0
2024-07-21 00:56:17 -04:00
// Control input (Load/Shift Enable)
[-5, -1, true], // Load/Shift Enable
// Clock input
[-5, 1, true], // Clock
// Serial output
[5, -1, false], // Serial Out (Q)
[5, 1, false] // Transmission Flag
];
initializeCircuit(pixel, pins, 9, 5);
// Read data inputs
2025-06-30 20:38:23 -04:00
let D = [
2024-07-21 00:56:17 -04:00
checkPin(pixel, pins, 0),
checkPin(pixel, pins, 1),
checkPin(pixel, pins, 2),
checkPin(pixel, pins, 3)
];
// Read control and clock inputs
2025-06-30 20:38:23 -04:00
let loadShiftEnable = checkPin(pixel, pins, 4);
let clock = checkPin(pixel, pins, 5);
2024-07-21 00:56:17 -04:00
// Initialize the state if not already done
if (pixel._state === undefined) {
pixel._state = [false, false, false, false];
pixel.bitIndex = 0;
pixel.prevLoadShiftEnable = false;
pixel.prevClock = false;
}
// Detect the positive edge on the control pin
if (loadShiftEnable && !pixel.prevLoadShiftEnable) {
// Load the data into the register on the first positive edge
pixel._state = [D[0], D[1], D[2], D[3]];
pixel.bitIndex = 0;
}
// Detect the positive edge on the clock pin
if (clock && !pixel.prevClock) {
if (pixel.bitIndex < 4) {
// Shift the register and output the next bit
2025-06-30 20:38:23 -04:00
let serialOut = pixel._state[0];
for (let i = 0; i < 3; i++) {
2024-07-21 00:56:17 -04:00
pixel._state[i] = pixel._state[i + 1];
}
pixel._state[3] = false; // Clear the last bit after shifting
// Output the serial value
setPin(pixel, pins, 6, serialOut);
// Update bit index
pixel.bitIndex++;
}
}
// Set the transmission flag
2025-06-30 20:38:23 -04:00
let transmitting = pixel.bitIndex < 4 && loadShiftEnable;
2024-07-21 00:56:17 -04:00
setPin(pixel, pins, 7, transmitting);
// Update previous state of control and clock inputs
pixel.prevLoadShiftEnable = loadShiftEnable;
pixel.prevClock = clock;
}
};
elements.four_bit_SIPO_shift_register_circuit = {
2025-06-30 20:38:23 -04:00
cc_stableTick: function (pixel) {
let pins = [
2024-07-21 00:56:17 -04:00
// Serial input (Data In)
[-2, -3, true], // Data In
// Clock input
[-2, -1, true], // Clock
// Parallel outputs (Q0-Q3)
2024-08-10 23:13:02 -04:00
[2, 3, false], // Q3
2024-07-21 00:56:17 -04:00
[2, 1, false], // Q2
2024-08-10 23:13:02 -04:00
[2, -1, false], // Q1
[2, -3, false] // Q0
2024-07-21 00:56:17 -04:00
];
initializeCircuit(pixel, pins, 3, 9);
// Read serial and clock input
2025-06-30 20:38:23 -04:00
let serialIn = checkPin(pixel, pins, 0);
let clock = checkPin(pixel, pins, 1);
2024-07-21 00:56:17 -04:00
// Initialize the state if not already done
if (pixel._state === undefined) {
pixel._state = [false, false, false, false];
pixel.prevClock = false;
}
// Detect the positive edge on the clock pin
if (clock && !pixel.prevClock) {
pixel._state = [serialIn, pixel._state[0], pixel._state[1], pixel._state[2]];
}
// Output the parallel values
2025-06-30 20:38:23 -04:00
for (let i = 0; i < 4; i++) {
2024-07-21 00:56:17 -04:00
setPin(pixel, pins, 2 + i, pixel._state[i]);
}
// Update previous state of control and clock inputs
pixel.prevClock = clock;
}
};
elements.four_bit_program_counter_circuit = {
2025-06-30 20:38:23 -04:00
cc_stableTick: function (pixel) {
let pins = [
// Data inputs (D0-D3)
[-3, -3, true], // D0
[-1, -3, true], // D1
[1, -3, true], // D2
[3, -3, true], // D3
// Control inputs (Increment, Write Enable)
[5, -1, true], // Increment
[5, 1, true], // Write Enable
// Outputs (Q0-Q3)
[-3, 3, false], // Q0
[-1, 3, false], // Q1
[1, 3, false], // Q2
[3, 3, false], // Q3
];
initializeCircuit(pixel, pins, 9, 5);
// Read data inputs
2025-06-30 20:38:23 -04:00
let D = [
checkPin(pixel, pins, 0),
checkPin(pixel, pins, 1),
checkPin(pixel, pins, 2),
checkPin(pixel, pins, 3)
];
// Read control inputs
2025-06-30 20:38:23 -04:00
let Increment = checkPin(pixel, pins, 4);
let WriteEnable = checkPin(pixel, pins, 5);
// Initialize the state if not already done
if (pixel._state === undefined) {
pixel._state = [false, false, false, false];
pixel.prevIncrement = false; // Previous state of Increment pin
}
// Convert the state to a 4-bit binary number
2025-06-30 20:38:23 -04:00
let stateValue = binaryArrayToNumber(pixel._state);
// Detect the positive edge on the Increment pin
if (Increment && !pixel.prevIncrement) {
stateValue = (stateValue + 1) % 16; // Ensure the value wraps around at 4 bits
}
// Update the register state if WriteEnable is active
if (WriteEnable) {
stateValue = binaryArrayToNumber(D); // Load data inputs into state
}
// Update the state
pixel._state = [
(stateValue & 8) !== 0,
(stateValue & 4) !== 0,
(stateValue & 2) !== 0,
(stateValue & 1) !== 0
];
// Output the register state
setPin(pixel, pins, 6, pixel._state[0]); // Q0
setPin(pixel, pins, 7, pixel._state[1]); // Q1
setPin(pixel, pins, 8, pixel._state[2]); // Q2
setPin(pixel, pins, 9, pixel._state[3]); // Q3
// Update previous state of Increment pin
pixel.prevIncrement = Increment;
}
2024-07-21 00:56:17 -04:00
};
elements.four_bit_register_circuit = {
2025-06-30 20:38:23 -04:00
cc_stableTick: function (pixel) {
let pins = [
2024-07-21 00:56:17 -04:00
// Data inputs (D0-D3)
[-3, -3, true], // D0
[-1, -3, true], // D1
[1, -3, true], // D2
[3, -3, true], // D3
// Control inputs (Enable, Write Enable)
[5, -1, true], // Enable
[5, 1, true], // Write Enable
// Outputs (Q0-Q3)
[-3, 3, false], // Q0
[-1, 3, false], // Q1
[1, 3, false], // Q2
[3, 3, false], // Q3
];
initializeCircuit(pixel, pins, 9, 5);
// Read data inputs
2025-06-30 20:38:23 -04:00
let D = [
2024-07-21 00:56:17 -04:00
checkPin(pixel, pins, 0),
checkPin(pixel, pins, 1),
checkPin(pixel, pins, 2),
checkPin(pixel, pins, 3)
];
// Read control inputs
2025-06-30 20:38:23 -04:00
let Enable = checkPin(pixel, pins, 4);
let WriteEnable = checkPin(pixel, pins, 5);
2024-07-21 00:56:17 -04:00
// Initialize the state if not already done
if (pixel._state === undefined) {
pixel._state = [false, false, false, false];
}
// Update the register state if WriteEnable is active
if (WriteEnable && Enable) {
pixel._state = [D[0], D[1], D[2], D[3]];
}
// Output the register state if Enable is active
if (Enable) {
setPin(pixel, pins, 6, pixel._state[0]); // Q0
setPin(pixel, pins, 7, pixel._state[1]); // Q1
setPin(pixel, pins, 8, pixel._state[2]); // Q2
setPin(pixel, pins, 9, pixel._state[3]); // Q3
} else {
// Disable outputs if Enable is not active
setPin(pixel, pins, 6, false); // Q0
setPin(pixel, pins, 7, false); // Q1
setPin(pixel, pins, 8, false); // Q2
setPin(pixel, pins, 9, false); // Q3
}
}
};
elements.SR_latch_circuit = {
2025-06-30 20:38:23 -04:00
cc_stableTick: function (pixel) {
let pins = [
2024-07-21 00:56:17 -04:00
[0, -2, true], // Input: Set
[0, 2, true], // Input: Reset
[2, 0, false], // Output
[-2, 0, false] // Output
];
initializeCircuit(pixel, pins, 3, 3);
2025-06-30 20:38:23 -04:00
if (checkPin(pixel, pins, 0)) {
pixel._state = true;
} // Set
if (checkPin(pixel, pins, 1)) {
pixel._state = false;
} // Reset
2024-07-21 00:56:17 -04:00
setPin(pixel, pins, 2, pixel._state);
setPin(pixel, pins, 3, pixel._state);
}
};
elements.T_flip_flop_circuit = {
2025-06-30 20:38:23 -04:00
cc_stableTick: function (pixel) {
let pins = [
2024-07-21 00:56:17 -04:00
[0, -2, true], // Input: Toggle (T)
[2, 0, false], // Output (Q)
[-2, 0, false] // Output (not Q) - Optional
];
initializeCircuit(pixel, pins, 3, 3);
// Check the current state of the Toggle (T) input
2025-06-30 20:38:23 -04:00
let T = checkPin(pixel, pins, 0);
2024-07-21 00:56:17 -04:00
// Initialize the previous state of T if not already done
if (pixel.prevT === undefined) {
pixel.prevT = false;
}
// Detect the positive edge
if (T && !pixel.prevT) {
pixel._state = !pixel._state; // Toggle state on positive edge
}
// Update the previous state of T
pixel.prevT = T;
setPin(pixel, pins, 1, pixel._state);
setPin(pixel, pins, 2, pixel._state);
}
};
elements.D_latch_circuit = {
2025-06-30 20:38:23 -04:00
cc_stableTick: function (pixel) {
let pins = [
2024-07-21 00:56:17 -04:00
[0, -2, true], // Input: Data
[2, 0, true], // Input: Enable
[0, 2, false], // Output
[-2, 0, false] // Output
];
initializeCircuit(pixel, pins, 3, 3);
2025-06-30 20:38:23 -04:00
let D = checkPin(pixel, pins, 0); // Data input
let E = checkPin(pixel, pins, 1); // Enable input
2024-07-21 00:56:17 -04:00
if (E) {
pixel._state = D; // Q follows D when E is active
}
setPin(pixel, pins, 2, pixel._state);
setPin(pixel, pins, 3, pixel._state);
}
};
elements.D_flip_flop_circuit = {
2025-06-30 20:38:23 -04:00
cc_stableTick: function (pixel) {
let pins = [
2024-07-21 00:56:17 -04:00
[0, -2, true], // Input: Data
[2, 0, true], // Input: Enable
[0, 2, false], // Output
[-2, 0, false] // Output
];
initializeCircuit(pixel, pins, 3, 3);
// Read inputs
2025-06-30 20:38:23 -04:00
let D = checkPin(pixel, pins, 0); // Data input
let C = checkPin(pixel, pins, 1); // Control input
2024-07-21 00:56:17 -04:00
// Initialize previous state of control input if not already done
if (pixel.prevC === undefined) {
pixel.prevC = false;
}
// Previous state initialization
if (pixel._state === undefined) {
pixel._state = false;
}
// Update flip-flop state on positive edge of control input
if (C && !pixel.prevC) {
pixel._state = D; // Q follows D on positive edge of C
}
// Update the previous state of control input
pixel.prevC = C;
// Output the flip-flop state
setPin(pixel, pins, 2, pixel._state);
setPin(pixel, pins, 3, pixel._state);
}
};
elements.four_bit_D_latch_circuit = {
2025-06-30 20:38:23 -04:00
cc_stableTick: function (pixel) {
let pins = [
2024-07-21 00:56:17 -04:00
// Data inputs (D0-D3)
[-3, -2, true], // D0
[-1, -2, true], // D1
[1, -2, true], // D2
[3, -2, true], // D3
// Control input (C)
[5, 0, true], // Control (C)
// Outputs (Q0-Q3)
[-3, 2, false], // Q0
[-1, 2, false], // Q1
[1, 2, false], // Q2
[3, 2, false] // Q3
];
initializeCircuit(pixel, pins, 9, 3);
// Read inputs
2025-06-30 20:38:23 -04:00
let D = [
2024-07-21 00:56:17 -04:00
checkPin(pixel, pins, 0),
checkPin(pixel, pins, 1),
checkPin(pixel, pins, 2),
checkPin(pixel, pins, 3)
];
2025-06-30 20:38:23 -04:00
let C = checkPin(pixel, pins, 4); // Control input
2024-07-21 00:56:17 -04:00
// Previous state initialization
if (pixel._state === undefined) {
pixel._state = [false, false, false, false];
}
// Update latch state based on control input
if (C) {
pixel._state = [D[0], D[1], D[2], D[3]]; // Update latch state with data inputs
}
// Output the latch state
setPin(pixel, pins, 5, pixel._state[0]); // Q0
setPin(pixel, pins, 6, pixel._state[1]); // Q1
setPin(pixel, pins, 7, pixel._state[2]); // Q2
setPin(pixel, pins, 8, pixel._state[3]); // Q3
}
};
elements.four_bit_D_flip_flop_circuit = {
2025-06-30 20:38:23 -04:00
cc_stableTick: function (pixel) {
let pins = [
2024-07-21 00:56:17 -04:00
// Data inputs (D0-D3)
[-3, -2, true], // D0
[-1, -2, true], // D1
[1, -2, true], // D2
[3, -2, true], // D3
// Control input (C)
[5, 0, true], // Control (C)
// Outputs (Q0-Q3)
[-3, 2, false], // Q0
[-1, 2, false], // Q1
[1, 2, false], // Q2
[3, 2, false] // Q3
];
initializeCircuit(pixel, pins, 9, 3);
// Read inputs
2025-06-30 20:38:23 -04:00
let D = [
2024-07-21 00:56:17 -04:00
checkPin(pixel, pins, 0),
checkPin(pixel, pins, 1),
checkPin(pixel, pins, 2),
checkPin(pixel, pins, 3)
];
2025-06-30 20:38:23 -04:00
let C = checkPin(pixel, pins, 4); // Control input
2024-07-21 00:56:17 -04:00
// Initialize previous state of control input if not already done
if (pixel.prevC === undefined) {
pixel.prevC = false;
}
// Previous state initialization
if (pixel._state === undefined) {
pixel._state = [false, false, false, false];
}
// Update flip-flop state on positive edge of control input
if (C && !pixel.prevC) {
pixel._state = [D[0], D[1], D[2], D[3]]; // Update flip-flop state with data inputs
}
// Update the previous state of control input
pixel.prevC = C;
// Output the flip-flop state
setPin(pixel, pins, 5, pixel._state[0]); // Q0
setPin(pixel, pins, 6, pixel._state[1]); // Q1
setPin(pixel, pins, 7, pixel._state[2]); // Q2
setPin(pixel, pins, 8, pixel._state[3]); // Q3
}
};
elements.four_bit_incrementer_circuit = {
2025-06-30 20:38:23 -04:00
cc_stableTick: function (pixel) {
let pins = [
2024-07-21 00:56:17 -04:00
// 4-bit number inputs (N0-N3)
[3, -2, true], // N3
2024-08-10 23:13:02 -04:00
[1, -2, true], // N2
[-1, -2, true], // N1
[-3, -2, true], // N0
2024-07-21 00:56:17 -04:00
// Increment control input (INC)
[-5, 0, true], // Increment (INC)
// Outputs (Q0-Q3)
[3, 2, false], // Q3
2024-08-10 23:13:02 -04:00
[1, 2, false], // Q2
[-1, 2, false], // Q1
[-3, 2, false], // Q0
2024-07-21 00:56:17 -04:00
// Carry out
[5, 0, false] // Carry out (COUT)
];
initializeCircuit(pixel, pins, 9, 3);
// Read inputs
2025-06-30 20:38:23 -04:00
let N = [
2024-07-21 00:56:17 -04:00
checkPin(pixel, pins, 0),
checkPin(pixel, pins, 1),
checkPin(pixel, pins, 2),
checkPin(pixel, pins, 3)
];
2025-06-30 20:38:23 -04:00
let INC = checkPin(pixel, pins, 4); // Increment control input
2024-07-21 00:56:17 -04:00
// Calculate the incremented value when control is active
2025-06-30 20:38:23 -04:00
let carry = 0;
let result = [];
2024-07-21 00:56:17 -04:00
if (INC) {
carry = 1; // Start with a carry of 1 to increment by 1
}
2025-06-30 20:38:23 -04:00
for (let i = 0; i < 4; i++) {
let sum = N[i] + carry;
2024-07-21 00:56:17 -04:00
result[i] = sum % 2; // Current bit sum
carry = Math.floor(sum / 2); // Carry for next bit
}
// Output the incremented value and carry out
setPin(pixel, pins, 5, result[0]); // Q0
setPin(pixel, pins, 6, result[1]); // Q1
setPin(pixel, pins, 7, result[2]); // Q2
setPin(pixel, pins, 8, result[3]); // Q3
setPin(pixel, pins, 9, carry); // Carry out (COUT)
}
};
elements.four_bit_adder_circuit = {
2025-06-30 20:38:23 -04:00
cc_stableTick: function (pixel) {
let pins = [
2024-07-21 00:56:17 -04:00
// First 4-bit number (A)
[-1, -2, true], // A3
2024-08-10 23:13:02 -04:00
[-3, -2, true], // A2
[-5, -2, true], // A1
[-7, -2, true], // A0
2024-07-21 00:56:17 -04:00
// Second 4-bit number (B)
[7, -2, true], // B3
2024-08-10 23:13:02 -04:00
[5, -2, true], // B2
[3, -2, true], // B1
[1, -2, true], // B0
2024-07-21 00:56:17 -04:00
// Carry-in (C_in)
[9, 0, true], // Carry-in (C_in)
// Output sum (S)
[-1, 2, false], // S3
2024-08-10 23:13:02 -04:00
[-3, 2, false], // S2
[-5, 2, false], // S1
[-7, 2, false], // S0
2024-07-21 00:56:17 -04:00
[1, 2, false], // Carry Out (C4)
];
initializeCircuit(pixel, pins, 17, 3);
// Read inputs
2025-06-30 20:38:23 -04:00
let A = [
2024-07-21 00:56:17 -04:00
checkPin(pixel, pins, 0),
checkPin(pixel, pins, 1),
checkPin(pixel, pins, 2),
checkPin(pixel, pins, 3)
];
2025-06-30 20:38:23 -04:00
let B = [
2024-07-21 00:56:17 -04:00
checkPin(pixel, pins, 4),
checkPin(pixel, pins, 5),
checkPin(pixel, pins, 6),
checkPin(pixel, pins, 7)
];
2025-06-30 20:38:23 -04:00
let C_in = checkPin(pixel, pins, 8); // Carry-in
2024-07-21 00:56:17 -04:00
// Calculate the sum and carry
2025-06-30 20:38:23 -04:00
let sum = [];
let carry = C_in;
2024-07-21 00:56:17 -04:00
2025-06-30 20:38:23 -04:00
for (let i = 0; i < 4; i++) {
let bitSum = A[i] + B[i] + carry;
2024-07-21 00:56:17 -04:00
sum[i] = bitSum % 2; // Current bit sum
carry = Math.floor(bitSum / 2); // Carry for next bit
}
// Output the sum
setPin(pixel, pins, 9, sum[0]); // S0
setPin(pixel, pins, 10, sum[1]); // S1
setPin(pixel, pins, 11, sum[2]); // S2
setPin(pixel, pins, 12, sum[3]); // S3
setPin(pixel, pins, 13, carry); // Carry Out (C4)
}
};
2024-08-23 05:15:43 -04:00
elements.four_bit_subtractor_circuit = {
2025-06-30 20:38:23 -04:00
cc_stableTick: function (pixel) {
let pins = [
2025-02-22 05:05:00 -05:00
// First 4-bit number (A)
[-1, -2, true], // A3
[-3, -2, true], // A2
[-5, -2, true], // A1
[-7, -2, true], // A0
// Second 4-bit number (B)
[7, -2, true], // B3
[5, -2, true], // B2
[3, -2, true], // B1
[1, -2, true], // B0
// Borrow-in (B_in)
[9, 0, true], // Borrow-in (B_in)
// Output difference (D)
[-1, 2, false], // D3
[-3, 2, false], // D2
[-5, 2, false], // D1
[-7, 2, false], // D0
[1, 2, false], // Borrow Out (B4)
];
initializeCircuit(pixel, pins, 17, 3);
// Read inputs
2025-06-30 20:38:23 -04:00
let A = [
2025-02-22 05:05:00 -05:00
checkPin(pixel, pins, 0),
checkPin(pixel, pins, 1),
checkPin(pixel, pins, 2),
checkPin(pixel, pins, 3)
];
2025-06-30 20:38:23 -04:00
let B = [
2025-02-22 05:05:00 -05:00
checkPin(pixel, pins, 4),
checkPin(pixel, pins, 5),
checkPin(pixel, pins, 6),
checkPin(pixel, pins, 7)
];
2025-06-30 20:38:23 -04:00
let B_in = checkPin(pixel, pins, 8); // Borrow-in
2025-02-22 05:05:00 -05:00
// Calculate the difference and borrow
2025-06-30 20:38:23 -04:00
let difference = [];
let borrow = B_in;
2025-02-22 05:05:00 -05:00
2025-06-30 20:38:23 -04:00
for (let i = 0; i < 4; i++) {
let bitDifference = A[i] - B[i] - borrow;
2025-02-22 05:05:00 -05:00
difference[i] = (bitDifference + 2) % 2; // Current bit difference
borrow = bitDifference < 0 ? 1 : 0; // Borrow for next bit
}
// Output the difference
setPin(pixel, pins, 9, difference[0]); // D0
setPin(pixel, pins, 10, difference[1]); // D1
setPin(pixel, pins, 11, difference[2]); // D2
setPin(pixel, pins, 12, difference[3]); // D3
setPin(pixel, pins, 13, borrow); // Borrow Out (B4)
}
2024-08-23 05:15:43 -04:00
};
2024-07-21 00:56:17 -04:00
function general_clock(speed, s2) {
2025-06-30 20:38:23 -04:00
return function (pixel) {
for (let i = 0; i < adjacentCoords.length; i++) {
let coord = adjacentCoords[i];
let x = pixel.x + coord[0];
let y = pixel.y + coord[1];
if (!isEmpty(x, y, true)) {
if (pixelMap[x][y].element === "logic_wire") {
if (pixelTicks % speed < s2) {
2024-07-21 00:56:17 -04:00
pixelMap[x][y].lstate = 2
pixelMap[x][y].color = pixelColorPick(pixelMap[x][y], "#ffe49c")
} else {
pixelMap[x][y].lstate = -2
pixelMap[x][y].color = pixelColorPick(pixelMap[x][y], "#3d4d2c")
}
}
}
}
};
}
elements.slow_clock = {
color: "#BB66BB",
cc_stableTick: general_clock(64, 32),
2024-07-21 00:56:17 -04:00
}
elements.medium_clock = {
color: "#DD88DD",
cc_stableTick: general_clock(32, 16),
2024-07-21 00:56:17 -04:00
}
elements.fast_clock = {
color: "#FFAAFF",
cc_stableTick: general_clock(16, 8),
2024-07-21 00:56:17 -04:00
}
elements.very_fast_clock = {
color: "#FFCCFF",
cc_stableTick: general_clock(8, 4),
2024-07-21 00:56:17 -04:00
}
2024-08-10 23:13:02 -04:00
elements.custom_RGB_led = {
2025-06-30 20:38:23 -04:00
cc_stableTick: function (pixel) {
let pins = [
2024-08-10 23:13:02 -04:00
// RGB values
2025-06-30 20:38:23 -04:00
[-2, -1, true], // R0
2024-08-10 23:13:02 -04:00
[-2, 1, true], // R1
[1, -2, true], // G0
2025-06-30 20:38:23 -04:00
[-1, -2, true], // G1
[2, -1, true], // B0
2024-08-10 23:13:02 -04:00
[2, 1, true], // B1
];
initializeCircuit(pixel, pins, 3, 3);
// Read inputs
2025-06-30 20:38:23 -04:00
let l = [
2024-08-10 23:13:02 -04:00
checkPin(pixel, pins, 0),
checkPin(pixel, pins, 1),
checkPin(pixel, pins, 2),
checkPin(pixel, pins, 3),
checkPin(pixel, pins, 4),
checkPin(pixel, pins, 5)
];
2025-06-30 20:38:23 -04:00
let color = {color: cc_scaleList([(l[0] * 2) + l[1], (l[2] * 2) + l[3], (l[4] * 2) + l[5]], 255 / 3 * 10)};
if (color.color.some(value => isNaN(value)))
return;
2024-08-10 23:13:02 -04:00
2025-06-30 20:38:23 -04:00
if (isLightmapEnabled) {
2024-08-10 23:13:02 -04:00
lightmap[Math.floor(pixel.y / lightmapScale)][Math.floor(pixel.x / lightmapScale)] = color;
}
2025-06-30 20:38:23 -04:00
let scaledColor = cc_scaleList(color.color, 0.1);
2024-08-10 23:13:02 -04:00
// pixelMap[pixel.x][pixel.y].color = scaledColor;
for (let dx = -1; dx <= 1; dx++) {
for (let dy = -1; dy <= 1; dy++) {
2025-06-30 20:38:23 -04:00
let nx = pixel.x + dx;
let ny = pixel.y + dy;
2024-08-10 23:13:02 -04:00
if (pixelMap[nx] && pixelMap[nx][ny]) {
2025-06-30 20:38:23 -04:00
let n = ((2 - (Math.abs(dx) + Math.abs(dy))) + 4) / 6;
2024-08-10 23:13:02 -04:00
pixelMap[nx][ny].color = cc_arrayToRgbString(cc_scaleList(scaledColor, n));
}
}
}
}
}
2025-06-30 20:38:23 -04:00
let addDisplayCallback = function (pixel, pins, w, h) {
for (let y = 1; y < h - 1; y++) {
for (let x = 1; x < w - 1; x++) {
let px = pixel.x + x;
let py = pixel.y + y;
if (!(0 <= px && px < width && 0 <= py && py < height)) {
continue;
}
2024-07-21 00:56:17 -04:00
deletePixel(px, py);
2024-08-23 05:15:43 -04:00
createPixel("displayPixel", px, py);
2024-07-21 00:56:17 -04:00
pixelMap[px][py].color = "rgb(16, 24, 32)";
}
}
}
elements.simple_seven_segment_display = {
2025-06-30 20:38:23 -04:00
cc_stableTick: function (pixel) {
let pins = [
2024-07-21 00:56:17 -04:00
// Data inputs (D0-D3)
2024-08-10 23:13:02 -04:00
[-1, 7, true],
[-1, 5, true],
[-1, 3, true],
[-1, 1, true],
2024-07-21 00:56:17 -04:00
];
initializeCircuit(pixel, pins, 5, 9, false, pixel.circuitRotation, addDisplayCallback);
// Read inputs
2025-06-30 20:38:23 -04:00
let D = [
2024-07-21 00:56:17 -04:00
checkPin(pixel, pins, 0),
checkPin(pixel, pins, 1),
checkPin(pixel, pins, 2),
checkPin(pixel, pins, 3)
];
2025-06-30 20:38:23 -04:00
let hexNumber = (D[3] * 8) + (D[2] * 4) + (D[1] * 2) + (D[0] * 1);
if (isNaN(hexNumber)) {
return;
}
2024-07-21 00:56:17 -04:00
// Draw the number
2025-06-30 20:38:23 -04:00
let hexGrid = createSevenSegmentGrid(hexNumber);
for (let y = 2; y <= 6; y++) {
for (let x = 1; x <= 3; x++) {
let px = pixel.x + x;
let py = pixel.y + y;
2024-07-21 00:56:17 -04:00
2025-06-30 20:38:23 -04:00
if (pixelMap[px][py] && pixelMap[px][py].element === "displayPixel") {
2024-07-21 00:56:17 -04:00
if (hexGrid[y - 2][x - 1]) {
pixelMap[px][py].color = "rgb(16, 230, 120)";
} else {
pixelMap[px][py].color = "rgb(16, 24, 32)";
}
}
}
}
}
};
elements.simple_double_seven_segment_display = {
2025-06-30 20:38:23 -04:00
cc_stableTick: function (pixel) {
let pins = [
2024-07-21 00:56:17 -04:00
// Data inputs (D0-D3)
2024-08-10 23:13:02 -04:00
[-1, 7, true],
[-1, 5, true],
[-1, 3, true],
[-1, 1, true],
[7, -1, true],
[5, -1, true],
[3, -1, true],
[1, -1, true],
2024-07-21 00:56:17 -04:00
];
initializeCircuit(pixel, pins, 9, 9, false, pixel.circuitRotation, addDisplayCallback);
// Read inputs
2025-06-30 20:38:23 -04:00
let D = [
2024-07-21 00:56:17 -04:00
checkPin(pixel, pins, 0),
checkPin(pixel, pins, 1),
checkPin(pixel, pins, 2),
checkPin(pixel, pins, 3),
checkPin(pixel, pins, 4),
checkPin(pixel, pins, 5),
checkPin(pixel, pins, 6),
checkPin(pixel, pins, 7)
];
2025-06-30 20:38:23 -04:00
let hexNumber = (D[3] * 8) + (D[2] * 4) + (D[1] * 2) + (D[0] * 1);
let hexNumber2 = (D[7] * 8) + (D[6] * 4) + (D[5] * 2) + (D[4] * 1);
if (isNaN(hexNumber) || isNaN(hexNumber2)) {
return;
}
2024-07-21 00:56:17 -04:00
// Draw the number
2025-06-30 20:38:23 -04:00
let hexGrid = createSevenSegmentGrid(hexNumber);
for (let y = 2; y <= 6; y++) {
for (let x = 1; x <= 3; x++) {
let px = pixel.x + x;
let py = pixel.y + y;
2024-07-21 00:56:17 -04:00
2025-06-30 20:38:23 -04:00
if (pixelMap[px][py] && pixelMap[px][py].element === "displayPixel") {
2024-07-21 00:56:17 -04:00
if (hexGrid[y - 2][x - 1]) {
pixelMap[px][py].color = "rgb(16, 230, 120)";
} else {
pixelMap[px][py].color = "rgb(16, 24, 32)";
}
}
}
}
2025-06-30 20:38:23 -04:00
let hexGrid2 = createSevenSegmentGrid(hexNumber2);
for (let y = 2; y <= 6; y++) {
for (let x = 5; x <= 7; x++) {
let px = pixel.x + x;
let py = pixel.y + y;
2024-07-21 00:56:17 -04:00
2025-06-30 20:38:23 -04:00
if (pixelMap[px][py] && pixelMap[px][py].element === "displayPixel") {
2024-07-21 00:56:17 -04:00
if (hexGrid2[y - 2][x - 5]) {
pixelMap[px][py].color = "rgb(16, 230, 120)";
} else {
pixelMap[px][py].color = "rgb(16, 24, 32)";
}
}
}
}
}
};
function malfunction_chip(pixel) {
2025-06-30 20:38:23 -04:00
let emptySpaces = [];
// Search in a 5x5 neighborhood for empty spaces
2025-06-30 20:38:23 -04:00
for (let dy = -2; dy <= 2; dy++) {
for (let dx = -2; dx <= 2; dx++) {
let neighborX = pixel.x + dx;
let neighborY = pixel.y + dy;
if (pixelMap[neighborX] && pixelMap[neighborX][neighborY] === undefined) {
2025-06-30 20:38:23 -04:00
emptySpaces.push({x: neighborX, y: neighborY});
}
}
}
if (emptySpaces.length > 0) {
// Randomly select one of the empty spaces
2025-06-30 20:38:23 -04:00
let randomSpace = emptySpaces[Math.floor(Math.random() * emptySpaces.length)];
// Determine what to spawn based on probability
2025-06-30 20:38:23 -04:00
let rand = Math.random();
if (rand < 0.7) {
createPixel("electric", randomSpace.x, randomSpace.y);
} else if (rand < 0.99) {
createPixel("fire", randomSpace.x, randomSpace.y);
} else {
createPixel("explosion", randomSpace.x, randomSpace.y);
}
}
}
2024-08-23 05:15:43 -04:00
elements.displayPixel = {
color: "#000000",
category: "logic",
state: "solid",
behavior: behaviors.WALL,
2025-06-30 20:38:23 -04:00
tick: function (pixel) {
if (pixel.start === pixelTicks) {
2024-08-23 05:15:43 -04:00
pixel.color = "rgb(16, 24, 32)";
}
2025-06-30 20:38:23 -04:00
if (isLightmapEnabled && pixel.color) {
let x = Math.floor(pixel.x / lightmapScale);
let y = Math.floor(pixel.y / lightmapScale);
lightmap[y][x] = {color: scaleList(rgbToArray(pixel.color), 0.2)};
2024-08-23 05:15:43 -04:00
}
}
};
2024-07-21 00:56:17 -04:00
elements.circuit_material = {
color: "#444444",
category: "logic",
state: "solid",
behavior: behaviors.WALL,
2025-06-30 20:38:23 -04:00
hoverStat: function (pixel) {
return `Circuit: ${pixel.corePosition}`;
},
2025-06-30 20:38:23 -04:00
cc_stableTick: function (pixel) {
// Make it that extreme temperatures can stop the chip from working (for realism)
2025-06-30 20:38:23 -04:00
if (Math.random() < 0.003 && cc_heat_emit_setting.value) { // Chance to check for temperature or nearby particles
// Check temperature
if (pixel.temp > 120) {
// Replace the circuit core with lead if overheating
if (pixel.corePosition && Math.random() < (0.00015) * (pixel.temp - 120)) {
2025-06-30 20:38:23 -04:00
let corePos = pixel.corePosition;
if (pixelMap[corePos.x] && pixelMap[corePos.x][corePos.y]) {
deletePixel(corePos.x, corePos.y);
createPixel("lead", corePos.x, corePos.y);
}
}
// Randomly trigger malfunction if overheating
if (Math.random() < 0.001 * (pixel.temp - 120)) {
malfunction_chip(pixel);
}
// Break the circuit material itself if overheating
if (Math.random() < 0.001 * (pixel.temp - 120)) {
2025-06-30 20:38:23 -04:00
let px = pixel.x;
let py = pixel.y;
deletePixel(px, py);
2025-06-30 20:38:23 -04:00
if (Math.random() < 0.5) {
createPixel("lead", px, py);
}
}
}
}
}
2024-07-21 00:56:17 -04:00
};
2024-07-21 00:56:17 -04:00
elements.input_pin = {
color: "#DDAA33",
category: "logic",
state: "solid",
2024-07-21 00:56:17 -04:00
active: false,
stateHigh: "lead",
tempHigh: 570,
behavior: behaviors.WALL,
2025-06-30 20:38:23 -04:00
cc_stableTick: function (pixel) {
2024-07-21 00:56:17 -04:00
pixel.active = false;
2025-06-30 20:38:23 -04:00
let neighbors = getNeighbors(pixel);
for (let i = 0; i < neighbors.length; i++) {
let neighbor = neighbors[i];
2024-07-21 00:56:17 -04:00
2025-06-30 20:38:23 -04:00
if (neighbor.charge > 0 || neighbor.lstate === 2 || neighbor.active) {
2024-07-21 00:56:17 -04:00
pixel.active = true;
}
}
}
};
elements.output_pin = {
color: "#AAAAAA",
category: "logic",
state: "solid",
2024-07-21 00:56:17 -04:00
active: false,
stateHigh: "lead",
tempHigh: 570,
behavior: behaviors.WALL,
2025-06-30 20:38:23 -04:00
cc_stableTick: function (pixel) {
let neighbors = getNeighbors(pixel);
for (let i = 0; i < neighbors.length; i++) {
let neighbor = neighbors[i];
2024-07-21 00:56:17 -04:00
// Check if it's a wire
if (elements[neighbor.element].conduct > 0 && pixel.active) {
neighbor.charge = 1;
}
// Check if it's a logic wire (logicgates.js)
2025-06-30 20:38:23 -04:00
if (neighbor.lstate !== undefined) {
2024-07-21 00:56:17 -04:00
if (pixel.active) {
neighbor.lstate = 2;
neighbor.color = pixelColorPick(neighbor, "#ffe49c");
} else {
neighbor.lstate = -2;
neighbor.color = pixelColorPick(neighbor, "#3d4d2c");
}
}
}
}
};
elements.logic_corrupt = {
color: "#DD33DD",
category: "logic",
2025-06-30 20:38:23 -04:00
tool: function (pixel) {
if (pixel.element === "logic_wire") {
2024-07-21 00:56:17 -04:00
if (Math.random() < 0.01) {
if (Math.random() < 0.5) {
pixel.lstate = 2;
pixel.color = pixelColorPick(pixel, "#ffe49c");
} else {
pixel.lstate = -2;
pixel.color = pixelColorPick(pixel, "#3d4d2c");
}
}
}
},
excludeRandom: true,
}
elements.logic_corrupter_machine = {
color: "#DD33DD",
category: "logic",
2025-06-30 20:38:23 -04:00
cc_stableTick: function (pixel) {
let radius = 10
for (let y = pixel.y - radius; y < pixel.y + radius; y++) {
for (let x = pixel.x - radius; x < pixel.x + radius; x++) {
2024-07-21 00:56:17 -04:00
if (!isEmpty(x, y, true)) {
2025-06-30 20:38:23 -04:00
if (pixelMap[x][y].element === "logic_wire" && Math.random() < Math.min(Math.max((pixel.temp + 273) / 473, 0), 1) * 0.005) {
2024-07-21 00:56:17 -04:00
if (Math.random() < 0.5) {
pixelMap[x][y].lstate = 2
pixelMap[x][y].color = pixelColorPick(pixelMap[x][y], "#ffe49c")
} else {
pixelMap[x][y].lstate = -2
pixelMap[x][y].color = pixelColorPick(pixelMap[x][y], "#3d4d2c")
}
}
}
}
}
},
}
2025-06-30 20:38:23 -04:00
// Add link to documentation
let tutorialLink = document.createElement("a");
2024-08-23 05:15:43 -04:00
tutorialLink.textContent = "CircuitCore Tutorial";
tutorialLink.href = "https://redbirdly.github.io/circuitcore_tutorial.html";
tutorialLink.target = "_blank";
2025-06-30 20:38:23 -04:00
tutorialLink.style.color = "#33FF66";
tutorialLink.style.fontSize = "14px";
2024-08-23 05:15:43 -04:00
document.body.appendChild(tutorialLink);
2024-07-21 00:56:17 -04:00
// cc_ is circuit core prefix
const cc_BROWN = "#773317";
const cc_RED = "#DD3322";
const cc_ORANGE = "#DD8833";
const cc_YELLOW = "#DDCC44";
const cc_LIME = "#77DD44";
const cc_GREEN = "#33BB44";
const cc_BLUE = "#224499";
const cc_LIGHT_BLUE = "#77CCFF";
const cc_LAVENDER = "#AA88EE";
const cc_WHITE = "#DDDDDD";
2025-06-30 20:38:23 -04:00
let circuits = [
2024-07-21 00:56:17 -04:00
// Misc and I/O: brown
2025-06-30 20:38:23 -04:00
{circuit: elements.four_bit_selector_circuit, color: cc_BROWN, size: [17, 3, true]},
{circuit: elements.four_bit_enabler_circuit, color: cc_BROWN, size: [9, 3, true]},
{circuit: elements.randomizer, color: cc_BROWN},
{circuit: elements.four_bit_randomizer_circuit, color: cc_BROWN, size: [9, 3, true]},
{circuit: elements.two_bit_reverser_circuit, color: cc_BROWN, size: [5, 3, true]},
{circuit: elements.reverser_circuit, color: cc_BROWN},
2024-07-21 00:56:17 -04:00
// ROM/RAM: red
// { circuit: elements.ROM_circuit, color: cc_RED, size: [18, 18, false] },
// Encoders and de-multiplexers: orange
2025-06-30 20:38:23 -04:00
{circuit: elements.two_to_one_encoder_circuit, color: cc_ORANGE, size: [5, 3, true]},
{circuit: elements.four_to_two_encoder_circuit, color: cc_ORANGE, size: [9, 5, true]},
{circuit: elements.eight_to_three_encoder_circuit, color: cc_ORANGE, size: [17, 7, true]},
{circuit: elements.sixteen_to_four_encoder_circuit, color: cc_ORANGE, size: [33, 9, true]},
{circuit: elements.one_to_two_demultiplexer_circuit, color: cc_ORANGE, size: [3, 5, true]},
{circuit: elements.one_to_four_demultiplexer_circuit, color: cc_ORANGE, size: [3, 9, true]},
{circuit: elements.one_to_eight_demultiplexer_circuit, color: cc_ORANGE, size: [3, 17, true]},
{circuit: elements.one_to_sixteen_demultiplexer_circuit, color: cc_ORANGE, size: [3, 33, true]},
2024-07-21 00:56:17 -04:00
// Decoders and multiplexers: yellow
2025-06-30 20:38:23 -04:00
{circuit: elements.one_to_two_decoder_circuit, color: cc_YELLOW, size: [3, 5, true]},
{circuit: elements.two_to_four_decoder_circuit, color: cc_YELLOW, size: [5, 9, true]},
{circuit: elements.three_to_eight_decoder_circuit, color: cc_YELLOW, size: [7, 17, true]},
{circuit: elements.four_to_sixteen_decoder_circuit, color: cc_YELLOW, size: [9, 33, true]},
{circuit: elements.two_to_one_multiplexer_circuit, color: cc_YELLOW, size: [3, 5, true]},
{circuit: elements.four_to_one_multiplexer_circuit, color: cc_YELLOW, size: [5, 9, true]},
{circuit: elements.eight_to_one_multiplexer_circuit, color: cc_YELLOW, size: [7, 17, true]},
{circuit: elements.sixteen_to_one_multiplexer_circuit, color: cc_YELLOW, size: [9, 33, true]},
2024-07-21 00:56:17 -04:00
// Program counter and shift registers: lime
2025-06-30 20:38:23 -04:00
{circuit: elements.four_bit_PISO_shift_register_circuit, color: cc_LIME, size: [9, 5, true]},
{circuit: elements.four_bit_SIPO_shift_register_circuit, color: cc_LIME, size: [3, 9, true]},
{circuit: elements.four_bit_program_counter_circuit, color: cc_LIME, size: [9, 5, true]},
2024-07-21 00:56:17 -04:00
// Registers: green
2025-06-30 20:38:23 -04:00
{circuit: elements.four_bit_register_circuit, color: cc_GREEN, size: [9, 5, true]},
// Latches and flip-flops: light blue
{circuit: elements.SR_latch_circuit, color: cc_LIGHT_BLUE, size: [3, 3, true]},
{circuit: elements.T_flip_flop_circuit, color: cc_LIGHT_BLUE, size: [3, 3, true]},
{circuit: elements.D_latch_circuit, color: cc_LIGHT_BLUE, size: [3, 3, true]},
{circuit: elements.D_flip_flop_circuit, color: cc_LIGHT_BLUE, size: [3, 3, true]},
{circuit: elements.four_bit_D_latch_circuit, color: cc_LIGHT_BLUE, size: [9, 3, true]},
{circuit: elements.four_bit_D_flip_flop_circuit, color: cc_LIGHT_BLUE, size: [9, 3, true]},
2024-07-21 00:56:17 -04:00
// Addition/subtraction arithmetic: blue
2025-06-30 20:38:23 -04:00
{circuit: elements.four_bit_adder_circuit, color: cc_BLUE, size: [17, 3, true]},
{circuit: elements.four_bit_subtractor_circuit, color: cc_BLUE, size: [17, 3, true]},
{circuit: elements.four_bit_incrementer_circuit, color: cc_BLUE, size: [9, 3, true]},
2024-07-21 00:56:17 -04:00
// Complex circuits: lavender
2025-06-30 20:38:23 -04:00
2024-07-21 00:56:17 -04:00
// Clocks: pink
2025-06-30 20:38:23 -04:00
{circuit: elements.slow_clock},
{circuit: elements.medium_clock},
{circuit: elements.fast_clock},
{circuit: elements.very_fast_clock},
2024-07-21 00:56:17 -04:00
// Displays/visual circuits: white
2025-06-30 20:38:23 -04:00
{circuit: elements.simple_seven_segment_display, color: cc_WHITE, size: [5, 9, false]},
{circuit: elements.simple_double_seven_segment_display, color: cc_WHITE, size: [9, 9, false]},
{circuit: elements.custom_RGB_led, color: cc_WHITE, size: [3, 3, true]},
2024-07-21 00:56:17 -04:00
];
circuits.forEach(circuitInfo => {
2025-06-30 20:38:23 -04:00
if (circuitInfo.color) {
circuitInfo.circuit.color = circuitInfo.color;
}
2024-07-21 00:56:17 -04:00
circuitInfo.circuit.category = "logic";
circuitInfo.circuit.maxSize = 1;
2024-08-10 23:13:02 -04:00
circuitInfo.circuit.behavior = behaviors.WALL;
circuitInfo.circuit.state = "solid";
2024-07-21 00:56:17 -04:00
circuitInfo.circuit.isCircuitCore = true;
circuitInfo.circuit.previewSize = circuitInfo.size;
// Exclude circuits without a frame
if (circuitInfo.size) {
2025-06-30 20:38:23 -04:00
let previousCircuitTick = circuitInfo.circuit.cc_stableTick;
circuitInfo.circuit.cc_stableTick = function (pixel) {
previousCircuitTick(pixel);
// Don't constantly check
if (Math.random() < 0.1) {
// If there aren't 4 neighboring circuit_material elements then remove the circuit's core
2025-06-30 20:38:23 -04:00
let neighbors = getNeighbors(pixel);
let circuitMaterialCount = 0;
for (let i = 0; i < neighbors.length; i++) {
if (neighbors[i].element === "circuit_material") {
circuitMaterialCount++;
}
}
if (circuitMaterialCount < 2) {
deletePixel(pixel.x, pixel.y);
}
2024-08-10 23:13:02 -04:00
// Check if circuit overheating is enabled
2025-06-30 20:38:23 -04:00
if (cc_heat_emit_setting.value) {
pixel.temp += Math.random() * 0.7;
2024-08-10 23:13:02 -04:00
}
}
}
}
2024-07-21 00:56:17 -04:00
});
2025-06-30 20:38:23 -04:00
let circuitRotation = 0;
document.addEventListener('keydown', function (event) {
2024-07-21 00:56:17 -04:00
if (event.key === 'ArrowUp') {
circuitRotation = (circuitRotation + 90) % 360;
logMessage('CircuitRotation changed to ' + circuitRotation);
}
});
2025-02-22 05:05:00 -05:00
function drawCircuitExtras(ctx) {
if (elements[currentElement].isCircuitCore && elements[currentElement].previewSize) {
2025-06-30 20:38:23 -04:00
let circuitWidth = elements[currentElement].previewSize[0];
let circuitHeight = elements[currentElement].previewSize[1];
let centered = elements[currentElement].previewSize[2];
let rotation = circuitRotation;
2025-06-30 20:38:23 -04:00
let startX = 0;
let startY = 0;
let endX = circuitWidth - 1;
let endY = circuitHeight - 1;
if (centered) {
startX = -Math.floor(circuitWidth / 2);
startY = -Math.floor(circuitHeight / 2);
endX = Math.floor(circuitWidth / 2);
endY = Math.floor(circuitHeight / 2);
}
2025-06-30 20:38:23 -04:00
for (let y = startY; y <= endY; y++) {
for (let x = startX; x <= endX; x++) {
// if (!(0 <= x && x < width && 0 <= y && y < height)) {continue;}
2025-06-30 20:38:23 -04:00
let [rx, ry] = rotateCoordinateAroundOrigin(x, y, rotation);
let px = mousePos.x + rx;
let py = mousePos.y + ry;
ctx.fillStyle = "rgba(255, 255, 255, 0.1)";
2025-06-30 20:38:23 -04:00
if ((rotation !== 0 && !centered) || (0 <= px && px < width && 0 <= py && py < height) && pixelMap[px][py]) {
2024-07-21 00:56:17 -04:00
ctx.fillStyle = "rgba(255, 0, 0, 0.3)";
}
ctx.fillRect(px * pixelSize, py * pixelSize, pixelSize, pixelSize);
}
}
}
2024-07-21 00:56:17 -04:00
}
function runLogicTick() {
2025-06-30 20:38:23 -04:00
for (let i = 0; i < currentPixels.length; i++) {
const pixel = currentPixels[i];
const elementData = elements[pixel.element];
if (elementData.cc_stableTick) {
elementData.cc_stableTick(pixel);
}
}
}
2024-08-23 05:15:43 -04:00
function stabilizeLogicGates() {
2025-06-30 20:38:23 -04:00
let logicgatesElements = ["output", "logic_wire", "not_gate", "and_gate", "xor_gate", "or_gate", "nand_gate", "nor_gate", "nxor_gate", "E2L_lever", "E2L_button", "L2E_constant", "logic_shock", "logic_unshock"]
2024-08-23 05:15:43 -04:00
2025-06-30 20:38:23 -04:00
for (let i = 0; i < logicgatesElements.length; i++) {
2024-08-23 05:15:43 -04:00
elements[logicgatesElements[i]].cc_stableTick = elements[logicgatesElements[i]].tick;
elements[logicgatesElements[i]].tick = null;
}
}
2025-06-30 20:38:23 -04:00
// Hooks
2024-08-10 23:13:02 -04:00
renderPostPixel(drawCircuitExtras);
runEveryTick(runLogicTick);
2025-06-30 20:38:23 -04:00
if (cc_stable_tick_setting.value)
runAfterLoad(stabilizeLogicGates);