शतरंज के खेल के लिए ऑब्जेक्ट ओरिएंटेड डिज़ाइन [बंद]


88

मैं ऑब्जेक्ट ओरिएंटेड तरीके से डिजाइन और सोचने का तरीका पाने की कोशिश कर रहा हूं और इस विषय पर समुदाय से कुछ प्रतिक्रिया प्राप्त करना चाहता हूं। निम्नलिखित एक शतरंज के खेल का एक उदाहरण है जिसे मैं एक OO तरीके से डिजाइन करना चाहता हूं। यह एक बहुत व्यापक डिजाइन है और इस स्तर पर मेरा ध्यान बस यह पहचानना है कि कौन से संदेश के लिए जिम्मेदार है और खेल को अनुकरण करने के लिए ऑब्जेक्ट एक-दूसरे से कैसे बातचीत करते हैं। कृपया ध्यान दें कि यदि खराब डिज़ाइन (उच्च युग्मन, खराब सामंजस्य आदि) के तत्व हैं और उन पर सुधार कैसे किया जाए।

शतरंज के खेल में निम्न वर्ग हैं

  • मंडल
  • खिलाड़ी
  • टुकड़ा
  • वर्ग
  • शतरंज का खेल

बोर्ड वर्गों से बना है और इसलिए बोर्ड को स्क्वायर ऑब्जेक्ट बनाने और प्रबंधित करने के लिए जिम्मेदार बनाया जा सकता है। प्रत्येक टुकड़ा एक वर्ग पर भी होता है इसलिए प्रत्येक टुकड़े में उस वर्ग का संदर्भ भी होता है जो उस पर है। (इसका कोई मतलब भी है क्या?)। प्रत्येक टुकड़ा तब एक वर्ग से दूसरे में खुद को स्थानांतरित करने के लिए जिम्मेदार होता है। खिलाड़ी वर्ग अपने पास मौजूद सभी टुकड़ों का संदर्भ रखता है और उनके निर्माण के लिए भी ज़िम्मेदार है (खिलाड़ी को टुकड़ों का निर्माण करना चाहिए?)। प्लेयर में एक मेथड टेकटर्न होता है जो बदले में एक मेथड मूव को कॉल करता है जो कि पीस क्लास से संबंधित होता है जो कि अपने वर्तमान स्थान से दूसरे स्थान पर टुकड़े के स्थान को बदलता है। अब मैं इस बात पर असमंजस में हूँ कि बोर्ड वर्ग के लिए वास्तव में क्या जिम्मेदार होना चाहिए। मुझे लगा कि खेल की वर्तमान स्थिति को निर्धारित करने और यह जानने की आवश्यकता है कि खेल कब खत्म हुआ है। लेकिन जब एक टुकड़ा इसे बदल देता है ' स्थान को बोर्ड को कैसे अपडेट करना चाहिए? यह वर्गों के एक अलग सरणी बनाए रखना चाहिए जिस पर टुकड़े मौजूद हैं और जो टुकड़ों के रूप में अद्यतन प्राप्त करता है?

इसके अलावा, 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 टिप्पणी: P2 को कॉल नहीं करना चाहिए takeTurn()यदि p1 की चाल खेल को समाप्त करती है। कम नाइटिककी टिप्पणी: मुझे खिलाड़ियों को बुलाना और अधिक स्वाभाविक लगता है whiteऔर black
क्रिस्टोफर जॉनसन

माना। लेकिन जैसा कि मैंने कहा, मैं डिजाइन पहलुओं में अधिक दिलचस्पी रखता हूं और किन कार्यों के लिए वस्तुओं को जिम्मेदार होना चाहिए और कौन से संदर्भ रखते हैं।
सिड

