【问题标题】:Java programming - Chess moves (basic, no AI)Java 编程 - 国际象棋走法(基础,无 AI)
【发布时间】:2023-03-13 18:12:01
【问题描述】:

我在设计国际象棋游戏时需要帮助。我已经开始了,但还没有走多远,因为我对 Java 还很陌生,实际上对编程完全不熟悉。

无论如何,我有我的抽象类 Piece 和各种片段作为子类。 我的抽象类中有一个方法 movePiece,我想为所有子类定义它。

它目前所做的只是将棋子从一个方格移动到另一个方格。我有一个可以容纳 Piece 对象的 Square 类,该板由一个 64x1 Square 数组组成。

我知道棋子是如何移动的,但实际上我该如何进行编程呢? 我想尝试应用 MVC 模式,但这真的是我第一次使用模式。

基本上,我正在考虑使用 Graphics2D 为每个 Square 创建一个框。然后,当玩家点击棋子时,移动后可用作目的地的方格将以某种颜色勾勒出来。在玩家点击其中一个方块后,我在 movePiece 方法中已有的代码将运行。

我想要做的是在 Piece 的每个子类中覆盖我的 movePiece 方法。问题是,代码如何以这些方法之一显示?以 Pawn 子类为例。

我不是要求复制/粘贴代码,只是一些有关如何执行此操作的指示,最后是一些示例代码。

谢谢!

public class Game {


@SuppressWarnings("unused")
public static void main(String[] args){
    Board board = new Board();
} }

public class Board {

Square[] grid;

public Board(){
    grid = new Square[64];
}   
public Square getSquare(int i){
    return grid[i];
}   
public void setDefault(){

}   
public Boolean isMoveValid(){
    return null;    
} }

public class Square {

private Piece piece;

public void addPiece(Piece pieceType, String pieceColour, String pieceOwner) 
        throws ClassNotFoundException, InstantiationException, IllegalAccessException{

    PieceFactory factory = new PieceFactory();
    Piece piece = factory.createPiece(pieceType);

    piece.setColour(pieceColour);
    piece.setOwner(pieceOwner);

    this.piece = piece; 
}
public void addPiece(Piece pieceType){ 
    this.piece = pieceType; 
}
public void removePiece(){  
    piece = null;
}
public Piece getPiece(){
    return piece;       
}

class PieceFactory {     
     @SuppressWarnings("rawtypes")
     public Piece createPiece(Piece pieceType) 
            throws ClassNotFoundException, InstantiationException, IllegalAccessException{
         Class pieceClass = Class.forName(pieceType.toString());
         Piece piece = (Piece) pieceClass.newInstance();

         return piece;       
     } }

public void setColour(String colour){

} }

public abstract class Piece {

Board board;

public void setColour(String pieceColour) {
}

public void setOwner(String pieceOwner) {
}

public String getColour() {
    return "";
}

public String getOwner() {
    return "";      
}
public void movePiece(int oldIndex, int newIndex){
    board.getSquare(oldIndex).removePiece();
    board.getSquare(newIndex).addPiece(this);
}
public String toString(){
    return this.getClass().getSimpleName();
} }

您想查看代码,我知道非常基本。我会将 [64] 更改为 [8][8]。 我试图不让它变得更难,那么它必须是。我可能可以将 Color 和 Owner 组合为一个属性,并使其成为一个枚举(黑色或白色)。

如果格式不好,请见谅。

【问题讨论】:

  • board consists of a 64x1 Square array. 为什么?棋盘是 8x8,而不是 64x1。 Basically I was thinking on using Graphics2D to create a box for each Square GUI 排在第二位。别担心。在编写 GUI 之前让您的代码工作。您应该向我们展示您已经编写的内容,而不是询问代码的外观。看起来您已经具备设计的基础知识(实现抽象类方法)。就这样吧。
  • 为什么要区分pieceColour和pieceOwner?他们是一样的
  • 另外,stackoverflow 不是国际象棋问题的好地方。这里的人们对如何编写国际象棋游戏知之甚少或一无所知。试试 Talkchess.com,这是一个专业的国际象棋网站。
  • 不知道,只是接受了一个我喜欢的答案!此外,这里的答案对我帮助很大。
  • @Falmarri 是一个物理棋盘。表示棋盘的数据结构不是必须的,使用单个数组而不是矩阵意味着有很多不需要发生的数组索引。

