Dezvoltarea unei aplicații care găzduiește mai multe jocuri de cărți sub aceeași umbrelă logică este o provocare de design software. Nu vrei să scrii cod separat pentru fiecare joc; vrei un engine care să consume un “registru de variante”. În proiectul crazy-eights, am abordat această problemă prin crearea unui sistem bazat pe reguli, capabil să gestioneze de la clasica Șeptică până la variante complet customizabile.
Registrul de variante: Inima sistemului
Fiecare joc din aplicație este definit printr-un obiect de configurare care dictează comportamentul motorului de joc. Această structură include numele variantei, numărul de cărți împărțite inițial și, cel mai important, dicționarul de “cărți speciale”.
const variants = {
macao: {
id: 'macao',
initialCards: 5,
specialCards: {
'A': { action: 'skip_turn' },
'2': { action: 'draw_two' },
'J': { action: 'change_color' }
}
},
septica: {
id: 'septica',
initialCards: 4,
specialCards: {
'7': { action: 'take_all' },
'10': { action: 'clear_table' }
}
}
};
Editorul de reguli proprii
Cea mai interesantă parte a fost implementarea unui editor care permite utilizatorilor să își definească propriile jocuri. Provocarea principală aici este validarea. Trebuie să ne asigurăm că utilizatorul nu definește reguli contradictorii. De exemplu, o carte nu poate avea două acțiuni diferite pentru aceeași culoare.
Am implementat o funcție de validare care verifică integritatea regulilor înainte de a le salva în baza de date:
function validateCustomRule(rule) {
const errors = [];
if (rule.specialCards.some(c => c.rank === 'Joker' && c.color === 'All')) {
errors.push({ code: 'INVALID_JOKER_CONFIG', message: 'Jokerul nu poate fi limitat la o singură culoare.' });
}
// Verificăm duplicatele care ar putea fi înghițite de primul match
const seen = new Set();
rule.specialCards.forEach(card => {
const key = `${card.rank}-${card.color}`;
if (seen.has(key)) {
errors.push({ code: 'DUPLICATE_CARD_RULE', message: `Regula pentru ${key} este definită de două ori.` });
}
seen.add(key);
});
return { valid: errors.length === 0, errors };
}
Gestionarea conflictelor: First-match logic
Când avem o carte care îndeplinește mai multe condiții (de exemplu, un 7 de pică într-un joc unde 7 este special, dar și pica are o valoare), motorul aplică o strategie de first-match.
În codul nostru de backend, procesăm validarea mutării astfel:
- Verificăm dacă cartea jucată are o acțiune definită în
specialCardsspecifică pentru acel rang. - Dacă nu, verificăm dacă se potrivește cu culoarea sau rangul cărții de pe masă.
Această ierarhie previne comportamentele imprevizibile. Dacă un utilizator definește o regulă pentru “7 de pică” în editor, acea regulă va avea prioritate absolută față de regula generală de “7” sau “pica”, evitând astfel erorile de tipul shadowing în logica de joc.
Concluzie
Construirea unui sistem modular pentru jocuri de cărți nu înseamnă doar să pui cărți pe ecran, ci să creezi un limbaj prin care utilizatorul să poată exprima orice variantă de joc. Prin decuplarea regulilor de motorul de randare, am reușit să integrăm 7 jocuri diferite în crazy-eights fără a dubla logica de bază. Data viitoare, vom discuta despre cum optimizăm sincronizarea acestor reguli în timp real prin WebSockets.

