텍스트 어드벤처 게임에서 사용자 입력을 어떻게 구문 분석해야합니까?


16

텍스트 어드벤처에서 사용자 명령을 파싱하는 것은 어드벤처 의 단순한 "북쪽으로"부터 hhgttg의 어리석은 영리한 명령까지의 스펙트럼입니다 .

80 년대 컴퓨터 매거진에서 좋은 방법을 읽은 것을 기억하는 것 같지만, 지금은 간단한 Wikipedia 심판을 제외하고는 인터넷에서 거의 아무것도 찾지 못합니다 .

어떻게 것입니다 당신이 그것을 할?


업데이트 : 나는 Ludum Dare 항목 에서 가능한 가장 간단한 방법을 사용했습니다 .


3
해결하려는 특정 문제가 있습니까?
Trevor Powell

@TrevorPowell은 재미를 위해 작은 텍스트 모험을 시작하는 것을 고려하고 그냥 뛰어 들어 내 길을 해결하는 것보다는 '예술의 상태'에 대해 알고 싶어합니다.
Will

1
알림 사용 ; 그것이 당신이 사용할 수있는 최고의 전략입니다. 요즘에는 텍스트 어드벤처를 직접 코딩 할 이유가 없습니다.
Nicol Bolas

Ludum Dare가 다가 오지 않는 한 @NicolBolas? )

1
실용적이기 때문에 패배자가 아닙니다. 모든 기술에 고급 텍스트 파싱을 사용하는 것은 (아무도 알아낼 수있는 명백한 것 이외의) 아마도 단일 답변의 범위를 벗어나는 것일 것입니다.
Tetrad

답변:


10

대화 형 소설 커뮤니티에서 검색 했습니까? 그들은 여전히 ​​파서를 작성하고 일부는 자연어 처리와 같은 새로운 기술을 구현하여 봉투를 밀려 고 시도합니다.

사용 된 접근 방식을 설명하는 기사를 보려면이 링크를 참조하십시오.

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


4
아하, "텍스트 어드벤처"는 "인터랙티브 픽션"이되고 갑자기 훨씬 더 엉망진창입니다! 내가 연주 한 이래로 이름이 바뀐다 고 누가 생각 했습니까? :) 아직도, 그 리드를보고 실제로 많이 슬프게 설명되지 않습니다
Will

9

원하는 용어는 '자연어 처리'또는 NLP입니다. 그러나 공식적인 방법은 실제 텍스트를 이해하고 이해하기 위해 고안된 것이지만 일반적으로 자연어의 제한된 하위 집합에 적합한 방법 만 있으면됩니다.

일반적으로 간단한 문법과 어휘로 시작한 후 파서를 작성할 수 있습니다. 문법은 다음과 같이 간단 할 수 있습니다.

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"

위는 문법을 나타내는 표준 방식 인 Backus-Naur 형식의 변형입니다. 어쨌든 구문 분석기 생성기를 사용하여이 문법을 구문 분석하는 코드를 생성하거나 언어에 적절한 문자열 처리 기능이있는 경우 사용자가 쉽게 작성할 수 있습니다. (문법의 각 줄마다 하나의 기능을 사용하는 '재귀 강하 파서'를 찾으십시오.)

구문 분석이 끝나면 문장이 의미가 있는지 알아낼 수 있습니다. "북쪽으로 가십시오"는 의미가있을 수 있지만 "녹색 북쪽으로 가십시오"는 의미가 없습니다. 두 가지 방법으로이 문제를 해결할 수 있습니다. 문법을보다 형식적으로 만들거나 (예 : 특정 유형의 명사에서만 다른 유형의 동사를 사용할 수 있음) 나중에 동사에 대해 명사를 확인하십시오. 첫 번째 방법은 플레이어에게 더 나은 오류 메시지를 제공하는 데 도움이 될 수 있지만 항상 컨텍스트를 확인해야하는 경우와 같이 항상 두 번째 수준까지 수행해야합니다. "녹색 키 사용"은 문법적으로 정확하고 구문 적으로 정확하지만 여전히 녹색 키가 있는지 확인해야합니다.

결국 프로그램은 모든 다양한 부분을 확인한 검증 된 명령으로 끝납니다. 그런 다음 인수를 사용하여 올바른 함수를 호출하여 작업을 수행하는 경우입니다.


6

오늘날 텍스트 모험을위한 최첨단은 Inform 7을 사용하고 있습니다. Inform 7 소스는 Inform 기반 게임에서 "영어를 쓸 수있는"것과 같은 방식으로 "영어와 같은"을 읽습니다. 예를 들어 Emily Short의 Bronze는 다음과 같습니다.

