)
}
}
)
(
}
{
)
)
(
)
(
(
{
}
)
(
)
}
)
)
{
(
(
)
)
}
)
(
}

Em3w parensordot

  1. const vals = {
  2.   E: 0, M: 90, '3': 180, W: 270
  3. }
  4.  
  5. // const byDeg = {
  6. //   0: 'E', 90: 'M', 180: '3', 270: 'W'
  7. // }
  8. const byDeg = Object.keys(vals).reduce((acc, key) => { 
  9.   acc[vals[key]] = key;
  10.   return acc;
  11. }, {});
  12.  
  13.  
  14. const ops = {
  15.   ')': 90, '(' : -90, '|': 180
  16. }
  17.  
  18. function rotToSym(v) {
  19.   if (v < 0) v += 360;
  20.   const deg = Math.abs(v % 360);
  21.   return byDeg[deg]
  22. }
  23.  
  24. function op(char, op) {
  25.   if (char === 'M' && op === '|') return 'M'
  26.   if (char === 'W' && op === '|') return 'W'
  27.   let v = vals[char]
  28.   v += ops[op]
  29.   return rotToSym(v)
  30. }
  31.  
  32.  
  33. function emw(prog) {
  34.   prog = [...prog]
  35.  
  36.   let chars = []
  37.   let res = []
  38.   for (let i = 0; i < prog.length; i++) {
  39.     let char = prog[i]
  40.     if (vals[char] != null) {
  41.       chars.push(char)
  42.     } else if (ops[char]) {
  43.       chars = chars.map(v => op(v, char))
  44.     } else if (char === '.' && chars.length > 0) {
  45.       const num = chars.map(v => vals[v])
  46.         .reduce((a, b) => a + b, 0);
  47.       chars = [rotToSym(num)]
  48.     }
  49.   }
  50.   return chars.join``
  51. }
  52.  
  53. function expr(prog) {
  54.   const orig = prog;
  55.   prog = prog.split(/\s/)
  56.   return orig + ' = ' + prog.map(emw).join` `
  57. }
  58.  
  59. document.body.appendChild(
  60.   Object.assign(
  61.     document.createElement`pre`, {
  62.       innerHTML: 
  63. `
  64. Em3w parensordot 
  65.  
  66. // rotate 90 clockwise
  67. ${expr('EEE)')}
  68.  
  69. // rotate -90 clockwise
  70. ${expr('W(')}
  71. ${expr('W((')}
  72.  
  73. // mirror
  74. ${expr('E|')}
  75.  
  76. // mirror
  77. ${expr('EM|')}
  78.  
  79. ${expr('WW3) MM) EEEM).')}
  80.  
  81. // sum rotations
  82. ${expr('MMM.')}
  83.  
  84. ${expr(
  85. `E) E( E)) EEE))) E)))) MM33).`)}
  86. `}
  87.   )
  88. )

Fun thing I wanted to make for awhile… definitely going to do some doodles of this in my sketchbook…

Code description from GPT4 because I’m too lazy to write one…

This code is implementing a form of symbolic computation using a transformational language. This language has four primary symbols: “E”, “M”, “3”, and “W”. Each of these symbols is associated with a specific angle: 0, 90, 180, and 270 degrees, respectively.

