Come progettare un database sufficientemente flessibile da ospitare diversi giochi di carte molto diversi.
Di recente, abbiamo mostrato come utilizzare un database per archiviare i risultati dei giochi da tavolo. I giochi da tavolo sono divertenti, ma non sono l'unica versione online dei giochi classici in circolazione. Anche i giochi di carte sono molto popolari. Introducono un elemento di fortuna nel gameplay e in un buon gioco di carte c'è molto di più della semplice fortuna!
In questo articolo, ci concentreremo sulla creazione di un modello di dati per memorizzare partite, risultati, giocatori e punteggi. La sfida principale qui è la memorizzazione dei dati relativi a molti giochi di carte diversi. Potremmo anche considerare l'analisi di questi dati per determinare le strategie vincenti, migliorare le nostre capacità di gioco o costruire un avversario AI migliore.
I giochi a quattro carte che useremo nel nostro database
Poiché i giocatori non possono controllare la mano che ricevono, i giochi di carte combinano strategia, abilità e fortuna. Quel fattore fortuna dà a un principiante la possibilità di battere un giocatore esperto e rende i giochi di carte avvincenti. (Questo è diverso da giochi come gli scacchi, che si basano molto su logica e strategia. Ho sentito da molti giocatori che non sono interessati a giocare a scacchi perché non riescono a trovare avversari al loro livello di abilità.)
Ci concentreremo su quattro famosi giochi di carte:poker, blackjack, belot (o belote) e préférence. Ognuno di loro ha regole relativamente complesse e richiede del tempo per padroneggiarlo. Anche il rapporto tra fortuna e conoscenza è diverso per ogni gioco.
Daremo una rapida occhiata alle regole semplificate e alle specifiche per tutti e quattro i giochi di seguito. Le descrizioni dei giochi sono piuttosto scarse, ma abbiamo incluso abbastanza per mostrare le diverse modalità di gioco e le diverse regole che incontreremo durante il processo di progettazione del database.
Blackjack:
- Mazzo: Da uno a otto mazzi da 52 carte ciascuno; niente carte jolly
- Giocatori: Dealer e 1 o più avversari
- Unità utilizzata: Di solito denaro
- Regole di base: I giocatori ottengono 2 carte che solo loro possono vedere; il dealer riceve due carte, una scoperta e l'altra coperta; ogni giocatore decide di pescare più carte (o meno); il dealer pesca per ultimo. Alle carte sono assegnati valori in punti compresi tra 1 e 11.
- Possibili azioni del giocatore: Colpisci, stai in piedi, dividi, arrenditi
- Goal e condizioni di vittoria: La somma delle carte di un giocatore è maggiore di quella del dealer; se un giocatore supera 21, quel giocatore perde.
Poker (Texas Hold'Em):
- Mazzo: Standard (noto anche come seme francese) mazzo da 52 carte; niente carte jolly. Le carte sono spesso di colore rosso e nero.
- Giocatori: Da due a nove; i giocatori distribuiscono a turno
- Unità utilizzata:solitamente chip
- Regole di base: Ogni giocatore inizia ricevendo due carte; i giocatori piazzano le loro scommesse; tre carte vengono distribuite scoperte al centro del tavolo; i giocatori piazzano nuovamente le loro scommesse; una quarta carta viene posta al centro ei giocatori scommettono nuovamente; quindi viene piazzata la quinta e ultima carta e l'ultimo giro di scommesse è completato.
- Possibili azioni del giocatore: Passa, chiama, rilancia, Small Blind, Big Blind, Reraise
- Obiettivo: Combina la migliore mano possibile di cinque carte (dalle due carte nella mano del giocatore e dalle cinque carte al centro del tavolo)
- Condizione di vittoria:di solito per vincere tutte le fiches sul tavolo
Belot (variante croata di Belote):
- Mazzo: Di solito il tradizionale mazzo da 32 carte tedesco o ungherese; niente carte jolly
- Giocatori: Da due a quattro; di solito quattro giocatori in coppia di due
- Unità utilizzata: Punti
- Regole di base: Per una partita a quattro giocatori, ogni giocatore riceve sei carte in mano e due carte coperte; i giocatori prima fanno un'offerta per il seme di briscola; dopo aver determinato la briscola, prendono le due carte coperte e le mettono in mano; segue un round di dichiarazione, durante il quale vengono annunciate alcune combinazioni di carte per punti aggiuntivi; il gioco continua finché tutte le carte non sono state utilizzate.
- Possibili azioni del giocatore: Passare, Bid Suit, Dichiarazione, Throw Card
- Gol per la mano: Per vincere più della metà dei punti
- Condizione di vittoria: Sii la prima squadra a segnare 1001 punti o più
Preferenza:
- Mazzo: Molto spesso un mazzo da 32 carte tradizionale tedesco o ungherese; niente carte jolly
- Giocatori: Tre
- Unità: Punti
- Regole di base: A tutti i giocatori vengono distribuite 10 carte; due carte “gattino” o “artiglio” sono poste al centro del tavolo; i giocatori determinano se vogliono fare un'offerta su un seme; i giocatori decidono di giocare o meno.
- Possibili azioni del giocatore: Passa, dichiara il seme, gioca, non giocare, lancia la carta
- Obiettivo: Dipende dalla variante di Préférence in riproduzione; nella versione standard, l'offerente deve vincere un totale di sei prese.
- Condizione di vittoria: Quando la somma dei punteggi di tutti e tre i giocatori è 0, vince il giocatore con il minor numero di punti.
Perché combinare database e giochi di carte?
Il nostro obiettivo qui è progettare un modello di database in grado di memorizzare tutti i dati rilevanti per questi quattro giochi di carte. Il database potrebbe essere utilizzato da un'applicazione Web come luogo in cui archiviare tutti i dati rilevanti. Vogliamo memorizzare le impostazioni di gioco iniziali, i partecipanti al gioco, le azioni intraprese durante il gioco e il risultato di una singola mano, mano o presa. Dobbiamo anche tenere presente il fatto che a una partita possono essere associati uno o più accordi.
Da ciò che memorizziamo nel nostro database, dovremmo essere in grado di ricreare tutte le azioni che hanno avuto luogo durante il gioco. Useremo i campi di testo per descrivere le condizioni di vittoria, le azioni di gioco e i loro risultati. Questi sono specifici per ogni gioco e la logica dell'applicazione web interpreterà il testo e lo trasformerà secondo necessità.
Una rapida introduzione al modello
Questo modello ci consente di archiviare tutti i dati di gioco rilevanti, inclusi:
- Proprietà del gioco
- Elenco di partite e partite
- Partecipanti
- Azioni di gioco
Poiché i giochi differiscono in molti modi, useremo spesso varchar(256) tipo di dati per descrivere proprietà, mosse e risultati.
Giocatori, partite e partecipanti
Questa sezione del modello è composta da tre tabelle e viene utilizzata per memorizzare i dati sui giocatori registrati, le partite giocate e i giocatori che hanno partecipato.
Il player
la tabella memorizza i dati sui giocatori registrati. Il username
e email
gli attributi sono valori univoci. Il nick_name
l'attributo memorizza i nomi delle schermate dei giocatori.
La match
la tabella contiene tutti i dati di corrispondenza rilevanti. Generalmente, una partita è composta da una o più carte distribuite (note anche come round, mani o prese). Tutte le partite hanno regole stabilite prima dell'inizio del gioco. Gli attributi sono i seguenti:
game_id
– fa riferimento alla tabella contenente l'elenco dei giochi (poker, blackjack, belot e préférence, in questo caso).start_time
eend_time
sono i tempi effettivi in cui una partita inizia e finisce. Nota che ilend_time
può essere NULL; non avremo il suo valore fino alla fine del gioco. Inoltre, se una corrispondenza viene abbandonata prima che sia terminata, ilend_time
il valore può rimanere NULL.number_of_players
– è il numero di partecipanti necessario per iniziare il giocodeck_id
– fa riferimento al mazzo utilizzato nel gioco.decks_used
– è il numero di mazzi utilizzati per giocare. Di solito questo valore sarà 1, ma alcuni giochi utilizzano più mazzi.unit_id
– è l'unità (punti, fiches, denaro, ecc.) utilizzata per segnare il gioco.entrance_fee
– è il numero di unità necessarie per partecipare al gioco; questo può essere NULL se il gioco non richiede che ogni giocatore inizi con un determinato numero di unità.victory_conditions
– determina quale giocatore ha vinto la partita. Useremo il varchar tipo di dati per descrivere la condizione di vittoria di ogni partita (cioè prima squadra a raggiungere 100 punti) e lasciare che l'applicazione lo interpreti. Questa flessibilità lascia spazio per aggiungere molti giochi.match_result
– memorizza il risultato della partita in formato testo. Come convictory_conditions
, lasceremo che l'applicazione interpreti il valore. Questo attributo può essere NULL perché riempiremo quel valore nello stesso momento in cui inseriamo ilend_time
valore.
Il participant
la tabella memorizza i dati su tutti i partecipanti a una partita. Il match_id
e player_id
gli attributi sono riferimenti alla match
e player
tavoli. Insieme, questi valori formano la chiave alternativa della tabella.
La maggior parte dei giochi ruota quale giocatore fa un'offerta o gioca per primo. Di solito nel primo round, il giocatore che gioca per primo (il primo giocatore) è determinato dalle regole del gioco. Nel round successivo, il giocatore a sinistra (o talvolta a destra) del giocatore iniziale originale andrà per primo. Useremo il initial_player_order
attributo per memorizzare il numero ordinale del giocatore di apertura del primo round. Il match_id
e il initial_player_order
gli attributi formano un'altra chiave alternativa perché due giocatori non possono giocare contemporaneamente.
Il score
l'attributo viene aggiornato quando un giocatore finisce una partita. A volte questo avverrà nello stesso momento per tutti i giocatori (ad es. in belot o préférence) ea volte mentre la partita è ancora in corso (ad es. poker o blackjack).
Azioni e tipi di azioni
Quando pensiamo alle azioni che i giocatori possono compiere in un gioco di carte, ci rendiamo conto che dobbiamo memorizzare:
- Qual è stata l'azione
- Chi ha eseguito quell'azione
- Quando (in quale accordo) è avvenuta l'azione
- Quale/e carta/e sono state utilizzate in quell'azione
Il action_type
table è un semplice dizionario che contiene i nomi delle azioni del giocatore. Alcuni valori possibili includono pescare carte, giocare carte, passare carte a un altro giocatore, fare check e rilanciare.
Nell'action
tabella, memorizzeremo tutti gli eventi accaduti durante un affare. Il deal_id
, card_id
, participant_id
e action_type_id
sono riferimenti alle tabelle che contengono i valori di mano, partecipante carta e tipo_azione. Nota che il participant_id
e card_id
possono essere valori NULL. Ciò è dovuto al fatto che alcune azioni non sono fatte dai giocatori (es. il dealer pesca una carta e la mette a faccia in su), mentre alcune non includono le carte (es. un rilancio nel poker). Dobbiamo memorizzare tutte queste azioni per poter ricreare l'intera partita.
Il action_order
attributo memorizza il numero ordinale di un'azione in-game. Ad esempio, un'offerta di apertura riceverà un valore 1; l'offerta successiva avrebbe un valore 2, ecc. Non possono verificarsi più di un'azione contemporaneamente. Pertanto, il deal_id
e action_order
gli attributi insieme formano la chiave alternativa.
La action_notation
l'attributo contiene una descrizione dettagliata di un'azione. Nel poker, ad esempio, possiamo memorizzare un rilancio azione e un importo arbitrario. Alcune azioni potrebbero essere più complicate, quindi è consigliabile memorizzare questi valori come testo e lasciare che l'applicazione lo interpreti.
Offerte e ordini di offerte
Una partita è composta da una o più carte distribuite. Abbiamo già discusso del participant
e il match
tabelle, ma le abbiamo incluse nell'immagine per mostrare la loro relazione con il deal
e deal_order
tabelle.
Il deal
la tabella memorizza tutti i dati di cui abbiamo bisogno su una singola istanza di corrispondenza.
Il match_id
l'attributo mette in relazione quell'istanza con la corrispondenza appropriata, mentre start_time
e end_time
denota l'ora esatta in cui l'istanza è iniziata e quando è stata completata.
Il move_time_limit
e il deal_result
gli attributi sono entrambi campi di testo utilizzati per memorizzare i limiti di tempo (se applicabile) e una descrizione del risultato di quell'affare.
Nel participant
tabella, il initial_player_order
l'attributo memorizza l'ordine del giocatore per l'istanza della partita di apertura. La memorizzazione degli ordini per i turni successivi richiede una tabella completamente nuova:il deal_order
tabella.
Ovviamente, deal_id
e participant_id
sono riferimenti a un'istanza di corrispondenza e a un partecipante. Insieme, formano la prima chiave alternativa nel deal_order
tavolo. Il player_order
l'attributo contiene valori che denotano gli ordini che i giocatori hanno partecipato a quell'istanza della partita. Insieme a deal_id
, costituisce la seconda chiave alternativa in questa tabella. Il deal_result
attributo è un campo di testo che descrive il risultato della partita per un singolo giocatore. Il score
memorizza un valore numerico relativo al risultato dell'offerta.
Semi, gradi e carte
Questa sezione del modello descrive le carte che utilizzeremo in tutti i giochi supportati. Ogni carta ha un seme e un valore.
Il suit_type
table è un dizionario che contiene tutti i tipi di seme che useremo. Per suit_type_name
, utilizzeremo valori come "semi francesi", "semi tedeschi", "semi svizzero-tedeschi" e "semi latini".
Il suit
la tabella contiene i nomi di tutti i semi contenuti in specifici tipi di mazzo. Ad esempio, il mazzo francese ha semi chiamati "Spades", "Hearts", "Diamonds" e "Clubs".
Nella rank
dizionario, troveremo carte con valori noti come "Asso", "Re", "Regina" e "Fante".
La card
la tabella contiene un elenco di tutte le carte possibili. Ogni carta apparirà in questa tabella solo una volta. Questo è il motivo per cui suit_id
e rank_id
gli attributi formano la chiave alternativa di questa tabella. I valori di entrambi gli attributi possono essere NULL perché alcune carte non hanno un seme o un valore (es. carte jolly). La is_joker_card
è un valore booleano autoesplicativo. Il card_name
attributo descrive una carta per testo:"Asso di picche".
Carte e mazzi
Le carte appartengono ai mazzi. Poiché una carta può apparire in più mazzi, avremo bisogno di un n:n relazione tra la card
e deck
tabelle.
Nel deck
tabella, memorizzeremo i nomi di tutti i mazzi di carte che vogliamo utilizzare. Un esempio di valori memorizzati nel deck_name
gli attributi sono:"Mazzo standard da 52 carte (francese)" o "Mazzo da 32 carte (tedesco)".
Il card_in_deck
la relazione viene utilizzata per assegnare le carte ai mazzi appropriati. Il card_id
– deck_id
pair è la chiave alternativa del deck
tavolo.
Proprietà della partita, mazzi e unità utilizzate
Questa sezione del modello contiene alcuni parametri di base per iniziare una nuova partita.
La parte principale di questa sezione è il game
tavolo. Questa tabella memorizza i dati sui giochi supportati dall'applicazione. Il game_name
contiene valori come "poker", "blackjack", "belot" e "préférence".
Il min_number_of_players
e max_number_of_players
sono il numero minimo e massimo di partecipanti a una partita. Questi attributi fungono da limiti per il gioco e vengono mostrati sullo schermo all'inizio di una partita. La persona che avvia la corrispondenza deve selezionare un valore da questo intervallo.
La min_entrance_fee
e il max_entrance_fee
attributi denota l'intervallo del biglietto d'ingresso. Ancora una volta, questo si basa sul gioco in corso.
In possible_victory_condition
, memorizzeremo tutte le condizioni di vittoria che potrebbero essere assegnate a una partita. I valori sono separati da un delimitatore.
L'unit
il dizionario viene utilizzato per memorizzare ogni unità utilizzata in tutti i nostri giochi. Il unit_name
l'attributo conterrà valori come "punto", "dollaro", "euro" e "chip".
Il game_deck
e game_unit
le tabelle usano la stessa logica. Contengono elenchi di tutti i mazzi e le unità che possono essere utilizzati in una partita. Pertanto, il game_id
– deck_id
coppia e il game_id
– unit_id
coppia forma chiavi alternative nelle rispettive tabelle.
Punteggi
Nella nostra applicazione, vorremo memorizzare i punteggi di tutti i giocatori che hanno partecipato ai nostri giochi di carte. Per ogni partita viene calcolato e memorizzato un unico valore numerico. (Il calcolo si basa sui risultati del giocatore in tutte le partite di un singolo tipo.) Il punteggio di questo giocatore è simile a un grado; consente agli utenti di sapere all'incirca quanto è bravo un giocatore.
Torna al processo di calcolo. Creeremo un n:n relazione tra il player
e game
tavoli. Questo è il player_score
tavolo nel nostro modello. Il player_id
e il score_id
” insieme formano la chiave alternativa della tabella. Il "score
viene utilizzato per memorizzare il valore numerico menzionato in precedenza.
Ci sono una varietà di giochi di carte che usano regole, carte e mazzi molto diversi. Per creare un database che memorizzi i dati per più di un gioco di carte, dobbiamo fare alcune generalizzazioni. Un modo per farlo è utilizzare campi di testo descrittivi e lasciare che l'applicazione li interpreti. Potremmo trovare modi per coprire le situazioni più comuni, ma ciò complicherebbe esponenzialmente la progettazione del database.
Come mostrato in questo articolo, puoi utilizzare un database per molti giochi. Perché dovresti farlo? Tre motivi:1) puoi riutilizzare lo stesso database; 2) semplificherebbe l'analisi; e questo porterebbe a 3) la costruzione di avversari IA migliori.