Flatten Array Golf
console.log([100, 200, [2], [3, 4, [2, 3, [5]], 100]]+'')
// outputs: "100,200,2,3,4,2,3,5,100"
Coercing a multidimensional array to a string flattens it and joins it with commas
console.log([100, 200, [2], [3, 4, [2, 3, [5]], 100]]+'')
// outputs: "100,200,2,3,4,2,3,5,100"
Coercing a multidimensional array to a string flattens it and joins it with commas
// some circles made of text
const cols = 50;
const rows = 50;
const sym = '.';
const body = document.body;
Object.assign(body.style, {
userSelect: 'none',
fontFamily: 'Courier, monospace',
position: 'fixed',
width: '100%',
height: '100%',
margin: 0
});
const el = body.appendChild(
document.createElement('span')
);
el.innerHTML = sym;
Object.assign(el.style, {
position: 'absolute',
left: '50%',
top: '50%',
wordWrap: 'break-word',
cursor: 'pointer'
});
const charSize = el.getBoundingClientRect().width;
const elWidth = charSize * cols;
el.style.display = 'block'
el.style.width = `${elWidth}px`;
const info = body.appendChild(
document.createElement('div')
);
Object.assign(info.style, {
background: '#fff',
padding: '4px',
position: 'absolute'
})
info.innerHTML = 'click/tap and hold for different fx';
function resize() {
const scl = Math.min(
1.23,
Math.min(innerWidth, innerHeight) / elWidth * .93
);
el.style.transform = `translate(-50%, -50%) scale(${scl}, ${scl * .55})`
}
addEventListener('resize', resize);
resize();
const size = cols * rows;
const pix = sym.repeat(size);
el.innerHTML = pix;
let cells = pix.split('')
const blank = cells.concat();
function setSym(x, y, col) {
const idx = x + y * cols;
if (cells[idx] != null) {
cells[idx] = col;
}
return setSym
}
const grad = '::;|0UU888NN';
function circ(shooter) {
let x = Math.round(Math.random() * cols);
let y = Math.round(Math.random() * rows);
const rad = Math.round(
shooter ? Math.random() * 2 :
Math.random() * Math.random() * 13 + 1
);
const sym = shooter ? '#' : grad.charAt(rad % grad.length);
let speed = rad / 10 + .1;
let dir = Math.random() * 2 - 1;
return () => {
drawCircle(x, y, rad, sym, shooter);
y += speed;
if (shooter) x += speed * 3 * dir;
if (y > rows + 10) y = -14;
}
}
const circs = [];
const NUM = 40;
for (let i = 0; i < NUM; i++) {
circs.push(circ(Math.random() > 0.5))
}
let down;
document.addEventListener('mousedown', () => {
down = true;
});
document.addEventListener('mouseup', () => {
down = false;
});
document.addEventListener('touchstart', () => {
down = true;
});
document.addEventListener('touchend', () => {
down = false;
});
let tweak;
let tweakChoice;
let tweakChance = 0.4;
const clear = () => cells = blank.concat();
function draw() {
if (!down) {
tweak = false;
tweakChoice = Math.random();
clear();
} else {
if (tweakChoice < tweakChance) {
tweak = true;
clear();
}
}
circs.forEach(circ => circ());
el.innerHTML = cells.join('');
}
// 60fps is too fast, so use 30ms interval
setInterval(draw, 30);
function hLine(xp, yp, w, col) {
for (let i = 0; i < w; i++) {
setSym(xp + i, yp, col);
}
return hLine;
}
// bresenham circle
function drawCircle(xp, yp, radius, sym = '@', isFilled) {
if (isFilled && tweak) sym = '';
xp = parseInt(xp, 10);
yp = parseInt(yp, 10);
radius = parseInt(radius, 10);
let balance = -radius,
xoff = 0,
yoff = radius;
while (xoff <= yoff) {
const p0 = xp - xoff;
const p1 = xp + xoff;
const p2 = yp + yoff;
const p3 = yp - yoff;
const p4 = yp + xoff;
const p5 = xp + yoff;
const p6 = xp - yoff;
const p7 = yp - xoff;
if (isFilled) {
const w0 = xoff + xoff;
const w1 = yoff + yoff;
hLine
(p0, yp + yoff, w0, sym)
(p0, yp - yoff, w0, sym)
(p6, yp + xoff, w1, sym)
(p6, yp - xoff, w1, sym);
} else {
setSym
(p1, p2, sym)
(p0, p2, sym)
(p0, p3, sym)
(p1, p3, sym)
(p5, p4, sym)
(p6, p4, sym)
(p6, p7, sym)
(p5, p7, sym);
}
// never been able to find the original
// source for the below condition
// more info here: https://actionsnippet.com/?p=492
if ((balance += xoff++ + xoff) >= 0) {
balance -= --yoff + yoff;
}
}
}
This is a bit of a longer snippet that uses the Bresenham circle drawing algorithm to draw some circles with text. I recommend looking at it with the fullscreen button.
I like to do this:
hLine
(p0, yp + yoff, w0, sym)
(p0, yp - yoff, w0, sym)
(p6, yp + xoff, w1, sym)
(p6, yp - xoff, w1, sym);
Make a function return itself, so that if you need to call it many times without having to repeat the function name. Because this isn’t a common style it is generally frowned upon, I’m always tempted to use it at work for some reason… maybe next April fools.
N = -1
S = 30
H = 90
h = 45
d = document
b = d.body
m = _ => d.createElement(_)
a = _ => b.appendChild(_)
with(a(
Object.assign(
m`canvas`,
{ width: 80, height: 120 }
)
).getContext`2d`
) {
a(m`br`)
L = (a, b, C, d) => {
moveTo(a, b)
lineTo(C, d)
}
M = _ => L(0, 0, 0, H)
O = _ => L(0, 0, S, 0)
W = _ => L(0, S, S, S)
T = _ => L(0, 0, S, S)
F = _ => L(0, S, S, 0)
X = _ => L(S, 0, S, S)
D = [
_ => {},
O, W, T, F,
[O,F],
X,
[O,X],
[W,X],
[O,W,X]
]
n = s => {
[...s].reverse().map((l, i) => {
save()
translate(S+9, h+9)
scale(N**i, N**~~(i/2))
translate(0, -h)
M() ;[D[l]].flat().map(x => x && x())
restore()
})
}
a(m`input`).oninput = e => {
clearRect(0, 0, 80, 120)
beginPath()
n(~~e.target.value+'')
stroke()
}
}
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.
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.
h='innerHTML'
C='children'
document.body[h]=`
<svg id=G width=99 viewBox="0 0 80 120"
style="stroke:black;fill:none;overflow:visible">
<g transform="translate(30,2)">
<path d="M0 0L 30 0"/>
<path d="M0 30L 30 30"/>
<path d="M0 0L 30 30"/>
<path d="M0 30L30 0"/>
<path d="M0 30L30 0 0 0"/>
<path d="M30 0L30 30"/>
<path d="M30 30L30 0 0 0"/>
<path d="M0 30L30 30 30 0"/>
<path d="M0 30L30 30 30 0 0 0"/>
</g>
<g transform=translate(30,2)scale(-1,1)></g>
<g transform=translate(30,92)scale(1,-1)></g>
<g transform=translate(30,92)scale(-1,-1)></g>
<path id=m d="M 30 2 L 30 92"/>
</svg>
<style>path:not(#m){opacity:0}</style><br>
<input id=I>`
c=G[C]
p=c[0][h]
n = s =>
[...s].reverse().map((l, i) =>
l-1>-1 && (c[i][C][l-1].style.opacity=1))
I.oninput = e => {
for(i=0;i<4;i++)c[i][h]=p
n(~~e.target.value+'')
}
((
d = document,
b = d.body,
canvas = b.appendChild(
d.createElement('canvas')
),
c = canvas.getContext('2d'),
r = _ => Math.random(),
map = [
[0, 0, 0, 0, 0, 2],
[1, 1, 0, 0, 0, 1],
[1, 1, 2, 0, 0, 0],
[2, 2, 0, 0, 0, 1],
[0, 1, 0, 0, 2, 0],
[2, 0, 0, 0, 2, 1],
],
mapW = map[0].length,
mapH = map.length,
cols = [, 'red', 'black'],
cell = (
x, y, idx,
col = cols[idx],
size = 30,
xp = x * size,
yp = y * size,
dir,
mv = f => {
map[y][x] = 0
f()
map[y][x] = idx
}
) => (move) => {
if (move) {
dir = ~~(r() * 4)
if (dir == 0 &&
x != 0 &&
map[y][x - 1] == 0) {
mv(_ => x--)
} else if (dir == 1 &&
x != mapW - 1 &&
map[y][x + 1] == 0) {
mv(_ => x++)
} else if (dir == 2 &&
y != 0 &&
map[y - 1][x] == 0) {
mv(_ => y--)
} else if (dir == 3 &&
y != mapH - 1 &&
map[y + 1][x] == 0
) {
mv(_ => y++)
}
}
xp += (x * size - xp) / 4
yp += (y * size - yp) / 4
c.fillStyle = col
c.fillRect(xp, yp, size, size)
c.strokeStyle = 'gray'
c.strokeRect(xp, yp, size, size)
},
cells = [],
w, h, idx, val, i, j,
draw = () => {
c.fillStyle = 'gray'
c.fillRect(0, 0, w, h)
idx = ~~(r() * mapH * mapW)
cells.forEach((cell, i) =>
cell(idx == i && r() < .3))
}
) => {
b.style.margin = 0
onresize = () => {
w = canvas.width = innerWidth
h = canvas.height = innerHeight
draw()
}
onresize()
for (i = 0; i < mapH; i++) {
for (j = 0; j < mapW; j++) {
val = map[i][j]
if (val != 0) cells.push(cell(j, i, val))
}
}
setInterval(draw, 16)
})()
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.
const arr = [
'one', 'two',
'three', 'four'
];
console.log(
arr.at(-1),
arr.at(-2),
arr.at(0)
);
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.