Interpretação de comandos
como construir um sistema de interpretação de comandos

Um dos modelos mais tradicionais de jogos de raciocínio ficou conhecido no Brasil como adventures (que numa tradução mais livre significa "jogos de aventuras"). Havia então uma distinção clara entre esses jogos e os demais, chamados de arcades (Space Invaders, Pong, Penetrator, Pacman, etc).

Com o desenvolvimento de novas técnicas e novos recursos de hardware à disposição dos desenvolvedores, os jogos de aventuras evoluiram para modelos mais sofisticados e hoje eles englobam desde os jogos Point & Click, até os modernos jogos MMORPGs, passando pelos tradicionais MUDs e Adventures.

A essência do termo (adventure) então passou a indicar um tipo de jogo onde o jogador tem controles sofisticados sobre o que pode ser feito, como ação do seu personagem, englobando inclusive coisas que supostamente poderiam vir a ser realizadas (deixando portanto a imaginação do jogador livre e solta).

A base desse modelo está apoiada num sistema de reconhecimento de comando digitado, uma vez que as demais formas (teclas de ação, áreas clicáveis, lista de ações, etc) não conseguem resolver eficientemente todas as situações de um jogo desta natureza. O ideal é portanto mesclar os sistemas, para que o jogador decida como seria melhor resolvida a situação ou problema atual que ele enfrenta.

O que vamos mostrar nesta matéria é uma alternativa para um modelo de interpretador de comandos, que pode depois ser implementado com recursos específicos para determinados jogos. Ele se baseia no reconhecimento (a partir de uma frase digitada) de verbo mais dois objetos. O uso de dois e não apenas um objeto na frase nos permite construções extremamente complexas e requintadas, como por exemplo:

pegue a mala pequena
pegue a caneta azul
coloque a caneta na mala

O sistema exige, no entanto, que a palavra seja digitada corretamente e por completo, para evitar situações dúbias.

Para processar a frase, precisamos de um TMemo (que chamaremos Frase) para a digitação da mesma e um TLabel (que chamaremos de Mensag) para ser usado em resposta à ação pretendida. Dois TListBox (um chamado Verbos e o outro Palavs) completam o sistema básico de reconhecimento. A figura abaixo mostra como poderia ser montado o Form deste processador.

O TListBox Verbos conterá a lista de ações possíveis (ou verbos). Cada um deles deve ser definido numa linha, começando e terminando por um sinal ":" e que pode separar, na mesma linha, sinônimos. Assim, PEGAR e PEGUE seriam sinônimos de uma mesma ação e o jogador tanto poderia escrever pegue a mala, quanto pegar a mala. O mesmo vale para os objetos (ou palavras).

Uma vez digitada a frase, ao pressionar a tecla Enter, o sistema tentará identificar as palavras de tal forma a compor três códigos (índices), sendo que o zero (0) indicará que a palavra não foi reconhecida.

procedure TForm1.FraseKeyPress(Sender: TObject;
          var Key: Char);
var
  Tm: integer;
begin
  Key:= UpCase(Key);
  if Key = 'ç' then Key:='Ç'; if Key = 'á' then Key:='Á';
  if Key = 'é' then Key:='É'; if Key = 'í' then Key:='Í';
  if Key = 'ó' then Key:='Ó'; if Key = 'ú' then Key:='Ú';
  if Key = 'ã' then Key:='Ã'; if Key = 'õ' then Key:='Õ';
  if Key = 'â' then Key:='Â'; if Key = 'ê' then Key:='Ê';
  if Key = 'ô' then Key:='Ô'; if Key = 'ü' then Key:='Ü';
  if Key = 'à' then Key:='À';
  if Key = chr(13) then begin
     Mensag.Caption:= ''; Mensag.Repaint;
     Frase.Text:= trim(Frase.Text);
     if Frase.Text = '' then begin
        Mensag.Caption:= 'Especifique o comando desejado.';
        Frase.Text:= ''; Frase.SetFocus;
        exit;
     end;
     DecodeFrase; Frase.Text:= ''; Frase.Repaint;
     B1:= P1; B2:= P2; B3:= P3;
     CdDc:= IntToHex(P1,3)+IntToHex(P2,3)+IntToHex(P3,3)+
            '.txt';
     Label2.Caption:= CdDc;
     if LoadArq(CdDc) then begin
        Memo1.Lines.LoadFromFile(CdDc);
        Executar;
        Frase.SetFocus;
        exit;
     end;
     Mensag.Caption:= 'Comando desconhecido.';
     Frase.SetFocus;
  end;
end;

A procedure DecodeFrase faz esse serviço, colocando os respectivos índices das palavras reconhecidas em três variáveis globais (integer: P1, P2 e P3).

