Come devo analizzare l'input dell'utente in un gioco di avventura testuale?


16

L'analisi dei comandi utente in un'avventura testuale è una gamma che va dal semplice "vai a nord" di Adventure ad alcuni incredibilmente intelligenti in hhgttg .

Mi sembra di aver letto delle belle spiegazioni sulle riviste di computer degli anni '80, ma ora non trovo quasi nulla in rete se non un breve riferimento a Wikipedia .

Come sarebbe faresti ?


Aggiornamento : sono andato con l'approccio più semplice possibile nella mia voce Ludum Dare .


3
C'è un problema particolare che stai cercando di risolvere?
Trevor Powell,

@TrevorPowell sta pensando di intraprendere una piccola avventura testuale per divertimento, e voglio solo conoscere me stesso lo "stato dell'arte" piuttosto che immergermi e risolverlo a modo mio
Will

1
Usa Inform ; questa è la migliore strategia che potresti mai usare. Oggigiorno non c'è motivo di scrivere a mano un'avventura testuale.
Nicol Bolas,

@NicolBolas a meno che Ludum Dare non si stia avvicinando? ;)
Will

1
Non è disfattista quanto pragmatico. Analizzare tutte le tecniche di analisi avanzata del testo (al di là delle cose ovvie che chiunque può inventare) è probabilmente al di fuori dell'ambito di una singola risposta qui.
Tetrad,

Risposte:


10

Hai cercato nella community di fiction interattiva? Scrivono ancora parser e alcuni cercano di spingere la busta implementando nuove tecniche come l'elaborazione del linguaggio naturale.

Vedi ad esempio questo link per articoli che descrivono gli approcci utilizzati:

http://ifwiki.org/index.php/Past_raif_topics:_Development:_part_2#Parsing


4
Ah, "l'avventura testuale" diventa "narrativa interattiva" e improvvisamente è molto più googlabile! Chi avrebbe mai pensato che avrebbe cambiato nome da quando l'ho giocato? :) Tuttavia, guardando quei contatti, e in realtà non molto viene spiegato tristemente
Will

9

Il termine desiderato è "elaborazione del linguaggio naturale" o PNL. Tuttavia, tieni presente che i metodi formali sono progettati per cercare di comprendere i testi del mondo reale, mentre di solito hai bisogno solo di qualcosa che funzioni per un sottoinsieme limitato del tuo linguaggio naturale.

In genere puoi iniziare con una semplice grammatica e vocabolario, quindi scrivere un parser per questo. Una grammatica potrebbe essere qualcosa di semplice come questo:

sentence = verb [preposition] object
verb = "get" | "go" | "look" | "examine"
preposition = "above" | "below"
object = ["the"] [adjective] noun
adjective = "big" | "green"
noun = "north" | "south" | "east" | "west" | "house" | "dog"

Quanto sopra è una variante della forma Backus-Naur, il modo standard di rappresentare le grammatiche. Ad ogni modo, puoi usare un generatore di parser per generare codice per analizzare questa grammatica, o scrivere il tuo abbastanza facilmente se la tua lingua ha una buona gestione delle stringhe. (Cerca "parser di discesa ricorsivi", che usano una funzione per ogni riga della grammatica.)

Una volta analizzato, puoi capire se la frase ha senso: "vai a nord" può avere senso, ma "ottieni il nord verde" non lo è. Puoi risolverlo in 2 modi; rendere la grammatica più formale (ad es. avere diversi tipi di verbi validi solo con alcuni tipi di sostantivo) o controllare successivamente i sostantivi contro il verbo. Il primo modo può aiutarti a distribuire messaggi di errore migliori al giocatore, ma devi sempre fare il secondo in qualche modo, poiché devi sempre controllare il contesto, ad es. "prendi il tasto verde" è grammaticalmente corretto e sintatticamente corretto, ma devi comunque verificare che il tasto verde sia presente.

Alla fine il tuo programma finisce con un comando validato con tutte le varie parti controllate; quindi si tratta solo di chiamare la funzione giusta con gli argomenti per eseguire l'azione.


6

Lo stato dell'arte per fare avventure testuali oggi sta usando Inform 7 . Inform 7 fonte legge "come l'inglese", allo stesso modo in cui i giochi basati su Inform ti consentono di "scrivere inglese". Ad esempio, da Emily Short's Bronze :

Una cosa ha del testo chiamato profumo. Il profumo di una cosa è di solito "niente".
La regola di odore di blocco non è elencata in nessun libro delle regole.
Esegui l'odore di qualcosa:
    dì "Dal [nome] senti l'odore [profumo del nome]."
Invece di annusare una stanza:
    se una cosa profumata può essere toccata dal giocatore, dì "Senti l'odore [l'elenco delle cose profumate che possono essere toccate dal giocatore].";
    altrimenti dire "Il posto è beato inodore".

Il parser Inform 7 è strettamente integrato con l'IDE Inform 7 e l'intero codice sorgente non è ancora disponibile per lo studio:


5

Le due migliori fonti attuali per imparare a creare un parser di avventura testuale sono (come detto) la comunità IF e la comunità mud. Se cerchi nei forum principali (Intfiction.org/forum, il newsgroup rec.arts.int-fiction, Mud Connector, Mudbytes, Mudlab, Top Mud Sites) troverai delle risposte, ma se stai solo cercando per articoli consiglierei la spiegazione di Richard Bartle sul parser nel MUD II:

http://www.mud.co.uk/richard/commpars.htm

E questa spiegazione su rec.arts.int-fiction:

http://groups.google.com/group/rec.arts.int-fiction/msg/f545963efb72ec7b?dmode=source

Nessuna mancanza di rispetto per le altre risposte, ma la creazione di una grammatica CF o l'uso di BNF non è la soluzione a questo problema. Questo non vuol dire che non potrebbe essere una soluzione per un problema diverso, ovvero la creazione di un parser di linguaggio naturale più avanzato, ma è oggetto di ricerche considerevoli e non dell'IMO nell'ambito di un'avventura testuale.


4

Nel mio primo anno all'università abbiamo realizzato un gioco di avventura in Prolog e per l'input dell'utente abbiamo dovuto usare la grammatica della clausola definita o DCG. Vedere http://www.amzi.com/manuals/amzi/pro/ref_dcg.htm#DCGCommandLanguage per un esempio dell'uso come linguaggio di comando. Sembrava un principio (dopotutto era uni) e un approccio flessibile all'epoca.


1

Devi definire una lingua specifica del dominio che sia tutte le frasi corrette nel tuo gioco. A tal fine devi definire una grammatica per la tua lingua (vocabolario e sintassi). Il tipo di grammatica di cui hai bisogno è una grammatica senza contesto e ci sono strumenti che generano automaticamente un parser a partire da una descrizione sintetica della grammatica come ANTLR (www.antlr.org). Il parser verifica solo se una frase è corretta o meno e produce un albero di sintassi astratto (AST) della frase che è una rappresentazione navigabile della frase in cui ogni parola ha il ruolo specificato nella grammatica. Navigando nell'AST devi aggiungere il codice che valuta qual è la semantica che ogni parola assume quando gioca quel ruolo rispetto alle altre parole della frase e verifica se la semantica è corretta.

Ad esempio, la frase "La pietra mangia l'uomo" è sintatticamente corretta ma non necessariamente semanticamente corretta (a meno che nel tuo mondo le pietre, forse le pietre magiche, possano mangiare gli uomini).

Se anche la semantica è corretta, ad esempio puoi cambiare il mondo in base ad essa. Questo potrebbe cambiare il contesto e quindi la stessa frase non potrebbe più essere semanticamente corretta (per esempio non ci potrebbe essere nessun uomo da mangiare)


1

Ho usato il motore Tads3 (www.tads3.org) per alcune delle avventure testuali che ho scritto. È più per programmatori di computer, ma un linguaggio molto potente. Se sei un programmatore, Tads3 sarà molto più facile da programmare più velocemente di Inform7, che ho usato anche in precedenza. Il problema con Inform7 per i programmatori è famoso quanto "indovina il verbo" è per i giocatori di avventure testuali in quanto se non scrivi le tue frasi MOLTO attentamente interromperai il gioco. Se hai la pazienza di farlo, puoi facilmente scrivere un parser in Java usando la classe Tokenizer. Esempio che ho scritto usando un JTextArea globale e un array String [] globale. Elimina i caratteri indesiderati tranne le foglie AZ e 0-9 e il punto interrogativo (per una scorciatoia da comando "aiuto"):

// put these as global variables just after your main class definition
public static String[] parsed = new String[100];
// outputArea should be a non-editable JTextArea to display our results
JTextArea outputArea = new JTextArea();
/*
 * parserArea is the JTextBox used to grab input
 * and be sure to MAKE sure somewhere to add a 
 * java.awt.event.KeyListener on it somewhere where
 * you initialize all your variables and setup the
 * constraints settings for your JTextBox's.
 * The KeyListener method should listen for the ENTER key 
 * being pressed and then call our parseText() method below.
 */
JTextArea parserArea = new JTextArea();

public void parseText(){
    String s0 = parserArea.getText();// parserArea is our global JTextBox
    s0 = s0.replace(',',' ');
    s0 = s0.replaceAll("[^a-zA-Z0-9? ]","");
    // reset parserArea back to a clean starting state
    parserArea.setCaretPosition(0);
    parserArea.setText("");
    // erase what had been parsed before and also make sure no nulls found
    for(int i=0;i < parsed.length; i++){
      parsed[i] = "";
    }
    // split the string s0 to array words by breaking them up between spaces
    StringTokenizer tok = new StringTokenizer(s0, " ");
    // use tokenizer tok and dump the tokens into array: parsed[]
    int iCount = 0;
    if(tok.countTokens() > 0){
      while(tok.hasMoreElements()){
        try{
          parsed[iCount] = tok.nextElement().toString();
          if(parsed[iCount] != null && parsed[iCount].length()>1){
            // if a word ENDS in ? then strip it off
            parsed[iCount] = parsed[iCount].replaceAll("[^a-zA-Z0-9 ]","");
           }
        }catch(Exception e){
          e.printStackTrace();
        }
          iCount++;
        }


      /*
       * handle simple help or ? command.
       * parsed[0] is our first word... parsed[1] the second, etc.
       * we can use iCount from above as needed to see how many...
       * ...words got found.
       */
      if(parsed[0].equalsIgnoreCase("?") || 
        parsed[0].equalsIgnoreCase("help")){
          outputArea.setText("");// erase the output "screen"
          outputArea.append("\nPut help code in here...\n");
        }
      }

      // handle other noun and verb checks of parsed[] array in here...

    }// end of if(tok.countTokens() > 0)... 

}// end of public void parseText() method

... ho tralasciato la definizione della classe principale e il metodo di inizializzazione variabile (), ecc. Perché si presume che se conosci Java sai già come configurarlo. La classe principale per questo dovrebbe probabilmente estendere JFrame e nel tuo metodo main () vuoto statico pubblico crearne un'istanza. Eventualmente parte di questo codice aiuta.

