체스 게임을위한 객체 지향 디자인 [닫힘]


88

저는 객체 지향 방식으로 디자인하고 생각하는 방법에 대한 느낌을 얻으려고 노력하고 있으며이 주제에 대한 커뮤니티의 피드백을 받고 싶습니다. 다음은 제가 OO 방식으로 디자인하고 싶은 체스 게임의 예입니다. 이것은 매우 광범위한 디자인이며이 단계에서 저의 초점은 누가 어떤 메시지를 담당하고 게임을 시뮬레이션하기 위해 오브젝트가 서로 어떻게 상호 작용하는지 식별하는 것입니다. 잘못된 설계 요소 (높은 커플 링, 나쁜 응집성 등)가 있는지 여부와 개선 방법을 지적하십시오.

체스 게임에는 다음과 같은 클래스가 있습니다.

  • 플레이어
  • 조각
  • 광장
  • 체스 게임

Board는 사각형으로 구성되어 있으므로 Board는 Square 개체의 생성 및 관리를 담당 할 수 있습니다. 각 조각은 또한 사각형에 있으므로 각 조각에는 해당 사각형에 대한 참조도 있습니다. (이게 말이 되요?). 그런 다음 각 조각은 한 사각형에서 다른 사각형으로 이동해야합니다. Player 클래스는 그가 소유 한 모든 조각에 대한 참조를 보유하고 있으며 또한 조각을 생성해야합니다 (플레이어가 조각을 만들어야합니까?). Player에는 현재 위치에서 다른 위치로 조각의 위치를 ​​변경하는 조각 클래스에 속하는 movePiece 메서드를 차례로 호출하는 takeTurn 메서드가 있습니다. 이제 Board 클래스가 정확히 무엇을 담당해야하는지 혼란 스럽습니다. 나는 게임의 현재 상태를 확인하고 게임이 언제 끝나는 지 알기 위해 필요하다고 생각했습니다. 하지만 조각이 바뀌면 s 위치 게시판은 어떻게 업데이트해야합니까? 조각이 존재하고 조각이 이동함에 따라 업데이트를받는 별도의 사각형 배열을 유지해야합니까?

또한 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();

}

이사회 상태가 어떻게 업데이트되는지 확실하지 않습니다. 조각에 보드에 대한 참조가 있어야합니까? 책임은 어디에 있어야합니까? 누가 어떤 참조를 가지고 있습니까? 당신의 의견을 도와 주시고이 디자인의 문제점을 지적 해주세요. 나는 디자인 측면에만 관심이 있기 때문에 의도적으로 알고리즘이나 게임 플레이의 추가 세부 사항에 초점을 맞추지 않습니다. 이 커뮤니티가 귀중한 통찰력을 제공 할 수 있기를 바랍니다.


3
Nitpicky 코멘트 : takeTurn()p1의 움직임이 게임을 끝내면 p2는 콜해서는 안됩니다 . 덜 간결한 댓글 : 플레이어 whiteblack.
Kristopher Johnson

동의합니다. 하지만 제가 말했듯이, 저는 디자인 측면과 어떤 작업에 대해 어떤 오브젝트가 어떤 참조를 보유하고 있는지에 대해 더 관심이 있습니다.
Sid

스 니펫에서 위에서 설명한대로 좋아했습니다. 내 구현에서 각 조각에는 자체 canMove()기능 에서 사용하기 때문에 전체 위치의 내부 사본이 있습니다. 그리고 이동이 완료되면 다른 모든 조각은 자신의 보드 내부 사본을 업데이트합니다. 최적이 아니라는 것을 알고 있지만 그 당시 C ++를 배우는 것은 흥미로 웠습니다. 나중에 체스 선수가 아닌 친구가 classes각 조각 대신 각 사각형에 대해 가질 것이라고 말했습니다 . 그리고 그 말은 저에게 매우 흥미로 웠습니다.
eigenfield

답변:


54