물건에는 향기라는 텍스트가 있습니다. 사물의 향기는 보통 "아무것도 없습니다".
차단 냄새 규칙은 규칙 집에 나열되지 않습니다.
무언가 냄새 맡기 :
    "[명사]에서 [명사의 향기] 냄새를 맡으십시오."라고 말합니다.
방에서 냄새를 맡는 대신 :
    플레이어가 향기로운 물건을 만질 수 있다면, "[플레이어가 만질 수있는 향기 나는 물건의 목록] 냄새를 맡으십시오.";
    그렇지 않으면 "장소가 행복하게 무취입니다"라고 말합니다.

Inform 7 구문 분석기는 Inform 7 IDE와 밀접하게 통합되어 있으며 전체 소스 코드를 아직 연구 할 수 없습니다.


5

텍스트 어드벤처 파서를 만드는 법을 배우는 데 가장 적합한 두 가지 최신 소스는 IF 커뮤니티와 진흙 커뮤니티입니다. 주요 포럼 (Intfiction.org/forum, 뉴스 그룹 rec.arts.int-fiction, Mud Connector, Mudbytes, Mudlab, Top Mud Sites)을 검색하면 몇 가지 답변을 찾을 수 있습니다. 기사의 경우 MUD II의 파서에 대한 Richard Bartle의 설명을 추천합니다.

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

그리고 rec.arts.int-fiction에 대한이 설명 :

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

다른 답변에 대한 무례 함은 없지만 CF 문법을 만들거나 BNF를 사용하는 것이이 문제에 대한 해결책이 아닙니다. 그것은 다른 문제에 대한 해결책이 될 수 없다고 말하는 것이 아닙니다. 즉, 고급 자연 언어 파서를 만드는 것이지만 텍스트 모험의 범위에서 IMO가 아닌 상당한 연구의 주제입니다.



1

게임에서 올바른 모든 문장 인 도메인 특정 언어를 정의해야합니다. 이를 위해 언어에 대한 문법 (어휘 및 구문)을 정의해야합니다. 필요한 문법 유형은 Context Free Grammar이며 ANTLR (www.antlr.org)과 같은 문법의 합성 설명에서 시작하여 구문 분석기를 자동으로 생성하는 도구가 있습니다. 구문 분석기는 문장이 올바른지 여부 만 확인하고 문장에서 AST (Abstract Syntax Tree)를 생성합니다.이 구문은 각 단어가 문법에서 지정한 역할을하는 문장을 탐색 가능한 표현입니다. AST를 탐색하여 문장의 다른 단어와 관련하여 해당 역할을 수행 할 때 각 단어가 수행하는 의미를 평가하고 의미가 올바른지 확인하는 코드를 추가해야합니다.

예를 들어, '돌이 사람을 먹는다'는 문장은 정확하지만 의미 적으로 정확할 필요는 없습니다 (세계의 돌, 아마도 마법의 돌, 남자를 먹을 수 없다면).

의미가 올바른 경우 예를 들어 세계를 그에 따라 변경할 수 있습니다. 이것은 문맥을 바꿀 수 있으므로 같은 문장이 더 이상 의미 론적으로 정확하지 않을 수 있습니다 (예를 들어 먹을 사람이 없을 수 있습니다)


1

필자가 작성한 텍스트 어드벤처에 Tads3 (www.tads3.org) 엔진을 사용했습니다. 컴퓨터 프로그래머에게는 더 많은 것이지만 매우 강력한 언어입니다. 프로그래머라면 Tads3는 이전에도 사용했던 Inform7보다 훨씬 빠르게 코딩하는 것이 훨씬 쉬울 것입니다. 프로그래머를위한 Inform7의 문제는 "동사 추측"만큼이나 텍스트 어드벤처 플레이어에게 매우 유명합니다. 문장을 매우 신중하게 쓰지 않으면 게임을 중단하게됩니다. 인내심이 있다면 Tokenizer 클래스를 사용하여 Java로 파서를 쉽게 작성할 수 있습니다. 예제는 전역 JTextArea와 전역 String [] 배열을 사용하여 작성했습니다. AZ와 0-9를 제외한 원치 않는 문자와 물음표 ( "도움말"명령 바로 가기)를 제거합니다.

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

... 기본 클래스 정의와 변수 initialize () 메소드 등은 생략했습니다 .Java를 알고 있다면 이미 설정 방법을 알고 있다고 가정하기 때문입니다. 이것에 대한 메인 클래스는 아마도 JFrame을 확장해야하며 공개 정적 void main () 메소드에서 인스턴스를 생성하십시오. 이 코드 중 일부가 도움이 되길 바랍니다.

