Progettare un gioco di carte

1

Voglio espandere il mio portafoglio di progetti personali, quindi ho deciso di creare un gioco di carte. Per essere più precisi, si chiama Macau. Ho letto questo answer su StackOverflow e ho provato a seguire i passaggi dalla prima risposta. Ho realizzato una narrativa in cui descrivo le regole generali del gioco e le sue particolarità.

Macao game. The minimum number of players is 2. The deck of cards initially contains 52 cards. Each card has the following attributes:

  • Suit (diamonds, clubs, hearts and spades)
  • Rank (2, 3, 4, 5, 6, 7, 8, 9, A, J, Q, K, Joker)

Starting the game, the deck is shuffled and every player receives 5 cards from it. The first player that remains out of cards is the winner. Afterwards, a card is taken from the deck and put on the table with the face up.

The current player looks at the card that's been put on the table and decides whether he can put a card from his packet on the table or not. A player can put a card on the table if their card is compatible with the other one.

Two cards are compatible if they accomplish one of the following conditions: they have the same rank or the same suit. Expanding upon the cards domain, there exist several special cards. These have to obey the specified rules too, but they have an extra attribute: the special ability. They may have any suit, but these ranks qualify a card to be special: 2, 3, 4, 7, A, and Joker. The Joker is a wildcard as it doesn’t need to obey to the earlier specified rule. That is, the player can put it on the table at any time.

The next section gives a thorough description of the mentioned cards’ special abilities:

  • Rank: 7. When put on the table, this card requires the current player to specify a suit. The next player is obliged to put any card that has the earlier specified suit, otherwise he will receive a new card from the deck.

  • Rank: 2 and 3. This card obliges the next player to receive 2/3 (2 cards if the rank is 2, same rule for 3) cards from the deck. Now, if the next player also has a 2 or a 3 with the same suit, or a Joker, then he can put it on the table and the next player has to receive the total amount of cards. The same rules apply in a circular manner. If the next player has a 4 with the same suit, then he can stop the obligation injected by the previous player.

  • Rank: 4. Special ability: it can stop an obligation injected by the previous player. If it is put over a regular card (i.e. a card that has no special ability), then it acts like one.

  • Rank: A (Ace). Special ability: it skips the next player's turn.
  • Rank: Joker. It is a wildcard so it can be put on the table at almost all instances. The exception arises when the card that is on the top of the table is an A (Ace), then the current player's turn is skipped. Special ability: the same as the 2/3 card, except that the next player will have to receive 5 cards (for the Black Joker) or 10 (in case of the Red one).

Having the rules clarified, the game continues. If the current player owns such a compatible card (or more), then it's his choice whether to put one on the table or receive a new card from the deck. Also, while in game, there can appear the situation when the deck would be emptied. In that case, all the cards from the table that are located under the top one are taken, shuffled, and used to refill the deck. The card on the top is untouched.

Now it's the next player's turn, and the earlier described steps repeat until one of the players remains out of cards. That player is the winner.

Da questa narrazione ho estratto le seguenti classi:

  • Scheda
  • Deck
  • Player (forse Hand?)
  • Tabella (che contiene il mazzo e la pila di carte già giocate)
  • gioco

Ecco i miei problemi.

  1. Non sono sicuro di aver fatto le giuste astrazioni.
  2. Non saprei come progettare le carte speciali. Dovrebbe esistere una classe separata (ereditata dalla carta) per ogni carta speciale? O dovrei usare la composizione? Inoltre, come devo implementare le carte Joker? Non hanno un completo, hanno solo il grado.
  3. La classe di gioco dovrebbe gestire i turni dei giocatori e i casi eccezionali (quei casi in cui la prima carta sarebbe stata speciale)?
  4. Dovrebbe Player essere solo un'interfaccia? Ciò lascerebbe spazio per funzionalità aggiuntive come un giocatore di IA (che implementa il Player).

Questo è il mio primo progetto orientato agli oggetti di medie dimensioni e voglio essere sicuro di essere sulla strada giusta.

    
posta I. S. 25.06.2018 - 20:41
fonte

5 risposte

4

I'm not sure if I made the right abstractions.