मुझे अच्छा लगा जैसे आपने अपने स्निपेट में ऊपर उल्लिखित किया। मेरे कार्यान्वयन में, प्रत्येक टुकड़े में पूरी स्थिति की आंतरिक प्रति है क्योंकि यह इसे अपने canMove()कार्य में उपयोग करेगा । और जब चाल हो जाती है, तो अन्य सभी टुकड़े बोर्ड की अपनी आंतरिक प्रति को अपडेट करते हैं। मुझे पता है कि यह इष्टतम नहीं है, लेकिन यह उस समय दिलचस्प था C ++ सीखना। बाद में, एक गैर-शतरंज खिलाड़ी मित्र ने मुझे बताया कि वह classesप्रत्येक टुकड़े के बजाय प्रत्येक वर्ग के लिए होगा । और वह टिप्पणी मुझ पर बहुत दिलचस्प लगी।
eigenfield

जवाबों:


54

मैं वास्तव में सिर्फ एक शतरंज बोर्ड, टुकड़े, नियम, आदि की एक पूरी सी # कार्यान्वयन लिखा था यहाँ मोटे तौर पर बताया गया है कि मैं इसे मॉडलिंग (हटाया वास्तविक क्रियान्वयन के बाद से मैं लेने के लिए नहीं करना चाहते हैं सब मज़ा अपने कोडिंग में से):

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जो गेम मेटाडेटा के भंडारण के लिए जिम्मेदार है जैसे कि कौन खेल रहा था, जब खेल हुआ, आदि।

संपादित करें: ध्यान दें कि इस उत्तर का उद्देश्य वास्तव में आपको टेम्पलेट कोड देने के लिए नहीं है जिसे आप भर सकते हैं - मेरे कोड में वास्तव में प्रत्येक आइटम पर कुछ अधिक जानकारी संग्रहीत है, अधिक विधियाँ, आदि उद्देश्य आपको दिशा देने के लिए है। लक्ष्य आप प्राप्त करने की कोशिश कर रहे हैं।


1
विस्तृत उत्तर के लिए धन्यवाद। हालाँकि, डिज़ाइन के संबंध में मेरे कुछ प्रश्न हैं। उदाहरण के लिए, यह तुरंत स्पष्ट नहीं है कि क्यों हटना एक वर्ग होना चाहिए। मेरा एकमात्र ध्यान जिम्मेदारियों को सौंपना है और कक्षाओं के बीच बातचीत को सबसे साफ संभव तरीके से तय करना है। मैं किसी भी डिजाइन निर्णय के पीछे "क्यों" जानना चाहता हूं। मैं इस बात पर स्पष्ट नहीं हूं कि आप उन डिजाइन निर्णयों पर कैसे पहुंचे जो आपने किए और वे अच्छे विकल्प क्यों हैं।
सिड

Moveएक वर्ग है ताकि आप एक चाल सूची में पूरे चाल इतिहास को स्टोर कर सकते हैं, नोटेशन और सहायक जानकारी के साथ जैसे कि क्या टुकड़ा पकड़ा गया था, क्या एक मोहरा को बढ़ावा दिया गया हो सकता है, आदि
cdhowie

@ LCDhowie क्या आप Gameकिसी कार्यान्वयनकर्ता के लिए डेलिगेट कर रहे हैं IGameRulesया आप ऑब्जेक्ट के बाहर नियम लागू करते हैं? उत्तरार्द्ध सहज लगता है क्योंकि खेल की रक्षा नहीं कर सकता है यह खुद का राज्य नहीं है?
plalx

1
यह बेवकूफी हो सकती है, लेकिन गेम क्लास में ट्राईकोलर की बजाय पीस टाइप का नहीं होना चाहिए?
डेनिस वैन गिल्स

