【问题标题】:How do I clone an object for the purposes of moving something and seeing if the move is valid? (Java, Chess)如何克隆对象以移动某些东西并查看移动是否有效? (Java、国际象棋)
【发布时间】:2012-02-20 22:15:01
【问题描述】:

我正在编写一个简单的国际象棋游戏。我不会在这里全部发布,但我会给你必要的细节。

我通过单击上面有棋子的方块来移动,然后该方块被选中,然后单击我想要棋子移动的位置。有时在国际象棋中,移动可能无法响应检查或对自己的国王创建检查,因此是非法的。我发现,决定一个动作是否非法的最好方法是在“ifBoard”(棋盘的克隆版)上走棋,如果我认为走棋合法,则将真实棋盘设置为等于 ifBoard。

这是我响应鼠标点击的代码 sn-p(板是真正的板,目标是点击的方块,selectedSquare 是之前选择的方块(如果不为空))

    public void mousePressed(MouseEvent e){
            Square selectedSquare = board.selectedSquare();
            Square destination = board.getSquare(e.getX(), e.getY());
            board.deselect();
            if(destination == null){
                repaint();
                return;
            }

            if(selectedSquare == null){
                System.out.println("SelectedSquare is null");
                if(destination.occupiedByTeam(turn)){
                    System.out.println("destination is occupied by right team and is null");
                    board.select(destination);
                }
            }
            else{
                if(!selectedSquare.occupiedByTeam(turn)){
                    System.out.println("SelectedSquare not occupied by correct team");
                    repaint();
                    return;
                }

                if(destination.occupiedByTeam(turn)){
                    System.out.println("ChosenSquare occupied by same team");
                    board.select(destination);
                    repaint();
                    return;
                }

                //move on a dummy board and check for conflicts
                Board ifBoard = (Board)board.clone();

                System.out.println(ifBoard.toString());
                System.out.println(board.toString());
                //check if you can't move due to piece movement limitations
//.place() is a coordinate of the square on the tile system (A-H and 1-8)
                if(
                !ifBoard.move((int)selectedSquare.place().getX(), (int)selectedSquare.place().getY(), (int)destination.place().getX(), (int)destination.place().getY())
                ){
                    repaint();
                    return;
                }

                //if moving results in self-check
                if(ifBoard.check(turn)){
                    //don't move
                    repaint();
                    return;
                }
                else{
                    //move
                    System.out.println("Board moved!");
                    board = new Board(ifBoard);
                    cycleTurns();
                }
            }

            repaint();
        }

toString 调用的注册方式不同,但我已将问题缩小到 ifBoard.move() 调用实际上移动了真正的板。

这是棋盘类,或其中的一部分。

import java.awt.Color;
import java.lang.Cloneable;
import java.awt.geom.*;
import java.awt.Graphics;
import java.awt.Graphics2D;

public class Board implements Cloneable{
    private Square[][] squares;

    private Rectangle2D squareWrap;
    private Rectangle2D boardBorder;

    private Square selectedSquare;

    public Board(){
        squares = new Square[8][8];
        for(int i = 0; i < 8; i++){
            for(int j = 0; j < 8; j++){
                squares[i][j] = new Square(new Point2D.Double(i, j));
            }
        }

        boardBorder = new Rectangle2D.Double(Constants.boardX,
                                             Constants.boardY,
                                             Constants.borderWidth * 2 + Constants.boardSide,
                                             Constants.borderHeight * 2 + Constants.boardSide);

        squareWrap = new Rectangle2D.Double(Constants.boardX + Constants.borderWidth,
                                            Constants.boardY + Constants.borderHeight,
                                            Constants.boardSide,
                                            Constants.boardSide);

        selectedSquare = null;
    }

    public Object clone() {
        Board obj = new Board();
        obj.setSquares(this.squares);
        obj.setSelectedSquare(this.selectedSquare);

        return obj;
    }...

我是否克隆不正确?有没有更好的办法?提前谢谢你。

【问题讨论】:

    标签: java oop class object chess


    【解决方案1】:

    我是否克隆不正确?有没有更好的办法?

    clone 方法应始终以调用 super.clone() 开始,原因我不会在本文中讨论。

    另外,您没有克隆对象的属性(您正在执行浅拷贝而不是深拷贝)。因此,克隆的 Board 将共享squares 结构的相同引用。 (更改克隆的 Board 会更改原始 Board。)

    (许多人认为你应该避免同时使用cloneCloneable。)

    如果我是你,我会强烈考虑让 Board 类不可变,并且可能会采用一些写时复制机制。我相信这样可以省去很多麻烦。

    【讨论】:

    • 谢谢,我现在明白浅拷贝和深拷贝的区别了。我也喜欢让 Board 不可变的想法,我将研究如何实现写时复制机制。
    • 能否顺便给我指出一个好的 Java 写时复制实现,这样我就可以学习如何制作一个?我似乎找不到任何东西。
    【解决方案2】:

    您的问题可能是您的 clone() 实现没有创建深层副本。被克隆的对象与其被克隆的实例共享至少一些状态。我的意思是它们引用了相同的对象:

    public Object clone() {
        Board obj = new Board();
        obj.setSquares(this.squares); // Square instances are the same for both boards
        return obj;
    }
    

    如果您更改克隆上的状态 - 例如在一个正方形上 - 你也可以在真正的棋盘上改变它。

    根据您发布的内容,您拨打的是ifBoard.move()。如果该方法影响“共享状态”的一部分,那么它将影响两个板实例。

    【讨论】:

    • 谢谢。这也是正确的,但鉴于我必须选择一个答案,我选择了下面更详细(和更早)的一个。
    猜你喜欢
    • 1970-01-01
    • 2015-07-16
    • 1970-01-01
    • 2016-07-06
    • 1970-01-01
    • 2022-01-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多