procedure TForm1.DecodeFrase;
label
Lp1,Lp2;
var
S,W: string;
Tm: integer;
Pr: boolean;
begin
P1:= 0; P2:= 0; P3:= 0;
S:= Frase.Text; Frase.Text:= '';
Pr:= false;
S:= trim(S); if S = '' then exit;
Tm:= pos('-',S);
if Tm = 0 then Tm:= pos(' ',S) else Pr:= true;
if Tm = 0 then begin W:= S; S:= '';
end else begin
W:= copy(S,1,Tm-1);
S:= copy(S,Tm+1,length(S));
end;
W:= ':' + W + ':';
//Verifica se é complemento de frase
if Cp <> 0 then begin
P1:= B1;
if Cp = 1 then begin
Cp:= 0;
goto Lp1;
end;
P2:= B2;
Cp:= 0;
goto Lp2;
end;
//Indetifica o verbo
for Tm:= 0 to Verbos.Items.Count-1 do begin
if
pos(W,Verbos.Items[Tm]) <> 0 then begin
P1:= Tm+1; //Achei paridade
Break
end;
end;

if S = '' then exit;
if P1 = 0 then exit;
if Pr then begin
P2:= B2;
Tm:= pos(' ',S);
if Tm = 0 then begin W:= S; S:= '';
end else begin
W:= copy(S,1,Tm-1);
S:= copy(S,Tm+1,length(S));
end;
goto
Lp2;
end;
Lp1:
S:= trim(S); if S = '' then exit;
Tm:= pos(' ',S);
if Tm = 0 then begin W:= S; S:= '';
end else begin W:= copy(S,1,Tm-1); S:= copy(S,Tm+1,length(S)); end;
W:= ':' + W + ':';
//Identifica primeira palavra
for Tm:= 0 to Palavs.Items.Count-1 do begin
if
pos(W,Palavs.Items[Tm]) <> 0 then begin
P2:= Tm+1; //Achei paridade
Break
end;
end;

if S = '' then exit;
if P2 = 0 then goto Lp1;
Lp2:
S:= trim(S); if S = '' then exit;
Tm:= pos(' ',S);
if Tm = 0 then begin W:= S; S:= '';
end else begin W:= copy(S,1,Tm-1); S:= copy(S,Tm+1,length(S)); end;
W:= ':' + W + ':';
//Identifica segunda palavra
for Tm:= 0 to Palavs.Items.Count-1 do begin
if
pos(W,Palavs.Items[Tm]) <> 0 then begin
P3:= Tm+1; //Achei paridade
Break
end;
end;
if
S = '' then exit;
if P3 = 0 then goto Lp2;
end;

No final deste processamento temos uma string composta pelos índices das palavras reconhecidas, convertidos em representação hexadecimal de três dígitos (o que nos permitira compor listas com até 4.094 verbos ou palavras). Acrescentamos a extensão ".txt" para que a string formada seja tratada como um arquivo texto, onde estarão definidas as ações referentes aquele conjunto específico de palavras.

Por exemplo, digamos que a frase pegue a mala resulte no aquivo 001001000.txt e que, existindo, o mesmo é carregado para um TMemo (Memo1), para ser processado.

procedure TForm1.Executar;
var
Tm,Tp: integer;
begin
if
Memo1.Lines.Count = 0 then exit;
Tm:= 0;
while Tm < Memo1.Lines.Count do begin
Tp:= DecodeLin(Memo1.Lines[Tm]);
if Tp = -1 then inc(Tm) else Tm:= Tp;
end;
end;

Cada linha do arquivo texto é portanto um comando que deve ser criado com um mnemônico e parâmetros. Para exemplificar esse processo, vamos mostrar uma instrução que chamaremos msg, cuja função é simplesmente mostrar a mensagem parâmetro para o jogador. A sintaxe da instrução seria:

msg Não existe nenhuma mala aqui.

function TForm1.DecodeLin(S: string): integer;
var
Cmd: string;
begin
result:= -1;
if S = '' then exit;
while S[1] = ' ' do S:= copy(S,2,length(S));
Cmd:= '';
if (S = '') or (S[1] = ':') or (S[1] = '/') then exit;
if pos(' ',S) = 0 then Cmd:= S
else Cmd:= copy(S,1,pos(' ',S)-1);
S:= copy(S,pos(' ',S)+1,length(S));
//A partir deste ponto Cmd contém o mnemônico da
//instrução a ser executada, seguindo o esquema
//abaixo:
//-Mensagem de avisdo --- msg texto
if Cmd = 'msg' then begin
Mensag.Caption:= S;
exit;
end;
end;

Simples assim. Daqui em diante fica por conta da imaginação de cada desenvolvedor.


Download...
Clique no links para fazer o download dos arquivos. O download desses arquivos é exclusivo para os assinantes do club TILT. Se ainda não é assinante, clique aqui e saiba como assinar.

Fonte completo deste exemplo
 
online
Tira Dúvidas