1
@ मिलनिल वे संकेत देते हैं कि दोनों खिलाड़ी किस दिशा में अभी भी महल (ए और एच फ़ाइलों की ओर) हो सकते हैं। ये मूल्य सच होने लगते हैं। यदि सफेद ए का बदमाश चलता है, तो कैनव्हाइटकैस्टले को झूठा बना दिया जाता है, और इसी तरह एच बदमाश के लिए। यदि श्वेत राजा चलता है, तो दोनों झूठे बन जाते हैं। और काले के लिए एक ही प्रक्रिया।
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 में एक शतरंज कार्यक्रम बनाया ( वेबसाइट यहाँ क्लिक करें , स्रोत यहाँ क्लिक करें ) और मैंने इसे ऑब्जेक्ट ओरिएंटेड बनाया। यहां वे कक्षाएं हैं जिनका मैंने उपयोग किया है।

  • शतरंज की किताब (स्थिर) - मैंने अपना सारा generate_legal_moves()कोड यहाँ डाल दिया है। उस पद्धति को एक बोर्ड दिया जाता है, जिसकी बारी है, और आउटपुट के विस्तार के स्तर को निर्धारित करने के लिए कुछ चर हैं, और यह उस स्थिति के लिए सभी कानूनी चालें उत्पन्न करता है। यह ChessMoves की एक सूची देता है।
  • ChessMove - बीजीय संकेतन बनाने के लिए आवश्यक सभी चीजें संग्रहीत करता है , जिसमें वर्ग शुरू करना, वर्ग, रंग, टुकड़ा प्रकार, कब्जा, चेक, चेकमेट, पदोन्नति टुकड़ा प्रकार, और एन पास शामिल है। वैकल्पिक अतिरिक्त चर में वितरण (Rae4 जैसे चाल के लिए) शामिल हैं, कास्टिंग, और बोर्ड।
  • ChessBoard - शतरंज FEN के रूप में वही जानकारी संग्रहीत करता है, जिसमें वर्गों का प्रतिनिधित्व करने वाला एक 8x8 सरणी शामिल है और शतरंजपार्ट्स को संग्रहीत करते हैं, जिनकी बारी है, एन पासेंट टारगेट स्क्वायर, कालिंग अधिकार, हाफमॉव घड़ी, और फुलमोव घड़ी।
  • शतरंजपॉइस - स्टोर का टुकड़ा प्रकार, रंग, वर्ग और टुकड़ा मूल्य (उदाहरण के लिए, प्यादा = 1, नाइट = 3, किश्ती = 5, आदि)
  • ChessSquare - intएस के रूप में रैंक और फ़ाइल संग्रहीत करता है ।

मैं वर्तमान में इस कोड को शतरंज AI में बदलने का प्रयास कर रहा हूं, इसलिए इसे FAST होना चाहिए। मैंने generate_legal_moves()फ़ंक्शन को 1500ms से 8ms तक अनुकूलित किया है , और अभी भी इस पर काम कर रहा हूं। सबक जो मैंने उससे सीखा है ...

  • डिफ़ॉल्ट रूप से प्रत्येक ChessMove में पूरे ChessBoard को संग्रहीत न करें। जरूरत पड़ने पर ही बोर्ड को मूव में स्टोर करें।
  • intजब संभव हो तो आदिम प्रकारों का उपयोग करें । इसीलिए मानव पठनीय शतरंज वर्ग संकेतन जैसे "a4" के साथ अल्फ़ान्यूमेरिक को संग्रहीत करने के बजाय ChessSquareरैंक और फ़ाइल को intसंग्रहीत करता है string
  • चाल पेड़ की खोज करते समय कार्यक्रम दसियों हजार शतरंज बनाता है। मैं शायद ChessSquares का उपयोग नहीं करने के लिए कार्यक्रम को फिर से तैयार करूंगा, जिसे गति को बढ़ावा देना चाहिए।
  • अपनी कक्षाओं में किसी भी अनावश्यक चर की गणना न करें। मूल रूप से, मेरे प्रत्येक ChessBoards में FEN की गणना वास्तव में कार्यक्रम की गति को मार रही थी। मुझे यह पता लगाना था एक प्रोफाइलर के साथ ।

मुझे पता है कि यह पुराना है, लेकिन उम्मीद है कि यह किसी की मदद करता है। सौभाग्य!

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.