Bir metin macera oyununda kullanıcı girişini nasıl ayrıştırmalıyım?


17

Bir metin macerasında kullanıcı komutlarını ayrıştırma, Adventure'ın basit "kuzeye git" in hhgttg'deki akıl almaz derecede akıllı olanlarına kadar uzanan bir spektrumdur .

80'lerde bilgisayar dergilerinde nasıl güzel şeyler okuduğumu hatırlıyorum, ama şimdi kısa bir Wikipedia ref dışında net üzerinde neredeyse hiçbir şey bulamıyorum .

Nasıl olur sen bunu?


Güncelleme : Ludum Dare girişimde mümkün olan en basit yaklaşımla gittim .


3
Çözmeye çalıştığınız belirli bir sorun var mı?
Trevor Powell

@TrevorPowell eğlence için biraz metin macera yapmaya başlamadan düşünen ve sadece ziyade sadece dalış 'sanatın devlet' kendimi bilgilendirmek istiyoruz ve bildiğim gibi çözme hepsi
Will

1
Use Inform ; bu şimdiye kadar kullanabileceğiniz en iyi stratejidir. Bu günlerde bir metin macerasını el ile kodlamak için neredeyse hiçbir neden yok.
Nicol Bolas

Ludn Dare yaklaşmadıkça @NicolBolas? ;)
Will

1
Pragmatik olmak kadar yenilgici değil. Gelişmiş metin ayrıştırmanın tüm tekniklerine girmek (herkesin gelebileceği açık şeylerin ötesinde) muhtemelen tek bir cevabın kapsamı dışındadır.
Tetrad

Yanıtlar:


10

Etkileşimli kurgu topluluğunda arama yaptınız mı? Hala ayrıştırıcılar yazıyorlar ve bazıları doğal dil işleme gibi yeni teknikler uygulayarak zarfı zorlamaya çalışıyorlar.

Kullanılan yaklaşımları açıklayan makaleler için örneğin bu bağlantıya bakınız:

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


4
Aha, "metin macera" "interaktif kurgu" olur ve aniden çok daha googlable! Oynadığımdan beri adını bile değiştireceğini kim düşünebilirdi? :) Yine de, Bu potansiyel bakıyor ve aslında çok ne yazık ki izah alır değil
Will

9

İstediğiniz terim 'doğal dil işleme' veya NLP'dir. Bununla birlikte, resmi yöntemlerin gerçek dünyadaki metinleri anlamaya çalışmak için tasarlandığını, ancak genellikle doğal dilinizin sınırlı bir alt kümesi için çalışan bir şeye ihtiyacınız olduğunu unutmayın.

Genellikle basit bir dilbilgisi ve kelime bilgisi ile başlayabilir, daha sonra bunun için bir ayrıştırıcı yazabilirsiniz. Bir dilbilgisi böyle basit bir şey olabilir:

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"

Yukarıdaki, gramerleri temsil etmenin standart yolu olan Backus-Naur formunda bir varyanttır. Her neyse, bu dilbilgisini ayrıştırmak için kod oluşturmak için bir ayrıştırıcı oluşturucu kullanabilir veya dilinizde iyi bir dize kullanımı varsa kendi yazdıklarınızı kolayca yazabilirsiniz. (Dilbilgisinin her satırı için bir işlev kullanan 'özyinelemeli iniş ayrıştırıcıları' konusuna bakın.)

Ayrıştırıldıktan sonra, cümlenin mantıklı olup olmadığını anlayabilirsiniz - "kuzeye git" mantıklı olabilir, ancak "yeşil kuzeyi al" mantıklı değildir. Bunu 2 şekilde çözebilirsiniz; dilbilgisini daha resmi hale getirin (örneğin, yalnızca belirli ad türleriyle geçerli farklı fiil türlerine sahip olun) veya daha sonra fiillere karşı adları kontrol edin. İlk yol, oynatıcıya daha iyi hata mesajları vermenize yardımcı olabilir, ancak her zaman bağlamı her zaman kontrol etmeniz gerektiğinden, her zaman ikincisini bir dereceye kadar yapmanız gerekir - örn. "yeşil anahtarı al" dilbilgisi açısından doğru ve sözdizimsel olarak doğrudur, ancak yine de yeşil anahtarın mevcut olup olmadığını kontrol etmeniz gerekir.

Sonunda, programınız tüm çeşitli parçaları kontrol ederek doğrulanmış bir komutla sonuçlanır; yalnızca eylemi gerçekleştirmek için argümanlarla doğru işlevi çağırmak söz konusudur.


6

Bugün metin maceraları yapmak için en son teknoloji Inform 7'yi kullanıyor . Inform tabanlı oyunlar, Inform tabanlı oyunlarda "İngilizce yazmanıza" izin verdiği gibi "İngilizce gibi" yazıyor. Örneğin, Emily Short's Bronze'den :

