simplegameframe
v1.0.0
Published
Framework for rule based games
Downloads
2
Readme
Un framework per giochi basati su regole
Simple Game Frame
Durante la mia breve esperienza con i giochi da tavolo ho potuto notare che buona parte di questi seguono dei principi di base molto simili, quindi ho pensato di formulare un modello pseudomatematico-semiformale che potesse descrivere questi principi di base per poi tradurlo in JavaScript per verificarne la correttezza e magari cimentarmi anche nella riscrittura di alcuni giochi.
Definizione ~quasi~Formale
Lo stato del gioco è una tupla (o un semplice insieme?) che contiene tutti
i dati che possono cambiare nel gioco es punteggio, giocatori, notifiche ecc.
Gli elementi della tupla dello stato del gioco possono essere diversi a seconda
del gioco: un gioco di carte può avere il mazzo, le mani del giocatore e il
punteggio mentre un gioco di dadi avrà solamente i punteggi
STATE := { s : stato del gioco } = <s>
Una regola è una funzione che può causare un cambio dello stato del gioco
r(s)
RULE := ───────────
<s> ─> <s'>
PLAYERS := { p : un giocatore }
Il giocatore è colui che chiama le regole e riceve le notifiche sullo
svolgimento della partira.
L'applicaione di una regola può modificare la percezione che i giocatori
hanno della partita stessa (sia lo stesso giocatore che anche altri giocatori),
quindi l'applicazione di una regola può causare modifiche allo stato globale
del gioco e anche notificare ai giocatori l'evento.
Le notifiche avvenute fanno parte dello stato.
U ⊆ PLAYERS
U<s> ⊆ STATE := { U<s> : <s> ⊆ STATE relativo a U ⊆ PLAYERS }
p.do(r)
PLAYERS::DO := ──────────────
U<s> ──> U<s'>
ES.
Un giocatore pesca una carta da un mazzo:
- lo stato del gioco si aggiorna
- ai giocatori viene notificato che c'è stato un cambio di stato
- al giocatore che ha pecato la carta viene notificata quale carta ha pescato
L'applicazione di una regola può anche lasciare lo stato invariato.
ES. una regola che viene rigettata.
p.do(r)
─────────────
<s> ────> <s>
Una regola può portare lo stato in FINAL STATE.
FINAL STATE := f ∈ STATE
r(s)
───────────
<s> ─> <f>
Se un gioco è in FINAL STATE i giocatori non possono cambiare lo stato
invocando altre regole.
p.do(r)
─────────────
p<f> ──> p<f>
A questo punto si può descrivere un gioco come un insieme di regole.
SOMEGAME := { r1, r2, r3, ...rN }
Una partita a un gioco è una successione di regole invocate da un giocatore.
MATCH(SOMEGAME) := { pA.do(rX), pB.do(rY), ...pK.do(rF) }
Questa sequenza cusa una serie di cambi di stato nella partita fino al
ragiungimento dello stato finale.
pA.do(rX) pB.do(rY) pK.do(rF)
MATCH(SOMEGAME) := { ──────────────, ───────────────, ... ────────────── }
<s> ────> <s'> <s'> ────> <s"> <sN> ────> <f>
Implementazione e interfaccie
Le classi implementate sono
- Rule per rappresentare una regola
- Storage per contenere lo stato
- Game è una interfaccia per il gioco
- Player astrae il giocatore, cioé applica le regole e riceve le notifiche
- Notification per avvertire un giocatore di un evento, es cambio di stato a seguito dell'applicazione di una regola
- Alcune entità es Dice
TODO più meno per priorità
storage deve triggerare gli eventi on notifications e unlock anche su istanze del gioco diverse, quindi in caso di storage condivisi (non storage memory) anche su processi diversi, in modo che le notification vengono triggerate anche su riferimenti diversi in diversi processi
quando fai storage file le istanze di storage file nei processi si devono appendere alle modifiche dei path per poter triggerare gli eventi anche loro
per fare i test serve un istanza condivisa di storage memory *
in generale fare bene il memory dovrebbe far uscire fuori la classe per lo storage dei file facilmente. Quindi sarebbe buono separare database e farlo il più simile a come sarà storage file (mah... poi si perderebbe il fatto che memory è l'unico per istanza). Nota che gli eventi dello storage non devono portare con se a risorsa modificata, al massimo solo alune info tipo il path.
Open negli storage deve essere atomica veramente per risorsa: non deve essere possibile mai che due open con modifier
throw
sulla stessa risorsa ritornino entrambe. Una deve sollevare un GameErrorfile storage
giochi, esempi, test e documentazione
- dice esempio cli e integrare il test
- gioco esempio web server + test
- più documentazione
mongo storage
sql storage
more entities (Card, Deck, Hand, Pown, Board)
Note Se read e lock devono notificare, devono farlo a livello di istanza, cioè io leggo da istanza x di storage e istanza x triggra, le altre no.
Write e lock devono triggerare e lo devono fare a livello globale, quindi se uno storage in un istanza di un gioco in un processo scrive, l'evento deve arrivare anche agli altri storage dello stesso tipo e configurati ugualmente, anche in processi diversi
- creo un istanza sorage
- storage triggera quando viene scritta una risorsa
- es quando viene aggiunta una notifica, essendo risorse anche le notifiche
- se viene aggiunta una notifica al giocatore deve arrivare l'evento che è arrivata una notifica
- potrebbe non dover essere notify a triggerate direttamente, bensi una catena di eventi:
- ~game in join~ player nel costruttore si appende all'evento dello storage (preso dal game)
- quando parte un evento di scrittura di una notifica nello storage questa viene notificata al player
- in destroy vengono rimossi gli handler.
Note sotrage file, watch e linux
Su linux fx.watch {recursive: true} non funziona, watch -d -t -g du -sb;
potrebbe essere il degno fallback. No è na merda.. dai..
Si potrebbero dedicare un file agli eventi write e uno a gli eventi unlock dello storage e si fa watch di quei file, dopo le scritture si scrive su quel file il path del file modificato, così si possono triggerare gli eventi e passare il path della risorsa modificata Ricorda: quei file servono solo per l'evento. Nient'altro. Mai!!! Non usarli mai per vedere che ci sta di aperto. Vanno scritti e letti solo per sollevare l'evento.