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

Cistercian Numerals (semi-golfed)

  1. N = -1
  2. S = 30
  3. H = 90
  4. h = 45
  5. d = document
  6. b = d.body
  7. m = _ => d.createElement(_)
  8. a = _ => b.appendChild(_)
  9.  
  10. with(a(
  11.     Object.assign(
  12.       m`canvas`,
  13.       { width: 80, height: 120 }
  14.     )
  15.   ).getContext`2d`
  16. ) { 
  17.   a(m`br`)
  18.  
  19.   L = (a, b, C, d) => {
  20.     moveTo(a, b)
  21.     lineTo(C, d)
  22.   }
  23.  
  24.   M = _ => L(0, 0, 0, H)
  25.   O = _ => L(0, 0, S, 0)
  26.   W = _ => L(0, S, S, S)
  27.   T = _ => L(0, 0, S, S)
  28.   F = _ => L(0, S, S, 0)
  29.   X = _ => L(S, 0, S, S)
  30.  
  31.   D = [
  32.     _ => {},
  33.     O, W, T, F, 
  34.     [O,F],
  35.     X,
  36.     [O,X],
  37.     [W,X],
  38.     [O,W,X]
  39.   ]
  40.  
  41.   n = s => {
  42.     [...s].reverse().map((l, i) => {
  43.       save()
  44.       translate(S+9, h+9)
  45.       scale(N**i, N**~~(i/2))
  46.       translate(0, -h)
  47.       M() ;[D[l]].flat().map(x => x && x())
  48.       restore()
  49.     })
  50.   }
  51.  
  52.   a(m`input`).oninput = e => {
  53.     clearRect(0, 0, 80, 120)
  54.     beginPath()
  55.     n(~~e.target.value+'')
  56.     stroke()
  57.   }
  58. }

Type any number from 0-9999 into the input field and see the corresponding Cistercian Numeral. This snippet is partially golfed, I left the canvas commands intact to make things a bit easier to understand.

Simpler SVG Version

This one hardcodes all numbers 0-9 as paths, unlike the canvas version which only defines 1,2,3,4 and 6 as paths and then combines them to create 5,7,8 and 9.

  1. h='innerHTML'
  2. C='children'
  3.  
  4. document.body[h]=`
  5. <svg id=G width=99 viewBox="0 0 80 120" 
  6.   style="stroke:black;fill:none;overflow:visible">
  7.   <g transform="translate(30,2)">
  8.     <path d="M0 0L 30 0"/>
  9.     <path d="M0 30L 30 30"/>
  10.     <path d="M0 0L 30 30"/>
  11.     <path d="M0 30L30 0"/>
  12.     <path d="M0 30L30 0 0 0"/>
  13.     <path d="M30 0L30 30"/>
  14.     <path d="M30 30L30 0 0 0"/>
  15.     <path d="M0 30L30 30 30 0"/>
  16.     <path d="M0 30L30 30 30 0 0 0"/>
  17.   </g>
  18.   <g transform=translate(30,2)scale(-1,1)></g>
  19.   <g transform=translate(30,92)scale(1,-1)></g>
  20.   <g transform=translate(30,92)scale(-1,-1)></g>
  21.   <path id=m d="M 30 2 L 30 92"/>
  22. </svg>
  23. <style>path:not(#m){opacity:0}</style><br>
  24. <input id=I>`
  25.  
  26. c=G[C]
  27. p=c[0][h] 
  28.  
  29. n = s => 
  30.   [...s].reverse().map((l, i) => 
  31.    l-1>-1 && (c[i][C][l-1].style.opacity=1))
  32.  
  33. I.oninput = e => {
  34.   for(i=0;i<4;i++)c[i][h]=p
  35.   n(~~e.target.value+'')
  36. }

