1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
|
import { SessionState, GAME_SIZE, PADDLE_WIDTH, GameState } from "../state";
import readline from "node:readline";
import {
clearTerminal,
getCurrentTerminalSize,
TERM_SIZE as RENDER_SIZE,
} from "./utils";
let lastTermSize: ReturnType<typeof getCurrentTerminalSize> | undefined;
export const renderGameState = (gameState: GameState): string[] => {
let rows: string[] = [];
for (let row = -1; row < GAME_SIZE.rows + 1; row++) {
// let rowOut: string = " ".repeat(marginCols);
let rowOut: string = " ";
if (row === -1) {
rowOut = rowOut.concat("--".repeat(GAME_SIZE.cols + 2));
} else {
for (let col = -1; col < GAME_SIZE.cols + 1; col++) {
if (col === -1 || col === GAME_SIZE.cols) {
rowOut = rowOut.concat("||");
} else {
const [paddleX, paddleY] = gameState.paddle.position;
const paddleXMin = paddleX;
const paddleXMax = paddleX + PADDLE_WIDTH;
const ballPositions = gameState.balls.map(({ position }) => position);
const brickPositions = gameState.bricks.map(
({ position }) => position
);
const hasPaddle =
col >= paddleXMin && col <= paddleXMax && row === paddleY;
const firstBall = ballPositions.find(
([ballX, ballY]) =>
col === Math.round(ballX) && row === Math.round(ballY)
);
const hasBrick = brickPositions.some(
([brickX, brickY]) => col === brickX && row === brickY
);
if (hasPaddle) {
rowOut = rowOut.concat("##");
} else if (firstBall) {
const fx = firstBall[0] - Math.round(firstBall[0]);
let chars;
if (fx < 0) {
chars = "O ";
} else {
chars = " O";
}
rowOut = rowOut.concat(chars);
} else if (hasBrick) {
rowOut = rowOut.concat("▒▒");
} else {
rowOut = rowOut.concat(" ");
}
}
}
}
rows.push(rowOut);
}
return rows;
};
export const renderState = (sessionState: SessionState) => {
const rl = new readline.promises.Readline(process.stdout, {
autoCommit: true,
});
rl.cursorTo(0, 0);
const termSize = getCurrentTerminalSize();
if (
lastTermSize &&
(lastTermSize.cols !== termSize.cols || lastTermSize.rows !== termSize.rows)
) {
clearTerminal();
}
lastTermSize = termSize;
if (termSize.cols < RENDER_SIZE.cols || termSize.rows < RENDER_SIZE.rows) {
process.stdout.write("Please increase the screen size");
return;
}
const marginCols = (termSize.cols - RENDER_SIZE.cols) / 2;
const marginRows = (termSize.rows - RENDER_SIZE.rows) / 2;
let allOut: string = "\n".repeat(marginRows);
const localDisplay = renderGameState(sessionState.localPlayerGameState);
const remoteDisplay = renderGameState(sessionState.remotePlayerGameState);
localDisplay.forEach(
(row, i) =>
(allOut = allOut
.concat(" ".repeat(marginCols / 2))
.concat(row)
.concat(" ".repeat(marginCols / 2))
.concat(remoteDisplay[i])
.concat("\n"))
);
process.stdout.write(allOut);
};
|