You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

俄罗斯方块旋转函数修改原始形状对象问题求助

问题分析:俄罗斯方块旋转时修改原始方块形状的原因及解决办法

嘿,我一眼就看出问题出在哪了——你遇到的是浅拷贝导致的引用共享问题!

为什么旋转会修改原始pieces对象?

你在choosePiece里用了slice(0)来拷贝方块数组,还有rotate函数里也用了grid.slice(0),但这俩都是浅拷贝。对于二维数组来说,slice(0)只会复制外层数组的结构,内层的每个子数组还是和原始pieces里的子数组指向同一个内存地址。

举个例子:当你拿到pieces["I"]并执行.slice(0),你得到的是一个新的外层数组,但里面的每一行[0,1,0,0]还是原始数组的引用。所以当rotate函数里交换piece[i][j]和piece[j][i]的时候,你其实是在修改原始pieces里的内层数组元素!

另外,你用了Object.freeze(pieces),但这个方法是浅冻结,它只会冻结外层对象的属性,内层的数组依然可以被修改,所以原始方块的形状还是会被改动。

解决办法:用深拷贝彻底切断引用

要解决这个问题,我们需要对二维数组进行深拷贝,确保每个层级的数组都是新的副本,和原始数据完全无关。

1. 修改choosePiece函数

把浅拷贝换成深拷贝,对每个子数组也进行拷贝:

function choosePiece() {
  const choices = "OISZLJT";
  const chosenLetter = choices[~~(Math.random() * choices.length)];
  // 深拷贝二维数组:遍历每一行并拷贝
  const chosenPiece = pieces[chosenLetter].map(row => row.slice());
  return chosenPiece;
}

2. 修改rotate函数

同样,在旋转前先深拷贝输入的方块数组,避免修改传入的原数组:

function rotate(grid, num) {
  // 深拷贝输入的grid,确保操作的是副本
  const piece = grid.map(row => row.slice());
  const size = piece.length;
  
  // 矩阵转置逻辑不变
  for (let i = 0; i < size; i++) {
    for (let j = i; j < size; j++) {
      [piece[i][j], piece[j][i]] = [piece[j][i], piece[i][j]];
    }
  }
  
  // 根据旋转方向反转数组
  return num > 0 ? piece.map(row => row.reverse()) : piece.reverse();
}

为什么这样有效?

map(row => row.slice())会创建一个全新的外层数组,并且每个内层数组都是原行的拷贝,这样你后续对这个副本的任何修改(比如旋转)都不会影响原始的pieces对象。下一次生成同类型方块时,拿到的依然是最初定义的原始形状,不会被之前的旋转操作改变。

额外小提示

如果你想彻底保护pieces里的原始数据,可以考虑用深冻结,但一般来说只要确保每次使用的都是深拷贝后的副本,就足够解决问题了。

内容的提问来源于stack exchange,提问作者Michael Z

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.14 09:12:36