C ++ Progettazione scacchiera e puntatori intelligenti [chiusa]

0

Ho scritto un motore di scacchi in Java e lo sto trasferendo su C ++. Sono nuovo di C ++.

L'idea:

Ho un oggetto Board che contiene una matrice bidimensionale di oggetti Piece . Queen, Rook, Bishop, ecc. Sono sottoclassi di Piece. Hanno tutti un metodo getPossibleMoves (). Pertanto hanno bisogno di avere un riferimento alla scheda per essere in grado di generare le mosse possibili.

L'implementazione:

Classe della scheda:

class Board
{
public:
    using PiecePtr = std::shared_ptr<Piece>;
    std::array<std::array<PiecePtr, BOARD_SIZE>, BOARD_SIZE> myPieces;
};

Classe pezzo:

class Piece
{
public:
    std::weak_ptr<Board> myBoard
}; 

Ho usato shared_ptr e weak_ptr perché la Board può / dovrebbe esistere senza Pezzi. Questo approccio è corretto?

Nota : so che questo approccio OOP per rappresentare la scacchiera non è il massimo per le prestazioni. Altri approcci ( Bitboard per esempio) saranno più efficienti.

    
posta Romain 30.09.2014 - 13:58
fonte

3 risposte

4

Questo è un caso di over-design prematuro.

I pezzi non hanno bisogno di un puntatore al tabellone. Basta passare la scheda al metodo 'generateMoves'. Non hai nemmeno bisogno di oggetti per i singoli scacchi. Tutti i pedoni sono uguali, quindi hai solo bisogno di una sola istanza di pedone.

Inoltre, secondo le leggi degli scacchi, c'è uno stato di gioco che è anche necessario per generare correttamente le mosse. I pegni possono catturare e.p. ma solo dopo un precedente anticipo di due quadrati. Un re può arroccare con una torre ma non se uno dei due si è precedentemente spostato.

Il mio suggerimento è di dimenticare tutte le cose del design di classe. Inizia con una singola classe ChessGame con due metodi: generateMoves e makeMove. Scrivilo prima per prova. Inizia avendo solo due re sulla scacchiera. Quindi aggiungi i diversi tipi di scacchi uno alla volta. Estrai le classi come ritieni opportuno.

    
risposta data 30.09.2014 - 19:31
fonte
0

Un piccolo "essere lì, fatto quel" suggerimento ... migrare il mio motore di scacchi da una rappresentazione della scheda 0x88 al bitboard era relativamente semplice. Ottenere un miglioramento delle prestazioni è stato molto più difficile di quanto mi aspettassi.

Il fatto è che puoi avere un design meraviglioso per la tua classe tavola / pezzo, perfetta modularità, dipendenze minime tra le classi ... ma è sicuro che devi aver organizzato la tua logica di valutazione a seconda di cosa puoi fare veloce o lento (e questo è collegato alla rappresentazione della scheda).

La modifica della rappresentazione della scheda determinerà cambiamenti nella logica di valutazione statica: è complessa, dispendiosa in termini di tempo e rischiosa.

Inoltre, mentre cambi la logica di valutazione, scopri che alcune estensioni di ricerca non sono più necessarie o che hai bisogno di una ricerca di quiescenza migliore.

Alla fine potresti avere un giocatore completamente diverso.

Quindi puoi iniziare con una rappresentazione "temporanea" della scheda ma tutto dovrebbe essere fatto tenendo presente la rappresentazione finale (i punti di forza / debole di una rappresentazione che non stai usando potrebbero essere inaspettati).

La tua implementazione è la tipica relazione Team / Membri. L'oggetto Team (la scacchiera) avrà dei puntatori ai suoi membri (i pezzi) e i membri avranno anche un puntatore posteriore all'oggetto Team.

Quindi weak_ptr viene usato per interrompere il ciclo di dipendenza: il "proprietario" usa shared_ptr e il "proprietario" usa un weak_ptr al suo genitore e lo converte temporaneamente in shared_ptr quando ha bisogno di accedere al suo genitore.

Quindi, da questo punto di vista, è un'implementazione corretta.

Tuttavia la funzione getPossibleMoves() richiederà la posizione del pezzo per eseguire il suo compito.

La funzione potrebbe scoprire la posizione di scansione della scheda, ma questo rallenterebbe la generazione del movimento. Per evitare la ridondanza dei dati, non devi memorizzare le coordinate all'interno di Piece e getPossibleMoves() avrà bisogno di due argomenti:

std::list<Move> ThePiece::getPossibleMoves(unsigned row, unsigned column)
{
  // ...
}

// ...
Board b;
// ...
auto moves = b[row][column]->getPossibleMoves(row, column);

Ora perché non rimuovere la dipendenza iniettata myBoard da Piece e aggiungere un altro argomento a getPossibleMoves ?

std::list<Move> ThePiece::getPossibleMoves(const Board &b, unsigned row, unsigned column)
{
  // ...
}

Dopo questa modifica, shared_ptr non è più necessario ... in questo modo cambieremo completamente il design.

    
risposta data 30.09.2014 - 16:53
fonte
-1

Controlla lo sfondo e i casi d'uso di weak_ptr. Non penso che questo sia uno di quelli.

weak_ptr viene in genere utilizzato come cache. Il proprietario di weak_ptr deve essere ok con weak_ptr che è null. Se weak_ptr è nullo, il proprietario può adattarsi costruendo una nuova istanza o altrimenti operando diversamente.

Nel tuo caso, un pezzo non può funzionare senza una tavola. Il weak_ptr dovrebbe essere un smart_ptr.

Vorrei andare più lontano e dire che non dovresti usare affatto i puntatori. L'intero sistema è ben definito senza alcun riferimento. Prova un design interamente basato sul valore. Ad esempio, la scheda per i pezzi potrebbe essere un riferimento anziché un puntatore.

    
risposta data 30.09.2014 - 17:26
fonte

Leggi altre domande sui tag