There are also special symbols, which act as operators that perform certain transformations on the primary symbols:

  • “)”: Represents a rotation 90 degrees clockwise.
  • “(“: Represents a rotation 90 degrees counter-clockwise.
  • “|”: Represents a mirror operation, which reverses the direction. However, “M” and “W” are unaffected by this operation.
  • “.”: This operator sums up all previous primary symbols (by their respective degree values), resulting in a new symbol. The degree value of the new symbol is the sum of the previous degrees modulo 360.

The function emw(prog) interprets a string prog as a sequence of symbols and operations. It reads each character in the string and based on whether it’s a primary symbol, operator, or a “.”, it either adds the symbol to an array or performs an operation on the existing symbols in the array.

The expr(prog) function splits the input string prog into space-separated substrings. It interprets each substring as a sequence in the transformational language, using the emw function. It then joins the results together with a space and prepends the original input and an equals sign to give a string representation of the transformations.

The final section of the code generates a series of examples of expressions in this language, evaluates them using the expr function, and adds the results to the HTML body of a webpage in a formatted manner. This gives the user a way to see the transformations in action.

Concatenative JavaScript

  1. const vars = {}
  2.  
  3. const as = s => v => vars[s] = v
  4.  
  5. const def = s => v => {
  6.   vars[s] = v
  7. }
  8.  
  9. const set = (last, prop, val) => {
  10.   last[prop] = val
  11. }
  12.  
  13. const put = (last, prop, val) => {
  14.   last[prop] = val
  15.   return last
  16. }
  17.  
  18. const of = k => run.last[k]
  19. const $ = k => () => vars[k]
  20.  
  21. const run = (prog, log) => {
  22.   run.stack = []
  23.   let curr
  24.   for (let i = 0; i < prog.length; i++) {
  25.     let val = prog[i]
  26.     if (val != null && val === $) { 
  27.       run.stack.push(val())
  28.     } else if (typeof val === 'function') {
  29.       let ctx = null
  30.       if (document.body[val.name]) { 
  31.         ctx = document.body 
  32.         val = val.bind(ctx)
  33.       } else if (document[val.name]) { 
  34.         ctx = document
  35.         val = val.bind(ctx)
  36.       } 
  37.  
  38.       if (log) console.log('exec', 
  39.         val.name == 'bound ' ? '\u03BB' : val.name, run.stack)
  40.  
  41.       curr = val.apply(ctx, run.stack)
  42.       if (curr != run.stack) { 
  43.         run.stack = curr != null ? [curr] : []
  44.         if (typeof curr === 'object') {
  45.           run.last = curr
  46.         }
  47.       }
  48.     } else {
  49.       if (val != null) run.stack.push(val)
  50.     }
  51.   }
  52. }
  53.  
  54. const showStack = () => {
  55.   console.log('- stack:', run.stack)
  56.   console.log('  -> last object', run.last)
  57.   return run.stack
  58. }
  59.  
  60. const _ = new Proxy({}, { 
  61.   get: (o, key) => (() => (...a) => 
  62.     run.last[key].apply(run.last, a)
  63.   )()
  64. })
  65.  
  66. // try it out
  67.  
  68. run( 
  69.   [ 'div', 
  70.     document.createElement, 
  71.     document.body.appendChild, 
  72.     as`myDiv`,
  73.     'innerHTML', 'mini <b>concatenative</b>', set,
  74.     'style', of, 'color', 'green', set,
  75.  
  76.     'just logging the element', $`myDiv`, console.log, 
  77.  
  78.     'canvas', document.createElement, 
  79.     document.body.appendChild, 
  80.     'width', 500, put,
  81.     'height', 500, put,
  82.     def`myCanvas`,
  83.  
  84.     '2d',
  85.     _.getContext,
  86.     'fillStyle', 'red', set,
  87.     100, 30, _.translate,
  88.     0, 0, 10, 100, _.fillRect,
  89.     20, 0, 10, 100, _.fillRect,
  90.     .2, _.rotate,
  91.     -30, 25, 100, 10, _.fillRect,
  92.     -20, 55, 100, 10, _.fillRect,
  93.  
  94.     {}, 
  95.     'name', 'zevan', put,
  96.     'age', 82, put,
  97.     JSON.stringify,
  98.     as`person`,
  99.  
  100.     showStack
  101.   ]
  102. )
  103. console.log(vars);

I recently spent some time reading about and playing with the FORTH language. I keep finding myself creating weird vaguely FORTH inspired snippets now… This one processes an array that’s in a concatenative format like this:

[ 'hello world', Date.now(), console.log ]

Arguments are collected on a stack and passed to a function when a function is hit in the array. There are some odd helper functions for setting properties, setting nested properties, running functions with a specific context etc…


Here is GPT4’s mostly correct explanation of what it does

This is a rather unusual piece of JavaScript code that creates a simple “programming language” based on the concept of a stack machine, and runs a “program” in this language. It resembles some sort of DSL (domain-specific language) or script-like construct.

Here are what the main components do:

  1. vars: This is a dictionary that stores variables defined in the program.
  2. as: This is a function that takes a string s and returns a function that takes a value v, storing v in vars under the key s.
  3. def: Similar to as, it defines a variable in vars.
  4. set: This function is used to set the property of an object. It takes the object last, the property name prop, and the value val, and assigns val to the property prop of the object last.
  5. put: Similar to set, it sets the property of an object and returns the updated object.
  6. of: This function gets the property with name k of the last object returned by the program.
  7. $: This function is a getter for variables in vars.
  8. run: This is the main function that runs the program. The program is an array of actions, where an action can be a function, an object with a name of ‘get’ (which pushes the result of calling the function onto the stack), or a value (which is pushed onto the stack).
  9. showStack: This function logs the current state of the stack and the last object returned.
  10. _: This creates a new Proxy object that allows arbitrary methods to be called on the last object returned by the program.

The run function is then called with a list of actions to perform. The actions include creating a div and canvas element, appending them to the body of the document, setting their properties, and drawing on the canvas. It also creates an object with a name and age, converts it to JSON, and stores it in a variable. Finally, it shows the state of the stack and logs all the variables.

Little Trading Program

  1. let id = 0;
  2. function person(startValue = 100) {
  3.   const props = {
  4.     id: id++, 
  5.     el: document.body.appendChild(
  6.       Object.assign(
  7.         document.createElement`b`, { 
  8.           innerText: startValue,
  9.           style: 'padding: 1em'
  10.       })
  11.     ),
  12.     value: startValue,
  13.     trade(other) {
  14.       if (props.value <= 0) return;
  15.       other.value += 1;
  16.       props.value -= 1;
  17.       other.el.innerText = other.value
  18.       props.el.innerText = props.value
  19.     }
  20.   }
  21.   return props
  22. }
  23.  
  24.  
  25. const NUM = 4
  26. const people = Array.from({ length: NUM }, person)
  27.  
  28. setInterval(() => {
  29.   const a = people[~~(Math.random() * people.length)]
  30.   const b = people[~~(Math.random() * people.length)]
  31.  
  32.   if (a.id != b.id) a.trade(b)
  33. }, 1)

4 “people” randomly trade…

I asked Chat GPT to describe this… funny response…

The trading program you provided is essentially an example of a random walk or “drunkard’s walk” problem. In such problems, each individual (in this case, the ‘people’) make random moves (in this case, trades), and we observe the resulting dynamics of the system as a whole. In this particular case, each trade involves one individual giving one unit of value to another individual. Since each trade is random, there’s a possibility that over time, all the value might get concentrated in one individual. This isn’t guaranteed, but given enough time, it’s likely. You could compare it to the idea that given enough time, a group of monkeys randomly typing on a keyboard would eventually type out the complete works of Shakespeare. It’s also worth noting that this concentration of value becomes more likely because the individuals with no value left cannot trade anymore, so they are essentially removed from the trading pool. This progressively reduces the number of individuals that can gain value and increases the probability of the remaining individuals gaining more value. This is a simple model of economic transactions and it illustrates how wealth can concentrate over time even if all the transactions are fair and random. Of course, it’s a very simplified model, and real-world economics are much more complex.

Other Gates from NAND

  1. const nand = ([a, b]) => +!(a & b)
  2.  
  3. const not = ([a]) => nand([a, a])
  4.  
  5. const and = ([a, b]) => nand([nand([a, b]), nand([a, b])])
  6.  
  7. const or = ([a, b]) => nand([nand([a, a]), nand([b, b])])
  8.  
  9. const nor = ([a, b]) => 
  10.   nand([ 
  11.     nand([nand([a, a]), nand([b, b])]), 
  12.     nand([nand([a, a]), nand([b, b])])
  13.   ])
  14.  
  15. const xor = ([a, b]) =>
  16.   nand([
  17.     nand([a, nand([a, b])]),
  18.     nand([b, nand([a, b])])
  19.   ])
  20.  
  21. const xnor = ([a, b]) => 
  22.   nand([ 
  23.     nand([nand([a, a]), nand([b, b])]),
  24.     nand([a, b])
  25.   ])
  26.  
  27.  
  28. const inputs = [
  29.   [0, 0],
  30.   [0, 1],
  31.   [1, 0],
  32.   [1, 1]
  33. ]
  34.  
  35. const testGate = ({ gate, truth, result }) => console.log(
  36.   gate + ' matches truth? ', 
  37.   truth+'' === result+'' ? 
  38.     'yes :D' : `nope :( ${truth} ${result}`
  39. )
  40.  
  41. testGate({
  42.   gate: 'NAND',
  43.   truth: [1, 1, 1, 0],
  44.   result: inputs.map(nand)
  45. })
  46.  
  47. testGate({
  48.   gate: 'NOT',
  49.   truth: [0, 1],
  50.   result: [[1], [0]].map(not)
  51. })
  52.  
  53. testGate({
  54.   gate: 'AND',
  55.   truth: [0, 0, 0, 1],
  56.   result: inputs.map(and)
  57. })
  58.  
  59. testGate({
  60.   gate: 'OR',
  61.   truth: [0, 1, 1, 1],
  62.   result: inputs.map(or)
  63. })
  64.  
  65. testGate({
  66.   gate: 'NOR',
  67.   truth: [1, 0, 0, 0],
  68.   result: inputs.map(nor)
  69. })
  70.  
  71. testGate({
  72.   gate: 'XOR',
  73.   truth: [0, 1, 1, 0],
  74.   result: inputs.map(xor)
  75. })
  76.  
  77. testGate({
  78.   gate: 'XNOR',
  79.   truth: [1, 0, 0, 1],
  80.   result: inputs.map(xnor)
  81. })

Use NAND to create a bunch of other gates 😀 – I used this wikipedia article for reference

Multiplicative Persistence

  1. const multp = (val, count = 1, res) => 
  2.   (res = (val + '').split``
  3.     .reduce((a, b) => a * b, 1) + '').length > 1 ?
  4.       multp(res, count + 1) : count
  5.  
  6.  
  7. console.log('test:', multp(2678789))

Started watching this youtube video from numberphile and instantly made this half-golfed thing

Found this:

f=n=>[n,...n>9?f(eval([...n+''].join`*`)):[]]

By Arnauld over at codegolf.stackexchange

will definitely remember: [...n+'']

// golfed // hacks // humor // javascript // math
snippet.zone ~ 2021-24 /// {s/z}