Open7

CLI GAME 遊び: ぷよぷよ

mae616mae616

TechCommitでゲームを公開されている方のぷよぷよが面白かったのでCLIのJavaScriptで実装してみる遊び

まだ途中
https://github.com/mae616/puyo

参考になりそうなの

https://kiryuanzu.hatenablog.com/entry/2020/04/15/005934

https://www.npmjs.com/package/tetris?activeTab=code

下記のパッケージを入れるみたい
https://www.npmjs.com/package/clivas

https://www.npmjs.com/package/keypress

余談
https://www.npmjs.com/package/convas

というのも面白そう。もしかしたらこっち使った方がいいのかな?
まあ、一旦いいや。

https://www.npmjs.com/package/blessed

GitHub Copilotさんに勧められたこれも面白そう

https://qiita.com/toshi-toma/items/ea76b8894e7771d47e10

これも今度いろいろみたい

mae616mae616

パッケージを作る

mkdir puyo
cd puyo

npm init
; 全てEnter

touch index.js

npm install clivas
npm install keypress

; 確認
npm list
package.json
{
  "name": "puyo",
  "version": "1.0.0",
  "main": "index.js",
  "scripts": {
+    "start": "node index.js"
-    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "author": "",
  "license": "ISC",
  "description": "",
  "dependencies": {
    "clivas": "^0.2.0",
    "keypress": "^0.2.1"
  }
}
mae616mae616

2024/11/24 日 とりあえず何か描画して表示する

index.js
const clivas = require("clivas");
const keypress = require("keypress");

keypress(process.stdin);
const color = ["red", "green", "blue", "yellow", "cyan", "magenta", "white"];
const HEIGHT = 10;
const WIDTH = 5;

clivas.cursor(false);

const randomColor = () => color[Math.floor(Math.random() * color.length)];

const fields = new Array(WIDTH).fill("").map(() => new Array(HEIGHT).fill("."));

fields[2][0] = "red";
fields[2][1] = "green";

const draw = () => {
  clivas.clear();

  const fields2 = new Array(HEIGHT)
    .fill("")
    .map(() => new Array(WIDTH).fill("."));
  for (let column = 0; column < WIDTH; column++) {
    for (let row = 0; row < HEIGHT; row++) {
      fields2[row][column] = fields[column][row];
    }
  }

  clivas.line("{white:┌──────────┐}");
  for (const row of fields2) {
    clivas.write("{white:│}");
    for (const column of row) {
      if (column === ".") {
        clivas.write("{2}");
      } else {
        clivas.write(`{2+${column}:●}`);
      }
    }
    clivas.write("{white:│}\n");
  }
  clivas.line("{white:└──────────┘}");
};

draw();

process.stdin.on("keypress", (ch, key) => {
  if (key && key.ctrl && key.name === "c") {
    process.exit();
  }
  if (key.name === "left") {
    // move left
  }
  if (key.name === "right") {
    // move right
  }
  if (key.name === "down") {
    // move down
  }
  if (key.name === "up") {
    // rotate
  }
  draw();
});


動かないけど。

mae616mae616

とりあえず落としてみた

const clivas = require("clivas");
const keypress = require("keypress");

keypress(process.stdin);
process.stdin.setRawMode(true);
process.stdin.resume();

const color = ["red", "green", "blue", "yellow", "cyan", "magenta", "white"];
const HEIGHT = 10;
const WIDTH = 5;

clivas.cursor(false);
clivas.pin(HEIGHT);

const randomColor = () => color[Math.floor(Math.random() * color.length)];

const fields = new Array(WIDTH).fill("").map(() => new Array(HEIGHT).fill("."));

fields[2][0] = "red";
fields[2][1] = "green";

const draw = () => {
  const fields2 = new Array(HEIGHT)
    .fill("")
    .map(() => new Array(WIDTH).fill("."));
  for (let column = 0; column < WIDTH; column++) {
    for (let row = 0; row < HEIGHT; row++) {
      fields2[row][column] = fields[column][row];
    }
  }

  clivas.line("{white:┌──────────┐}");
  for (const row of fields2) {
    clivas.write("{white:│}");
    for (const column of row) {
      if (column === ".") {
        clivas.write("{2}");
      } else {
        clivas.write(`{2+${column}:●}`);
      }
    }
    clivas.write("{white:│}\n");
  }
  clivas.line("{white:└──────────┘}");
};

let flame = 0;
setInterval(function () {
  clivas.clear();
  fields[2].pop();
  fields[2].unshift(".");
  draw();
  flame++;
}, 800);

process.stdin.on("keypress", (ch, key) => {
  if (key && key.name === "return") {
    return;
  }
  if (key && key.ctrl && key.name === "c") {
    process.exit();
  }
  if (key.name === "left") {
    // move left
  }
  if (key.name === "right") {
    // move right
  }
  if (key.name === "down") {
    // move down
  }
  if (key.name === "up") {
    // rotate
  }
  draw();
});

今日は一旦ここまで。また明日。

mae616mae616

2024/12/01 日 とりあえず積んでみる

index.js
const clivas = require("clivas");
const keypress = require("keypress");

keypress(process.stdin);
process.stdin.setRawMode(true);
process.stdin.resume();

const color = ["red", "green", "blue", "yellow", "cyan", "magenta", "white"];
const HEIGHT = 10;
const WIDTH = 5;

clivas.cursor(false);
clivas.pin(HEIGHT);

const randomColor = () => color[Math.floor(Math.random() * color.length)];

let fields = new Array(WIDTH).fill("").map(() => new Array(HEIGHT).fill("."));

// 落ちる処理
const fall = (fields) => {
  for (let column = WIDTH - 1; column >= 0; column--) {
    for (let row = HEIGHT - 1; row >= 0; row--) {
      if (fields[column][row].startsWith("ACT:")) {
        if (
          row + 1 === HEIGHT ||
          (fields[column][row + 1] !== "." &&
            !fields[column][row + 1].startsWith("ACT:"))
        ) {
          // もう落ちれない ACT: を取り除く(一応の処理)
          fields[column][row] = fields[column][row].split(":")[1];
        } else if (
          row + 2 === HEIGHT ||
          (fields[column][row + 2] !== "." &&
            !fields[column][row + 2].startsWith("ACT:"))
        ) {
          // 1個落ちたあと、もう落ちれない ACT: を取り除く(通常時この処理に入る)
          fields[column][row + 1] = fields[column][row].split(":")[1];
          fields[column][row] = ".";
        } else {
          // まだ落ちれる
          fields[column][row + 1] = fields[column][row];
          fields[column][row] = ".";
        }
      }
    }
  }
  return fields;
};
const draw = () => {
  // すでに落ちているものがない場合
  if (!fields.some((column) => column.some((row) => row.startsWith("ACT:")))) {
    // 落とす処理
    fields[2][0] = `ACT:${randomColor()}`;
    fields[2][1] = `ACT:${randomColor()}`;
  } else {
    // 落ちる処理
    fields = fall(fields);
  }

  // 行と列を入れ替える処理
  const fields2 = new Array(HEIGHT)
    .fill("")
    .map(() => new Array(WIDTH).fill("."));
  for (let column = 0; column < WIDTH; column++) {
    for (let row = 0; row < HEIGHT; row++) {
      fields2[row][column] = fields[column][row];
    }
  }

  clivas.clear();
  clivas.line("{white:┌──────────┐}");
  for (const row of fields2) {
    clivas.write("{white:│}");
    for (const column of row) {
      if (column === ".") {
        clivas.write("{2}");
      } else if (column.startsWith("ACT:")) {
        clivas.write(`{2+${column.split(":")[1]}:●}`);
      } else {
        clivas.write(`{2+${column}:●}`);
      }
    }
    clivas.write("{white:│}\n");
  }
  clivas.line("{white:└──────────┘}");
};

let flame = 0;
setInterval(function () {
  draw();
  flame++;
}, 800);

process.stdin.on("keypress", (ch, key) => {
  if (key && key.name === "return") {
    return;
  }
  if (key && key.ctrl && key.name === "c") {
    process.exit();
  }
  if (key.name === "left") {
    // move left
  }
  if (key.name === "right") {
    // move right
  }
  if (key.name === "down") {
    // move down
  }
  if (key.name === "up") {
    // rotate
  }
  draw();
});

mae616mae616

2024/12/10 火 左右に移動させる

index.js
const clivas = require("clivas");
const keypress = require("keypress");

keypress(process.stdin);
process.stdin.setRawMode(true);
process.stdin.resume();

// ぷよの色たち
const color = ["red", "green", "blue", "yellow", "cyan", "magenta", "white"];
// フィールドの大きさ
const HEIGHT = 10;
const WIDTH = 5;
// enum key移動
const MOVE = Object.freeze({
  LEFT: {x: -1, y: 0, check: (x) => x > 0},
  RIGHT: {x: 1, y: 0, check: (x) => x < WIDTH - 1},
});

clivas.cursor(false);
clivas.pin(HEIGHT);

const randomColor = () => color[Math.floor(Math.random() * color.length)];

let fields = new Array(WIDTH).fill("").map(() => new Array(HEIGHT).fill("."));

// 移動処理
const keypressMove = (move) => {
  const isMoved = new Set();
  for (let column = WIDTH - 1; column >= 0; column--) {
    for (let row = HEIGHT - 1; row >= 0; row--) {
      if (fields[column][row].startsWith("ACT:")) {
        if (move.check(column)
          && !isMoved.has(`${column}:${row}`)
          && fields[column + move.x][row] === ".") {
          fields[column + move.x][row] = fields[column][row];
          fields[column][row] = ".";
          isMoved.add(`${column + move.x}:${row}`);
        }
      }
    }
  }
};

// 落ちる処理
const fall = (fields) => {
  for (let column = WIDTH - 1; column >= 0; column--) {
    for (let row = HEIGHT - 1; row >= 0; row--) {
      if (fields[column][row].startsWith("ACT:")) {
        if (
          row + 1 === HEIGHT ||
          (fields[column][row + 1] !== "." &&
            !fields[column][row + 1].startsWith("ACT:"))
        ) {
          // もう落ちれない ACT: を取り除く(一応の処理)
          fields[column][row] = fields[column][row].split(":")[1];
        } else if (
          row + 2 === HEIGHT ||
          (fields[column][row + 2] !== "." &&
            !fields[column][row + 2].startsWith("ACT:"))
        ) {
          // 1個落ちたあと、もう落ちれない ACT: を取り除く(通常時この処理に入る)
          fields[column][row + 1] = fields[column][row].split(":")[1];
          fields[column][row] = ".";
        } else {
          // まだ落ちれる
          fields[column][row + 1] = fields[column][row];
          fields[column][row] = ".";
        }
      }
    }
  }
  return fields;
};

const draw = (isFall) => {
  clivas.clear();

  if (isFall) {
    // すでに落ちているものがない場合
    if (
      !fields.some((column) => column.some((row) => row.startsWith("ACT:")))
    ) {
      // 落とす処理
      fields[2][0] = `ACT:${randomColor()}`;
      fields[2][1] = `ACT:${randomColor()}`;
    } else {
      // 落ちる処理
      fields = fall(fields);
    }
  }

  // 行と列を入れ替える
  const fields2 = new Array(HEIGHT)
    .fill("")
    .map(() => new Array(WIDTH).fill("."));
  for (let column = 0; column < WIDTH; column++) {
    for (let row = 0; row < HEIGHT; row++) {
      fields2[row][column] = fields[column][row];
    }
  }

  // 実描画
  clivas.line("{white:┌──────────┐}");
  for (const row of fields2) {
    clivas.write("{white:│}");
    for (const column of row) {
      if (column === ".") {
        clivas.write("{2}");
      } else if (column.startsWith("ACT:")) {
        clivas.write(`{2+${column.split(":")[1]}:●}`);
      } else {
        clivas.write(`{2+${column}:●}`);
      }
    }
    clivas.write("{white:│}\n");
  }
  clivas.line("{white:└──────────┘}");
};

let flame = 0;
setInterval(function () {
  draw(true);
  flame++;
}, 800);

process.stdin.on("keypress", (ch, key) => {
  if (key && key.name === "return") {
    return;
  }
  if (key && key.ctrl && key.name === "c") {
    process.exit();
  }
  if (key.name === "left") {
    // move left
    keypressMove(MOVE.LEFT);
  }
  if (key.name === "right") {
    // move right
    keypressMove(MOVE.RIGHT);
  }
  if (key.name === "down") {
    // move down
  }
  if (key.name === "up") {
    // rotate
  }
  draw(false);
});