summaryrefslogtreecommitdiff
path: root/src/ui/render.ts
blob: 95464dc3a99cac5823e9f47d807f2f953f09e43f (plain)
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);
};