用二维数组表示俄罗斯方块游戏区域:主数组(如20×10)仅存已锁定砖块,活动方块独立用4×4模板+坐标运算,通过转置+翻转实现旋转,消行需自底向上复制并排除临时占位干扰,渲染时缓存字符串、只更新变化行。

怎么用二维数组表示俄罗斯方块的当前游戏区域
游戏区域本质是个固定大小的网格,比如 20×10(20 行高、10 列宽),每一格存一个状态:空(0)或已被某颜色方块占据(如 1~7)。别用对象或嵌套对象模拟格子——性能差、判断消行时遍历慢。
常见错误是把“下落中的方块”也硬塞进主数组里实时覆盖。这样会导致旋转、碰撞检测逻辑和消行逻辑互相污染。正确做法是:主数组只存“已锁定”的砖块;活动方块单独用一个 4×4 小数组(或坐标+形状模板)计算位置和旋转,每次移动前先做碰撞预检(检查目标位置在主数组中是否全为空)。
-
board[y][x]下标顺序建议是[row][col],和屏幕 y 向下增长一致,避免后期坐标翻转混乱 - 初始化时用
Array(20).fill().map(() => Array(10).fill(0)),别用Array(20).fill(Array(10).fill(0))——后者所有行引用同一数组,一改全改 - 边界检查必须包含:x 是否在
[0, 9]、y 是否 ≥0(允许短暂负 y 表示方块刚入场),且不能超出board行数上限(比如 y ≥ 20 就算触底)
方块旋转怎么靠二维数组转置+翻转实现
标准 Tetris 的 I、O、T、S、Z、J、L 七种方块,每种最多 4 种朝向。硬编码每个朝向的坐标偏移太容易出错。更稳的方式是:为每种方块定义一个基准 4×4 模板(值为 1 表示有砖,0 表示空),然后用矩阵转置 + 水平/垂直翻转生成新朝向。
例如,对模板 shape 顺时针旋转 90°:先转置(shape[i][j] → newShape[j][i]),再水平翻转(每行 reverse)。注意:转置后尺寸可能变化,要确保结果仍是 4×4,否则碰撞检测会越界。
- O 方块旋转前后完全一样,可直接跳过变换,省点 CPU
- 有些方块(如 I)旋转后“视觉中心”偏移,需额外加偏移补偿(比如右旋后整体左移 1 列),否则看起来像瞬移
- 不要在每次按键都重新生成旋转后数组——缓存 4 个朝向的数组,切换朝向只是换引用
消行逻辑为什么不能只看“某一行全非零”
表面看,只要 board[row].every(cell => cell !== 0) 就能消行。但实际运行中常出现“明明填满了却没消”或“消了不该消的行”,根本原因是:你没区分“临时填充”和“真实砖块”。比如旋转时临时写入、或者预判下落位置时污染了原数组。
另一个坑是消行后“上层下落”的实现方式。如果用 board.splice(row, 1) 再 unshift 空行,会破坏数组索引连续性,导致正在下落的方块 y 坐标失效。必须用“自底向上复制”:从倒数第二行开始,把上面一行拷下来,最顶上补空行。
- 消行判定前务必确认该行所有格子都是“已锁定砖块”,排除活动方块临时占位干扰
- 一次下落可能触发多行连消,要一次性扫描全部行,标记所有满行,再统一处理——避免边消边移导致中间行被跳过
- 消行后重绘前,记得重置活动方块的
y坐标:它可能正卡在刚被清空的行里,不调整就会直接掉穿
控制台渲染时字符对齐和刷新抖动怎么解决
用 console.log 打印二维数组,天然换行,但中文字符、全角符号、不同终端字体宽度不一致,会导致列错位。别用空格对齐,改用制表符 \t 或固定宽度 Unicode 字符(如 █ 宽度稳定)。
更关键的是刷新频率:每帧都 console.clear() + 全量重绘,肉眼可见闪烁。应只更新变化的行。比如只重绘活动方块新旧位置、消行区域、分数栏——其余行复用上一帧内容。
- 用
process.stdout.cursorTo(0, 0)(Node.js)或 ANSI 转义序列\x1b[H回到顶部,比clear更轻量 - 不要在每帧都调用
console.log(board.map(row => row.join('')).join('\n')),提前把 board 渲染成字符串数组缓存,只 diff 变化行 - 终端默认行高 ≠ 字符高,
█比■在多数等宽字体里更稳;避免用□这类空心符号,小字号下易看不清









