Como devo analisar a entrada do usuário em um jogo de aventura de texto?

16

A análise dos comandos do usuário em uma aventura de texto é um espectro, desde o simples "vá para o norte" da Adventure até alguns incrivelmente inteligentes em hhgttg .

Eu me lembro de ter lido boas instruções em revistas de informática nos anos 80, mas agora não encontro quase nada na rede, exceto uma breve ref .

Como você faria isso?


Atualização : Eu segui a abordagem mais simples possível na minha entrada no Ludum Dare .

Vai
fonte
3
Há um problema específico que você está tentando resolver?
Trevor Powell
@TrevorPowell considerando embarcar em fazer um pouco de aventura de texto para se divertir, e só quero me familiarizar do 'estado da arte' em vez de apenas mergulho em e resolver meu jeito é tudo
Will
1
Use Informar ; essa é a melhor estratégia que você poderia usar. Atualmente, não há razão para codificar manualmente uma aventura de texto.
Nicol Bolas
@NicolBolas, a menos que Ludum Dare esteja se aproximando? ;)
Será
1
Não é tão derrotista quanto pragmático. Entrar em todas as técnicas de análise avançada de texto (além das coisas óbvias que qualquer pessoa pode inventar) provavelmente está fora do escopo de uma única resposta aqui.
Tetrad #

Respostas:

10

Você pesquisou na comunidade de ficção interativa? Eles ainda escrevem analisadores e alguns tentam empurrar o envelope implementando novas técnicas, como o processamento de linguagem natural.

Veja, por exemplo, este link para artigos que descrevem abordagens usadas:

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

krolth
fonte
4
Ah, "aventura de texto" se torna "ficção interativa" e de repente é muito mais googlable! Quem pensaria que mudaria de nome desde que eu o toquei? :) Ainda assim, olhando para esses leads, e na verdade não muito é explicado com tristeza
Será
9

O termo que você deseja é 'processamento de linguagem natural' ou PNL. No entanto, lembre-se de que os métodos formais são projetados para tentar entender os textos do mundo real, enquanto você normalmente só precisa de algo que funcione para um subconjunto limitado da sua linguagem natural.

Normalmente, você pode começar com uma gramática e vocabulário simples e depois escrever um analisador. Uma gramática pode ser algo simples assim:

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"

A descrição acima é uma variante da forma Backus-Naur, a maneira padrão de representar gramáticas. De qualquer forma, você pode usar um gerador de analisador para gerar código para analisar essa gramática ou escrever seu próprio com bastante facilidade se o seu idioma tiver um tratamento decente de cadeias. (Consulte 'analisadores de descida recursiva', que usam uma função para cada linha da gramática.)

Uma vez analisado, você pode descobrir se a sentença faz sentido - "vá para o norte" pode fazer sentido, mas "obtenha o norte verde" não. Você pode resolver isso de duas maneiras; torne a gramática mais formal (por exemplo, tenha tipos diferentes de verbos válidos apenas para certos tipos de substantivo) ou verifique os substantivos no verbo posteriormente. A primeira maneira pode ajudá-lo a fornecer melhores mensagens de erro para o player, mas você sempre precisa executar a segunda em algum grau, pois sempre precisa verificar o contexto - por exemplo. "pegue a tecla verde" está gramaticalmente correto e sintaticamente correto, mas você ainda precisa verificar se a chave verde está presente.

Eventualmente, seu programa acaba com um comando validado com todas as várias partes verificadas; então é apenas um caso de chamar a função correta com os argumentos para executar a ação.

Kylotan
fonte
6

O estado da arte para fazer aventuras de texto hoje em dia é usar o Inform 7 . A fonte do Inform 7 lê "como o inglês", da mesma forma que os jogos baseados no Inform permitem "escrever em inglês". Por exemplo, de Emily Short's Bronze :

Uma coisa tem algum texto chamado perfume. O cheiro de uma coisa é geralmente "nada".
A regra de cheiro de bloco não está listada em nenhum livro de regras.
Faça cheirar algo:
    diga "De [o substantivo] você cheira [o cheiro do substantivo]".
