【问题标题】:Java Chess: Unsure How to Clone ProperlyJava Chess:不确定如何正确克隆
【发布时间】:2015-07-26 22:04:17
【问题描述】:

我正在编写一个基本的 Java 国际象棋游戏并编写了以下类:Game、Player、Board、Square、Piece(每个特定棋子的超类)和每个特定棋子类(例如 Pawn、Knight、Bishop等)

其中一个比较棘手的方法是根据是否导致移动的玩家被检查来确定移动是否有效。我的解决方案如下:

  1. 克隆当前板
  2. 行动
  3. 查看移动玩家是否在检查中
  4. 如果是,禁止移动;否则,允许移动

我正在了解如何从这里克隆:http://www.jusfortechies.com/java/core-java/cloning.php

现在,棋盘对象由 Square 对象的二维数组组成。每个方形对象都有一个片段字段,该字段要么为空(上面没有片段),要么引用片段对象(上面有片段)。棋盘对象还有一个 whiteKingSquare 和一个 blackKingSquare(两者都是 Square 对象),以便更快/更轻松地定位白王或黑王。

我在 Board 类中编写了以下方法:

public Object clone() throws CloneNotSupportedException {
    Board clonedBoard = (Board) super.clone();
    for (int i = 0; i < HEIGHT; i++) {
        for (int j = 0; j < WIDTH; j++) {
            clonedBoard.myBoard[i][j] = new Square(this, i, j); 
            clonedBoard.whiteKingSquare = myBoard[7][4];
            clonedBoard.blackKingSquare = myBoard[0][4];
        }
    }
    return clonedBoard; 
}

但是,因为 Board 引用了一个 8 x 8 的 Square 对象数组,所以我必须克隆每个对象。我在 Square 类中编写了这个方法:

public Object clone() throws CloneNotSupportedException {
    return (Square) super.clone();
}

最后,我在 Piece 类中编写了这个方法:

public Object clone() throws CloneNotSupportedException {
    return (Piece) super.clone();
}

关于问题:

  1. 这看起来大致正确吗?
  2. 我的 Square 对象也有一个 Board 字段,基本上可以让我 参考他们所属的董事会。这会不会影响我的尝试 通过让我的 64 个方块中的每一个分别克隆板来克隆?

【问题讨论】:

  • 请参阅this incredibly simple solution 以克隆对象。第二个问题的答案:是的,它会干扰你的尝试,因为方块仍然有原始参考。
  • @pietv8x 谢谢;我需要为 Board 对象中引用的每个对象提供复制构造函数吗?
  • 要么,要么坚持使用clone()。例如,在我的 2048 游戏中,我采用了类似的方法来检查 AI 移动的有效性。请参阅this 方法。它非常有效地重新创建了所有图块,它是一个深层副本。
  • 你为什么不记住某个变量中的前一个位置,如果移动不检查,只需将棋子放回前一个位置?我不知道您的代码,但在我看来,这似乎是最简单的解决方案。至少我会这样做,因为这是一次一步的游戏。
  • 我认为你不应该使用克隆。我以前做过一个中国象棋程序。它的工作原理是这样的,如果用户选择了一个片段,然后点击监听器被执行。然后它调用一个名为 getValidLocations 的方法。它返回一个 xy 坐标数组,并将按钮放在这些线上。你明白我的意思吗?你不需要检查移动是否有效。

标签: java oop


【解决方案1】:

我有一个替代方案,看看你喜不喜欢。

没有必要使用clone!我前段时间写了一个中国象棋程序。让我解释一下我做了什么:(这只是一个大纲,你必须自己做细节)

我有一个 2D 数组 Pieces 和另一个 2D 按钮数组。当用户点击 Piece 时,Piece 类中的抽象方法 getValidLocations 被调用,该方法返回一堆坐标以指示 Piece 可以去哪里。当用户点击按钮时,棋子被移动到按钮的位置。

坏消息是,我不会下棋。我只懂中国象棋所以不能告诉你getValidLocations的算法怎么写,对不起!

【讨论】:

  • @bcclayman 我同意这可能是一种更简洁的方法,因为您肯定需要执行其他比较来确定移动是否有效。你不想一起做所有这些比较吗?请记住,仅仅检查移动后的位置并不能真正确认移动是合法的(即:国王不能“通过”检查,即使它到达最后一格时是安全的)。我想要这样的东西:用户尝试移动//检查一般范围限制//检查自己的棋子在那里//检查通过棋子(如果不是骑士)//...
  • ... 检查它是否是进入检查的国王的移动 // 如果它是易位,它是否通过检查进行城堡 [我假设每个正方形类都有一个属性指示它当前是否是被每一方攻击] // 检查它是否是一个非国王的移动,这使得国王的方格现在表明它正在被攻击。
  • 最后的想法 - 考虑一下,您可以通过简单地检查玩家的 King 和玩家的棋子之间的方向来检查“固定”的棋子是否会导致 King 受到检查。即:玩家的棋子与国王在同一等级/文件中,因此请按照该等级/文件直到击中第一块 - 它是沿该方向攻击的敌人吗?如果它不是同一个等级/文件,它的行/列距离是否相等(对角保护国王)?如果是这样,请检查该方向的下一个棋子 - 是否是沿该方向攻击的敌方棋子?
  • @Grade'Eh'Bacon 正如我之前所说的,我真的不懂国际象棋,所以我只能给出一种一般的指导方针。对不起!
【解决方案2】:

不要使用clone()。它坏了,而是对你的板做一个完整的深拷贝。这是discussion with Josh Bloch on copy constructor vs cloning

通过制作深层副本,您将为每个棋子拥有不同的对象,因此移动它们不会弄乱代表替代移动的不同棋盘中相应棋子的状态。

在所有片段上创建一个copy() 方法可能是有意义的,以允许它们制作其所有内部状态的有效副本。

复制方法示例:

让我们假设你的Pawn 类有一个变量来判断它是否已经移动过。这决定了它是可以移动 2 个空格还是只能移动 1 个。

class Pawn{ 
       private boolean hasMovedAlready=false;
       ...
       public Pawn copy(){
          return new Pawn(hasMovedAlready, ...);
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2010-10-18
    相关资源
    最近更新 更多