Bene, è un inizio, ma lo scoprirai solo se smetterai di pensarlo e inizi a scrivere del codice usando queste astrazioni. Questo codice potrebbe essere test, vera logica di gioco o entrambi.

I wouldn't know how to design the special cards. Should there exist a separate (inherited from Card) class for every special card? Or should I use composition?

Nessuno dei due. Se ho capito bene, essere "speciale" è solo una proprietà che può essere derivata dal rango. Quindi, quando una classe di carte ha un attributo Rank (così come Suit ), contiene tutte le informazioni necessarie alla logica di gioco per prendere una decisione sulle "abilità speciali".

Also, how should I implement the Joker cards? They don't really have a Suit, they just have the Rank.

Semplice: presenta un quinto completo "Nessuno" per i Jolly. Il costruttore della tua carta può lanciare un'eccezione se qualcuno tenta di inizializzare una carta con un seme "Nessuno" per non-jolly o una carta con un grado "Joker" e un seme diverso.

Should the Game class manage players' turns and the exceptional cases (those cases when the top card would be a special one)?

Da qualche parte devi implementare la logica del gioco. Metterlo inizialmente nella classe Game è un inizio ragionevole. Se questa classe si evolve e diventa troppo grande / assume troppe responsabilità, prova ad identificare tali responsabilità, dare loro un nome e rifattorizzarle per separare le classi. Non c'è bisogno di sovra-analizzare questo in anticipo.

Should Player be just an interface? This would leave space for additional features like an AI player (that implements Player).

Ancora una volta, non è necessario prendere questa decisione ora . Inizia con una lezione di un giocatore e quando arrivi al punto in cui hai bisogno di un HumanPlayer e di un AIPlayer , è abbastanza presto per fare il refactoring.

    
risposta data 26.06.2018 - 07:15
fonte
2

Motore di gioco di carte

Hai un buon vantaggio, ma non pensare che tu debba assolutamente avere ogni classe possibile prima di iniziare a programmare. Il codice si evolve naturalmente per aggiungere funzionalità aggiuntive o estendere le classi esistenti.

Se posso dare una raccomandazione, prova a separare la parte del tuo programma che si occupa della logica generica di "gioco di carte", come trattare carte da un mazzo, giocatore che gira, scarta, disegna, in pratica qualsiasi cosa tenderebbe a voglio fare in un gioco di carte.

Quindi trasformo la tua classe Game in una classe astratta per la gestione di un gioco di carte, con metodi di base per permetterti di eseguire queste funzioni basilari come disegnare, scartare, cambiare turni, ecc.

MacaoGame

Estendi Game per essere MacaoGame , e MacaoGame è il "cervello" della gestione della logica di gioco. Tutto ciò che MacaoGame richiede specificamente per gestire questo particolare tipo di gioco può essere aggiunto direttamente alla classe MacaoGame , ma dovresti trattarlo come una parte separata del tuo programma (la parte che ti aiuta a implementare MacaoGame , e non la parte che si occupa del tuo motore di gioco di carte di base).

Ad esempio, forse crei un enum per identificare le carte che hanno un significato speciale (in Java, puoi passare direttamente la carta al suo costruttore, in modo da poter facilmente verificare se una carta è SuitChangeCard per esempio). In questo modo sarebbe facile cambiare in un secondo momento, e inoltre non cambia il motore del gioco di carte sottostante, rendendolo una decisione di progettazione molto flessibile.

Estensione del giocatore?

Per ora, Player può essere una classe di implementazione che contiene tutto ciò che devi sapere su un giocatore. Tuttavia, se il comportamento del giocatore differisce se il giocatore è umano o un'intelligenza artificiale, allora sì, questa sarebbe la prima classe a diventare astratta ed estendere. Penso che la parte cruciale del giocatore sia il gioco reale, quindi stai chiedendo un input. L'input può venire da un giocatore umano o un'IA potenzialmente, e mentre questo non fa troppa differenza, dovresti notare che uno è sincrono mentre l'altro no.

Potrebbe quindi valere la pena utilizzare un callback per ricevere input (a meno che non ti dispiaccia che il thread in esecuzione sia sospeso in attesa dell'input dell'utente).

