मुझे लगता है कि यहाँ समस्या यह है कि आपने यह स्पष्ट रूप से नहीं बताया है कि किन कार्यों को किन वर्गों द्वारा संचालित किया जाना है। मैं बताऊंगा कि मुझे क्या लगता है कि प्रत्येक वर्ग को क्या करना चाहिए, इसका एक अच्छा वर्णन है, फिर मैं जेनेरिक कोड का एक उदाहरण दूंगा जो विचारों को दिखाता है। हम देखेंगे कि कोड कम युग्मित है, और इसलिए इसमें वास्तव में परिपत्र संदर्भ नहीं हैं।
आइए प्रत्येक वर्ग क्या करता है इसका वर्णन करने के साथ शुरू करें।
GameState
वर्ग केवल खेल की वर्तमान स्थिति के बारे में जानकारी शामिल करना चाहिए। इसमें इस बारे में कोई जानकारी नहीं होनी चाहिए कि खेल की पिछली अवस्थाएँ या भविष्य की चालें क्या संभव हैं। इसमें केवल इस बारे में जानकारी होनी चाहिए कि शतरंज में कौन से टुकड़े हैं, या बैकगैमौन में कितने और किस प्रकार के चेकर्स हैं। GameState
शतरंज में या बैकगैमौन में दोहरीकरण घन के बारे में कैसलिंग बारे में जानकारी, कुछ अतिरिक्त जानकारी शामिल करने के लिए होगा।
Move
वर्ग एक छोटे से मुश्किल है। मैं कहूंगा कि मैं इस कदम को खेलने से GameState
उस परिणाम को निर्दिष्ट करके खेलने के लिए एक कदम निर्दिष्ट कर सकता हूं । तो आप सोच सकते हैं कि एक चाल को एक के रूप में लागू किया जा सकता है GameState
। हालाँकि, जाने में (उदाहरण के लिए) आप कल्पना कर सकते हैं कि बोर्ड पर एक बिंदु को निर्दिष्ट करके एक चाल को निर्दिष्ट करना बहुत आसान है। हम चाहते हैं कि Move
इनमें से किसी भी मामले को संभालने के लिए हमारी कक्षा पर्याप्त लचीली हो। इसलिए Move
वर्ग वास्तव में एक विधि के साथ एक इंटरफ़ेस होने वाला है जो एक पूर्व-चाल लेता है GameState
और एक नया पोस्ट-मूव लौटाता हैGameState
।
अब RuleBook
वर्ग नियमों के बारे में सब कुछ जानने के लिए जिम्मेदार है। इससे तीन चीजों को तोड़ा जा सकता है। यह जानना आवश्यक है कि प्रारंभिक क्या हैGameState
है, यह जानने की जरूरत है कि कौन सी चाल कानूनी हैं, और यह बताने में सक्षम होने की आवश्यकता है कि क्या खिलाड़ियों में से कोई एक जीता है।
आप GameHistory
उन सभी चालों पर नज़र रखने के लिए एक वर्ग भी बना सकते हैं जो सभी किए गए हैं और GameStates
जो कुछ हुआ है। एक नया वर्ग आवश्यक है क्योंकि हमने तय किया कि इससे पहले आने वाले GameState
सभी GameState
एस को जानने के लिए एक जिम्मेदार नहीं होना चाहिए ।
यह उन कक्षाओं / इंटरफेस को समाप्त करता है जिन पर मैं चर्चा करूंगा। आपकी भी Board
क्लास है। लेकिन मुझे लगता है कि अलग-अलग खेलों में बोर्ड काफी अलग हैं कि यह देखना मुश्किल है कि बोर्डों के साथ उदारता से क्या किया जा सकता है। अब मैं जेनेरिक इंटरफेस देने और जेनेरिक कक्षाओं को लागू करने के लिए जाऊंगा।
सबसे पहले है GameState
। चूंकि यह वर्ग पूरी तरह से विशेष खेल पर निर्भर है, इसलिए कोई सामान्य Gamestate
इंटरफ़ेस या वर्ग नहीं है।
अगला है Move
। जैसा कि मैंने कहा, यह एक ऐसे इंटरफ़ेस के साथ दर्शाया जा सकता है जिसमें एक एकल विधि है जो एक पूर्व-चाल राज्य को ले जाती है और एक बाद की चाल की स्थिति पैदा करती है। यहाँ इस इंटरफ़ेस के लिए कोड है:
package boardgame;
/**
*
* @param <T> The type of GameState
*/
public interface Move<T> {
T makeResultingState(T preMoveState) throws IllegalArgumentException;
}
ध्यान दें कि एक प्रकार का पैरामीटर है। यह इसलिए है, उदाहरण के लिए, ChessMove
पूर्व-चाल के विवरण के बारे में जानने की आवश्यकता होगी ChessGameState
। इसलिए, उदाहरण के लिए, वर्ग की घोषणा ChessMove
होगी
class ChessMove extends Move<ChessGameState>
,
जहाँ आपने पहले से ही एक ChessGameState
वर्ग को परिभाषित किया होगा ।
आगे मैं सामान्य RuleBook
वर्ग पर चर्चा करूंगा । यहाँ कोड है:
package boardgame;
import java.util.List;
/**
*
* @param <T> The type of GameState
*/
public interface RuleBook<T> {
T makeInitialState();
List<Move<T>> makeMoveList(T gameState);
StateEvaluation evaluateState(T gameState);
boolean isMoveLegal(Move<T> move, T currentState);
}
फिर से GameState
कक्षा के लिए एक प्रकार का पैरामीटर है । चूंकि यह RuleBook
जानना चाहिए कि प्रारंभिक अवस्था क्या है, इसलिए हमने प्रारंभिक अवस्था देने के लिए एक विधि रखी है। चूंकि RuleBook
माना जाता है कि कौन सी चालें कानूनी हैं, हमारे पास यह परीक्षण करने के तरीके हैं कि क्या किसी दिए गए राज्य में एक चाल कानूनी है और किसी दिए गए राज्य के लिए कानूनी चालों की एक सूची देने के लिए। अंत में, मूल्यांकन करने के लिए एक विधि है GameState
। सूचना RuleBook
केवल यह बताने के लिए ज़िम्मेदार होनी चाहिए कि क्या एक या दूसरे खिलाड़ी पहले ही जीत चुके हैं, लेकिन ऐसा नहीं है जो खेल के बीच में बेहतर स्थिति में है। यह तय करना कि कौन बेहतर स्थिति में है, एक जटिल चीज है जिसे अपनी कक्षा में स्थानांतरित किया जाना चाहिए। इसलिए StateEvaluation
वर्ग वास्तव में केवल एक सरल एनम निम्नानुसार है:
package boardgame;
/**
*
*/
public enum StateEvaluation {
UNFINISHED,
PLAYER_ONE_WINS,
PLAYER_TWO_WINS,
DRAW,
ILLEGAL_STATE
}
अन्त में, GameHistory
कक्षा का वर्णन करते हैं । यह वर्ग उन सभी पदों को याद रखने के लिए ज़िम्मेदार है जो खेल में पहुँच गए थे और साथ ही चालें भी खेली गई थीं। मुख्य बात यह करने में सक्षम होना चाहिए एक के Move
रूप में खेला रिकॉर्ड है । आप पूर्ववत् Move
s के लिए कार्यक्षमता भी जोड़ सकते हैं । मेरे पास एक कार्यान्वयन है।
package boardgame;
import java.util.ArrayList;
import java.util.List;
/**
*
* @param <T> The type of GameState
*/
public class GameHistory<T> {
private List<T> states;
private List<Move<T>> moves;
public GameHistory(T initialState) {
states = new ArrayList<>();
states.add(initialState);
moves = new ArrayList<>();
}
void recordMove(Move<T> move) throws IllegalArgumentException {
moves.add(move);
states.add(move.makeResultingState(getMostRecentState()));
}
void resetToNthState(int n) {
states = states.subList(0, n + 1);
moves = moves.subList(0, n);
}
void undoLastMove() {
resetToNthState(getNumberOfMoves() - 1);
}
T getMostRecentState() {
return states.get(getNumberOfMoves());
}
T getStateAfterNthMove(int n) {
return states.get(n + 1);
}
Move<T> getNthMove(int n) {
return moves.get(n);
}
int getNumberOfMoves() {
return moves.size();
}
}
अंत में, हम एक Game
साथ सब कुछ बाँधने के लिए एक वर्ग बनाने की कल्पना कर सकते थे। यह Game
वर्ग उन तरीकों को उजागर करने वाला है जो लोगों के लिए यह देखना संभव बनाता है कि वर्तमान क्या GameState
है, यह देखें कि यदि किसी के पास एक है, तो देखें कि कौन सी चाल चल सकते हैं, और एक चाल खेल सकते हैं। मेरे पास एक कार्यान्वयन है
package boardgame;
import java.util.List;
/**
*
* @author brian
* @param <T> The type of GameState
*/
public class Game<T> {
GameHistory<T> gameHistory;
RuleBook<T> ruleBook;
public Game(RuleBook<T> ruleBook) {
this.ruleBook = ruleBook;
final T initialState = ruleBook.makeInitialState();
gameHistory = new GameHistory<>(initialState);
}
T getCurrentState() {
return gameHistory.getMostRecentState();
}
List<Move<T>> getLegalMoves() {
return ruleBook.makeMoveList(getCurrentState());
}
void doMove(Move<T> move) throws IllegalArgumentException {
if (!ruleBook.isMoveLegal(move, getCurrentState())) {
throw new IllegalArgumentException("Move is not legal in this position");
}
gameHistory.recordMove(move);
}
void undoMove() {
gameHistory.undoLastMove();
}
StateEvaluation evaluateState() {
return ruleBook.evaluateState(getCurrentState());
}
}
इस वर्ग में देखें कि RuleBook
वर्तमान क्या है, यह जानने के लिए जिम्मेदार नहीं GameState
है। यही GameHistory
काम है। तो यह Game
पूछता है GameHistory
कि वर्तमान स्थिति क्या है और यह जानकारी यह बताने के लिए देता है कि कानूनी कदम क्या हैं या यदि कोई जीता है, तो यह कहने RuleBook
की Game
आवश्यकता है।
वैसे भी, इस उत्तर की बात यह है कि एक बार जब आपने प्रत्येक वर्ग के लिए क्या जिम्मेदार है, इसका उचित निर्धारण कर लिया है, और आप प्रत्येक वर्ग को कम संख्या में जिम्मेदारियों पर ध्यान केंद्रित करते हैं, और आप प्रत्येक जिम्मेदारी को एक अद्वितीय वर्ग को सौंपते हैं, तो कक्षाएं डिकोड हो जाते हैं, और सब कुछ कोड के लिए आसान हो जाता है। उम्मीद है कि मेरे द्वारा दिए गए कोड उदाहरणों से स्पष्ट है।
RuleBook
उदाहरण के लिएState
एक तर्क के रूप में लिया गया , और वैध लौटाMoveList
, यानी "यहाँ हम अभी क्या हैं, आगे क्या किया जा सकता है?"