사실 난 단지 내가 (내가 먹고 싶어하지 않기 때문에 실제 구현 제거를 모델링 방법은 다음과 대략의 등 체스 보드, 조각, 규칙, 전체의 C # 구현을 쓴 모든 당신의 코딩의 즐거움을) :

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 인터페이스를 구현하는 클래스가 있습니다.

  • castling 및 en passant를 포함하여 유효한 동작을 결정합니다.
  • 특정 이동이 유효한지 확인합니다.
  • 플레이어가 체크 / 체크 메이트 / 지체 상태에있는시기를 결정합니다.
  • 동작을 실행합니다.

게임 / 보드 클래스에서 규칙을 분리하면 변형을 비교적 쉽게 구현할 수 있습니다. 규칙 인터페이스의 모든 메서드는 Game어떤 동작이 유효한지 확인하기 위해 검사 할 수 있는 개체를 사용합니다.

에 플레이어 정보를 저장하지 않습니다 Game. Table누가 플레이했는지, 언제 게임을했는지 등과 같은 게임 메타 데이터를 저장 하는 별도의 클래스 가 있습니다.

편집 : 이 답변의 목적은 실제로 작성할 수있는 템플릿 코드를 제공하는 것이 아닙니다. 제 코드에는 실제로 각 항목, 더 많은 메서드 등에 저장된 정보가 조금 더 있습니다. 목적은 달성하려는 목표입니다.


1
자세한 답변 감사합니다. 그러나 디자인과 관련하여 몇 가지 질문이 있습니다. 예를 들어 Move가 클래스 여야하는 이유가 즉시 명확하지 않습니다. 저의 유일한 초점은 책임을 할당하고 가능한 가장 깨끗한 방식으로 클래스 간의 상호 작용을 결정하는 것입니다. 모든 디자인 결정 뒤에 "이유"를 알고 싶습니다. 나는 당신이 디자인 결정에 어떻게 도달했는지, 왜 그것이 좋은 선택인지 명확하지 않습니다.
Sid

Move는 전체 이동 이력을 이동 목록에 저장할 수 있도록하는 클래스이며, 어떤 조각이 캡처되었는지, 폰이 승격되었을 수있는 것과 같은 표기법 및 보조 정보와 함께 이동 목록에 저장됩니다.
cdhowie

@cdhowie 객체 Game의 구현 자에 대한 델 게팅입니까 IGameRules아니면 객체 외부의 규칙을 시행합니까? 후자는 게임이 자신의 상태를 보호 할 수 없기 때문에 부적절 해 보입니다.
plalx 2014

1
이것은 어리석은 일이지만 Game 클래스에서 PieceType 대신 PieceColor 유형을 사용해서는 안됩니다.
Dennis van Gils

1
@nikhil 두 플레이어가 어느 방향으로 성을 유지할 수 있는지 표시합니다 (A 및 H 파일 방향). 이 값은 참으로 시작합니다. 백인의 A 루크가 움직이면 CanWhiteCastleA가 거짓이되고 H 루크도 마찬가지입니다. 백인의 왕이 움직이면 둘 다 거짓이됩니다. 그리고 블랙도 마찬가지입니다.
cdhowie

6

꽤 기본적인 체스 게임에 대한 제 아이디어는 다음과 같습니다.

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 {....}

0

나는 최근에 PHP로 체스 프로그램을 만들었고 ( website click here , source click here ) 객체 지향적으로 만들었습니다. 내가 사용한 수업은 다음과 같습니다.

  • ChessRulebook (정적)- generate_legal_moves()여기 에 모든 코드를 넣었습니다 . 그 방법은 순서가있는 보드와 출력의 세부 수준을 설정하는 몇 가지 변수가 제공되며 해당 위치에 대한 모든 법적 이동을 생성합니다. ChessMoves 목록을 반환합니다.
  • ChessMove- 시작 정사각형, 끝 정사각형, 색상, 조각 유형, 캡처, 확인, 체크 메이트, 판촉 조각 유형 및 통과를 포함하여 대수 표기법 을 만드는 데 필요한 모든 것을 저장합니다 . 선택적 추가 변수에는 명확성 (Rae4와 같은 이동의 경우), 캐슬 링 및 보드가 포함됩니다.
  • ChessBoard- 정사각형을 나타내는 8x8 배열을 포함하여 Chess FEN 과 동일한 정보를 저장하고 그 차례 인 ChessPieces를 저장합니다. 대상 정사각형, 캐슬 링 권한, 반 동작 시계 및 완전 이동 시계입니다.
  • ChessPiece-조각 유형, 색상, 사각형 및 조각 값을 저장합니다 (예 : 폰 = 1, 나이트 = 3, 루크 = 5 등).
  • ChessSquare-순위와 파일을 ints 로 저장합니다 .

나는 현재이 코드를 체스 AI로 바꾸려고 노력하고 있으므로 빠르다. generate_legal_moves()기능을 1500ms에서 8ms로 최적화했으며 아직 작업 중입니다. 그로부터 배운 교훈은 ...

  • 기본적으로 모든 ChessMove에 전체 ChessBoard를 저장하지 마십시오. 필요할 때만 이동 중에 보드를 보관하십시오.
  • int가능한 경우 와 같은 기본 유형을 사용하십시오 . 그렇기 때문에 "a4"와 같이 사람이 읽을 수있는 체스 스퀘어 표기법으로 영숫자 를 저장하는 대신 ChessSquare순위와 파일을로 int저장합니다 string.
  • 이 프로그램은 이동 트리를 검색 할 때 수만 개의 ChessSquares를 생성합니다. 나는 아마도 속도 향상을 가져다 줄 ChessSquares를 사용하지 않도록 프로그램을 리팩토링 할 것입니다.
  • 클래스에서 불필요한 변수를 계산하지 마십시오. 원래 내 체스 보드 각각에서 FEN을 계산하는 것은 프로그램의 속도를 정말로 죽였습니다. 나는 이것을 프로파일 러로 찾아야했다 .

나는 이것이 오래되었다는 것을 알고 있지만 누군가에게 도움이되기를 바랍니다. 행운을 빕니다!

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.