Joker

La carta Joker è la sua carta. Nel motore del gioco di carte, parte del tuo programma, puoi gestire il grado, ma non mettere troppa enfasi su di esso, dato che il grado può cambiare in base al gioco. Ad esempio, in alcuni giochi, l'asso è considerato inferiore al 2. Tratta come qualsiasi altra carta ovunque tranne in MacaoGame e dovresti stare bene.

Conclusione

Sei decisamente sulla strada giusta. Non penso che potresti incontrare seri problemi se combinassi MacaoGame logic con il resto, tuttavia è un progetto un po 'meno flessibile se vuoi implementare altri giochi di carte. Ti aiuterà anche a organizzare lo sviluppo nella tua mente come parte del programma che riguarda la funzionalità del motore di gioco di carte e il gioco specifico di Macao. Buona fortuna!

    
risposta data 26.06.2018 - 08:55
fonte
1

Per giochi di modellazione come questo, dimentica OO. Va bene per l'interfaccia utente, ma finché non puoi creare un semplice modello del gioco che ti dice chi è e che cosa è la mossa valida ti farà solo finire nei guai.

Tutti i giochi a turni seguono lo stesso schema. un giocatore ha il controllo e fa giocare fino a quando non passa il controllo su un altro giocatore o il gioco è finito.

Nel tuo caso le mosse sono enumerabili come tutte le carte nel mazzo, con qualche extra per le offerte speciali con opzioni, come la specifica del seme seguente. (Dovrai decidere se è meglio rappresentare due mosse o una)

1h, 2h, 3h .... 1c, 2c ... Jr, Jb ecc.

i giocatori hanno un ordine in cui il gioco procede e le loro mosse sono limitate alle carte nelle loro mani.

Ora puoi modellare il gioco in modo molto semplice con alcuni array di stringhe. Puoi persino fare dei semplici robot che scelgono solo una carta a caso.

Una volta che hai funzionato. Quindi inizia a preoccuparti di ciò che le astrazioni hanno senso. Ma potrebbero finire per non avere alcuna relazione con l'apparato fisico richiesto per gli umani per giocare

    
risposta data 25.06.2018 - 23:16
fonte
0

Regola generale per il controllo del design

Esistono due semplici regole per verificare se il tuo progetto è conforme ai principi orientati agli oggetti:

  1. Tutti i nomi (oggetto e metodo) significano qualcosa nel dominio. Ciò significa che nessun nome di servizio , responsabile , nessun metodo execute () e così via.

  2. Non ci sono dati trapelati dagli oggetti. Ciò significa che nessun metodo restituisce solo i dati che erano già disponibili. Cioè nessun metodo getter .

responsabilità

Ora, non sto dicendo che questo dovrebbe sempre essere fatto con ogni progetto senza compromessi. Ma per il tuo caso, e se vuoi davvero farlo bene, non dovresti allontanarti da questo percorso.

Questo non sarà facile all'inizio, ma prima o poi ti porterà nella giusta mentalità. Inoltre garantisce quasi automaticamente la conformità a principi come: The Law of Demeter , Principio di responsabilità singola , e così via.

    
risposta data 26.06.2018 - 09:22
fonte
0

Diamo un'occhiata prima alle carte:

4 semi, 13 gradi. Per facilitare la color-wish-card, si può aggiungere un secondo seme o dividere quel grado in uno per seme. Il primo può essere più facilmente utilizzato per molti altri giochi di carte.

Quindi, 2 bit per seme, 2 bit per il seme successivo, 4 bit per grado, rende un ottetto. Il rango zero deve essere riservato, quindi "zero" significa "non una carta" e hai ancora molti valori da lasciare per altre considerazioni.

Crea un vettore per mano di ogni giocatore, pila degli scarti e pila per il ridisegno, non ne hai bisogno per il mazzo, aggiungi una funzione per ottenerne uno completamente nuovo. Ora hai solo bisogno di qualche altra funzione per fare effettivamente le cose e sei pronto.

    
risposta data 26.06.2018 - 15:01
fonte

Leggi altre domande sui tag