Last active
February 28, 2024 02:20
-
-
Save rafa-br34/b48c0a1b10877237e87db9b6077cf7a4 to your computer and use it in GitHub Desktop.
NodeJS save serializer for CM2
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
let CircuitMaker = {} | |
const Components = Object.freeze({ | |
NOR: 0, | |
AND: 1, | |
OR: 2, | |
XOR: 3, | |
Button: 4, | |
FlipFlop: 5, | |
LED: 6, | |
Sound: 7, | |
Conductor: 8, | |
Custom: 9, | |
NAND: 10, | |
XNOR: 11, | |
Random: 12, | |
Text: 13, | |
Tile: 14, | |
Node: 15, | |
Delay: 16, | |
Antenna: 17 | |
}) | |
CircuitMaker.Components = Components | |
const States = Object.freeze({ | |
Off: 0, | |
On: 1 | |
}) | |
CircuitMaker.State = States | |
const WaveTypes = Object.freeze({ | |
Sine: 0, | |
Square: 1, | |
Triangle: 2, | |
Sawtooth: 3 | |
}) | |
CircuitMaker.WaveTypes = WaveTypes | |
const DefaultAugments = Object.freeze({ | |
[Components.NOR]: [], | |
[Components.AND]: [], | |
[Components.OR]: [], | |
[Components.XOR]: [], | |
[Components.Button]: [], | |
[Components.FlipFlop]: [], | |
[Components.LED]: [175, 175, 175, 100, 25, 0], | |
[Components.Sound]: [1567.98, WaveTypes.Sine], | |
[Components.Conductor]: [], | |
[Components.Custom]: [], | |
[Components.NAND]: [], | |
[Components.XNOR]: [], | |
[Components.Random]: [0.5], | |
[Components.Text]: [65], | |
[Components.Tile]: [75, 75, 75], | |
[Components.Node]: [], | |
[Components.Delay]: [20], | |
[Components.Antenna]: [0] | |
}) | |
CircuitMaker.DefaultAugments = DefaultAugments | |
class Component { | |
Type = Components.NOR | |
State = States.Off | |
Position = { X: 0, Y: 0, Z: 0 } | |
Augments = null | |
Inputs = [] | |
Outputs = [] | |
constructor(Values) { | |
this.Position = Values.Position || { X: 0, Y: 0, Z: 0 } | |
this.Augments = Values.Augments || DefaultAugments[this.Type] | |
this.Outputs = Values.Outputs || [] | |
this.Inputs = Values.Inputs || [] | |
this.State = Values.State || States.Off | |
this.Type = Values.Type || Components.NOR | |
this.Position = {...this.Position} | |
} | |
ConnectionCount() { | |
return this.Inputs.length + this.Outputs.length | |
} | |
Clone() { | |
return new Component(this.Type, this.State, {...this.Position}, this.Inputs.slice(), this.Outputs.slice(), this.Augments.slice()) | |
} | |
} | |
CircuitMaker.Component = Component | |
function SerializeBlock(Block, Rounding = 0) { | |
let PositionVector = Object.values(Block.Position) | |
let Position = (Rounding > 0 ? PositionVector.map(V => Math.round(V * Rounding) / Rounding) : PositionVector).join(',') | |
let Augments = Block.Augments.join() == DefaultAugments[Block.Type].join() ? "" : Block.Augments.join('+') | |
return `${Block.Type},${Block.State || ''},${Position},${Augments}` | |
} | |
function DeserializeBlock(Data) { | |
let [ Type, State, X, Y, Z, Augments ] = Data.split(",") | |
return new Component({ | |
Type, | |
State, | |
Position: { X, Y, Z }, | |
Augments: Augments.length > 0 ? Augments.split('+') : DefaultAugments[Type] | |
}) | |
} | |
function SerializeWire(Source, Target) { | |
return `${Source},${Target}` | |
} | |
function DeserializeWire(Data, Components) { | |
let [Source, Target] = Data.split(",").map((Index) => Components[Index - 1]) | |
Source.Outputs.push(Target) | |
Target.Inputs.push(Source) | |
} | |
class Creation { | |
Components = [] | |
constructor(Components = null) { | |
this.Components = Components || [] | |
} | |
RemoveComponent(Component) { | |
let Index = this.Components.findIndex((Value) => Value == Component) | |
return Index < 0 ? null : this.Components.pop(Index) | |
} | |
RemoveComponents(Components) { | |
return this.Components = this.Components.map((A) => Components.find((B) => A == B) > 0 ? null : A).filter(A => A) | |
} | |
AddComponent(Item) { | |
this.Components.push(Item = Item.constructor == Component ? Item : new Component(Item)) | |
return Item | |
} | |
AddComponents(Items) { | |
let List = [] | |
for (let Item of Items) { | |
List.push(this.AddComponent(Item)) | |
} | |
return List | |
} | |
Clear() { | |
this.Components = [] | |
} | |
Clone() { | |
return new Creation(this.Components.map((Component) => Component.Clone())) | |
} | |
MakeConnection(Source, Target) { | |
console.assert(this.Components.find(Source), "Source component does not exist") | |
console.assert(this.Components.find(Target), "Target component does not exist") | |
Source.Outputs.push(Target) | |
Target.Inputs.push(Source) | |
} | |
BridgeConnections() { | |
let Changes = 0 | |
for (let Component of this.Components) { | |
for (let Input of Component.Inputs) { | |
if (!Input.Outputs.find(Component)) { | |
Input.Outputs.push(Component) | |
Changes++ | |
} | |
} | |
for (let Outputs of Component.Outputs) { | |
if (!Outputs.Inputs.find(Component)) { | |
Outputs.Inputs.push(Component) | |
Changes++ | |
} | |
} | |
} | |
return Changes | |
} | |
Serialize(Rounding = 0, OptimizeWires = true) { | |
let SerializedBlocks = "" | |
let SerializedWires = "" | |
let Flags = new Set() | |
function SerializeConnection(A, B) { | |
if (OptimizeWires) { | |
let Connection = `${A}-${B}` | |
if (Flags.has(Connection)) { | |
return '' | |
} | |
else { | |
Flags.add(Connection) | |
} | |
} | |
return SerializeWire(A + 1, B + 1) + ';' | |
} | |
let Components = this.Components | |
let ComponentToIndex = new Map() | |
for (let Index = 0; Index < Components.length; Index++) { | |
ComponentToIndex.set(Components[Index], Index) | |
} | |
for (let Source = 0; Source < Components.length; Source++) { | |
let Component = Components[Source] | |
SerializedBlocks += SerializeBlock(Component, Rounding) + ';' | |
for (let I of Component.Inputs) { | |
SerializedWires += SerializeConnection(ComponentToIndex.get(I), Source) // i -> src | |
} | |
for (let O of Component.Outputs) { | |
SerializedWires += SerializeConnection(Source, ComponentToIndex.get(O)) // src -> o | |
} | |
} | |
return `${SerializedBlocks.substring(0, SerializedBlocks.length - 1)}?${SerializedWires.substring(0, SerializedWires.length - 1)}??` | |
} | |
Deserialize(Data, Clear = true) { | |
Clear && this.Clear() | |
let [Components, Connections, _Buildings, _BuildingsData] = Data.split('?').map(Chunk => Chunk.split(';')) | |
for (let Component of Components) { | |
this.AddComponent(DeserializeBlock(Component)) | |
} | |
for (let Connection of Connections) { | |
DeserializeWire(Connection, this.Components) | |
} | |
} | |
} | |
CircuitMaker.Creation = Creation | |
return (typeof(module) == "undefined" ? {} : module).exports = CircuitMaker |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
let Commands = {} | |
let CM = this.CM | |
function CreateLine(Creation, Size, Configs, Lambda) { | |
let List = [] | |
for (let i = 0; i < Size; i++) { | |
let [ X, Y, Z ] = Lambda(i) | |
List.push(Creation.AddComponent({ ...Configs, Position: {X, Y, Z} })) | |
} | |
return List | |
} | |
let c_Orientation = Object.freeze({ | |
Horizontal: 0, | |
Vertical: 1 | |
}) | |
Commands.c_Orientation = c_Orientation | |
Commands.Register = function(Size = 8, Orientation = 1) { | |
let Creation = new CM.Creation() | |
let Axis0 = Orientation == c_Orientation.Vertical ? 'X' : 'Z' | |
let Axis1 = Orientation == c_Orientation.Vertical ? 'Y' : 'X' | |
let Position = { X: 0, Y: 0, Z: 0 } | |
Position[Axis0] = -1 | |
Position[Axis1] = Size | |
let RisingEdgeGate = Creation.AddComponent({ | |
Type: CM.Components.NOR, | |
Position: Position | |
}) | |
Position[Axis0]++ | |
let TriggerGate = Creation.AddComponent({ | |
Type: CM.Components.AND, | |
Position: Position, | |
Inputs: [RisingEdgeGate] | |
}) | |
Position[Axis0]++ | |
// ClockInput | |
Creation.AddComponent({ | |
Type: CM.Components.OR, | |
Position: Position, | |
Outputs: [TriggerGate, RisingEdgeGate] | |
}) | |
for (let i = 0; i < Size; i++) { | |
Position = { X: 0, Y: 0, Z: 0 } | |
Position[Axis0] = 1 | |
Position[Axis1] = i | |
let StateCarrier = Creation.AddComponent({ | |
Type: CM.Components.FlipFlop, | |
Position | |
}) | |
Position[Axis0]-- | |
let WriteGate = Creation.AddComponent({ | |
Type: CM.Components.AND, | |
Position, | |
Inputs: [TriggerGate], | |
Outputs: [StateCarrier] | |
}) | |
Position[Axis0]-- | |
// Comparator | |
Creation.AddComponent({ | |
Type: CM.Components.XOR, | |
Position, | |
Inputs: [StateCarrier], | |
Outputs: [WriteGate] | |
}) | |
} | |
return Creation.Serialize(0) | |
} | |
let c_MemoryFlags = Object.freeze({ | |
Writable: 1 << 0, | |
OutputLock: 1 << 1 | |
}) | |
Commands.c_MemoryFlags = c_MemoryFlags | |
Commands.Memory = function(Bandwidth, RowCount, Flags, Data = []) { | |
let Creation = new CM.Creation() | |
let Height = 2 + (Flags & c_MemoryFlags.Writable ? 2 : 0) + (Flags & c_MemoryFlags.OutputLock ? 1 : 0) | |
let ReadWrite | |
let RowSelect | |
let Outputs | |
let Inputs | |
let RowConfig = { Type: CM.Components.Node } | |
RowSelect = CreateLine(Creation, RowCount, RowConfig, (R) => [R, Height - 2, -1]) | |
Outputs = CreateLine(Creation, Bandwidth, RowConfig, (O) => [-1, Height - 2, O]) | |
if (Flags & c_MemoryFlags.Writable) { | |
Inputs = CreateLine(Creation, Bandwidth, RowConfig, (R) => [RowCount, Height - 3, R]) | |
ReadWrite = CreateLine(Creation, Bandwidth, RowConfig, (S) => [RowCount, Height - 4, S]) | |
} | |
for (let r = 0; r < RowCount; r++) { | |
for (let b = 0; b < Bandwidth; b++) { | |
let Bit = (Data || [])[r * Bandwidth + b] | |
let Position = {X: r, Y: Height - 1, Z: b} | |
let StateCarrier = Creation.AddComponent({ | |
Type: CM.Components.FlipFlop, | |
Position, | |
Augments: Bit && [2,0], | |
State: Bit, | |
}) | |
Position.Y-- | |
let OutputGate = Creation.AddComponent({ | |
Type: CM.Components.AND, | |
Position, | |
Inputs: [StateCarrier, RowSelect[r]], | |
Outputs: [Outputs[b]] | |
}) | |
Position.Y-- | |
if (Flags & c_MemoryFlags.Writable) { | |
let FeedbackGate = Creation.AddComponent({ | |
Type: CM.Components.XOR, | |
Position, | |
Inputs: [StateCarrier, Inputs[b]] | |
}) | |
Position.Y-- | |
Creation.AddComponent({ | |
Type: CM.Components.AND, | |
Position, | |
Inputs: [FeedbackGate, RowSelect[r], ReadWrite[b]], | |
Outputs: [StateCarrier] | |
}) | |
Position.Y-- | |
if (Flags & c_MemoryFlags.OutputLock) { | |
Creation.AddComponent({ | |
Type: CM.Components.NOR, | |
Position, | |
Inputs: [ReadWrite[b]], | |
Outputs: [OutputGate] | |
}) | |
Position.Y-- | |
} | |
} | |
} | |
} | |
return Creation.Serialize(0) | |
} | |
let c_CounterType = Object.freeze({ | |
Asynchronous: 0, | |
Synchronous: 1 | |
}) | |
Commands.c_CounterType = c_CounterType | |
Commands.Counter = function(Size = 8, Type = c_CounterType.Asynchronous) { | |
let Creation = new CM.Creation() | |
if (Type == c_CounterType.Synchronous) { | |
let StateChain = CreateLine(Creation, Size, { Type: CM.Components.FlipFlop }, (I) => [I, 0, 1]) | |
let StateGates = CreateLine(Creation, Size, { Type: CM.Components.AND }, (I) => [I, 0, 0]) | |
// Clock | |
Creation.AddComponent({ | |
Type: CM.Components.Node, | |
Position: {X: Size, Y: 0, Z: 0}, | |
Outputs: StateGates | |
}) | |
for (let b = 0; b < Size; b++) { | |
StateGates[b].Inputs = StateChain.slice(b + 1) | |
StateChain[b].Inputs = [StateGates[b]] | |
} | |
} | |
else { | |
let StateChain = CreateLine(Creation, Size, { Type: CM.Components.FlipFlop }, (I) => [I, 0, 0]) | |
let StateGates = CreateLine(Creation, Size, { Type: CM.Components.NOR }, (I) => [I, 0, 1]) | |
// Clock | |
let Last = Creation.AddComponent({ | |
Type: CM.Components.Node, | |
Position: {X: Size, Y: 0, Z: 0} | |
}) | |
for (let b = Size; b > 0; b--) { | |
StateChain[b - 1].Inputs = [Last] | |
Last = StateChain[b - 1] | |
StateGates[b - 1].Inputs = [Last] | |
} | |
} | |
return Creation.Serialize(0) | |
} | |
/* | |
CM = Function(String(fs.readFileSync("DiscordIntegration/CircuitMaker.js")))(); Commands = Function(String(fs.readFileSync("DiscordIntegration/Commands.js")))() | |
*/ | |
return (typeof(module) == "undefined" ? {} : module).exports = Commands |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
.t create/edit help ``` | |
Available tags: | |
.t text <text> // Generates a save string with some text | |
.t memory <bandwidth=8> <row-count=8> <writable?> <output_lock?> // Generates read-only or read-write memory | |
.t register <size=8> <orientation=(vertical[default], horizontal)> // Generates a register | |
.t counter <size=8> <type=(async/asynchronous, sync/synchronous[default])> // Generates a counter | |
// Unimplemented tags | |
.t cla-adder | |
.t image <resize-x> <resize-y> | |
``` | |
If some tag breaks please ping <@642064514476408832> | |
The source code for both the commands and the serializer can be found [here](<https://gist.github.com/rafa-br34/b48c0a1b10877237e87db9b6077cf7a4>) | |
*/ | |
.t create/edit text { | |
js: | |
let LOG = console.log | |
let Command = discord.message.content | |
if (!Command) { LOG("Invalid command"); return } | |
if ((Command = Command.replace(/\.t\s+[A-Za-z]+(\s?)+/, "")).length <= 0) { LOG("No command specified"); return } | |
let fs = require("fs") | |
let Result = "" | |
let X = 0, Y = 0 | |
for (let Character of Command) { | |
if (Character == '\r') { continue } | |
if (Character == '\n') { Y++; X=0; continue } | |
Result += `13,0,${X},0,${Y},${Character.charCodeAt()};` | |
X++ | |
} | |
fs.writeFileSync("./output/savestring.txt", Result.substring(0, Result.length - 1) + "???") | |
} | |
.t create/edit register { | |
js: | |
let TNOW = Date.now; let TCALC = (A,B) => (B-A)/1000 | |
let LOG = console.log | |
let Get = (Host, Path, Module = require("https")) => { | |
return new Promise((Resolve, _Reject) => { | |
Module.get({host: Host, path: Path}, (Response) => { | |
let Data = "" | |
Response | |
.on("data", (Chunk) => Data += Chunk) | |
.on("end", () => Resolve(Data)) | |
}) | |
}) | |
} | |
let Arguments = discord.message.content | |
if (!Arguments) { LOG("Invalid command"); return } | |
Arguments = Arguments.replace(/\.t\s+[A-Za-z]+(\s?)+/, "").split(/\s/).filter(S => S) | |
let FileLink = FileName => ["gist.githubusercontent.com", `/rafa-br34/b48c0a1b10877237e87db9b6077cf7a4/raw/${FileName}?_=${Math.random()}`] | |
let GetModule = FileName => Get(...FileLink(FileName)) | |
let LoadStart = TNOW() | |
Promise.all([GetModule("CircuitMaker.js"), GetModule("Commands.js")]).then(([_CM, _Commands]) => { | |
let CM = Function(_CM)() | |
let Commands = Function(_Commands).call({CM}) | |
let CompStart = TNOW() | |
require("fs").writeFileSync( | |
"./output/savestring.txt", | |
Commands.Register( | |
parseInt(Arguments[0]) || 8, | |
{HORIZONTAL: 0, VERTICAL: 1}[(Arguments[1] || "HORIZONTAL").toUpperCase()] || 0 | |
) | |
) | |
let LT = TCALC(LoadStart, CompStart); let CT = TCALC(CompStart, TNOW()); LOG(`Loaded modules in ${LT.toFixed(3)} seconds\nSynthesized circuit in ${CT.toFixed(3)} seconds`); | |
}) | |
} | |
.t create/edit memory { | |
js: | |
let TNOW = Date.now; let TCALC = (A,B) => (B-A)/1000 | |
let LOG = console.log | |
let Get = (Host, Path, Module = require("https")) => { | |
return new Promise((Resolve, _Reject) => { | |
Module.get({host: Host, path: Path}, (Response) => { | |
let Data = "" | |
Response | |
.on("data", (Chunk) => Data += Chunk) | |
.on("end", () => Resolve(Data)) | |
}) | |
}) | |
} | |
let Arguments = discord.message.content | |
if (!Arguments) { LOG("Invalid command"); return } | |
Arguments = Arguments.replace(/\.t\s+[A-Za-z]+(\s?)+/, "").split(/\s/).filter(S => S) | |
let FileLink = FileName => ["gist.githubusercontent.com", `/rafa-br34/b48c0a1b10877237e87db9b6077cf7a4/raw/${FileName}?_=${Math.random()}`] | |
let GetModule = FileName => Get(...FileLink(FileName)) | |
let LoadStart = TNOW() | |
Promise.all([GetModule("CircuitMaker.js"), GetModule("Commands.js")]).then(([_CM, _Commands]) => { | |
let CM = Function(_CM)() | |
let Commands = Function(_Commands).call({CM}) | |
let CompStart = TNOW() | |
require("fs").writeFileSync( | |
"./output/savestring.txt", | |
Commands.Memory( | |
parseInt(Arguments[0]) || 8, | |
parseInt(Arguments[1]) || 8, | |
(Arguments.find((V) => V.toUpperCase() == "WRITABLE") && Commands.c_MemoryFlags.Writable || 0) + (Arguments.find((V) => V.toUpperCase() == "OUTPUT_LOCK") && Commands.c_MemoryFlags.OutputLock || 0) | |
) | |
) | |
let LT = TCALC(LoadStart, CompStart); let CT = TCALC(CompStart, TNOW()); LOG(`Loaded modules in ${LT.toFixed(3)} seconds\nSynthesized circuit in ${CT.toFixed(3)} seconds`); | |
}) | |
} | |
.t create/edit counter { | |
js: | |
let TNOW = Date.now; let TCALC = (A,B) => (B-A)/1000 | |
let LOG = console.log | |
let Get = (Host, Path, Module = require("https")) => { | |
return new Promise((Resolve, _Reject) => { | |
Module.get({host: Host, path: Path}, (Response) => { | |
let Data = "" | |
Response | |
.on("data", (Chunk) => Data += Chunk) | |
.on("end", () => Resolve(Data)) | |
}) | |
}) | |
} | |
let Arguments = discord.message.content | |
if (!Arguments) { LOG("Invalid command"); return } | |
Arguments = Arguments.replace(/\.t\s+[A-Za-z]+(\s?)+/, "").split(/\s/).filter(S => S) | |
let FileLink = FileName => ["gist.githubusercontent.com", `/rafa-br34/b48c0a1b10877237e87db9b6077cf7a4/raw/${FileName}?_=${Math.random()}`] | |
let GetModule = FileName => Get(...FileLink(FileName)) | |
let LoadStart = TNOW() | |
Promise.all([GetModule("CircuitMaker.js"), GetModule("Commands.js")]).then(([_CM, _Commands]) => { | |
let CM = Function(_CM)() | |
let Commands = Function(_Commands).call({CM}) | |
let CompStart = TNOW() | |
require("fs").writeFileSync( | |
"./output/savestring.txt", | |
Commands.Counter( | |
parseInt(Arguments[0]) || 8, | |
{ASYNCHRONOUS: 0, ASYNC: 0, SYNCHRONOUS: 1, SYNC: 1}[(Arguments[1] || "ASYNC").toUpperCase()] || 0 | |
) | |
) | |
let LT = TCALC(LoadStart, CompStart); let CT = TCALC(CompStart, TNOW()); LOG(`Loaded modules in ${LT.toFixed(3)} seconds\nSynthesized circuit in ${CT.toFixed(3)} seconds`); | |
}) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment