Jak analizować dane wejściowe użytkownika w tekstowej grze przygodowej?

16

Analizowanie poleceń użytkownika w przygodzie tekstowej to spektrum od prostych przygód „idź na północ” po niektóre zadziwiająco sprytne w hhgttg .

Wydaje mi się, że pamiętam, jak czytałem fajne poradniki w czasopismach komputerowych w latach 80., ale teraz nie znalazłem w sieci prawie nic oprócz krótkiego odnośnika do Wikipedii .

Jak byś to zrobił?


Aktualizacja : Poszedłem z najprostszym możliwym podejściem we wpisie Ludum Dare .

Wola
źródło
3
Czy jest jakiś szczególny problem, który próbujesz rozwiązać?
Trevor Powell,
@TrevorPowell zastanawia się, czy nie zabrać się za przygodę tekstową dla zabawy i po prostu chcę zapoznać się z „najnowocześniejszym stanem techniki”, a nie po prostu zanurzyć się i rozwiązać go na swój sposób
Will
1
Użyj Inform ; to najlepsza strategia, jaką możesz kiedykolwiek zastosować. Obecnie nie ma praktycznie żadnego powodu, aby ręcznie kodować przygodę tekstową.
Nicol Bolas,
@NicolBolas, chyba że zbliża się Ludum Dare? ;)
Czy
1
To nie tyle defetyzm, co pragmatyczne. Przejście do wszystkich technik zaawansowanego parsowania tekstu (poza oczywistymi rzeczami, które każdy może wymyślić) jest prawdopodobnie poza zakresem jednej odpowiedzi tutaj.
Tetrad

Odpowiedzi:

10

Czy szukałeś w interaktywnej społeczności fikcyjnej? Nadal piszą parsery, a niektórzy próbują przesunąć kopertę, wdrażając nowe techniki, takie jak przetwarzanie języka naturalnego.

Zobacz na przykład ten link do artykułów opisujących zastosowane podejścia:

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

krolth
źródło
4
Aha, „przygoda tekstowa” staje się „interaktywną fikcją” i nagle staje się znacznie bardziej dostępna w Google! Kto by pomyślał, że zmieni nazwę nawet odkąd w nią zagrałem? :) Mimo to, patrząc na te potencjalne szanse, a właściwie niewiele wyjaśnia się smutno
Czy
9

Termin, który chcesz, to „przetwarzanie języka naturalnego” lub NLP. Pamiętaj jednak, że formalne metody mają na celu próbę zrozumienia tekstów z prawdziwego świata, podczas gdy zazwyczaj potrzebujesz tylko czegoś, co działa na ograniczoną część twojego naturalnego języka.

Zazwyczaj możesz zacząć od prostej gramatyki i słownictwa, a następnie napisać parser. Gramatyka może być czymś prostym:

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"

Powyżej jest wariantem formy Backus-Naur, standardowego sposobu reprezentowania gramatyki. W każdym razie możesz użyć generatora analizatora składni do wygenerowania kodu do przeanalizowania tej gramatyki lub dość łatwo napisać własny, jeśli Twój język ma przyzwoitą obsługę łańcuchów. (Wyszukaj „parsery zejścia rekurencyjnego”, które używają jednej funkcji dla każdej linii gramatyki.)

Po przeanalizowaniu możesz sprawdzić, czy zdanie ma sens - „idź na północ” może mieć sens, ale „zdobądź zieloną północ” nie. Możesz rozwiązać to na 2 sposoby; uczynić gramatykę bardziej formalną (np. mieć różne typy czasowników, które są ważne tylko z niektórymi rodzajami rzeczowników) lub później sprawdzić rzeczowniki względem czasownika. Pierwszy sposób może pomóc ci w przekazywaniu lepszych komunikatów o błędach do odtwarzacza, ale zawsze i tak musisz zrobić drugi stopień, ponieważ zawsze musisz sprawdzić kontekst - np. „Weź zielony klucz” jest poprawny gramatycznie i poprawny pod względem składniowym, ale nadal musisz sprawdzić, czy zielony klucz jest obecny.

W końcu twój program kończy sprawdzone polecenie z zaznaczonymi wszystkimi częściami; to tylko przypadek wywołania właściwej funkcji z argumentami do wykonania akcji.

Kylotan
źródło
6

Najnowocześniejszym sposobem tworzenia przygód tekstowych jest dzisiaj Inform 7 . Źródło Inform 7 brzmi „jak angielski” w taki sam sposób, jak gry oparte na Inform pozwalają „pisać po angielsku”. Na przykład z Emily Short's Bronze :

Rzecz ma jakiś tekst zwany zapachem. Zapach czegoś jest zwykle „niczym”.
Reguła pachnąca blokami nie jest wymieniona w żadnym zbiorze reguł.
Wykonaj coś wąchającego:
    powiedz „Od [rzeczownika] czujesz [zapach rzeczownika]”.