편집-자, 이제 다음으로 할 일은 Actions 클래스를 만들고 액션을 검색하는 것입니다 (예 : "get lamp"또는 "drop sword"). 더 간단하게하려면 범위에서 보이는 모든 것을 스캔하고 해당 작업에서 해당 객체 만 스캔 할 수있는 RoomScan 객체 또는 메소드가 있어야합니다. 객체 자체는 액션 처리를 처리하며 기본적으로 Item 클래스는 알려진 모든 액션을 기본 방식으로 처리해야하며이를 재정의 할 수 있습니다. 예를 들어, "가져 오려는"아이템이 플레이어가 아닌 캐릭터에 의해 유지된다면, 소유자가 그 아이템을 갖도록하는 기본 응답은 "당신이 갖지 못하게 할 것"과 같은 것이어야합니다. 이제 Item 또는 Thing 클래스에서 이에 대한 많은 기본 조치 응답을 작성해야합니다. 이것은 기본적으로 모든 디자인에 대한 Tads3 관점에서 나옵니다. Tads3에서 각 항목에는 자체 조치가 초기화 될 때 구문 분석기가 호출하는 고유 한 기본 조치 처리 루틴이 있습니다. Tads3는 이미이 모든 것을 갖추고 있으므로 해당 언어로 텍스트 어드벤처로 코딩하기가 매우 쉽습니다. 그러나 Java (위)와 같이 처음부터 그것을하고 싶다면 Tads3가 디자인 된 것과 거의 같은 방식으로 개인적으로 처리 할 것입니다. 이러한 방식으로 다른 객체 자체의 루틴을 처리하는 기본 작업을 무시할 수 있습니다. 예를 들어 "램프를 가져 오려면"버틀러가이를 유지하는 경우 Item의 기본 "get"액션 메서드에서 응답을 트리거 할 수 있습니다. 또는 반대하고 "버틀러가 놋쇠 램프를 넘겨주는 것을 거부합니다." 내 말은 ... 일단 당신이 내가 가진 것만 큼 오랫동안 프로그래머라면, 이것은 매우 쉬운 일입니다. 저는 50 세가 넘었고 7 살 때부터이 일을하고있었습니다. 아버지는 70 년대에 휴렛 팩커드 강사 였으므로 컴퓨터 프로그래밍에 대해 처음부터 TON을 배웠습니다. 나는 또한 기본적으로 서버 관리자로 미 육군 예비에 있습니다. 음 ... 네, 포기하지 마십시오. 일단 당신이 원하는 것을 프로그램으로 나누는 것은 그렇게 어렵지 않습니다. 때로는 시행 착오가 이런 종류의 일을하는 가장 좋은 방법입니다. 그냥 테스트하고 포기하지 마십시오. 괜찮아? 코딩은 예술입니다. 여러 가지 방법으로 수행 할 수 있습니다. 어느 쪽이든 다른 쪽이 당신을 디자인의 구석으로 막는 것처럼 보지 마십시오. m은 또한 미 육군 예비군에서 기본적으로 서버 관리자로 사용됩니다. 음 ... 네, 포기하지 마십시오. 일단 당신이 원하는 것을 프로그램으로 나누는 것은 그렇게 어렵지 않습니다. 때로는 시행 착오가 이런 종류의 일을하는 가장 좋은 방법입니다. 그냥 테스트하고 포기하지 마십시오. 괜찮아? 코딩은 예술입니다. 여러 가지 방법으로 수행 할 수 있습니다. 어느 쪽이든 다른 쪽이 당신을 디자인의 구석으로 막는 것처럼 보지 마십시오. m은 또한 미 육군 예비군에서 기본적으로 서버 관리자로 사용됩니다. 음 ... 네, 포기하지 마십시오. 일단 당신이 원하는 것을 프로그램으로 나누는 것은 그렇게 어렵지 않습니다. 때로는 시행 착오가 이런 종류의 일을하는 가장 좋은 방법입니다. 그냥 테스트하고 포기하지 마십시오. 괜찮아? 코딩은 예술입니다. 여러 가지 방법으로 수행 할 수 있습니다. 어느 쪽이든 다른 쪽이 당신을 디자인의 구석으로 막는 것처럼 보지 마십시오.


불행히도 이것은 텍스트 파서의 가장 어려운 부분, 즉 사용자 입력의 동사, 주제 및 대상을 식별하고 동작에 매핑하는 가장 어려운 부분을 제외합니다.
Philipp

사실이지만 내가하는 일은 액션 클래스를 만들고 사전 클래스에 많은 액션을 저장 한 다음 액션 단어를 스캔하는 것입니다. 액션에 두 번째 단어 (예 : "테이크"액션, "테이프 램프")가 포함 된 경우 해당 오브젝트 자체에 대한 조치를 처리하기위한 스크립트가있는 위치에 대해 여러 항목 (또는 명사) 오브젝트를 스캔하십시오. 이것은 모든 것을 Java로 직접 코딩하고 텍스트 어드벤처를 컴파일하기 위해 실제 외부 파일을 읽으려고하지 않는다고 가정합니다.
윌리엄 Chelonis
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.