Skip to content

Instantly share code, notes, and snippets.

@rafa-br34
Last active February 28, 2024 02:20
Show Gist options
  • Save rafa-br34/b48c0a1b10877237e87db9b6077cf7a4 to your computer and use it in GitHub Desktop.
Save rafa-br34/b48c0a1b10877237e87db9b6077cf7a4 to your computer and use it in GitHub Desktop.
NodeJS save serializer for CM2
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
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
/*
.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