moshcode 0.45.0 → 0.46.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "moshcode",
3
- "version": "0.45.0",
3
+ "version": "0.46.0",
4
4
  "type": "module",
5
5
  "description": "moshcode — a metal wrapper for coding engines and native UGig/CoinPay workflow CLIs, with OpenPRD and moshscript",
6
6
  "repository": {
@@ -4,6 +4,7 @@
4
4
  // decides the angle it leaves at, so the paddle is a steering wheel rather than
5
5
  // a wall. Without that you cannot dig a channel up the side of the wall, and
6
6
  // digging a channel is the entire reason anybody still plays this.
7
+ import { ballCell } from "./games-draw.mjs";
7
8
  import { acid, amber, bone, danger, rgb } from "./ui.mjs";
8
9
 
9
10
  export const WIDTH = 40;
@@ -197,7 +198,10 @@ export const BREAKOUT = {
197
198
  }
198
199
 
199
200
  for (let i = 0; i < PADDLE_W; i++) put(state.paddle + i, PADDLE_ROW, bone("▀"));
200
- put(Math.round(state.ball.x), Math.round(state.ball.y), state.stuck ? amber("●") : bone("●"));
201
+ // Drawn on half-rows, so the ball steps the same distance down the wall as
202
+ // it does across it. See games-draw.mjs.
203
+ const ball = ballCell(state.ball.x, state.ball.y);
204
+ put(ball.col, ball.row, (state.stuck ? amber : bone)(ball.glyph));
201
205
 
202
206
  return grid.map((row) => row.map((cell) => cell ?? " ").join(""));
203
207
  },
@@ -0,0 +1,50 @@
1
+ // Shared drawing for the things in the arcade that move on a diagonal.
2
+ //
3
+ // A terminal cell is about twice as tall as it is wide, so a board drawn one
4
+ // object per cell has half the resolution going down that it has going across.
5
+ // For a ball that is not a rounding detail, it is the whole reason the motion
6
+ // looks wrong. Two things fall out of it, and both of them read as bad physics
7
+ // rather than as a coarse grid:
8
+ //
9
+ // A step sideways moves the ball one pixel and a step down moves it two, so the
10
+ // same ball appears to travel faster down the board than across it, and a
11
+ // trajectory the maths has at forty-five degrees is drawn at sixty.
12
+ //
13
+ // Worse, the two steps keep their own schedules. Crossing into the next column
14
+ // and crossing into the next row almost never land on the same tick, so a ball
15
+ // going diagonally does not move diagonally — it hops sideways, then a tick or
16
+ // two later hops down. Measured on pong, the ball holds a cell for four ticks,
17
+ // four ticks, then two and two as a row change splits one of those spans. That
18
+ // 4-4-2-2 stutter, three or four times a second, is what is left of the jiggle
19
+ // after the clock was fixed.
20
+ //
21
+ // Half blocks fix both. `▀` and `▄` each fill half a cell, which is close to
22
+ // square, so the ball is drawn on a grid with the same pitch both ways: it
23
+ // steps the same distance whichever way it goes, a row is two steps rather than
24
+ // one, and the corner it turns is half as wide. It is also a better ball than
25
+ // `●` was — a square pixel moving on a square grid, rather than a round dot
26
+ // snapping between cells twice its own height apart.
27
+
28
+ /**
29
+ * Where to draw a ball whose true position is (x, y) in board coordinates.
30
+ *
31
+ * `y` is a row centre, so row r covers y from r - 0.5 up to r + 0.5: below the
32
+ * centre the ball is in the top half of the cell, at or above it the bottom.
33
+ * Returns the cell to write into and the half block to write there.
34
+ */
35
+ export function ballCell(x, y) {
36
+ const col = Math.round(x);
37
+ const row = Math.round(y);
38
+ return { col, row, glyph: y < row ? "▀" : "▄" };
39
+ }
40
+
41
+ /**
42
+ * The ball's position in half-rows — the unit it is actually drawn in.
43
+ *
44
+ * Only the tests use this, to assert that a step down the board is the same
45
+ * size as a step across it.
46
+ */
47
+ export const halfRow = (y) => {
48
+ const row = Math.round(y);
49
+ return y < row ? row * 2 : row * 2 + 1;
50
+ };
@@ -7,6 +7,7 @@
7
7
  // A flat return it will always get; one taken off the end of your paddle it will
8
8
  // not. That is the whole game, and it is why the angle off the paddle depends on
9
9
  // where the ball hit it.
10
+ import { ballCell } from "./games-draw.mjs";
10
11
  import { acid, bone, danger, dim } from "./ui.mjs";
11
12
 
12
13
  export const WIDTH = 44;
@@ -164,7 +165,10 @@ export const PONG = {
164
165
 
165
166
  for (const row of paddleRows(state.you)) put(YOU_COL, row, acid("█"));
166
167
  for (const row of paddleRows(state.them)) put(THEM_COL, row, danger("█"));
167
- put(Math.round(state.ball.x), Math.round(state.ball.y), bone("●"));
168
+ // Drawn on half-rows, so the ball steps the same distance up the table as
169
+ // it does across it. See games-draw.mjs.
170
+ const ball = ballCell(state.ball.x, state.ball.y);
171
+ put(ball.col, ball.row, bone(ball.glyph));
168
172
 
169
173
  return grid.map((row, y) => row.map((cell, x) => (
170
174
  // The net, which is only there so the middle of the table has a middle.