私は、オブジェクト指向の方法で設計および思考する方法の感覚を得ようとしています。このトピックについてコミュニティからフィードバックをもらいたいです。以下は、私がOOのように設計したいチェスゲームの例です。これは非常に広範な設計であり、この段階での焦点は、誰がどのメッセージに責任があるのかを特定することですそして、オブジェクトがゲームをシミュレートするために相互にどのように相互作用するか悪いデザインの要素(高いカップリング、悪い凝集など)があるかどうか、そしてそれらを改善する方法を指摘してください。
Chessゲームには次のクラスがあります
ボードは正方形で構成されているため、ボードは正方形オブジェクトの作成と管理を担当できます。また、各ピースは正方形上にあるため、各ピースにはそれが置かれている正方形への参照もあります。 (これは理にかなっていますか?)。次に、各ピースは、ある正方形から別の正方形に移動する責任があります。プレーヤークラスは、所有するすべてのピースへの参照を保持し、それらの作成にも責任があります(プレーヤーはピースを作成する必要がありますか?)。 PlayerにはtakeTurnメソッドがあり、このメソッドは、ピースの位置を現在の位置から別の位置に変更するピースクラスに属するmovePieceメソッドを呼び出します。今、私は理事会のクラスが正確に責任を負わなければならないことについて混乱しています。ゲームの現在の状態を判断し、ゲームがいつ終了するかを知る必要があると思いました。しかし、ピースが変更された場合、その場所はボードをどのように更新する必要がありますか?ピースが存在し、ピースの移動に応じて更新される正方形の個別の配列を維持する必要がありますか?
また、ChessGameは最初にボードとプレーヤーのオブジェクトを作成し、それらはそれぞれ正方形とピースを作成し、シミュレーションを開始します。簡単に言えば、これはChessGameのコードがどのように見えるかです
Player p1 =new Player();
Player p2 = new Player();
Board b = new Board();
while(b.isGameOver())
{
p1.takeTurn(); // calls movePiece on the Piece object
p2.takeTurn();
}
取締役会の状態がどのように更新されるかは不明です。作品にはボードへの参照が必要ですか?責任はどこにあるべきですか?誰がどのような参照を保持していますか?ご意見をお寄せください。この設計の問題点を指摘してください。設計の側面にのみ興味があるため、ゲームプレイのアルゴリズムや詳細には意図的に焦点を合わせていません。このコミュニティが貴重な洞察を提供してくれることを願っています。
私は実際にjustチェス盤、ピース、ルールなどの完全なC#実装を作成しました。 '(allコーディングの楽しさを取りたくない):
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
を持っています。このクラスは、誰がプレイしていたのか、ゲームがいつ行われたのかなど、ゲームのメタデータを保存します。
編集:この回答の目的は、記入できるテンプレートコードを提供することではないことに注意してください-私のコードには、実際には各項目に保存されている情報やメソッドなどが少しあります。目的は、達成しようとしている目標に向かって導くことです。
かなり基本的なチェスゲームの私の考えは次のとおりです。
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 {....}
最近、PHP( website click here 、 source click here )でチェスプログラムを作成し、オブジェクト指向にしました。私が使用したクラス。
generate_legal_moves()
コードをすべて入れます。このメソッドには、順番が変わるボードと、出力の詳細レベルを設定するいくつかの変数が与えられ、その位置のすべての正当な動きが生成されます。 ChessMovesのリストを返します。int
sとして保存します。現在、このコードをチェスA.I.に変換しようとしているので、高速である必要があります。 generate_legal_moves()
関数を1500ミリ秒から8ミリ秒に最適化しましたが、まだ作業中です。それから学んだ教訓は...
int
などのプリミティブ型を使用します。 ChessSquare
がランクとファイルをint
として保存する理由は、「a4」のような人間が読めるチェスの正方形表記で英数字string
も保存する理由です。私はこれが古いことを知っていますが、うまくいけば誰かの助けになります。幸運を!