Em vez de cheirar uma sala:
    se uma coisa perfumada puder ser tocada pelo jogador, diga "Você cheira [a lista de coisas perfumadas que podem ser tocadas pelo jogador].";
    caso contrário, diga "O lugar é maravilhosamente inodoro".

O analisador do Inform 7 está intimamente integrado ao IDE do Inform 7, e todo o código-fonte ainda não está disponível para estudo:


fonte
5

As duas melhores fontes atuais para aprender a criar um analisador de aventura de texto são (como foi mencionado) a comunidade IF e a comunidade de lama. Se você pesquisar nos fóruns principais (Intfiction.org/forum, grupo de notícias rec.arts.int-fiction, Mud Connector, Mudbytes, Mudlab, Top Mud Sites), encontrará algumas respostas, mas se estiver apenas procurando para artigos, eu recomendaria a explicação de Richard Bartle sobre o analisador no MUD II:

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

E esta explicação em rec.arts.int-fiction:

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

Sem desrespeito às outras respostas, mas criar uma gramática de CF ou usar BNF não é a solução para esse problema. Isso não quer dizer que não possa ser uma solução para um problema diferente, ou seja, criar um analisador de linguagem natural mais avançado, mas esse é o assunto de uma pesquisa considerável e não da OMI no escopo de uma aventura de texto.

georgek
fonte
4

No meu primeiro ano na universidade, fizemos um jogo de aventura em Prolog e, para a entrada do usuário, tivemos que usar gramática de cláusula definida ou DCG. Consulte http://www.amzi.com/manuals/amzi/pro/ref_dcg.htm#DCGCommandLanguage para obter um exemplo de como usá-lo como uma linguagem de comando. Parecia uma abordagem de princípios (era uni, afinal) e flexível na época.

Eric
fonte
1

Você precisa definir um idioma específico do domínio que seja todas as frases corretas no seu jogo. Para esse fim, você precisa definir uma gramática para o seu idioma (vocabulário e sintaxe). O tipo de gramática que você precisa é uma gramática livre de contexto e existem ferramentas que geram automaticamente um analisador a partir de uma descrição sintética da gramática, como ANTLR (www.antlr.org). O analisador apenas verifica se uma sentença está correta ou não e produz uma Árvore de Sintaxe Abstrata (AST) da sentença, que é uma representação navegável da sentença em que cada palavra tem a função especificada na gramática. Ao navegar no AST, você deve adicionar o código que avalia qual é a semântica que cada palavra assume ao desempenhar esse papel em relação às outras palavras da sentença e verificar se a semântica está correta.

Por exemplo, a frase 'A pedra come o homem' é sintaticamente correta, mas não necessariamente semanticamente correta (a menos que no mundo as pedras, talvez as mágicas, possam comer homens).

Se a semântica também estiver correta, você poderá, por exemplo, mudar o mundo de acordo com ela. Isso pode mudar o contexto e, portanto, a mesma sentença não pode mais ser semanticamente correta (por exemplo, não pode haver homem para comer)

www.Sillitoy.com
fonte
1

Usei o mecanismo Tads3 (www.tads3.org) para algumas das aventuras de texto que escrevi. É mais para programadores de computador, mas uma linguagem muito poderosa. Se você é um programador, o Tads3 será muito mais fácil de codificar as coisas mais rapidamente do que o Inform7, que eu já usei antes. O problema com o Inform7 para programadores é tão famoso quanto "adivinhe o verbo" é para jogadores de aventuras de texto, pois se você não escrever suas frases com muito cuidado, irá interromper o jogo. Se você tiver paciência para fazer isso, poderá escrever facilmente um analisador em Java usando a classe Tokenizer. Exemplo que escrevi usando um JTextArea global e uma matriz String [] global. Ele remove caracteres indesejados, exceto deixa AZ e 0-9, bem como o ponto de interrogação (para um atalho de comando "ajuda"):

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

... Eu deixei de fora a definição de classe principal e o método da variável initialize (), etc. porque é assumido que, se você conhece Java, já sabe como configurá-lo. A classe principal para isso provavelmente deve estender o JFrame e, em seu método público static void main (), crie apenas uma instância dele. Espero que parte desse código ajude.