EDITED - Okay, quindi ora quello che faresti dopo è creare una classe Actions e cercare un'azione (es. "Get lamp" o "drop sword"). Per semplificare, dovresti avere un oggetto o un metodo RoomScan per scansionare tutto ciò che è visibile nell'ambito e scansionare solo quegli oggetti su quell'azione. L'oggetto stesso gestisce la gestione delle azioni e per impostazione predefinita è necessario che una classe Item gestisca tutte le azioni note in modo predefinito, che può essere sovrascritto. Ora, se per esempio, un oggetto che vuoi "ottenere" è trattenuto da un personaggio non giocatore, la risposta predefinita per ottenere quell'oggetto trattenuto dal suo proprietario dovrebbe essere qualcosa del tipo "Non te lo lascerà avere". Ora dovresti creare un sacco di risposte di azione predefinite a questo nella classe Item o Thing. Questo deriva fondamentalmente da una prospettiva Tads3 su tutto il design. Perché in Tads3 ogni elemento ha la propria routine di gestione delle azioni predefinita su di essa che il parser chiama se un'azione su di essa è inizializzata. Quindi ... te lo sto solo dicendo, Tads3 ha già tutto questo a posto, quindi è MOLTO facile programmare un'avventura testuale in quella lingua. Ma se vuoi farlo da zero, come in Java (sopra), personalmente lo gestirò allo stesso modo in cui è stato progettato Tads3. In questo modo, puoi sovrascrivere le azioni predefinite gestendo le routine su diversi oggetti stessi, quindi ad esempio se vuoi "ottenere la lampada" e il maggiordomo lo tiene in mano, potrebbe innescare una risposta nel metodo di azione "get" di default per Item o Oggetto e dirti che "Il maggiordomo si rifiuta di consegnare la lampada di ottone". Voglio dire ... una volta che sei stato un programmatore abbastanza a lungo come me, allora è tutto molto facile. Ho più di 50 anni e lo faccio da quando avevo 7 anni. Mio padre era un istruttore di Hewlett Packard negli anni '70, quindi ho imparato da lui inizialmente un TON sulla programmazione al computer. Sono anche nelle riserve dell'esercito degli Stati Uniti come fondamentalmente un amministratore di server ora. Um ... sì, quindi non arrenderti. Non è così difficile una volta scomposto in quello che vuoi che faccia il tuo programma. A volte tentativi ed errori sono il modo migliore per affrontare questo tipo di cose. Basta testarlo e vedere e non mollare mai. Va bene? La codifica è un'arte. Può essere fatto in molti modi diversi. Non lasciare che un modo o l'altro sembrino bloccarti in un angolo del design. Sono anche nelle riserve dell'esercito degli Stati Uniti come fondamentalmente un amministratore di server ora. Um ... sì, quindi non arrenderti. Non è così difficile una volta scomposto in quello che vuoi che faccia il tuo programma. A volte tentativi ed errori sono il modo migliore per affrontare questo tipo di cose. Basta testarlo e vedere e non mollare mai. Va bene? La codifica è un'arte. Può essere fatto in molti modi diversi. Non lasciare che un modo o l'altro sembrino bloccarti in un angolo del design. Sono anche nelle riserve dell'esercito degli Stati Uniti come fondamentalmente un amministratore di server ora. Um ... sì, quindi non arrenderti. Non è così difficile una volta scomposto in quello che vuoi che faccia il tuo programma. A volte tentativi ed errori sono il modo migliore per affrontare questo tipo di cose. Basta testarlo e vedere e non mollare mai. Va bene? La codifica è un'arte. Può essere fatto in molti modi diversi. Non lasciare che un modo o l'altro sembrino bloccarti in un angolo del design.


Questo purtroppo esclude la parte più difficile di un parser di testo, vale a dire identificare il verbo, il soggetto e l'oggetto dell'input dell'utente e mapparlo su un'azione.
Philipp,

È vero, ma come lo farei è creare una classe di azioni e archiviare un mucchio di azioni in, diciamo, in una classe di dizionario, quindi cercare parole di azioni. Se l'azione coinvolge una seconda parola (come per un'azione "prendi", forse "prendi lampada"), fai scansionare un gruppo di oggetti (o sostantivo) oggetto in cui quegli stessi oggetti avrebbero script per gestire le azioni fatte su di essi. Tutto questo presuppone che tu codifichi l'intera cosa direttamente in Java e non provi a leggere un vero file esterno per compilare avventure testuali.
William Chelonis,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.