Zamiast
    wąchać pokój: jeśli gracz może dotknąć czegoś zapachowego, powiedz „Pachniesz [lista zapachowych rzeczy, które gracz może dotknąć].”;
    w przeciwnym razie powiedz „To miejsce jest błogo bezwonne”.

Parser Inform 7 jest ściśle zintegrowany z IDE Inform 7, a cały kod źródłowy nie jest jeszcze dostępny do analizy:


źródło
5

Dwoma najlepszymi obecnie źródłami do nauki tworzenia parsera przygody tekstowej są (jak wspomniano) społeczność IF i społeczność błota. Jeśli przeszukujesz główne fora (Intfiction.org/forum, grupa dyskusyjna rec.arts.int-fiction, Mud Connector, Mudbytes, Mudlab, Top Mud Sites), znajdziesz odpowiedzi, ale jeśli tylko szukasz w przypadku artykułów polecam wyjaśnienie przez Richard Bartle parsera w MUD II:

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

I to wyjaśnienie na temat rec.arts.int-fiction:

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

Brak braku szacunku dla innych odpowiedzi, ale utworzenie gramatyki CF lub użycie BNF nie jest rozwiązaniem tego problemu. Nie oznacza to, że nie może to być rozwiązanie innego problemu, tj. Stworzenia bardziej zaawansowanego parsera języka naturalnego, ale jest to przedmiotem poważnych badań, a nie IMO w zakresie przygody tekstowej.

georgek
źródło
4

Na pierwszym roku studiów stworzyliśmy grę przygodową w Prologu, a do wkładu użytkownika musieliśmy użyć gramatyki z określoną klauzulą lub DCG. Zobacz http://www.amzi.com/manuals/amzi/pro/ref_dcg.htm#DCGCommandLanguage na przykład użycia go jako języka poleceń. Wydawało się to wtedy oparte na zasadach (w końcu było to jednolite) i elastycznym podejściem.

Eric
źródło
1

Musisz zdefiniować język specyficzny dla domeny, czyli wszystkie zdania, które są poprawne w twojej grze. W tym celu musisz zdefiniować gramatykę dla swojego języka (słownictwo i składnia). Gramatyka, której potrzebujesz, to Gramatyka bezkontekstowa i istnieją narzędzia, które automatycznie generują analizator składni, zaczynając od syntetycznego opisu gramatyki, takiego jak ANTLR (www.antlr.org). Analizator składni sprawdza tylko, czy zdanie jest poprawne, czy nie, i tworzy abstrakcyjne drzewo składni zdania (AST), które jest nawigowalną reprezentacją zdania, w którym każde słowo ma rolę określoną przez ciebie w gramatyce. Poruszając się po AST, musisz dodać kod oceniający, jaka jest semantyka, jaką odgrywa każde słowo, gdy gra tę rolę w odniesieniu do innych słów w zdaniu, i sprawdź, czy semantyka jest poprawna.

Na przykład zdanie „Kamień zjada człowieka” jest poprawne składniowo, ale niekoniecznie poprawne semantycznie (chyba że w twoim świecie kamienie, może magiczne kamienie, mogą jeść ludzi).

Jeśli również semantyka jest poprawna, możesz na przykład zmienić świat zgodnie z nim. Może to zmienić kontekst, a tym samym to samo zdanie nie może być już semantycznie poprawne (na przykład nie może być człowieka do jedzenia)

www.Sillitoy.com
źródło
1

Użyłem silnika Tads3 (www.tads3.org) do niektórych tekstowych przygód, które napisałem. Jest to jednak bardziej dla programistów komputerowych, ale bardzo mocny język. Jeśli jesteś programistą, Tads3 będzie o wiele łatwiej kodować rzeczy szybciej niż Inform7, z którego również wcześniej korzystałem. Problem z Inform7 dla programistów jest tak znany, jak „odgadnięcie czasownika” dla graczy tekstowych, ponieważ jeśli nie napiszesz zdań BARDZO ostrożnie, zepsujesz grę. Jeśli masz na to cierpliwość, możesz łatwo napisać analizator składni w Javie, używając klasy Tokenizer. Przykład napisałem przy użyciu globalnego JTextArea i globalnej tablicy String []. Usuwa niechciane znaki oprócz liści AZ i 0-9, a także znak zapytania (skrót „pomocy”):

// 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

