如何为类似俄罗斯方块的游戏创建新的随机形状?

问题描述 投票:0回答:1

我希望你玩的时间越长,游戏中就会出现新的俄罗斯方块,但它不起作用,它会产生新的形状,但形状不居中,它们不连续(就像形状可能看起来像 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;
}

我尝试添加新的随机形状,这些形状应该看起来正常且居中,但它们被破坏且不居中,有时甚至被欺骗。

javascript arrays game-development tetris
1个回答
0
投票

我会在预处理阶段生成您需要的所有尺寸(1 到 6)的所有可能形状。这样做的好处是:

  1. 你只需要做一次这样的努力,并且
  2. 确保给定大小的所有不同形状都有相同的被选择概率变得很容易。当从头开始生成随机形状时,这是很难实现的。

形状的数量并不多,因此预先将它们存储在数组中很便宜。当我们消除通过旋转和/或移动形成的重复项时,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));
}

© www.soinside.com 2019 - 2024. All rights reserved.