标签: java chess


【解决方案1】:

在设计软件时,我发现考虑如何使用方法很有帮助,然后写下方法签名(如果您进行测试驱动开发,则进行单元测试),然后再考虑如何实现它。

在这里做这个,我发现需求

那么当玩家点击一个棋子时, 可用的正方形 搬家后的目的地是 用某种颜色勾勒出来。

不可能用像这样的方法来满足

void move(Square destination);

因为没有实际行动就无法找出可能的行动。为了突出显示正方形,最好有一个类似

的方法
Collection<Square> getPossibleMoves();

然后我们可以实现

void move(Square destination) {
    if (!getPossibleMoves().contains(destination) {
        throw new IllegalMoveException();
    }

    this.location.occupyingPiece = null;
    this.location = destination;
    this.location.occupyingPiece = this;
}

关于实现 getPossibleMoves:

class Pawn extends Piece {
    @Override Collection<Square> getPossibleMoves() {
        List<Square> possibleMoves = new ArrayList<Square>();
        int dy = color == Color.white ? 1 : -1;
        Square ahead = location.neighbour(0, dy);
        if (ahead.occupyingPiece == null) {
            possibleMoves.add(ahead);
        }
        Square aheadLeft = location.neighbour(-1, dy);
        if (aheadLeft != null && aheadLeft.occupyingPiece != null && aheadLeft.occupyingPiece.color != color) {
            possibleMoves.add(aheadLeft);
        }
        Square aheadRight = location.neighbour(1, dy);
        if (aheadRight != null && aheadRight.occupyingPiece != null && aheadRight.occupyingPiece.color != color) {
            possibleMoves.add(aheadRight);
        }
        return possibleMoves;
    }
}

编辑

class Knight extends Piece {
    @Override Collection<Square> getPossibleMoves() {
        List<Square> possibleMoves = new ArrayList<Square>();
        int[][] offsets = {
            {-2, 1},
            {-1, 2},
            {1, 2},
            {2, 1},
            {2, -1},
            {1, -2},
            {-1, -2},
            {-2, -1}
        };
        for (int[] o : offsets) {
            Square candidate = location.neighbour(o[0], o[1]);
            if (candidate != null && (candidate.occupyingPiece == null || candidate.occupyingPiece.color != color)) {
                possibleMoves.add(candidate);
            }
        }
        return possibleMoves;
    }
}

【讨论】:

  • 我想我明白了,只是我以前没有真正使用过集合,但我想用数组来做应该不是问题。谢谢!
  • 我可能只是尝试使用 ArrayLists,似乎不必指定元素的数量等很实用。只是想知道,你怎么能写 List possibleMoves = new ArrayList() ?我的意思是,List 既是类型又是接口?
  • 是的,接口就是类型。我使用 list 的原因是我们的调用者不关心列表是如何表示的,并且“List”比“Collection”或“ArrayList”短。
  • 您知道任何有效的方法来对骑士的移动方式进行编程吗?现在我正在使用 switch 语句。我有一个变量,我根据它的位置给出一个数字/大小写,但这真的很乏味。我现在有多达 150 行代码。也许是某种以某种方式翻转或旋转棋盘的方法,因为许多动作是相同的,但角度不同。
  • 只有当你郑重承诺给这个答案一个赞成票时;-) 认真:将编辑。
【解决方案2】:

按照您的描述方式,您需要的不仅仅是一个 movePiece 方法,而是一个 getPossibleMoves 方法,它可以为您提供所有可以移动到的位置。或者或另外,一个片段的moveAllowed 方法告诉您是否允许片段移动到给定位置。

您可以创建一个类来在板上定义Location,而不是使用原始 (x,y) 坐标。这可以为您提供将位置转换为国际象棋文献中使用的“国际象棋坐标”的方法。位置将被构造为new Location(x, y)

另外,我宁愿使用代表 8x8 单元板的 Board 类,它是片段的容器,并且可以具有比数组更丰富的逻辑(例如,可以给你一个 pathClear(from, to) 告诉你是否有任何碎片阻挡您从一个位置到另一个位置的通道)等。

