Programmazione funzionale e avventure testuali

7

Questa è principalmente una domanda teorica sulla FP, ma prenderò delle avventure testuali (come Zork della vecchia scuola) per illustrare il mio punto. Mi piacerebbe conoscere le tue opinioni su come modellerai una simulazione stateful con FP.

Le avventure testuali sembrano davvero richiedere OOP. Ad esempio, tutte le "stanze" sono istanze di una classe Room , puoi avere una classe base Item e interfacce come Item<Pickable> per le cose che puoi trasportare e così via.

La modellazione del mondo in FP funziona in modo diverso, specialmente se si vuole rafforzare l'immutabilità in un mondo che deve mutare mentre il gioco procede (gli oggetti vengono mossi, i nemici vengono sconfitti, il punteggio aumenta, il giocatore cambia la sua posizione). Immagino un unico grande oggetto World che ha tutto: quali sono le stanze che puoi esplorare, come sono collegate, cosa sta portando il giocatore, quali leve sono state attivate.

Penso che un approccio puro sarebbe fondamentalmente passare questo grande oggetto a qualsiasi funzione e farlo restituire da esso (eventualmente modificato). Ad esempio, ho una funzione moveToRoom che ottiene World e la restituisce con World.player.location cambiata nella nuova room, World.rooms[new_room].visited = True e così via.

Anche se questo è il modo più "corretto", sembra imporre la purezza per il gusto di farlo. A seconda del linguaggio di programmazione, il passaggio di questo oggetto World potenzialmente molto grande avanti e indietro può essere costoso. Inoltre, potrebbe essere necessario che la funzione ogni abbia accesso a qualsiasi oggetto World . Ad esempio, una stanza può essere accessibile o meno a seconda di una leva innescata in un'altra stanza perché potrebbe essere allagata, ma se il giocatore porta un salvagente, può entrarci comunque. Un mostro può essere aggressivo o meno a seconda che il giocatore abbia ucciso suo cugino in un'altra stanza. Ciò significa che la funzione roomCanBeEntered deve accedere a World.player.invetory e World.rooms , describeMonster deve accedere a World.monsters e così via (in pratica, deve passare l'intero carico intorno). Mi sembra davvero di chiedere una variabile globale, anche se questo è tutto tranne uno stile di programmazione buono in FP.

Come risolveresti questo problema?

    
posta pistacchio 08.05.2015 - 19:55
fonte

3 risposte

2

Ricorda che i linguaggi funzionali utilizzano strutture dati e funzioni separate anziché oggetti. Ad esempio, avresti un set di stanze e un inventario come un mondo.

Idealmente, limiteresti anche la quantità di dati che dai alle tue funzioni quanto effettivamente richiedono il più possibile invece di passare il mondo intero (ad esempio estrai la Stanza D dal tuo mondo, ovviamente i mondi completamente interdipendenti possono essere difficili separare). Il risultato verrebbe reincorporato nella struttura dei dati del mondo, creando un nuovo stato. Non è possibile modellare lo stato senza utilizzare lo stato; come dici tu, alcune cose richiedono intrinsecamente delle mutazioni.

La maggior parte dei linguaggi funzionali pratici fornisce un modo per realizzare la mutazione direttamente (ad esempio la monade IO in Haskell o transienti in Clojure), o simularla in modo efficiente (spesso riutilizzando parti invariate della struttura dati (le strutture dati predefinite di Clojure sono buone esempio qui)). In entrambi i casi viene mantenuta la purezza.

Dato che la quantità di stato che deve essere mutata sembra limitata, non mi preoccuperei troppo dei problemi di prestazioni e andrei con l'approccio funzionale naif (forse già ottimizzato).

Un'opzione diversa che ho visto restituirebbe solo le istruzioni per cambiare parte del mondo dalle singole funzioni e quindi aggiornare il tuo mondo in base a queste. Una serie di post di blog che descrivono questo è disponibile al link .

Entrambe queste risposte riguardano più come organizzare i cambiamenti che non passare il tuo intero mondo alle funzioni, perché una persona perfettamente interdipendente non può essere segmentata per definizione - ma cerca di farlo nel migliore dei modi puoi nel tuo caso, che non è solo funzionale, ma anche buona pratica.

    
risposta data 28.05.2015 - 19:21
fonte
0

Di sicuro, esaminerei la capacità della lingua in questione di accedere a qualche forma di database. La maggior parte degli eventi che cambiano simultaneamente lo stato del mondo verrebbero semplicemente registrati su disco e non influenzerebbero il giocatore corrente all'interno della stanza corrente (al di fuori di circostanze particolari come esplosioni o in un MMO, interruttori che aprono le porte da remoto, grida di altri giocatori, ecc.)

In quanto tale, il client corrente deve solo essere a conoscenza dell'oggetto Room e delle cose che lo riguardano direttamente. noticableEventsOutsideRoom potrebbe quindi essere semplicemente una sottoclasse di Room influenzata dalle recenti modifiche al database e il tuo oggetto di gioco è diventato molto più piccolo.

    
risposta data 15.05.2015 - 02:42
fonte
0

La vera soluzione è non raccogliendo tutto su un grande oggetto Mondo e poi passandolo. Invece, ti consigliamo di specificare con precisione il tipo di funzione che stai trattando. Ecco alcuni esempi:

BAD:
   f :: (World, Int) -> World

Good:
   f :: (Int,Int,Int,Int) -> World

L'esempio errato sta provando a modificare l'oggetto esistente, ma il buon esempio è provare a creare un mondo dallo spazio di stato del tuo gioco. Fondamentalmente, per creare un mondo, devi sapere tutti i dati necessari per selezionare il mondo corretto.

    
risposta data 27.06.2015 - 21:31
fonte

Leggi altre domande sui tag