Skip to content

Commit 1124a75

Browse files
authored
[Example] Abacus (#206)
* Add abacus * Update PR numbers
1 parent eb69c41 commit 1124a75

1 file changed

Lines changed: 134 additions & 0 deletions

File tree

app/examples/abacus.recho.js

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,134 @@
1+
/**
2+
* @title Abacus
3+
* @author Bairui Su
4+
* @created 2026-01-01
5+
* @github pearmini
6+
* @pull_request 206
7+
* @thumbnail_start 40
8+
* @label Algorithm
9+
*/
10+
11+
/**
12+
* ============================================================================
13+
* = Abacus =
14+
* ============================================================================
15+
*
16+
* Learning classic Chinese abacus (5+2). An abacus can be considered a a way
17+
* of visualizing numbers.
18+
*
19+
* - Ref. https://www.youtube.com/watch?v=FTVXUG_PngE&t=15s
20+
*/
21+
22+
const n = recho.number(0, {min: 0, max: Infinity, step: 1});
23+
24+
//➜ n = 7
25+
//➜ ╔═════════════╗
26+
//➜ ║●●●●●●●●●●●●●║
27+
//➜ ║●●●●●●●●●●●● ║
28+
//➜ ║ ●║
29+
//➜ ║═════════════║
30+
//➜ ║ ●║
31+
//➜ ║ ●║
32+
//➜ ║●●●●●●●●●●●● ║
33+
//➜ ║●●●●●●●●●●●● ║
34+
//➜ ║●●●●●●●●●●●●●║
35+
//➜ ║●●●●●●●●●●●●●║
36+
//➜ ║●●●●●●●●●●●●●║
37+
//➜ ╚═════════════╝
38+
abacus(n);
39+
40+
const x = recho.interval(200);
41+
42+
//➜ n = 152
43+
//➜ ╔═════════════╗
44+
//➜ ║●●●●●●●●●●●●●║
45+
//➜ ║●●●●●●●●●●● ●║
46+
//➜ ║ ● ║
47+
//➜ ║═════════════║
48+
//➜ ║ ● ●║
49+
//➜ ║ ●║
50+
//➜ ║●●●●●●●●●● ● ║
51+
//➜ ║●●●●●●●●●●●● ║
52+
//➜ ║●●●●●●●●●●●●●║
53+
//➜ ║●●●●●●●●●●●●●║
54+
//➜ ║●●●●●●●●●●●●●║
55+
//➜ ╚═════════════╝
56+
abacus(x);
57+
58+
function abacus(x = 0) {
59+
const bits = (x + "").padStart(13, "0");
60+
const canvas = new Canvas(15, 13);
61+
canvas.frame(0, 0, 15, 13);
62+
canvas.lineY(4, 1, 13, "═");
63+
for (let i = 0; i < bits.length; i++) {
64+
const n = parseInt(bits[i]);
65+
let line = "";
66+
// prettier-ignore
67+
switch (n) {
68+
case 0: line = "1100011111"; break;
69+
case 1: line = "1101001111"; break;
70+
case 2: line = "1101100111"; break;
71+
case 3: line = "1101110111"; break;
72+
case 4: line = "1101111100"; break;
73+
case 5: line = "1010011111"; break;
74+
case 6: line = "1011001111"; break;
75+
case 7: line = "1011100111"; break;
76+
case 8: line = "1011110011"; break;
77+
case 9: line = "1011111001"; break;
78+
}
79+
line = line.slice(0, 3) + "x" + line.slice(3);
80+
for (let j = 0; j < line.length; j++) {
81+
const b = line[j];
82+
if (b === "x") continue;
83+
canvas.point(i + 1, j + 1, b === "1" ? "●" : " ");
84+
}
85+
}
86+
const styled = echo.set("compact", true).set("typeface", "Google Sans Code");
87+
styled("n = " + x + "\n" + canvas.toString());
88+
}
89+
90+
class Canvas {
91+
constructor(width, height) {
92+
this.width = width;
93+
this.height = height;
94+
this.clear();
95+
}
96+
frame(x, y, width, height) {
97+
this.lineY(y, x, x + width - 1, "═");
98+
this.lineY(y + height - 1, x, x + width - 1, "═");
99+
this.lineX(x, y, y + height - 1, "║");
100+
this.lineX(x + width - 1, y, y + height - 1, "║");
101+
this.point(x, y, "╔");
102+
this.point(x + width - 1, y, "╗");
103+
this.point(x, y + height - 1, "╚");
104+
this.point(x + width - 1, y + height - 1, "╝");
105+
}
106+
lineY(y, x, x1, ch) {
107+
for (let i = x; i <= x1; i++) {
108+
this.point(i, y, ch);
109+
}
110+
}
111+
lineX(x, y, y1, ch) {
112+
for (let i = y; i <= y1; i++) {
113+
this.point(x, i, ch);
114+
}
115+
}
116+
point(x, y, ch) {
117+
this.buffer[y * this.width + x] = ch;
118+
}
119+
clear() {
120+
this.buffer = d3.range(this.width * this.height).map(() => " ");
121+
}
122+
toString() {
123+
let output = "";
124+
for (let i = 0; i < this.height; i++) {
125+
for (let j = 0; j < this.width; j++) {
126+
output += this.buffer[i * this.width + j];
127+
}
128+
output += i === this.height - 1 ? "" : "\n";
129+
}
130+
return output;
131+
}
132+
}
133+
134+
const d3 = recho.require("d3");

0 commit comments

Comments
 (0)