Array Based Collision Cells

  1. ((
  2.   d = document,
  3.   b = d.body,
  4.   canvas = b.appendChild(
  5.     d.createElement('canvas')
  6.   ),
  7.   c = canvas.getContext('2d'),
  8.   r = _ => Math.random(),
  9.   map = [
  10.     [0, 0, 0, 0, 0, 2],
  11.     [1, 1, 0, 0, 0, 1],
  12.     [1, 1, 2, 0, 0, 0],
  13.     [2, 2, 0, 0, 0, 1],
  14.     [0, 1, 0, 0, 2, 0],
  15.     [2, 0, 0, 0, 2, 1],
  16.   ],
  17.   mapW = map[0].length,
  18.   mapH = map.length,
  19.   cols = [, 'red', 'black'],
  20.   cell = (
  21.     x, y, idx,
  22.     col = cols[idx],
  23.     size = 30,
  24.     xp = x * size,
  25.     yp = y * size,
  26.     dir, 
  27.     mv = f => {
  28.       map[y][x] = 0
  29.       f()
  30.       map[y][x] = idx
  31.     }
  32.   ) => (move) => {
  33.     if (move) {
  34.       dir = ~~(r() * 4)
  35.       if (dir == 0 &&
  36.         x != 0 &&
  37.         map[y][x - 1] == 0) {
  38.         mv(_ => x--)
  39.       } else if (dir == 1 &&
  40.         x != mapW - 1 &&
  41.         map[y][x + 1] == 0) {
  42.         mv(_ => x++)
  43.       } else if (dir == 2 &&
  44.         y != 0 &&
  45.         map[y - 1][x] == 0) {
  46.         mv(_ => y--)
  47.       } else if (dir == 3 &&
  48.         y != mapH - 1 &&
  49.         map[y + 1][x] == 0
  50.       ) {
  51.         mv(_ => y++)
  52.       }
  53.     }
  54.  
  55.     xp += (x * size - xp) / 4
  56.     yp += (y * size - yp) / 4
  57.     c.fillStyle = col
  58.     c.fillRect(xp, yp, size, size)
  59.     c.strokeStyle = 'gray'
  60.     c.strokeRect(xp, yp, size, size)
  61.   },
  62.   cells = [],
  63.   w, h, idx, val, i, j,
  64.   draw = () => {
  65.     c.fillStyle = 'gray'
  66.     c.fillRect(0, 0, w, h)
  67.  
  68.     idx = ~~(r() * mapH * mapW)
  69.  
  70.     cells.forEach((cell, i) =>
  71.       cell(idx == i && r() < .3))
  72.   }
  73. ) => {
  74.   b.style.margin = 0
  75.  
  76.   onresize = () => {
  77.     w = canvas.width = innerWidth
  78.     h = canvas.height = innerHeight
  79.     draw()
  80.   }
  81.   onresize()
  82.  
  83.   for (i = 0; i < mapH; i++) {
  84.     for (j = 0; j < mapW; j++) {
  85.       val = map[i][j]
  86.       if (val != 0) cells.push(cell(j, i, val)) 
  87.     }
  88.   }
  89.  
  90.   setInterval(draw, 16)
  91. })()

Array based avoid. I was about to port an old thing that was similar to this and then thought it would be more fun to speedcode it instead. The result is a slightly golfed version of this old thing.

Array.at Negative Indices

  1. const arr = [
  2.   'one', 'two', 
  3.   'three', 'four'
  4. ];
  5. console.log(
  6.   arr.at(-1), 
  7.   arr.at(-2), 
  8.   arr.at(0)
  9. );

Array.at allows negative index values to be used to read elements from an array. I’ve seen this done using a Proxy in the past.

Golfed Min/Max

  1. Math.min(a,b)  // 13 chars
  2. a<b?a:b        //  7 chars
  3.  
  4. Math.max(a,b)
  5. a>b?a:b

Another small golfing gem from codegolf stackexchange. This isn’t immediately obvious, but cool to note when golfing.

It’s also worth mentioning that if your code is long enough, aliasing Math.min and/or Math.max may be shorter in the long run:

  1. m = Math.min
  2. Math.min(a,b)  // 13 chars
  3. a<b?a:b        //  7 chars
  4. m(a,b)         //  6 chars

Complementary HSL

  1. const a = document.body.appendChild(document.createElement('div')),
  2.       b = document.body.appendChild(document.createElement('div'));
  3. let degA = degB = 0;
  4.  
  5. const size = {
  6.   width: '100px',
  7.   height: '100px'
  8. };
  9. Object.assign(a.style, size);
  10. Object.assign(b.style, size);
  11.  
  12. function loop() {
  13.   degA += 1;
  14.   degB = degA + 180;
  15.   a.style.background = `hsl(${degA}deg, 100%, 50%)`;
  16.   b.style.background = `hsl(${degB}deg, 100%, 50%)`;
  17.   requestAnimationFrame(loop);
  18. }
  19. loop();

In HSL a hue difference of 180 degrees between two values will create a set of complimentary colors.

// animation // color // css // dom // javascript // tricks // ui
snippet.zone ~ 2021-24 /// {s/z}