2020-04-28
The fictional D'ni numeral system that players must deduce in Riven is a base-25 positional system, with the twist that its 25 digits are constructed using an unrelated base-5 scheme in which the would-be fives place is orthogonally overlaid to form a single glyph:
0 | 1 | 2 | 3 | 4 | |
---|---|---|---|---|---|
0 | |||||
5 | |||||
10 | |||||
15 | |||||
20 |
The number 241, for example, is written as
25¹ + 25⁰
25¹(5¹ + 5⁰ ) + 25⁰(5¹ + 5⁰ )
25¹(5¹1 + 5⁰4) + 25⁰(5¹3 + 5⁰1)
241
Given the amount of symmetry in the notation I thought it would be fun to generate these procedurally. The following registers a custom HTML element that renders as an inline D'ni numeral:
const strokes = [
"M 16 20 h 4",
"M 18 2 V 38",
"M -0.5 2 C 12 10 12 30 -0.5 38",
"M 18 2 L 0 20 m 0 0 L 18 38",
"M 36 11 H 18 V 38",
];
define("dni-numeral", {
value: 0,
content: ({ value }) => {
const digits = [...iterDigits(value, 25)];
const width = 12 + 36 * digits.length;
return html`<svg viewBox=${`0 0 ${width} 40`} style="height: 1em;">
<g fill="none" stroke="currentColor" stroke-width="3">
${svg`<path d="M 0 2 H ${width} M 0 38 H ${width} M 6 2 V 38 ${
digits.map((_, i) => `M ${6 + 36 * (1 + i)} 2 V 38`).join(" ")
}" />`}
${digits.map((d) => iterDigits(d, 5)).map(([p1, p5], i) =>
svg`<g transform="translate(${width - 6 - 36 * (i + 1)})">
${(p1 !== 0 || !p5) && svg`<path d=${strokes[p1]} />`}
${p5 && svg`<path d=${strokes[p5]} transform="rotate(-90)" />`}
</g>`)}
</g>
</svg>`;
},
});
Using it is as simple as:
Preheat the oven to <dni-numeral value="350"></dni-numeral> degrees.
This opens the door to such fun things as a D'ni numeral clock—
—or an interactive converter: