俄罗斯方块游戏实现

AI 智能总结

简介

俄罗斯方块是史上最经典的益智游戏之一。玩家操控下落的七种方块,填满横行即可消除得分。游戏厅版本完整实现了标准方块旋转系统、消行判定、预览下一个方块和速度递增。

数据结构

棋盘

使用二维数组表示 10x20 的游戏区域:

1var COLS = 10, ROWS = 20;
2var board = [];
3for (var r = 0; r < ROWS; r++) {
4  board[r] = [];
5  for (var c = 0; c < COLS; c++) board[r][c] = 0; // 0 = 空, 1-7 = 不同方块颜色
6}

七种方块定义

每种方块用 4x4 矩阵表示其形状:

 1var PIECES = [
 2  // I 型
 3  [[0,0,0,0],[1,1,1,1],[0,0,0,0],[0,0,0,0]],
 4  // O 型
 5  [[1,1],[1,1]],
 6  // T 型
 7  [[0,1,0],[1,1,1],[0,0,0]],
 8  // S 型
 9  [[0,1,1],[1,1,0],[0,0,0]],
10  // Z 型
11  [[1,1,0],[0,1,1],[0,0,0]],
12  // J 型
13  [[1,0,0],[1,1,1],[0,0,0]],
14  // L 型
15  [[0,0,1],[1,1,1],[0,0,0]]
16];
17
18var PIECE_COLORS = [
19  '#00f0f0', // I - 青色
20  '#f0f000', // O - 黄色
21  '#a000f0', // T - 紫色
22  '#00f000', // S - 绿色
23  '#f00000', // Z - 红色
24  '#0000f0', // J - 蓝色
25  '#f0a000'  // L - 橙色
26];

当前方块状态

1var currentPiece = {
2  type: 0,       // 方块类型索引
3  shape: null,   // 当前形状矩阵(旋转后)
4  x: 3,          // 左上角 X 坐标
5  y: 0           // 左上角 Y 坐标
6};

核心算法

方块旋转

将方块矩阵顺时针旋转 90 度:

 1function rotate(shape) {
 2  var n = shape.length;
 3  var rotated = [];
 4  for (var r = 0; r < n; r++) {
 5    rotated[r] = [];
 6    for (var c = 0; c < n; c++) {
 7      rotated[r][c] = shape[n - 1 - c][r];
 8    }
 9  }
10  return rotated;
11}

旋转后检查是否与棋盘或边界碰撞,如果碰撞则尝试左右平移(墙踢):

 1function tryRotate() {
 2  var rotated = rotate(currentPiece.shape);
 3  var offsets = [0, -1, 1, -2, 2]; // 墙踢偏移量
 4  for (var i = 0; i < offsets.length; i++) {
 5    if (!collides(rotated, currentPiece.x + offsets[i], currentPiece.y)) {
 6      currentPiece.shape = rotated;
 7      currentPiece.x += offsets[i];
 8      return;
 9    }
10  }
11}

碰撞检测

检查方块是否与棋盘边界或已有方块重叠:

 1function collides(shape, px, py) {
 2  for (var r = 0; r < shape.length; r++)
 3    for (var c = 0; c < shape[r].length; c++) {
 4      if (!shape[r][c]) continue;
 5      var newX = px + c;
 6      var newY = py + r;
 7      if (newX < 0 || newX >= COLS || newY >= ROWS) return true;
 8      if (newY >= 0 && board[newY][newX]) return true;
 9    }
10  return false;
11}

方块锁定与消行

方块落到底部后锁定到棋盘,然后检查消行:

 1function lockPiece() {
 2  // 将方块写入棋盘
 3  for (var r = 0; r < currentPiece.shape.length; r++)
 4    for (var c = 0; c < currentPiece.shape[r].length; c++) {
 5      if (!currentPiece.shape[r][c]) continue;
 6      var y = currentPiece.y + r;
 7      var x = currentPiece.x + c;
 8      if (y >= 0) board[y][x] = currentPiece.type + 1;
 9    }
10  clearLines();
11  spawnPiece();
12}
13
14function clearLines() {
15  var linesCleared = 0;
16  for (var r = ROWS - 1; r >= 0; r--) {
17    if (board[r].every(function(cell) { return cell !== 0; })) {
18      // 移除该行,上方所有行下移
19      board.splice(r, 1);
20      board.unshift(new Array(COLS).fill(0));
21      linesCleared++;
22      r++; // 重新检查当前行
23    }
24  }
25  if (linesCleared > 0) {
26    score += [0, 100, 300, 500, 800][linesCleared];
27  }
28}

预览下一个方块

生成随机方块预览,让玩家提前规划:

 1var nextPiece = Math.floor(Math.random() * PIECES.length);
 2
 3function spawnPiece() {
 4  currentPiece.type = nextPiece;
 5  currentPiece.shape = PIECES[currentPiece.type].map(function(row) { return row.slice(); });
 6  currentPiece.x = Math.floor((COLS - currentPiece.shape[0].length) / 2);
 7  currentPiece.y = 0;
 8  nextPiece = Math.floor(Math.random() * PIECES.length);
 9  renderNextPiece();
10  if (collides(currentPiece.shape, currentPiece.x, currentPiece.y)) {
11    gameOver();
12  }
13}

Canvas 渲染

绘制棋盘

 1function drawBoard() {
 2  for (var r = 0; r < ROWS; r++)
 3    for (var c = 0; c < COLS; c++) {
 4      var x = c * CELL_SIZE, y = r * CELL_SIZE;
 5      if (board[r][c]) {
 6        ctx.fillStyle = PIECE_COLORS[board[r][c] - 1];
 7        ctx.fillRect(x + 1, y + 1, CELL_SIZE - 2, CELL_SIZE - 2);
 8        // 高光效果
 9        ctx.fillStyle = 'rgba(255,255,255,0.2)';
10        ctx.fillRect(x + 1, y + 1, CELL_SIZE - 2, 3);
11      } else {
12        ctx.fillStyle = '#1a1a2e';
13        ctx.fillRect(x, y, CELL_SIZE, CELL_SIZE);
14      }
15    }
16}

绘制当前方块

 1function drawCurrentPiece() {
 2  var shape = currentPiece.shape;
 3  var color = PIECE_COLORS[currentPiece.type];
 4  for (var r = 0; r < shape.length; r++)
 5    for (var c = 0; c < shape[r].length; c++) {
 6      if (!shape[r][c]) continue;
 7      var x = (currentPiece.x + c) * CELL_SIZE;
 8      var y = (currentPiece.y + r) * CELL_SIZE;
 9      if (y < 0) continue; // 超出顶部不绘制
10      ctx.fillStyle = color;
11      ctx.fillRect(x + 1, y + 1, CELL_SIZE - 2, CELL_SIZE - 2);
12      ctx.fillStyle = 'rgba(255,255,255,0.2)';
13      ctx.fillRect(x + 1, y + 1, CELL_SIZE - 2, 3);
14    }
15}

操作控制

  • ← →:左右移动
  • :软降(加速下落)
  • :旋转
  • 空格:硬降(直接落到底部)
 1document.addEventListener('keydown', function(e) {
 2  if (!isActive()) return;
 3  switch(e.key) {
 4    case 'ArrowLeft':  movePiece(-1, 0); break;
 5    case 'ArrowRight': movePiece(1, 0);  break;
 6    case 'ArrowDown':  movePiece(0, 1);  break;
 7    case 'ArrowUp':    tryRotate();      break;
 8    case ' ':         hardDrop();       break;
 9  }
10  e.preventDefault();
11});

速度递增

随分数提高,方块下落速度加快:

1function getDropInterval() {
2  if (score >= 2000) return 100;
3  if (score >= 1500) return 200;
4  if (score >= 1000) return 300;
5  if (score >= 500) return 500;
6  return 800;
7}

总结

俄罗斯方块的核心在于方块旋转矩阵变换、墙踢偏移补偿和消行逻辑。Canvas 渲染提供了灵活的绘制控制,预览下一个方块增加了策略性。速度递增机制让游戏难度曲线平滑上升。

留言评论

期待你的想法

评论加载中