EDITADO - Ok, agora o que você faria a seguir é criar uma classe Actions e procurar uma ação (por exemplo, "obter lâmpada" ou "soltar a espada"). Para simplificar, você precisa ter um objeto ou método do RoomScan para verificar tudo o que estiver visível no escopo e verificar apenas os objetos dessa ação. O próprio objeto lida com a manipulação de ações e, por padrão, você deve ter uma classe Item para lidar com todas as ações conhecidas de uma maneira padrão, que pode ser substituída. Agora, se, por exemplo, um item que você deseja "receber" é mantido por um personagem que não é jogador, a resposta padrão para manter esse item pelo proprietário deve ser algo como "O não permitirá que você o tenha". Agora você teria que criar várias respostas de ação padrão para isso na classe Item ou Coisa. Isso basicamente vem da perspectiva do Tads3 em todo o design. Como no Tads3, cada item possui sua própria rotina de manipulação de ações padrão, que o analisador chama se uma ação for inicializada. Então ... só estou lhe dizendo, o Tads3 já possui tudo isso, por isso é MUITO fácil codificar em uma aventura de texto nesse idioma. Mas se você quiser fazer isso do zero, como em Java (acima), eu pessoalmente lidaria com isso da mesma maneira que o Tads3 foi projetado. Dessa forma, você pode substituir ações padrão que manipulam rotinas em objetos diferentes, por exemplo, se você deseja "obter lâmpada" e o mordomo a está segurando, isso pode desencadear uma resposta no método de ação "obter" padrão para Item ou Objete e diga que "o mordomo se recusa a entregar a lâmpada de bronze". Quero dizer ... depois que você é programador há tempo suficiente como eu, tudo isso é MUITO fácil. Tenho mais de 50 anos e faço isso desde os 7 anos. Meu pai era instrutor da Hewlett Packard nos anos 70, então aprendi uma TONELADA dele inicialmente em programação de computadores. Também estou nas Reservas do Exército dos EUA como administrador de servidores agora. Hum ... sim, então não desista. Não é tão difícil assim que você realmente divide o que deseja que seu programa faça. Às vezes, tentativa e erro é a melhor maneira de fazer esse tipo de coisa. Apenas teste e veja e nunca desista. OK? Codificação é uma arte. Isso pode ser feito de muitas maneiras diferentes. Não deixe que, de um jeito ou de outro, pareçam obstruí-lo em um canto do design. m também nas Reservas do Exército dos EUA como basicamente um administrador de servidor agora. Hum ... sim, então não desista. Não é tão difícil assim que você realmente divide o que deseja que seu programa faça. Às vezes, tentativa e erro é a melhor maneira de fazer esse tipo de coisa. Apenas teste e veja e nunca desista. OK? Codificação é uma arte. Isso pode ser feito de muitas maneiras diferentes. Não deixe que, de um jeito ou de outro, pareçam obstruí-lo em um canto do design. m também nas Reservas do Exército dos EUA como basicamente um administrador de servidor agora. Hum ... sim, então não desista. Não é tão difícil assim que você realmente divide o que deseja que seu programa faça. Às vezes, tentativa e erro é a melhor maneira de fazer esse tipo de coisa. Apenas teste e veja e nunca desista. OK? Codificação é uma arte. Isso pode ser feito de muitas maneiras diferentes. Não deixe que, de um jeito ou de outro, pareçam obstruí-lo em um canto do design.

William Chelonis
fonte
Infelizmente, isso deixa de fora a parte mais difícil de um analisador de texto, ou seja, identificar o verbo, assunto e objeto da entrada do usuário e mapeá-lo para uma ação.
Philipp
É verdade, mas como eu faria isso é criar uma classe de ações e armazenar várias ações, digamos, em uma classe de dicionário, e depois procurar palavras de ações. Se a ação envolver uma segunda palavra (como para uma ação "take", talvez "take lamp"), faça um monte de objetos de itens (ou substantivos) verificados onde os objetos em si teriam um script para lidar com as ações feitas neles. Isso tudo pressupõe que você codifique tudo em Java e não tente ler um arquivo externo real para compilar aventuras de texto.
William Chelonis