国际象棋游戏的面向对象设计

发布于 2024-10-02 07:58:06 字数 1431 浏览 9 评论 0原文

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(3

倾城泪 2024-10-09 07:58:06

实际上,我只是编写了棋盘、棋子、规则等的完整 C# 实现。以下是我对其建模的大致方式(实际实现已删除,因为我不想全部 em> 编码的乐趣):

public enum PieceType {
    None, Pawn, Knight, Bishop, Rook, Queen, King
}

public enum PieceColor {
    White, Black
}

public struct Piece {
    public PieceType Type { get; set; }
    public PieceColor Color { get; set; }
}

public struct Square {
    public int X { get; set; }
    public int Y { get; set; }

    public static implicit operator Square(string str) {
        // Parses strings like "a1" so you can write "a1" in code instead
        // of new Square(0, 0)
    }
}

public class Board {
    private Piece[,] board;

    public Piece this[Square square] { get; set; }

    public Board Clone() { ... }
}

public class Move {
    public Square From { get; }
    public Square To { get; }
    public Piece PieceMoved { get; }
    public Piece PieceCaptured { get; }
    public PieceType Promotion { get; }
    public string AlgebraicNotation { get; }
}

public class Game {
    public Board Board { get; }
    public IList<Move> Movelist { get; }
    public PieceType Turn { get; set; }
    public Square? DoublePawnPush { get; set; } // Used for tracking valid en passant captures
    public int Halfmoves { get; set; }

    public bool CanWhiteCastleA { get; set; }
    public bool CanWhiteCastleH { get; set; }
    public bool CanBlackCastleA { get; set; }
    public bool CanBlackCastleH { get; set; }
}

public interface IGameRules {
    // ....
}

基本思想是游戏/棋盘/等只是存储游戏的状态。您可以操纵它们来设置一个位置,如果这是您想要的。我有一个实现 IGameRules 接口的类,该接口负责:

  • 确定哪些动作有效,包括王位易位和过路。
  • 确定特定的移动是否有效。
  • 确定玩家何时处于将死/将死/相持状态。
  • 执行动作。

将规则与游戏/棋盘类分开还意味着您可以相对轻松地实现变体。规则接口的所有方法都采用一个 Game 对象,它们可以检查该对象以确定哪些移动是有效的。

请注意,我不会在 Game 上存储玩家信息。我有一个单独的类Table,它负责存储游戏元数据,例如谁在玩、游戏何时发生等。

编辑:请注意此答案的目的并不是真的给你提供可以填写的模板代码——我的代码实际上在每个项目上存储了更多信息、更多方法等。目的是引导你实现你想要实现的目标。

I actually just wrote a full C# implementation of a chess board, pieces, rules, etc. Here's roughly how I modeled it (actual implementation removed since I don't want to take all the fun out of your coding):

public enum PieceType {
    None, Pawn, Knight, Bishop, Rook, Queen, King
}

public enum PieceColor {
    White, Black
}

public struct Piece {
    public PieceType Type { get; set; }
    public PieceColor Color { get; set; }
}

public struct Square {
    public int X { get; set; }
    public int Y { get; set; }

    public static implicit operator Square(string str) {
        // Parses strings like "a1" so you can write "a1" in code instead
        // of new Square(0, 0)
    }
}

public class Board {
    private Piece[,] board;

    public Piece this[Square square] { get; set; }

    public Board Clone() { ... }
}

public class Move {
    public Square From { get; }
    public Square To { get; }
    public Piece PieceMoved { get; }
    public Piece PieceCaptured { get; }
    public PieceType Promotion { get; }
    public string AlgebraicNotation { get; }
}

public class Game {
    public Board Board { get; }
    public IList<Move> Movelist { get; }
    public PieceType Turn { get; set; }
    public Square? DoublePawnPush { get; set; } // Used for tracking valid en passant captures
    public int Halfmoves { get; set; }

    public bool CanWhiteCastleA { get; set; }
    public bool CanWhiteCastleH { get; set; }
    public bool CanBlackCastleA { get; set; }
    public bool CanBlackCastleH { get; set; }
}

public interface IGameRules {
    // ....
}

The basic idea is that Game/Board/etc simply store the state of the game. You can manipulate them to e.g. set up a position, if that's what you want. I have a class that implements my IGameRules interface that is responsible for:

  • Determining what moves are valid, including castling and en passant.
  • Determining if a specific move is valid.
  • Determining when players are in check/checkmate/stalemate.
  • Executing moves.

Separating the rules from the game/board classes also means you can implement variants relatively easily. All methods of the rules interface take a Game object which they can inspect to determine which moves are valid.

Note that I do not store player information on Game. I have a separate class Table that is responsible for storing game metadata such as who was playing, when the game took place, etc.

EDIT: Note that the purpose of this answer isn't really to give you template code you can fill out -- my code actually has a bit more information stored on each item, more methods, etc. The purpose is to guide you towards the goal you're trying to achieve.

终难愈 2024-10-09 07:58:06

这是我的想法,对于一个相当基本的国际象棋游戏:

class GameBoard {
 IPiece config[8][8];  

 init {
  createAndPlacePieces("Black");
  createAndPlacePieces("White");
  setTurn("Black");

 }

 createAndPlacePieces(color) {
   //generate pieces using a factory method
   //for e.g. config[1][0] = PieceFactory("Pawn",color);
 }

 setTurn(color) {
   turn = color;
 }

 move(fromPt,toPt) {
  if(getPcAt(fromPt).color == turn) {
    toPtHasOppositeColorPiece = getPcAt(toPt) != null && getPcAt(toPt).color != turn;
    possiblePath = getPcAt(fromPt).generatePossiblePath(fromPt,toPt,toPtHasOppositeColorPiece);
   if(possiblePath != NULL) {
      traversePath();
      changeTurn();
   }
  }
 } 

}

Interface IPiece {
  function generatePossiblePath(fromPt,toPt,toPtHasEnemy);
}

class PawnPiece implements IPiece{
  function generatePossiblePath(fromPt,toPt,toPtHasEnemy) {
    return an array of points if such a path is possible
    else return null;
  }
}

class ElephantPiece implements IPiece {....}

Here is my idea, for a fairly basic chess game :

class GameBoard {
 IPiece config[8][8];  

 init {
  createAndPlacePieces("Black");
  createAndPlacePieces("White");
  setTurn("Black");

 }

 createAndPlacePieces(color) {
   //generate pieces using a factory method
   //for e.g. config[1][0] = PieceFactory("Pawn",color);
 }

 setTurn(color) {
   turn = color;
 }

 move(fromPt,toPt) {
  if(getPcAt(fromPt).color == turn) {
    toPtHasOppositeColorPiece = getPcAt(toPt) != null && getPcAt(toPt).color != turn;
    possiblePath = getPcAt(fromPt).generatePossiblePath(fromPt,toPt,toPtHasOppositeColorPiece);
   if(possiblePath != NULL) {
      traversePath();
      changeTurn();
   }
  }
 } 

}

Interface IPiece {
  function generatePossiblePath(fromPt,toPt,toPtHasEnemy);
}

class PawnPiece implements IPiece{
  function generatePossiblePath(fromPt,toPt,toPtHasEnemy) {
    return an array of points if such a path is possible
    else return null;
  }
}

class ElephantPiece implements IPiece {....}
深府石板幽径 2024-10-09 07:58:06

我最近用 PHP 创建了一个国际象棋程序(网站点击此处源代码点击此处),我将其设为面向对象。这是我使用的类。

  • ChessRulebook(静态)- 我将所有 generate_legal_moves() 代码放在这里。该方法给出了一个棋盘(轮到谁了)和一些变量来设置输出的详细程度,并生成该位置的所有合法动作。它返回 ChessMoves 列表。
  • ChessMove - 存储创建代数符号所需的所有内容,包括起始方格、结束方格、颜色、棋子类型、吃子、检查、将死、升级棋子类型和顺便。可选的附加变量包括消歧(对于像 Rae4 这样的动作)、易位和棋盘。
  • ChessBoard - 存储与 国际象棋 FEN 相同的信息,包括 8x8代表方格的数组,存储轮到的棋子、经过的目标方格、易位权、半步时钟和全步时钟。
  • ChessPiece - 存储棋子类型、颜色、方格和棋子值(例如,pawn = 1、knight = 3、rook = 5 等)
  • ChessSquare - 存储排名和文件,如 ints 。

我目前正在尝试将这段代码变成国际象棋人工智能,所以它需要很快。我已将 generate_legal_moves() 函数从 1500 毫秒优化为 8 毫秒,并且仍在努力。我从中学到的教训是......

  • 默认情况下不要在每个 ChessMove 中存储整个 ChessBoard。仅在需要时将板存放在移动中。
  • 尽可能使用基本类型,例如int。这就是为什么 ChessSquare 将排名和文件存储为 int,而不是同时存储具有人类可读的国际象棋方块符号(例如“a4”)的字母数字字符串
  • 该程序在搜索着法树时创建了数万个 ChessSquares。我可能会重构程序以不使用 ChessSquares,这应该会提高速度。
  • 不要在类中计算任何不必要的变量。最初,计算每个棋盘中的 FEN 确实会降低程序的速度。我必须使用 分析器 来找到这一点。

我知道这已经很旧了,但希望它对某人有帮助。祝你好运!

I recently created a chess program in PHP (website click here, source click here) and I made it object oriented. Here are the classes I used.

  • ChessRulebook (static) - I put all my generate_legal_moves() code in here. That method is given a board, whose turn it is, and some variables to set the level of detail of the output, and it generates all the legal moves for that position. It returns a list of ChessMoves.
  • ChessMove - Stores everything needed to create algebraic notation, including starting square, ending square, color, piece type, capture, check, checkmate, promotion piece type, and en passant. Optional additional variables include disambiguation (for moves like Rae4), castling, and board.
  • ChessBoard - Stores the same information as a Chess FEN, including an 8x8 array representing the squares and storing the ChessPieces, whose turn it is, en passant target square, castling rights, halfmove clock, and fullmove clock.
  • ChessPiece - Stores piece type, color, square, and piece value (for example, pawn = 1, knight = 3, rook = 5, etc.)
  • ChessSquare - Stores the rank and file, as ints.

I am currently trying to turn this code into a chess A.I., so it needs to be FAST. I've optimized the generate_legal_moves() function from 1500ms to 8ms, and am still working on it. Lessons I learned from that are...

  • Do not store an entire ChessBoard in every ChessMove by default. Only store the board in the move when needed.
  • Use primitive types such as int when possible. That is why ChessSquare stores rank and file as int, rather than also storing an alphanumeric string with human readable chess square notation such as "a4".
  • The program creates tens of thousands of ChessSquares when searching the move tree. I will probably refactor the program to not use ChessSquares, which should give a speed boost.
  • Do not calculate any unnecessary variables in your classes. Originally, calculating the FEN in each of my ChessBoards was really killing the program's speed. I had to find this out with a profiler.

I know this is old, but hopefully it helps somebody. Good luck!

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文