Bir şeyin koku adı verilen bir metni vardır. Bir şeyin kokusu genellikle "hiçbir şey" dir.
Blok kokulu kural hiçbir kural defterinde listelenmez.
Bir şey koklamak için:
    "[isimden] [ismin kokusu] kokusu alıyorsun." Deyin.
Bir odayı koklamak yerine:
    oyuncu tarafından kokulu bir şeye dokunabiliyorsanız, "Sen kokuyorsun [oyuncu tarafından dokunabilecek kokulu şeylerin listesi]." Deyin;
    aksi takdirde "Yeri mutlulukla kokusuz" deyin.

Inform 7 ayrıştırıcısı Inform 7 IDE ile yakından entegredir ve kaynak kodun tamamı henüz çalışmaya hazır değildir:


5

Bir metin macera ayrıştırıcısı oluşturmayı öğrenmek için en iyi iki mevcut kaynak (belirtildiği gibi) IF topluluğu ve çamur topluluğudur. Bunlar için büyük forumlarda arama yaparsanız (Intfiction.org/forum, rec.arts.int-fiction, Mud Connector, Mudbytes, Mudlab, En İyi Çamur Siteleri haber grubu) bazı cevaplar bulacaksınız, ancak sadece Makaleler için Richard Bartle'ın MUD II'deki ayrıştırıcıya ilişkin açıklamasını tavsiye ederim:

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

Ve rec.arts.int-fiction üzerine bu açıklama:

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

Diğer cevaplar için hiçbir saygısızlık yoktu, ancak bir CF dilbilgisi oluşturmak veya BNF kullanmak bu sorunun çözümü değildir. Bu, farklı bir soruna çözüm olamayacağı anlamına gelmez, yani daha gelişmiş bir doğal dil ayrıştırıcısı oluşturmaktır, ancak bu, metin macerası kapsamında IMO'nun değil, önemli bir araştırmanın konusudur.



1

Oyununuzda doğru olan tüm cümleler olan alana özgü bir dil tanımlamanız gerekir. Bu amaçla diliniz için bir dilbilgisi tanımlamanız gerekir (kelime bilgisi ve sözdizimi). İhtiyacınız olan dilbilgisi türü, Bağlamdan Bağımsız Dilbilgisi'dir ve ANTLR (www.antlr.org) gibi dilbilgisinin sentetik bir tanımından başlayarak otomatik olarak bir ayrıştırıcı oluşturan araçlar vardır. Ayrıştırıcı yalnızca bir cümlenin doğru olup olmadığını kontrol eder ve her kelimenin dilbilgisinde belirttiğiniz role sahip olduğu cümlenin gezilebilir bir temsili olan cümlenin Özet Sözdizimi Ağacı (AST) üretir. AST'de gezinerek, cümledeki diğer kelimelere göre bu rolü oynarken her kelimenin anlambiliminin ne olduğunu değerlendiren kodu eklemeniz ve anlambilimin doğru olup olmadığını doğrulamanız gerekir.

Örneğin 'Taş adamı yiyor' cümlesi sözdizimsel olarak doğrudur, ancak anlamsal olarak doğru değildir (dünya taşlarınızda, belki sihirli taşlarınız erkekleri yiyemezse).

Anlambilim de doğruysa, örneğin, dünyayı buna göre değiştirebilirsiniz. Bu bağlamı değiştirebilir ve böylece aynı cümle artık anlamsal olarak doğru olamazdı (örneğin, yemek yiyebilecek kimse olamaz)


1

Yazdığım bazı metin maceraları için Tads3 (www.tads3.org) motorunu kullandım. Bilgisayar programcıları için daha çok ama çok güçlü bir dil. Eğer bir programcıysanız Tads3, daha önce kullandığım Inform7'den daha hızlı kodlamak için tonlarca daha kolay olacaktır. Programcılar için Inform7 ile ilgili sorun, "fiil tahmin" kadar ünlüdür metin cümleleri oyuncular için ki eğer cümlelerinizi ÇOK dikkatli yazmazsanız oyunu kıracaksınız. Bunu yapmak için sabrınız varsa, Tokenizer sınıfını kullanarak Java'da kolayca bir ayrıştırıcı yazabilirsiniz. Örnek Bir küresel JTextArea ve bir global String [] dizisi kullanarak yazdım. AZ ve 0-9 yapraklarının yanı sıra soru işaretini ("yardım" komut kısayolu için) hariç istenmeyen karakterleri çıkarır:

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

... ana sınıf tanımını ve değişken initialize () yöntemini, vb. Bunun ana sınıfı muhtemelen JFrame'i genişletmeli ve genel statik void main () yönteminizde bunun bir örneğini oluşturmalıdır. Umarım bu kodlardan bazıları yardımcı olur.

