summaryrefslogtreecommitdiff
path: root/src/ui/render.ts
diff options
context:
space:
mode:
Diffstat (limited to 'src/ui/render.ts')
-rw-r--r--src/ui/render.ts113
1 files changed, 113 insertions, 0 deletions
diff --git a/src/ui/render.ts b/src/ui/render.ts
new file mode 100644
index 0000000..95464dc
--- /dev/null
+++ b/src/ui/render.ts
@@ -0,0 +1,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);
+};