我希望你玩的时间越长,游戏中就会出现新的俄罗斯方块,但它不起作用,它会产生新的形状,但形状不居中,它们不连续(就像形状可能看起来像 2 个形状)因为它们没有接触),有时它会添加一个已经是 Shapes 数组一部分的形状。每个形状的最大正方形为 12,并且必须适合 4x4 的空间 到目前为止,这是我的代码:
function randomShape(score) {
const maxSize = 12; // Max number of squares per shape
const centerOffset = 1; // Offset to center the shape
const maxComplexity = Math.min(Math.floor(score / 100), 6); // Max complexity based on score
let shape;
do {
shape = Array.from({ length: 4 }, () => Array(4).fill(0)); // Create an empty 4x4 shape
const size = Math.max(1, Math.floor(Math.random() * (maxComplexity + 1))); // Shape size varies based on complexity
let squaresToPlace = Math.min(size, maxSize); // Limit the squares placed to maxSize
// Fill the shape with random positions of 1s based on size
while (squaresToPlace > 0) {
const x = Math.floor(Math.random() * 4);
const y = Math.floor(Math.random() * 4);
// Only place a 1 if the spot is empty
if (shape[y][x] === 0) {
shape[y][x] = 1;
squaresToPlace--;
}
}
} while (!shape.flat(5).includes(1)); // Retry if the shape is empty
// Centering logic
const centeredShape = Array.from({ length: 4 }, () => Array(4).fill(0));
// Get the positions of all filled cells
const filledPositions = [];
for (let r = 0; r < 4; r++) {
for (let c = 0; c < 4; c++) {
if (shape[r][c] === 1) {
filledPositions.push({ r, c });
}
}
}
let amount = 0;
shape.flat(5).forEach((s) => {
if (s === 1) amount++;
});
if (amount === 1) {
return [
[0, 0, 0, 0],
[0, 0, 0, 0],
[0, 0, 1, 0],
[0, 0, 0, 0],
];
}
// Determine how to center the shape
const minRow = Math.min(...filledPositions.map((p) => p.r));
const maxRow = Math.max(...filledPositions.map((p) => p.r));
const minCol = Math.min(...filledPositions.map((p) => p.c));
const maxCol = Math.max(...filledPositions.map((p) => p.c));
// Calculate the offsets needed to center the shape
const height = maxRow - minRow + 1;
const width = maxCol - minCol + 1;
// Calculate vertical and horizontal offsets
const rowOffset = Math.floor((4 - height) / 2); // Center vertically
const colOffset = Math.floor((4 - width) / 2); // Center horizontally
// Place the shape in the centered position
filledPositions.forEach(({ r, c }) => {
// Ensure we're placing the piece within bounds
const newRow = r + rowOffset;
const newCol = c + colOffset;
if (newRow >= 0 && newRow < 4 && newCol >= 0 && newCol < 4) {
centeredShape[newRow][newCol] = 1;
}
});
return centeredShape;
}
我尝试添加新的随机形状,这些形状应该看起来正常且居中,但它们被破坏且不居中,有时甚至被欺骗。
我会在预处理阶段生成您需要的所有尺寸(1 到 6)的所有可能形状。这样做的好处是:
形状的数量并不多,因此预先将它们存储在数组中很便宜。当我们消除通过旋转和/或移动形成的重复项时,4x4 网格中有 50 个大小为 6 的不同形状。
这里是用形状 (4x4) 填充六个数组的代码:每个尺寸一个(1 到 6)。该代码片段还打印了找到的 50 个尺寸为 6 的形状:
function getShapesOfSize(size) {
if (!size) return [];
const shapes = new Set;
const visited = new Set;
// Local functions:
function align(shape) {
while (!(shape & 0xF)) shape >>= 4;
while (!(shape & 0x1111)) shape >>= 1;
return shape;
}
const lineToArray = (shape) =>
[shape & 1, (shape >> 1) & 1, (shape >> 2) & 1, (shape >> 3) & 1];
function bitCount(n) {
let count = 0;
while (n) {
n &= n - 1;
count++;
}
return count;
}
function toCenterGrid(shape) {
shape = align(shape);
if (!(shape & 0xF000) && bitCount(shape & 0xF) > bitCount(shape & 0x0F00)) {
shape <<= 4
}
if (!(shape & 0x8888) && bitCount(shape & 0x1111) > bitCount(shape & 0x4444)) {
shape <<= 1;
}
return [
lineToArray(shape),
lineToArray(shape >> 4),
lineToArray(shape >> 8),
lineToArray(shape >> 12)
]
}
function rotateLine(shape) {
shape &= 0xF;
return ((shape << 3) | (shape << 6) | (shape << 9) | (shape << 12)) & 0x8888;
}
const rotate = (shape) => align(
rotateLine(shape )
| (rotateLine(shape >> 4) >> 1)
| (rotateLine(shape >> 8) >> 2)
| (rotateLine(shape >>12) >> 3)
);
function recur(shape, curSize) {
if (visited.has(shape)) return;
visited.add(shape);
if (curSize === size) {
// Get the "minimum" of (equivalent) rotations:
const rot90 = rotate(shape);
const rot180 = rotate(rot90);
shapes.add(Math.min(align(shape), rot90, rot180, rotate(rot180)));
return;
}
// Candidates must border on the shape
let candidates =
((shape & 0x7777) << 1)
| ((shape & 0xEEEE) >> 1)
| ((shape & 0x0FFF) << 4)
| ((shape & 0xFFF0) >> 4);
while (candidates) {
const bit = candidates & -candidates; // select
candidates ^= bit; // clear bit
recur(shape | bit, curSize + 1);
}
}
recur(1, 1); // Start with corner pixel
recur(2, 1); // Start with neighbor of corner pixel
return Array.from(shapes, toCenterGrid);
}
// Helper to visualise a tetris shape
const shapeStr = (shape) => shape.map(line => line.join("")).join("\n")
.replaceAll("0", ". ").replaceAll("1", "# ");
// Preprocessing: get all possible shapes, for all sizes between 1 and 6:
const shapesPerSize = Array.from({length: 7}, (_, i) => getShapesOfSize(i));
// This is the function you would use to pick a random shape
function randomShape(size) {
const shapes = shapesPerSize[size];
return shapes[Math.floor(Math.random() * shapes.length)];
}
// Demo
const size = 6; // Choose the size you want
const shapes = shapesPerSize[size];
console.log(`Here are all ${shapes.length} shapes of size ${size}:`);
for (const shape of shapes) {
console.log(shapeStr(shape));
}