【讨论】:

  • 位置的好主意,是否可以将数组 [x][y] 转换为 [d4] 之类的东西?因此,我只写“国际象棋坐标”,而不是写两个坐标来访问元素。我是否必须创建一个代表一条线的数组,然后是该数组的一个数组?还是更容易做到?
  • 不,只是写坐标系之间的转换函数。在内部,只使用更简单/更有效的存储和检索。例如。您最终可能会有不同的构造函数:Location(int x, int y) 或 Location(String chessCoords) 以及 getX() getY() 和 toString() 等方法来为您提供坐标,例如“D4”。您可以按位置访问棋盘:Board.getPieceAt(Location l)... 等。您可以将游戏存储为移动列表,其中每个移动代表将棋子移动到新位置(或两个列表,一个用于白色和一个黑色)。
【解决方案3】:

我会做一个 isValidMove(Board boardState, int square) 抽象方法,它会在每一块中被覆盖并调用以突出显示有效的动作(在每次绘图迭代中)。同样的方法将在 movePiece(int square) 中调用) 将检查移动是否有效,如果有效则执行移动。实际上,movePiece 方法属于 Board 本身(并且会调用 Piece.isValidMove(...) 进行检查。)

Pawn 的 isValidMove 类似于(注意这是伪代码):

if ( boardState.getSquare(square).isFree() && square == this.square+ONE_FILE)
    return true;
else
    return false;

【讨论】:

  • 这种方法看起来也很有趣!我是否必须在 isValidMove 所在的同一类中定义 boardState 变量?
  • 可能你不再需要这个答案了,但我错过了这个评论。我会在每个片段的类中实现'isValidMove'(为抽象类'Piece'实现它),并且'boardState'(属于'Board'类)将作为参数传递给'isValidMove'方法。
【解决方案4】:

我将创建一个名为 getValidMoves() 的方法,该方法返回给定棋子的所有可能移动的列表。这样你就不必遍历整个棋盘来找到要突出显示的方块。我还将基类抽象化,这样这样的方法就不需要基实现。

【讨论】:

  • 对,但是基类(Piece)已经是抽象的,所以一切都很好。然后它会返回一个我可以循环的数组?
  • 是的,您只需遍历validMoves。你甚至可以称它为 possibleMoves,它不会考虑碰撞,然后查看那些,并删除那里有另一块的那些。这样,validMoves 就可以在没有关于板上其他部分的任何信息的情况下运行。
【解决方案5】:

我刚刚完成我自己的棋局。以下是我想与您分享的一些提示/要点。

请记住,Java 是面向对象的语言,将游戏划分为逻辑对象并与方法交互。

保持方法尽可能简单(一如既往)。制作人类可读的代码。 有时! 8 种不同的检查棋子(King)可用动作的方法比一些试图完成所有计算工作的隐秘 for-sentence 更具可读性。

您想应用 MVC 模式,您可能希望将您的 gui 视为您的控制器,它引用您的实际电路板。

Piece.java 受保护的列表 allowedSquares; 公共抽象无效calculateAllowedSquares(ChessBoard chessBoard); public void move(ChessBoard chessBoard, Square fromSquare, Square toSquare)

在 King 类中,您可能需要覆盖移动方法。例如在易位移动中,您需要同时移动车。

  • 您还需要知道您从哪里搬家(检查过客搬家规则) 由于您使用的是 [][] 板,因此您可能还需要清除移动的“fromSquare”。

整个制作棋类游戏的概念有两个主要问题: 1. 你如何消除让你自己的王要过牌的走法。有时碎片根本无法移动。 2. 如何让棋子移动只是为了在过牌情况下保护你的王。这意味着如果 for.ex 主教正在威胁您的国王,您可以移动一些棋子来阻止该威胁,但无处可去。

最后,我建议您根据自己的逻辑创建您的 gui。当您单击您的棋子(或棋子所在的正方形)时,这是一个很大的帮助,您可以立即在屏幕上看到可用的正方形。它可以为您节省数小时的调试时间。

【讨论】:

    【解决方案6】:

    你得慢下来!

    首先,您应该为每个项目创建一个类并为每个项目添加可能的移动,并实现一个方法来检查游戏中可能的移动, 然后使用 Netbeans GUI 并添加一些 JLabel 并将它们的颜色更改为白色和黑色,这样您就可以很好地了解整个过程。

    刚开始,不要犹豫删除旧代码并用新的更好的代码更改它们。 这就是它的工作原理。

    祝你好运。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2023-03-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-03-02
      相关资源
      最近更新 更多