... Pominąłem definicję głównej klasy i metodę initialize () itd., Ponieważ zakłada się, że jeśli znasz Javę, wiesz już, jak to skonfigurować. Główna klasa do tego powinna prawdopodobnie rozszerzyć JFrame, a publiczna metoda static (void main () po prostu stwórz jego instancję. Mam nadzieję, że część tego kodu pomaga.

EDYTOWANE - OK, więc teraz powinieneś stworzyć klasę akcji i skanować w poszukiwaniu akcji (tj. „Zdobądź lampę” lub „upuść miecz”). Aby to uprościć, musisz mieć obiekt lub metodę RoomScan, aby skanować wszystko widoczne w zakresie i skanować tylko te obiekty w tej akcji. Sam obiekt obsługuje obsługę akcji i domyślnie powinieneś mieć klasę Item obsługującą wszystkie znane akcje w domyślny sposób, który można obejść. Teraz, na przykład, jeśli przedmiot, który chcesz „zdobyć”, znajduje się w posiadaniu postaci niebędącej graczem, domyślną odpowiedzią na zdobycie tego przedmiotu przez właściciela powinien być coś w rodzaju „Nie pozwoli ci go mieć”. Teraz musisz stworzyć mnóstwo domyślnych odpowiedzi na akcję w klasie Przedmiot lub Rzecz. Zasadniczo pochodzi to z perspektywy Tads3 w całym projekcie. Ponieważ w Tads3 każdy element ma własną domyślną procedurę obsługi akcji, którą wywołuje parser, jeśli akcja na nim zostanie zainicjowana. Więc ... Mówię tylko, Tads3 ma to wszystko na swoim miejscu, więc BARDZO łatwo jest napisać tekstową przygodę w tym języku. Ale jeśli chcesz to zrobić od zera, tak jak w Javie (powyżej), osobiście poradziłbym sobie z nim w ten sam sposób, w jaki zaprojektowano Tads3. W ten sposób można zastąpić domyślne procedury obsługi akcji na różnych obiektach, więc na przykład, jeśli chcesz „dostać lampę”, a kamerdyner ją trzyma, może wywołać odpowiedź w domyślnej metodzie akcji „get” dla przedmiotu lub Object i powiedzą ci, że „kamerdyner odmawia przekazania mosiężnej lampy”. Mam na myśli ... kiedy jesteś programistą wystarczająco długo, jak ja, to wszystko jest BARDZO łatwe. Mam ponad 50 lat i robię to, odkąd skończyłem 7 lat. Mój ojciec był instruktorem Hewlett Packard w latach 70., więc nauczyłem się od niego TON na temat programowania komputerowego. Jestem teraz w rezerwach armii amerykańskiej jako administrator serwera. Um ... tak, więc nie poddawaj się. Nie jest to takie trudne, kiedy naprawdę rozbijesz to, co chcesz, aby twój program zrobił. Czasami metoda prób i błędów jest najlepszym sposobem na zrobienie czegoś takiego. Po prostu przetestuj i zobacz i nigdy się nie poddawaj. W porządku? Kodowanie to sztuka. Można to zrobić na wiele różnych sposobów. Nie pozwól, aby jeden lub drugi sposób blokował cię w kącie projektowania. m również w rezerwach armii amerykańskiej jako administrator serwera. Um ... tak, więc nie poddawaj się. Nie jest to takie trudne, kiedy naprawdę rozbijesz to, co chcesz, aby twój program zrobił. Czasami metoda prób i błędów jest najlepszym sposobem na zrobienie czegoś takiego. Po prostu przetestuj i zobacz i nigdy się nie poddawaj. W porządku? Kodowanie to sztuka. Można to zrobić na wiele różnych sposobów. Nie pozwól, aby jeden lub drugi sposób blokował cię w kącie projektowania. m również w rezerwach armii amerykańskiej jako administrator serwera. Um ... tak, więc nie poddawaj się. Nie jest to takie trudne, kiedy naprawdę rozbijesz to, co chcesz, aby twój program zrobił. Czasami metoda prób i błędów jest najlepszym sposobem na zrobienie czegoś takiego. Po prostu przetestuj i zobacz i nigdy się nie poddawaj. W porządku? Kodowanie to sztuka. Można to zrobić na wiele różnych sposobów. Nie pozwól, aby jeden lub drugi sposób blokował cię w kącie projektowania.

William Chelonis
źródło
To niestety pomija najtrudniejszą część parsera tekstowego, a mianowicie identyfikację czasownika, podmiotu i obiektu danych wejściowych użytkownika i mapowanie go na akcję.
Philipp
To prawda, ale jak to zrobię, utwórz klasę akcji i zapisz kilka akcji w, powiedzmy, klasie słownikowej, a następnie wyszukaj słowa akcji. Jeśli akcja obejmuje drugie słowo (jak w przypadku akcji „weź”, może „weź lampę”), zeskanuj grupę przedmiotów (lub rzeczowników) w poszukiwaniu miejsc, w których same te obiekty miałyby skrypt do obsługi wykonanych na nich akcji. Wszystko to przy założeniu, że kodujesz całą rzecz bezpośrednio w Javie, a nie próbujesz odczytać zewnętrznego pliku do skompilowania tekstowych przygód.
William Chelonis