DÜZENLENMİŞ - Tamam, şimdi yapacağınız şey bir İşlemler sınıfı oluşturmak ve bir eylem taraması yapmaktır (yani "lamba al" veya "kılıcı düşür"). Daha basit hale getirmek için, kapsamda görünen her şeyi taramak ve yalnızca o eylemdeki nesneleri taramak için bir RoomScan nesnesine veya yöntemine sahip olmanız gerekir. Nesnenin kendisi eylem işlemeyi gerçekleştirir ve varsayılan olarak, bilinen tüm eylemleri varsayılan bir şekilde işlemek için bir Item sınıfına sahip olmanız gerekir. Şimdi, örneğin, "almak" istediğiniz bir öğe oyuncu olmayan bir karakter tarafından tutuluyorsa, o öğenin sahibi tarafından tutulmasına ilişkin varsayılan yanıt "Bu öğeye sahip olmanıza izin vermeyecek" şeklinde olmalıdır. Şimdi Item veya Thing sınıfında buna bir ton varsayılan eylem yanıtı oluşturmanız gerekir. Bu temelde tüm tasarıma Tads3 perspektifinden geliyor. Tads3'te her öğenin üzerinde bir eylem başlatıldığında ayrıştırıcının çağırdığı kendi varsayılan eylem işleme yordamı vardır. Yani ... Sadece söylüyorum, Tads3 zaten bütün bunları yerinde, bu yüzden o dilde bir metin macerasında kodlamak ÇOK kolay. Ancak, Java (yukarıda) gibi sıfırdan yapmak istiyorsanız, o zaman şahsen Tads3'ün tasarlandığı gibi ele alacağım. Bu şekilde, farklı nesnelerdeki rutinleri işleyen varsayılan eylemleri geçersiz kılabilirsiniz, bu nedenle örneğin "lamba almak" ve uşak tutuyorsa, Öğe için varsayılan "get" eylem yönteminde bir yanıt tetikleyebilir veya Object ve size "uşak pirinç lambayı teslim etmeyi reddediyor." Demek istediğim ... bir kez benim kadar uzun bir programcı olduktan sonra, hepsi çok kolay. 50 yaşın üzerindeyim ve bunu 7 yaşımdan beri yapıyorum. Babam 70'li yıllarda bir Hewlett Packard eğitmeniydi, bu yüzden başlangıçta bilgisayar programlama konusunda ondan bir TON öğrendim. Ayrıca şu anda ABD Ordusu Yedekleri'nde sunucu yöneticisi olarak bulunuyorum. Um ... evet, pes etme. Programınızın yapmasını istediğiniz şeye gerçekten böldüğünüzde o kadar da zor değil. Bazen deneme yanılma bu tür şeylere devam etmenin en iyi yoludur. Sadece test edin ve görün ve asla vazgeçmeyin. Tamam? Kodlama bir sanattır. Birçok farklı şekilde yapılabilir. Bir şekilde ya da diğerinin sizi tasarım köşesinde engellemesine izin vermeyin. Ayrıca şimdi ABD Ordusu Rezervleri m bir sunucu yöneticisi olarak. Um ... evet, pes etme. Programınızın yapmasını istediğiniz şeye gerçekten böldüğünüzde o kadar da zor değil. Bazen deneme yanılma bu tür şeylere devam etmenin en iyi yoludur. Sadece test edin ve görün ve asla vazgeçmeyin. Tamam? Kodlama bir sanattır. Birçok farklı şekilde yapılabilir. Bir şekilde ya da diğerinin sizi tasarım köşesinde engellemesine izin vermeyin. Ayrıca şimdi ABD Ordusu Rezervleri m bir sunucu yöneticisi olarak. Um ... evet, pes etme. Programınızın yapmasını istediğiniz şeye gerçekten böldüğünüzde o kadar da zor değil. Bazen deneme yanılma bu tür şeylere devam etmenin en iyi yoludur. Sadece test edin ve görün ve asla vazgeçmeyin. Tamam? Kodlama bir sanattır. Birçok farklı şekilde yapılabilir. Bir şekilde ya da diğerinin sizi tasarım köşesinde engellemesine izin vermeyin.


Bu maalesef bir metin ayrıştırıcısının en zor kısmını, yani kullanıcı girdisinin fiilini, konusunu ve nesnesini tanımlayıp onu bir eylemle eşleştiriyor.
Philipp

Doğru ama bunu nasıl yapacağım, bir eylem sınıfı oluşturmak ve bir grup eylemi, örneğin bir sözlük sınıfında saklamak, sonra eylem kelimelerini taramaktır. Eğer eylem 2. kelimeyi içeriyorsa (bir "al" eylemi gibi, belki "lamba al"), o zaman bu nesnelerin kendileri üzerinde yapılan eylemleri işlemek için komut dosyası olacakları yerde taranan bir grup öğe (veya isim) nesnesi bulundurun. Tüm bunlar, her şeyi doğrudan Java'ya kodlayacağınızı ve metin maceralarını derlemek için gerçek bir harici dosyayı okumayı denemeyeceğinizi varsayar.
William Chelonis
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.