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 {
// ....
}
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.
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!
发布评论
评论(3)
实际上,我只是编写了棋盘、棋子、规则等的完整 C# 实现。以下是我对其建模的大致方式(实际实现已删除,因为我不想全部 em> 编码的乐趣):
基本思想是游戏/棋盘/等只是存储游戏的状态。您可以操纵它们来设置一个位置,如果这是您想要的。我有一个实现 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):
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:
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 classTable
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.
这是我的想法,对于一个相当基本的国际象棋游戏:
Here is my idea, for a fairly basic chess game :
我最近用 PHP 创建了一个国际象棋程序(网站点击此处,源代码点击此处),我将其设为面向对象。这是我使用的类。
generate_legal_moves()
代码放在这里。该方法给出了一个棋盘(轮到谁了)和一些变量来设置输出的详细程度,并生成该位置的所有合法动作。它返回 ChessMoves 列表。int
s 。我目前正在尝试将这段代码变成国际象棋人工智能,所以它需要很快。我已将
generate_legal_moves()
函数从 1500 毫秒优化为 8 毫秒,并且仍在努力。我从中学到的教训是......int
。这就是为什么ChessSquare
将排名和文件存储为int
,而不是同时存储具有人类可读的国际象棋方块符号(例如“a4”)的字母数字字符串
。我知道这已经很旧了,但希望它对某人有帮助。祝你好运!
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.
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.int
s.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...int
when possible. That is whyChessSquare
stores rank and file asint
, rather than also storing an alphanumericstring
with human readable chess square notation such as "a4".I know this is old, but hopefully it helps somebody. Good luck!