Java下象棋游戏的面向对象设计(棋子/棋盘交互问题)

5

我正在尝试用Java编写自己的国际象棋游戏。 我已经开始编写类,我的高层次思路如下:

我有一个Piece类,其中包含以下字段:

private String name;
private String color;

最初我想为每个棋子设置x和y坐标,但这似乎更像是棋盘的属性。这就引出了...

我有一个Board类,其中包含以下字段:

Piece[][] myBoard = new Piece[8][8];

我不确定在哪里/如何跟踪棋子的位置。目前,我只有一个 Piece 对象的二维数组。然而,我认为这会带来一些挑战。例如,假设用户点击了一个棋子并想要移动它。我需要弄清楚移动是否有效,为此,我需要知道棋子所在的当前方格。

如果每个棋子都有 x 和 y 坐标,那么我就需要在两个地方更新游戏状态(在 Board 类的二维数组中和当前棋子的 x 和 y 坐标中)。这似乎不太好...

有什么建议吗?

感谢您的帮助, Mariogs


3
这段话的意思是:这个想法有点需要解释,但你可以考虑让棋盘由方格而非棋子组成。每个方格上可能会放置或不放置棋子。然后,根据所选棋子和用户试图移动到的方格,棋盘将负责确定移动是否有效。因此,在我看来,棋盘控制一切,棋子并不知道它们在哪里,只是具有指定移动尝试的有效偏移量函数。 - Dtor
3
这是一个比较宽泛的问题,因此很难回答。我认为名称和颜色绝对不应该是字符串。相反,应该使用枚举类型。enum Name { PAWN, ROOK, KNIGHT, BISHOP, KING, QUEEN }``enum Color { BLACK, WHITE } - Paul Boddington
我同意在“固定网格”场景中将位置保持在棋子之外 - 棋盘上有棋子,棋子本身只是一个值。包含棋子的网格单元格“被点击”,其偏移量在棋盘内是显而易见的。 - user2864740
在我看来,这个棋子应该有一个坐标,以便被棋盘解释。说实话,棋盘和棋子都不应该能够移动棋子。应该有一个单独的类来处理这个问题,比如 ChessGame 或者 ChessRules。如果你这样做了,那么你可以很容易地将相同的类用于棋子和棋盘,如果你想要实现跳棋,你只需要创建一个 CheckersGame - Jared
我猜我的困惑部分源于如何连接myBoard和实际的GUI。也许这似乎不相关,但是根据@Dtor建议的实现方式,是否会有一些setUp方法循环遍历2d数组,并为我2d数组中的每个方块对象创建可点击的GUI方块? - anon_swe
3个回答

0

我可以提供帮助,因为我已经用Java编写了一个完整的国际象棋引擎,你可以在这里找到。

有几件事需要考虑:

与其将棋盘作为8x8个棋子的数组,不如将其作为64个Tile的一维数组。 Tile类可以是抽象的,并且有两个子类,OccupiedTileEmptyTile。每个Tile可以有一个整数coordinate,编号从0到63,而OccupiedTile还有一个上面的Piece

Originally I was going to have an x and y coordinate for each piece but that seems like it's more a property of the board. Which brings me to...

让一个棋子“知道”它的位置是很方便的,因此在棋子中使用整数字段来跟踪这个位置,在我看来并不是一件坏事。

Piece 还应该有像 KnightBishopPawnRookQueenKing 这样的子类。这样它们的 toString() 方法就可以返回一个固定的值作为 pieceName,而不是将其作为成员字段进行跟踪。

最后,为什么不创建一个名为 Alliance 的枚举来跟踪白色或黑色棋子呢?使用 String 来完成这项工作相当脆弱,因为调用者实际上可以传递任何值。请参见我的 Alliance 版本。

在我的程序中,我会这样创建一个棋子:

King king = new King(Alliance.BLACK, 4);

我认为这相当易读。


0

你的 Piece[8][8] 方法似乎足以跟踪棋子的位置(除了我更倾向于将其作为自己的类,内部带有一个数组,您可能希望它具有更多的逻辑,而不仅仅是设置和移动棋子)。至于确定棋子可以移动到哪里,那似乎是实际棋子的功能:

public class BoardCoordinates {
   public final char vertical;
   public final int horizontal;
   public BoardCoordinates(char v, int h) { vertical = v; horizontal = h; }
}
public class Board {
    private Piece[8][8] state = new Piece[8][8]{};
    public Piece at(BoardCoordinates c) { 
        state[c.vertical - 'a'][c.horizontal];
    }
    public void set(Piece what, BoardCoordinates where) { 
        state[where.vertical - 'a'][where.horizontal] = what;
    }
    public void move(BoardCoordinates from, BoardCoordinates to) {
        set(at(from), to);
        set(null, from);           
    }
}

public class Piece {
    ....
    public List<BoardCoordinates> findMoves(BoardCoordinates from, Board theBoard) {
        ... 
    }
}

大多数情况下,您不需要第二个参数,但在处理特殊情况时(如王车易位或兵过河吃),有时需要考虑棋盘上其他棋子的位置信息(实际上,为了正确处理兵过河吃,您甚至需要更多信息,但这与手头的问题没有多少关系)。
我绝对赞成您保留每个棋子的坐标的看法,听起来确实是一种糟糕的想法。

他为什么需要8x8的棋子。那是一个可怕的想法,也不符合现实模型。国际象棋棋盘上没有64个棋子... - Amir Afghani
不是8x8个棋子,而是8x8的空间用于放置棋子。它甚至不仅仅是“模拟现实”,它就是现实 :) - Dima
你不同意什么?一个8x8的数组有64个元素空间?还是说象棋棋盘有64个格子?这些不是需要同意或反对的论点,它们只是生活中的事实... - Dima

0

你的描述中可能还缺少另一个对象:BoardTile。每个BoardTile都知道它的位置、推断出它的颜色和相邻的Tile,同时也知道它是否为空或者上面有任何棋子。此外,BoardTile和Board彼此认识(显然!)

鉴于棋子的行为强烈依赖于它在棋盘中的位置,它应该知道自己所在的Tile。因此,当你首次将所有棋子放入棋盘时,棋盘应该为每个棋子分配相应的Tile(请注意,这种知识对应于ChessGame的规则,而不是你可能想要使用棋盘和棋子进行象棋变体的规则)。同样地,当一个Piece从Board中取出时(大概是由ChessGame完成),Piece应该失去其Tile(通过湮灭Tile)。

有了这三个对象,你就有了几种选择。例如,一个Piece可以通过回答其Tile的位置来学习自己的位置。Board可以通过查询其Tile获知每个Piece的位置等。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接