Open7
CLI GAME 遊び: ぷよぷよ
TechCommitでゲームを公開されている方のぷよぷよが面白かったのでCLIのJavaScriptで実装してみる遊び
まだ途中
参考になりそうなの
下記のパッケージを入れるみたい
余談
というのも面白そう。もしかしたらこっち使った方がいいのかな?
まあ、一旦いいや。
GitHub Copilotさんに勧められたこれも面白そう
これも今度いろいろみたい
パッケージを作る
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"
}
}
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();
});
動かないけど。
とりあえず落としてみた
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();
});
今日は一旦ここまで。また明日。
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();
});
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);
});
2024/12/28 土
回す時の仕様。
常に一番最初に落ちてきた時の下が支点になって回るみたい。
○
●
●○
●
○
○●
参考にしたもの