Caçar os Wumpus

39

Quando eu era garoto, as crianças passeavam pelas lojas de computadores e brincavam de Hunt the Wumpus até a equipe nos expulsar. Era um jogo simples, programável nos computadores domésticos de meados da década de 1970, máquinas tão rudimentares que, em vez de microprocessadores do tamanho de filhotes, acho que alguns deles provavelmente tinham filhotes de verdade lá.

Vamos evocar essa época passada, reproduzindo o jogo em hardware moderno.

  1. O jogador começa em uma sala aleatória em um mapa icosaédrico (portanto, há 20 salas no total, conectadas umas às outras como as faces de um icosaedro, e cada sala tem exatamente três saídas).

  2. O wumpus começa em uma sala diferente selecionada aleatoriamente. O wumpus fede e seu odor pode ser detectado em qualquer uma das três salas adjacentes à sua localização, embora a direção do odor seja impossível para o jogador determinar. O jogo relata apenas "você cheira um wumpus".

  3. O jogador carrega um arco e um número infinito de flechas, as quais ele pode atirar a qualquer momento na sala à sua frente. Se o wumpus estiver naquela sala, ele morre e o jogador vence. Se o wumpus não estava naquela sala, ele se assusta e se move aleatoriamente para qualquer uma das três salas conectadas à sua localização atual.

  4. Uma sala selecionada aleatoriamente (garantida que não é a sala em que o jogador inicia) contém um poço sem fundo. Se o jogador estiver em qualquer sala adjacente ao poço, ele sentirá uma brisa, mas não terá idéia de qual porta veio da brisa. Se ele entrar na sala com o poço, ele morre e Wumpus vence. O wumpus não é afetado pelo poço.

  5. Se o jogador entrar na sala do wumpus, ou se o wumpus entrar na sala do jogador, o wumpus vence.

  6. O jogador especifica a direção que ele está enfrentando com um número (1 = direita, 2 = esquerda, 3 = atrás) e, em seguida, uma ação (4 = disparar uma flecha, 5 = caminhar na direção especificada).

  7. Para fins de pontuação, cada corda do jogo ("Você sente uma brisa", "Você sente um cheiro de wumpus", "Sua flecha não atingiu nada" etc.) pode ser considerada um byte. Não é permitido abusar disso para ocultar o código do jogo no texto; isso é apenas para interagir com o jogador.

  8. Deduza 10% da sua contagem de bytes para implementar megabats, que começam em uma sala aleatória diferente do jogador (embora eles possam compartilhar uma sala com o wumpus e / ou o poço). Se o jogador entrar na sala com os morcegos, eles levarão o jogador para outra sala selecionada aleatoriamente (garantido que não é a sala com a cova ou o wumpus nela), antes de voar para o seu próprio local aleatório. Nas três salas adjacentes aos morcegos, eles podem ser ouvidos rangendo, mas o jogador não recebe informações sobre de qual sala o som vem.

  9. Deduzir 35% da sua contagem de bytes para implementar uma interface gráfica que mostra o mapa icosaédrico e algum tipo de indicação das informações que o jogador tem até agora sobre a localização do poço, do wumpus e dos morcegos (se aplicável), em relação a o jogador. Obviamente, se o wumpus se mover ou o jogador se mover pelos morcegos, o mapa precisará ser redefinido de acordo.

  10. A contagem de bytes mais baixa, conforme ajustado, vence.

O código-fonte BÁSICO de uma versão do jogo (não necessariamente em conformidade com as regras acima e, em todo o caso, totalmente não destruído) pode ser encontrado neste site e provavelmente em outros.

Michael Stern
fonte
Alguns esclarecimentos: 3. Se o wumpus não estava naquela sala, ele se assusta e se muda para uma das TRÊS salas. Então, se você disparar uma flecha e errar, o wumpus pode vir e matá-lo, certo? E o wumpus só se moverá se assustado, senão ele fica parado? 6. Entendo que a direção do jogador é determinada pela sala de onde ele veio. Portanto, se ele viesse do sul, suas opções seriam 1.northeast 2.nwestwest 3.south e se ele viesse do norte, seria o contrário. Além disso, suas regras parecem mais simples / mais eficientes que o programa de referência (que ainda não investiguei em detalhes). Estou correto?
Level River St
Argh! Não consigo encontrar nenhuma imagem do gráfico duplo de um icosaedro em nenhum lugar da rede.
Jack M
1
@steveverrill Sim, se você assusta, pode vir e matar você. Se você não assusta, ele não se move. Existem muitas variações no jogo; muitas versões permitem que as setas circulem e matem você, por exemplo. Eu reduzi isso.
Michael Stern
3
@JackM o mapa das faces de um icosaedro é idêntico ao mapa dos vértices de um dodecaedro, e esse gráfico é facilmente encontrado. Tente, por exemplo, wolframalpha.com/input/?i=DodecahedralGraph+edgerules ou o comando equivalente do Mathematica GraphData ["DodecahedralGraph", "EdgeRules"]. De qualquer maneira, você obtém {1 -> 14, 1 -> 15, 1 -> 16, 2 -> 5, 2 -> 6, 2 -> 13, 3 -> 7, 3 -> 14, 3 -> 19, 4 -> 8, 4 -> 15, 4 -> 20, 5 -> 11, 5 -> 19, 6 -> 12, 6 -> 20, 7 -> 11, 7 -> 16, 8 -> 12, 8 -> 16, 9 -> 10, 9 -> 14, 9 -> 17, 10 -> 15, 10 -> 18, 11 -> 12, 13 -> 17, 13 -> 18, 17 -> 19, 18 -> 20}
Michael Stern
2
@JackM Não, "voltar" implica dar meia volta e voltar do jeito que você veio. Se você pressionar "voltar" duas vezes, você termina onde começou. Não há necessidade de armazenar estados anteriores do jogo.
Michael Stern

Respostas:

21

GolfScript, 163

:n;:`"You shot the wumpus.
""The wumpus ate you.
""The pit swallowed you.
"{19:|rand}2*0|{[:,~,4%"ftvh"=.,+,@-]{20%}%}:^{;.^.+.3$?>"You feel a breeze.
"1$6"You smell a wumpus.
"4$8{$?-1>*p}2*'"#{'|):|';`head -1`}"'++~{3%}/={=3$=|{"Your shot missed.
"p@^3rand=@@}if}{=@;}if.[|4$6$]?.)!}do])=

A pontuação é obtida pela contagem de bytes (290), adicionando o número de cadeias usadas para interação com o usuário (6) e subtraindo o comprimento combinado dessas cadeias (133). Os feeds de linha fazem parte das cadeias e contribuem para a contagem de bytes.

Milestones

  1. Portado resposta de professorfish do Bash para GolfScript. Pontuação: 269

  2. Atuei nas sugestões de Peter Taylor nos comentários. Pontuação: 250

  3. Peter Taylor refatorou meu código inteiro e me ajudou a compactar a tabela de pesquisa. Pontuação: 202

  4. Substituiu a tabela de pesquisa de salas adjacentes por uma abordagem matemática. Pontuação: 182

  5. Entrada, saída e função refatoradas que suportam a abordagem matemática. Pontuação: 163

Um grande "obrigado!" Vai para Peter Taylor por toda a sua ajuda.

Como funciona

As 20 salas são representadas como os vértices de um dodecaedro, aos quais foram atribuídos números de 0 a 19 da seguinte maneira:

Gráfico dodecaédrico

Para encontrar as salas adjacentes à sala N e ordená-las no sentido horário, temos que considerar quatro casos:

  • Se N ≡ 0 mod 4 (vértices azuis), a sala adjacente é 19 - N , N + 2 mod 20 e N - 2 mod 20 .

  • Se N ≡ 1 mod 4 (vértices verdes), a sala adjacente é 19 - N , N - 4 mod 20 e N + 4 mod 20 .

  • Se N ≡ 2 mod 4 (vértices amarelos), a sala adjacente é 19 - N , N - 2 mod 20 e N + 2 mod 20 .

  • Se N ≡ 3 mod 4 (vértices vermelhos), a sala adjacente é 19 - N , N + 4 mod 20 e N - 4 mod 20 .

# The function “p” is implemented as “{`print n print}”. By storing an empty string in 
# “n” and nullifying “`”, “p” becomes an alias for “print”.

:n;:`

# Push the messages corresponding to the three possible outcomes of the game.

"You shot the wumpus.\n""The wumpus ate you.\n""The pit swallowed you.\n"

# Place the wumpus and the pit in randomly selected rooms different from room 19; place 
# the player in room 19, with his back to room 0.

{19:|rand}2*0|

# Function “^” takes a single number as its argument and returns an array of all the
# adjacent rooms to the room that number corresponds to.

{

  [

    :,~       # Store the room number in “,” and negate it ( ~N ≡ 19 - N mod 20 )

    ,4%       # Push the room number modulus 4.

    "ftvh"=   # If it is equal to 0|1|2|3, push 102|116|118|104 ≡ 2|-4|-2|4 mod 20.

    .,+,@-    # Determine the room number plus and minus the integer from above.

  ]{20%}%     # Take all three room numbers modulus 20.

 }:^

{             # STACK: Strings Pit Wumpus Previous Current Function|Index

  ;           # STACK: Strings Pit Wumpus Previous Current

  # Find the adjacent rooms to the current room, duplicate them and remove the rooms 
  # before the first occurrence of the previous room. Since the rooms are ordered in
  # clockwise fashion, the array of adjacent rooms will begin with the rooms 
  # corresponding to the following directions: “Back Left Right”

  .^.+.3$?>   # STACK: Strings Pit Wumpus Previous Current Adjacent

  # Push two more messages and their respective triggers.

  "You feel a breeze.\n"1$6"You smell a wumpus.\n"4$8

  # STACK: ... Pit Wumpus Previous Current Adjacent String Adjacent 6 String Adjacent 8

  # Do the following twice: Duplicate the nth stack element and check if it's present in 
  # the array of adjacent rooms. If so, print the string below it.

  {$?-1>*p}2*

  # Read one line (direction, action, LF) from STDIN. The counter “|” is needed so the 
  # result won't get cached.

  '"#{'|):|';`head -1`}"'++~

  {3%}/       # Replace 1|2|3|4|5|LF with their character codes modulus 3 (1|2|0|1|2|1).

  ={          # If the player shoots an arrow:

    =3$=      # Determine the specified room and check if it corresponds to the wumpus.

      |       # If it does, push and invalid room number ( | > 19 ).

      # If it does not, say so and move the wumpus to a randomly selected adjacent room.

      {"Your shot missed."p@^3rand=@@}

    if

  }{           # If the player moves:

    =@;        # Place him into the selected room.

  }if

  # STACK: Pit Wumpus Previous Current Invalid?

  # Determine if the player's current room number is either invalid, the wumpus's room
  # number or the pit's room number (first match).

  .[|4$6$]?

  # If there is no match, the index is -1 and incrementing and negating it yields “true”.

  # STACK: Strings Pit Wumpus Precious Current Invalid? Index Boolean

# Repeat loop is the boolean is falsy. If repeated, the first instruction of the loop 
# will pop the index.

}do      

# Consolidate the entire stack into an array. And pop its last element: the index.
# Replace the array with the element corresponding to that index.

])=

# GolfScript will execute “print n print”.
Dennis
fonte
1
Você pode economizar 1 Qcom 19rand 97+; 2 em @com 97%3*&>..., mais 1 por inlining Qcomo {19rand 97+}2*:,\:H, alguns, substituindo |com *, que é muitas vezes a melhor maneira de fazer um if. Bnão serve para nada, e acho que mais algumas variáveis ​​poderiam ser eliminadas usando a pilha.
Peter Taylor
1
Esqueceu de mencionar outro truque frequente: conversão de base para tabelas de pesquisa. Você pode substituir os 62 caracteres da lista de adjacências por uma seqüência de 33 caracteres seguida por 256base 20base(e provavelmente também eliminar alguns +/- 97). A única desvantagem é que serão necessários caracteres não imprimíveis.
27414 Peter Peter Taylor
1
Salvei mais 13 refatorando para ser um GS mais idiomático (principalmente usando a pilha em vez de variáveis); e há 10 adicionais ao custo de tornar a saída menos bonita. Isso além da compactação da tabela de pesquisa mencionada no meu comentário anterior.
Peter Taylor
1
Nem um pouco, eu gostei. Estou desapontado que a abordagem da tabela de pesquisa seja muito melhor do que a mais matemática que eu pretendia usar. BTW Eu acho que sua versão atual tem um pequeno bug, porque se você disparar uma flecha, errar e assustar o wumpus no seu quarto, ele só será exibido You were killed by the wumpussem mencionar a falta da flecha. É por isso que eu estava anexando a versão não bonita.
Peter Taylor
1
2*2+=>)2*
Peter Taylor
15

REV0 C ++ (Visual Studio no Windows) 405

#include"stdafx.h"
#include<stdlib.h>
#include<time.h>
int main(){srand(time(NULL));char i,h=rand()%19,w=rand()%19,p=19,d=0,q,e,m[]="e@LwQMQOSOLT";while(p-h&&p-w){for(i=3;i--;){q=(p+m[p%4*3+i])%20;if(q==w)puts("you smell a wumpus");if(q==h)puts("you feel a breeze");}scanf_s("%d",&i);e=(d+i/10)*m[p%4]%3;q=(p+m[p%4*3+e])%20;if(i%5){if(q==w){puts("YOU KILLED THE WUMPUS!");h=p;}else{puts("arrow missed");w=(w+m[w%4*3+rand()%3])%20;}}else{p=q;d=e;if(p==h)puts("YOU FELL IN A HOLE!");}if(p==w)puts("THE WUMPUS GOT YOU!");}}

Abaixo está uma demonstração, demonstrando que (desde que você não comece ao lado de um perigo) com a jogada correta, você sempre poderá vencer. O jogador sente uma brisa, se vira e faz um loop completo no sentido anti-horário. Como ele leva exatamente 5 movimentos para sentir uma brisa novamente, ele conhece o buraco à direita e fica o mais longe possível. Da mesma forma, quando ele cheira o wumpus, sem saber se é direito ou esquerdo, ele se vira e faz um loop no sentido horário. São necessários 5 movimentos para cheirar o wumpus novamente, então ele sabe que está à esquerda e atira com certeza.

Se ele tivesse dado uma volta para o outro lado, teria encontrado o wumpus mais cedo e saberia que estava na mesma direção em que estava virando.

insira a descrição da imagem aqui

REV1 C (GCC em Cygwin), bônus de 431-35% = 280,15

#define u(t,s,c) if(t){puts(s);c;}
i,d,e,a,b;main(){srand(time(0));char q,p=19,h=rand()%p,w=rand()%p,*m="e@LwQMQOSOLT-\\/\n \v ";  
while(p-h&&p-w){
  for(i=3;i--;){q=(p+m[p%4*3+i])%20;u(q==w,"you smell a wumpus",a|=2<<p)u(q==h,"you feel a breeze",b|=1<<p)}
  for(i=20;i--;)printf("%c%c",i==p?m[d+12]:48+(a>>i&2)+(b>>i&1),m[i%4+15]);
  scanf("%d",&i);e=(d+i/10)*m[p%4]%3;q=(p+m[p%4*3+e])%20;
  if(i%5){u(q-w,"arrow missed",w=(w+m[w%4*3+rand()%3])%20;a=0)else u(1,"YOU KILLED THE WUMPUS!",h=p)}
  else{p=q;d=e;u(p==h,"YOU FELL IN A HOLE!",)}
  u(p==w,"THE WUMPUS GOT YOU!",)}}

Novas linhas adicionadas para maior clareza. As alterações do Rev 0 são as seguintes:

Um grande agradecimento ao @Dennis por recomendar o compilador GCC no emulador Cygwin Linux para Windows. Este compilador não requer os includes no programa rev 0 e permite o inttipo padrão para variáveis. main.Esta é uma dica de golfe para mudar a vida!

Além disso, a execução no Linux significa que \fo cursor é movido para baixo sem fazer um retorno de carro (diferente do Windows, onde apenas produz um símbolo imprimível.) Isso permitiu um encurtamento considerável da instrução printf que imprime a placa

Várias dicas adicionais de Dennis nos comentários, e uma das minhas: mudança de condição ao verificar se a flecha atingiu o wumpus: if(q==w)> if(q-w)(..else .. está invertido)

Adição de exibição gráfica mostrando as informações que o jogador sabe sobre onde um wumpus é fundido / uma brisa é sentida para reivindicar o bônus de 35%. (Excluí a versão antiga de depuração, que mostrava a posição exata do wumpus e do buraco. Ela pode ser vista no histórico de edições.)

REV2 C (GCC em Cygwin), bônus de 389-35% = 252,85

#define Q(N) (N+"QTLOQMQOSOLT"[N%4*3+e])%20
#define P printf(
i,d,e,a,b;main(){int p=19,q=srand(&p),h=rand()%p,w=rand()%p;
while(p-h&&p-w){
  for(e=3;e--;){q=Q(p);q-w||P"You smell a wumpus\n",a|=2<<p);q-h||P"You feel a breeze\n",b|=1<<p);}
  for(i=20;i--;)P"%c%c",i-p?48+(a>>i&2)+(b>>i&1):"-\\/"[d],"\n \v "[i%4]);
  scanf("%d",&i);e=(d+i/9)*"edde"[p%4]%3;q=Q(p);
  if(i%5){e=rand()%3;w=q-w?P"Your arrow didn't hit anything\n",a=0)&Q(w):(p=20);}
  else p=q,d=e;
}
P p-20?p-w?"YOU FELL IN A HOLE!\n":"THE WUMPUS GOT YOU!\n":"YOU KILLED THE WUMPUS!\n");}

Agradeço novamente a Dennis por refatorar meu código:

Constante de char m[]substituída por literais (eu não sabia que era possível indexar um literal.)

Semeadura de números aleatórios com variável de pilha (dependente do sistema, alguns sistemas randomizam a alocação de memória como uma medida de segurança.)

Macro putssubstituída por uma macro printfe código adicional que deve ser executado quando a mensagem exibida é colocada dentro de printfargumentos (vantagem da face que printf não imprime os últimos argumentos se não houver especfificadores de formato suficientes na cadeia de formato). ifsubstituído por||

Cálculo da nova posição do player / wumpus colocada dentro da nova macro.

Ganhar / perder mensagens colocadas fora do whileloop. ifsubstituído pelo operador condicional.

Uso do operador condicional em linha para disparar flecha. Se o jogador errar, isso requer a impressão de uma mensagem e o ajuste da posição do wumpus. Dennis ofereceu algumas maneiras de combinar printfe o cálculo da posição do wumpus em uma única expressão, mas eu optei por uma das minhas. printfretorna o número de caracteres impressos, que para Your arrow didn't hit anything\né 31 (11111 binário.) Portanto 31&Q(w)==Q(w),.

Minha outra contribuição para esta edição foi a eliminação de alguns colchetes desnecessários.

Saída

Aqui, o jogador já descobriu onde está o Wumpus, mas escolhe fazer uma exploração completa para descobrir exatamente onde está o poço. Ao contrário da minha versão antiga de depuração, que mostrava onde estavam os wumpus e os boxes durante todo o jogo, isso mostra apenas as salas onde o jogador visitou e sentiu uma brisa (1) cheirando os wumpus (2) ou ambos (3). (Se o jogador disparar uma flecha e errar, a variável que acontém as informações da posição do wumpus será redefinida.)

insira a descrição da imagem aqui

REPRESENTAÇÃO DE ICOSAHEDRON

Nota: esta seção é baseada na rev 1

Meu recurso de estrela! Não há gráfico no meu código. Para explicar como funciona, veja o mapa do mundo abaixo. Qualquer ponto no icosaedro pode ser representado por uma latitude 0-3 e uma longitude 0-4 (ou um único número long*4+lat.) A linha de longitude marcada no mapa passa apenas por aquelas faces com longitude zero e a linha de latitude passa por o centro das faces com latitude zero.

O jogador pode ser orientado em 3 eixos possíveis, representados pelos símbolos da seguinte forma: norte-sul -nordeste-sudoeste \noroeste-sudeste /. Em qualquer sala, ele tem exatamente uma saída em cada um desses eixos à sua disposição. No visor mostrado, o aparelho faz um loop completo no sentido horário. Geralmente é fácil identificar a partir da marcação do jogador de onde ele veio e, portanto, para onde ele pode ir.

O único caso um pouco difícil para os olhos não iniciados é o quarto. Quando você vê uma inclinação em uma dessas linhas polares, o jogador veio da célula polar mais próxima da extremidade externa da inclinação e está geralmente voltado para o equador. Portanto, o jogador está voltado para o sudeste e suas opções são: 15 (SUL, a célula à direita) 25 (nordeste, a célula acima) ou 35 (noroeste, a célula abaixo).

Então, basicamente mapeio o icosaedro em uma grade 5x4, com células numeradas de 19 a 0 na ordem em que são impressas. A mudança é feita adicionando ou subtraindo da posição atual, dependendo da latitude e direção do jogador, conforme a tabela abaixo.

Se o jogador sai do fundo (oeste) do tabuleiro, ele volta ao lado superior (leste) e vice-versa, então sua posição é tomada no módulo 20. Geralmente, os movimentos são codificados em m [] adicionando ascii 80 ( P) ao valor bruto, fornecendo os caracteres mostrados abaixo, mas o princípio de qualquer múltiplo de 20 pode ser adicionado sem afetar a operação.

Table of addition values for moves

Direction Symbol Latitude 0  1  2  3     Latitude 0 1 2 3

0, N-S      -             1 -1  1 -1              Q O Q O  
1, NE-SW    \            -4  1 -1  4              L Q O T
2, NW-SE    /             4 -3  3 -4              T M S L

A entrada do jogador (dividida por 10 para remover o segundo dígito) é adicionada à sua direção atual e tomada no módulo 3 para obter sua nova direção. Isso funciona bem na maioria dos casos. No entanto, há um problema quando ele está em uma sala polar e se move em direção ao polo. Ficará claro ao dobrar o mapa abaixo que, se ele sair da sala voltada para "nordeste", ele entrará no novo quadrado voltado para "sudeste", para que uma correção seja feita. Isso é feito na linha e=(d+i/10)*m[p%4]%3;pela multiplicação por m[p%4]. Os quatro primeiros valores de m [] são selecionados de modo que, além de sua função acima, eles também tenham a característica m[1]%3==m[2]%3==1e m[0]%3==m[3]%3==2. Isso deixa a direção sozinha para as salas equatoriais e aplica a correção necessária para as salas polares.

O tempo lógico para fazer a correção seria após a mudança. No entanto, para salvar os caracteres, isso é feito antes da mudança. Portanto, certos valores em m [] devem ser transpostos. Portanto, os dois últimos caracteres são LTsubstituídos TLpela tabela acima, por exemplo.

insira a descrição da imagem aqui

CÓDIGO NÃO GOLFE

este é o código da rev 1, que é menos ofuscado que a rev 2.

Isso será executado no GCC / Linux. Incluí nos comentários o código extra necessário para executá-lo no Visual studio / Windows. É uma grande diferença!

//Runs on gcc/linux. For visual studio / windows, change printf(...) 
//to printf(" %c%c%c",9*(i%4==1),i==p?m[d+12]:48+(a>>i&2)+(b>>i&1),10*!(i%2)) and uncomment the following lines
//#include"stdafx.h"
//#include<stdlib.h>
//#include<time.h>
//#pragma warning(once:996;once:430) //allow the use of scanf instead of scanf_s, allow default type=int. 
//Though rather than using the pragma, it is shorter to follow compiler recommendation and use scanf_s and int.

#define u(t,s,c) if(t){puts(s);c;}  //if(test){puts(string);additional code;}

i,     //player input, loop counter
d,e,   //current and proposed direction
a,b;   //bit flags for where wumpus smelt / breeze felt

main(){
    srand(time(0));
    char q,p=19,h=rand()%p,w=rand()%p,  //Initialise player, hole and wumpus. q stores proposed player position.
    *m="e@LwQMQOSOLT-\\/\n \f ";        //Chars 0-11: movetable. Chars 12-14:symbol for player. Chars 15-18: graphics format.   

    while(p-h&&p-w){

        // Print warnings
        for(i=3;i--;){q=(p+m[p%4*3+i])%20;u(q==w,"you smell a wumpus",a|=2<<p)u(q==h,"you feel a breeze",b|=1<<p)}

        // graphic display 
        for(i=20;i--;)printf("%c%c",i==p?m[d+12]:48+(a>>i&2)+(b>>i&1),m[i%4+15]);

        // Get player input and work out direction and room 
        scanf("%d",&i);
        e=(d+i/10)*m[p%4]%3;
        q=(p+m[p%4*3+e])%20;

        // i%5 is false if player inputs 5 (move player) otherwise true (shoot arrow) 
        if(i%5)
        {u(q-w,"arrow missed",w=(w+m[w%4*3+rand()%3])%20;a=0)else u(1,"YOU KILLED THE WUMPUS!",h=p)}
        else{p=q;d=e;u(p==h,"YOU FELL IN A HOLE!",)}
        u(p==w,"THE WUMPUS GOT YOU!",)
    }

}

QUESTÕES E CURIOSIDADES

Eu aproveitei o ponto mencionado por @professorfish, se o wumpus e o pit começarem em lugares aleatórios, não será necessário que o jogador comece em um lugar aleatório. O jogador sempre começa na sala 19, voltada para o norte.

Entendo que, como o wumpus "não é afetado pelo poço", o wumpus pode começar ou entrar na sala onde está o poço. Em geral, isso simplifica as coisas, exceto um ponto. Não tenho uma variável específica para indicar que o jogo acabou; acaba quando o jogador coincide com o wumpus ou poço. Então, quando o jogador vence, eu mostro a mensagem vencedora, mas movo o poço para o jogador para sair do circuito! Não posso colocar o jogador no poço, pois os wumpus podem estar lá e receberia uma mensagem sobre os wumpus que não quero.

O programa rev0 funcionou perfeitamente no visual studio, mas o IDE dizia "pilha corrompida em torno da variável i" na saída. Isso ocorre porque o scanf está tentando intinserir um char.comportamento incorreto relatado por Dennis em sua máquina Linux por causa disso. De qualquer forma, é corrigido pelo uso do tipo correto na rev 1.

A linha para exibir o quadro na rev 0 é desajeitada e parece um pouco diferente em outras plataformas. No printf(" %c%c%c")meio,% c é o caractere imprimível exibido. O último% c é ASCII 0 ou ASCII 10 (\ n, nova linha com retorno de carro no Windows.) Parece não haver nenhum caractere no Windows que funcione no console, que irá descer uma linha sem dar um retorno de carro. Se houvesse, não precisaria do primeiro c% (guia ASCII 0 ou ASCII 9 antes do caractere de latitude 1. As guias são notoriamente indefinidas em seu comportamento.) .) Rev 1 tem uma revisão dessa linha que usa um caractere de alimentação de formulário \ f e, portanto, não precisa de caractere de formato no início do printf. Isso o torna mais curto, mas o \ f não funciona no Windows.

Level River St
fonte
1
Eu amo a redação.
Michael Stern
Não tenho certeza se é por causa das modificações que tive que fazer para compilá-lo com o GCC no Linux (remova a primeira inclusão, substitua scanf_spor scanfe inclua stdio.hse eu compilar como avaliador C ++ que C), mas não funciona muito bem para mim. Por exemplo, se eu for para a esquerda e depois para a direita no início ( 15 35), estou em uma sala diferente daquela em que iniciei.
Dennis
@ Dennis Eu rastreei a fonte do erro na saída. é o scanf_s (supostamente seguro!) que "corrompe a pilha em torno da variável i" quando tenta colocar o que eu suponho ser um número inteiro de 32 bits em um caractere. Portanto, a primeira coisa que sugiro é verificar o tipo que scanf usa para um "% d" e alterar a variável i para esse tipo. Eu recebo a resposta certa sem erro de saída para char, a resposta certa sem erro de saída para int e a resposta errada com o tipo da Microsoft __int64 (equivalente a muito tempo, a menos que eu coloque "% lld".) a versão ungolfed e você teve algum problema com a tela?
Nível do rio St
@steveverrill: Sim, eu tentei as duas versões. O tipo de ié realmente o problema. A página do manual diz: " d Corresponde a um número inteiro decimal opcionalmente assinado; o próximo ponteiro deve ser um ponteiro para int ." Alterar o tipo faz com que funcione bem.
Dennis
@ steveverrill: Eu não sei como o VS lida com as coisas, mas se você compilar com o GCC (como C, não C ++), poderá salvar muitos caracteres. Nenhuma das inclusões é necessária se você substituir NULLpor 0e scanf_scom scanf, você não precisa intantes maine pode mover i-se para dfora do main (elas são padronizadas inte inicializadas 0). Além disso, você pode definir p=19,h=rand()%p,w=rand()%p, substituir m[]por *me deve ser possível definir uma macro para todas as instâncias de if(...==...)puts(...);.
Dennis
9

GolfScript, 269 caracteres

{puts}:|;20,{;9{rand}:r~}$3<(:>"B:%d`w85>2n+Fup`y/>@D-=J7ldnx/W5XsLAb8~"{32-}%"`\24"{base}/3/{[.[~@].[~@]]}%:A=3r=0=:F;~:W;:P;{>A={0=F=}?:^P&!!{"You feel a breeze"|}*^W&!!{"You smell a wumpus"|}*'"#{'9.?r';STDIN.gets()}"'++~);(3%^=\4`={W={"Your arrow hit the wumpus"|0}{"Your arrow didn't hit anything"|W A=0=3r=:W>=.!\{"The wumpus catches you"|}*}if}{>:F;:>W=.!\{"You ran into the wumpus"|}*>P=.!\{"You fell into the pit"|}*&}if}do

Observe que 163 foi subtraído da contagem de caracteres para as seqüências codificadas. Se você deseja uma saída de depuração indicando os números dos quartos, adicione a seguinte linha logo após a primeira ocorrência de ^:

'  YOU 'F'->'>+++puts'  DIRECTIONS [BRL] '^`+puts'  PIT 'P+puts'  WUMPUS 'W+puts 

Uma sessão de exemplo (com saída de depuração adicional):

  YOU 6->11
  DIRECTIONS [BRL] [6 7 16]
  PIT 7
  WUMPUS 5
You feel a breeze
25
  YOU 11->16
  DIRECTIONS [BRL] [11 17 15]
  PIT 7
  WUMPUS 5
35
  YOU 16->11
  DIRECTIONS [BRL] [16 6 7]
  PIT 7
  WUMPUS 5
You feel a breeze
15
  YOU 11->6
  DIRECTIONS [BRL] [11 10 1]
  PIT 7
  WUMPUS 5
15
  YOU 6->10
  DIRECTIONS [BRL] [6 15 5]
  PIT 7
  WUMPUS 5
You smell a wumpus
14
Your arrow didn't hit anything
  YOU 6->10
  DIRECTIONS [BRL] [6 15 5]
  PIT 7
  WUMPUS 0
25
  YOU 10->5
  DIRECTIONS [BRL] [10 14 0]
  PIT 7
  WUMPUS 0
You smell a wumpus
24
Your arrow hit the wumpus
Howard
fonte
Aqui está o primeiro código de trabalho. Voltar mais tarde para um pouco mais de golfe.
Howard
No momento, meu código tem 1 caractere a mais. Estou procurando qualquer maneira possível de jogar golfe!
Timtech #
Não que você precise da minha ajuda, mas você pode salvar 14 caracteres definindo {puts}:|;, 5 caracteres substituindo Re Wcom -e >(permite eliminar espaços ao redor) e 9 caracteres soltando '> 'print(não parece ser exigido pela pergunta).
Dennis
@ Dennis Obrigado. Certamente implementarei algumas de suas sugestões.
Howard
9

JavaScript (ECMAScript 6) - 2197 1759 -45% = 967,45 caracteres

Quase terminando o golfe ...

Inclui uma GUI com um mapa de Icosahedral e mega-morcegos para os bônus completos.

Wumpus GUI

  • Cada quarto possui 4 botões: X(o Pit); B(o Mega-Morcego); W(os Wumpus); e P(você).
  • Sua localização atual é azul.
  • Os botões são coloridos de vermelho se o objeto que ele representa pode estar naquele local e verde se ele definitivamente não estiver nesse local.
  • Os botões We Ppodem ser clicados apenas nas salas adjacentes à sua localização atual.
  • Se você ganhar, o fundo ficará verde e se você morrer, o fundo ficará vermelho.

Código:

P=x=>parseInt(x,36);Q=(y,a=4)=>[P(x)<<a for(x of y)];e=Q("def45c6di7ej1ai1bj2af3bf9dg8eh46b57a1gh0280390678ci9cj24g35h",0);X=Q("o6fl6afnik27bloscfaf");Y=Q("icp8i8t4jej4encjjan6");A='appendChild';C='createElement';W='width';H='height';G='background-color';L='disabled';I='innerHTML';N='className';D=document;R=Math.random;B=D.body;E=[];F=1<0;T=!F;Z="XBWP";s=D[C]('style');s.type='text/css';t='.A{position:absolute;left:25px;top:25px}.D{'+W+':50px;'+H+':50px}.D button{'+W+':25px;'+H+':25px;float:left}.R{'+G+':red}.G{'+G+':green}.B{'+G+':blue}';for(i in X)t+='#D'+i+'{left:'+X[i]+'px;top:'+Y[i]+'px}';s[A](D.createTextNode(t));D.head[A](s);c=D[C]('canvas');c[N]='A';c[W]=c[H]=500;B[A](c);x=c.getContext('2d');x.beginPath();d=(i,b,v)=>{for(j=0;j<3;j++){E[e[3*i+j]][b][L]=v}};a=(i,l,v)=>{t=F;for(j=0;j<3;j++)t=e[3*i+j]==l?T:t;if(t)M[v]++;b=E[i][v];b.c=-1;for(j=0;j<3;j++)E[e[3*i+j]][v].c+=t?1:-1;for(j of E)j[v][N]=j[v].c==M[v]?'R':'G';};M=[0,0,0];S=v=>{M[v]=0;for(i of E){i[v][N]='';i[v].c=0}};for(i in X){for(j=3*i;j<3*i+3;j++)x.moveTo(X[i],Y[i])|x.lineTo(X[e[j]],Y[e[j]]);B[A](v=D[C]('div'));v[N]='A D';v.id='D'+i;E[i]=[];for(j in Z){b=E[i][j]=v[A](D[C]('button'));b[L]=T;b.i=i;b.c=0;b[I]=Z[j];}E[i][4][O='onclick']=function(){d(P,2,T);d(P,3,T);if(this.i==W)c[N]+=' G';else{S(2);W=e[3*W+R()*3|0];if(W==P)c[N]+=' R';else{a(P,W,2);d(P,2,F);d(P,3,F)}}};E[i][3][O]=function(){d(P,2,T);d(P,3,T);E[P][3][N]='';P=this.i;if(W==P||Q==P){c[N]+=' R';return}else if(Z==P){j=P;do{P=R()*20|0}while(P==W||P==Q||P==j);do{Z=R()*20|0}while(Z==j||Z==P);S(1)}d(P,2,F);d(P,3,F);E[P][3][N]='B';a(P,Q,0);a(P,Z,1);a(P,W,2)}}x.stroke();P=R()*20|0;do{W=R()*20|0}while(W==P);do{Q=R()*20|0}while(Q==P);do{Z=R()*20|0}while(Z==P);E[P][3][N]='B';a(P,Q,0);a(P,Z,1);a(P,W,2);d(P,2,F);d(P,3,F)
MT0
fonte
Você obtém 1066 sem o ECMA 6 usando o compilador de fechamento.
AMK
Fiquei me perguntando o quão mais fácil seria quando você tem uma representação gráfica para ajudar a deduzir onde estão as coisas. 1+, mas é um pouco fácil :)
Sylwester
9

Bash, 365 (primeira versão de trabalho 726!)

TRABALHANDO COM GOLFSCRIPT?

@Dennis basicamente fez todo o golfe por mim. Obrigado!

O programa assume entrada válida. A entrada válida é a direção que você escolhe (1 para a direita, 2 para a esquerda, 3 para as costas), seguida pela sua ação (4 para disparar, 5 para caminhar).

Alguma explicação

Normalmente faço grandes explicações detalhadas, mas isso provavelmente é um pouco complicado demais para eu ser incomodada.

Cada vértice no gráfico do dodecaedro é codificado como uma letra (a = 1, b = 2, ... t = 20).

A posição inicial do jogador é sempre 20 (e eles estão de costas para 18), uma vez que isso não importa por si só, apenas as posições relativas do jogador, poço e wumpus são importantes.

A variável $p armazena a localização do jogador. $rarmazena a localização anterior do jogador. $wé o wumpus e $h(H para buraco) é o poço.

Código

p=t
r=r
j=echo
Z=npoemfsgnohtksblbtpckdpljqnriogelfhkbqrcaiadjhagimsmjtqecrdf
q(){ $j ${Z:RANDOM%19*3:1};}
C(){ [[ ${!1} =~ ${!2} ]];}
d(){ s=${Z:30#$1*3-30:3};}
w=`q`
h=`q`
for((;;));{
b=$p
d $p
u=u${s#*$r}$s
C w p&&$j The wumpus ate you&&exit
C h p&&$j You fell in the pit&&exit
C u w&&$j You smell the wumpus
C u h&&$j You feel a breeze from a pit
read i
F=5
y=${u:i/10:1};C i F&&p=$y&&r=$b||{ d $w;C y w&&$j You killed the wumpus&&exit;$j You missed;w=${s:RANDOM%3:1};};}

Histórico de Versões

  1. Versão inicial, 698 caracteres
  2. Correção do bug em que "Você sente uma brisa" e "Você sente o cheiro do wumpus" não pode ser exibido ao mesmo tempo; salvou 39 caracteres, tornando a geração de números aleatórios uma função.
  3. Lembre-se que o wumpus se move se você atirar e errar. 726 caracteres.
  4. Fez grep -oEuma variável. Guardado 5 caracteres.
  5. Fez [a-z]{3}uma variável. Guardado 3 caracteres.
  6. Fez echouma variável. Guardado 5 caracteres.
  7. Atuei na maioria das sugestões de @Dennis. Guardado 72 caracteres.
  8. Adicionadas todas as sugestões restantes. Guardado 68 caracteres.
  9. Guardou 2 caracteres da sugestão de @DigitalTrauma.
  10. Corrigido um erro grave em que você só pode disparar no wumpus se estiver à direita. Mesma contagem de caracteres.
  11. Expansão de parâmetro usada para cortar 2 caracteres usando $m.
  12. Raspou muitos caracteres abandonando grepe sendo um pouco mais sensível.
  13. Definida Ccomo uma função de pesquisa regexp a ser usada nas instruções if e Ecomo uma função imprimindo "Você matou o wumpus" e saindo.
  14. Salvou 1 caractere pelo rearranjo "if statement".
  15. Economizou muitos caracteres ao se livrar de removeu suportes desnecessários.
  16. Bugs corrigidos. Adicionados muitos caracteres :(
  17. POUPANÇA MOARR ( http://xkcd.com/1296/ )
  18. Outra das idéias de @Dennis (salvando alguns caracteres) e meu astuto (ab) uso de indireção (salvando 1 caractere).
  19. Correção de estilo para q ().
  20. adicionado novamente a saída adequada

Amostra de execução

"In:" é entrada, "Out: é saída".

O jogador vagueia um pouco, cheira o wumpus e atira. Eles sentem falta, e o wumpus entra em seu quarto e os come.

Em: 15

Em: 15

Em: 25

Em: 25

Em: 15

Fora: Você cheira o wumpus

Em: 14

Fora: Você perdeu

Fora: O wumpus comeu você


fonte
1
Eu acho que você pode tornar seu código pelo menos 100 bytes mais curto. 1. exitpossui apenas um byte a mais g=1e elimina a necessidade de testar galgumas elifinstruções diferentes de zero e algumas . 2. Você pode usar em ((i==35))vez de [ $i = 35 ]e em ...&&...vez de if ... then ... fi. 3. q(){ L=({a..s});$j ${L[RANDOM%19]};}e n=`$k $w$m<<<$d`;w=${n:RANDOM%2+1:1}ambos economizam alguns bytes.
21414 Dennis
1
Substitua while :;do... donepor for((;;);{... }para economizar 3 caracteres
Digital Trauma
1
@professorfish: Eu acho que uma função funcionaria melhor do que a atual abordagem string-grep-cut. Por exemplo, d(){ x=npoemfgnshtoksblbtckpdpljqniorelgfhkbqraicadjaghimsmjtqecrdf;s=${x:3*30#$1-30:3};}permitirá substituir as definições de se ncom d $pe d $w. Se, além disso, definir u=${s#*$r}$s(e ajustar as definições de le fconformidade), você não vai precisar $ke $mmais. Economiza 83 bytes, eu acho. Além disso, o espaço de entrada q ()não é necessário.
Dennis
1
@professorfish: E você pode salvar 3 bytes adicionais definindo c(){ [[ $1 =~ $2 ]];}e substituindo, por exemplo, a penúltima linha com c $r $b||{ $j You missed;d $w;w=${s:RANDOM%2+1:1};}.
Dennis
1
@professorfish: Usando a função que eu sugeri deve ser 3 bytes mais curto. Você pode salvar 106 bytes adicionais, substituindo as quatro linhas depois b=$pcom d $p;u=u${s#*$r}$sas linhas depois read icom y=${u:i/10:1};C $i 5&&{ p=$y;r=$b;}||{ d $w;C $y $w&&$j You killed the wumpus&&exit;$j You missed;w=${s:RANDOM%2:1};}e se livrar E().
Dennis
6

GolfScript ( 206 198)

[5:C,]{{.{[~@]}:>~.{-1%}:<~}%.&}8*({[.<><.<><]}:F~-{99rand}$~5,{.<{>.'You smell a wumpus.\n'4{$F@?~!!*}:Q~{print}:,~}3*{>.'You feel a breeze.\n'5Q,}3*'"#{'C):C';STDIN.gets()}"'++~~:&9/{>}*&5%{'You killed the wumpus.'3Q{\<{>}3rand*\"Your arrow didn't hit anything.\n",0}or}{\;.'You fell into the pit.'4Q}if\.'You were killed by the wumpus.'4Q@or:n!}do];

Finalmente pego a versão da tabela de pesquisa de Dennis, da qual ela empresta bastante. O interessante desta versão é que ela não possui uma tabela de pesquisa para o layout da sala.

As 60 simetrias rotacionais de um icosaedro são isomórficas ao grupo alternado de 5 letras A_5. Depois de tentar todos os tipos de abordagens para representar o grupo de maneira compacta, voltei à mais simples: cada elemento é uma permutação de paridade uniforme. O grupo pode ser gerado a partir de dois geradores de mais de uma maneira: a abordagem que estou adotando usa os geradores 3e 3 1. Estes permitem-nos gerar 1 = 3 3 1, 2 = 3 3 1 3 1e 3 = 3.

Observe que a direção 3corresponde a um elemento da ordem 2, porque depois de passar pela porta atrás de você, essa porta está atrás de você novamente. A direção 1corresponde a um elemento da ordem 5, andando em torno de um vértice do icosaedro. (Elemento da mesma forma 2). E a combinação 3 1é da ordem 3, pois percorre as salas adjacentes àquela que começa atrás de você.

Então, nós estamos procurando uma permutação de ordem 2 para representar direção 3e uma permutação de ordem 5 para representar direção 1tal que 3 1é de ordem 3.

Existem 15 permutações da ordem 2 em A_5 e para cada uma existem 8 permutações candidatas para 1(e, portanto, para 3 1). Há uma atração óbvia [4 3 2 1 0]para 3: inverter uma matriz é justa -1%. Das suas possíveis permutações complementares 3 1que eu escolhi [0 1 3 4 2], que admite uma implementação bastante curta como [~@].

Ungolfed

# Generate the 60 permutations by repeated application of `3 1` and `3`
[5:C,]{{.{[~@]}:>~.{-1%}:<~}%.&}8*
# Remove [0 1 2 3 4] and its equivalence class (apply 3 (3 1)^n 3 for n in 0,1,2)
({[.<><.<><]}:F~-
# Shuffle the remaining 57 options to select random starting points for wumpus and pit
# Note that this introduces a slight bias against them being in the same room,
# but it's still possible.
{99rand}$~
# Start player at [0 1 2 3 4]
5,
{
    # Stack: Pit Wumpus Player
    .<
    # The adjacent rooms to the player are Player<> , Player<>> , and Player<>>>
    # If the wumpus is in one of those rooms, say so.
    {
        >."You smell a wumpus.\n"4
        {
            # ... X str off
            $F@?~!!*
            # ... str off$F X ?~!! *
            # Which means that we leave either str (if off$ and X are equivalent)
            # or the empty string on the stack
        }:Q~
        {print}:,~
    }3*
    # Ditto for the pit
    {>."You feel a breeze.\n"5Q,}3*
    # Read one line from STDIN.
    '"#{'C):C';STDIN.gets()}"'++~~
    # Stack: Pit Wumpus Player Player< Input
    # Find the room corresponding to the specified direction.
    :&9/{>}*&
    # Stack: Pit Wumpus Player TargetRoom Input
    5%{
        # Shoots:
        "You killed the wumpus."3Q
        {
            \<{>}3rand*\ # Move the wumpus to an adjacent room
            "Your arrow didn't hit anything.\n", # Inform
            0 # Continue
        }
        or
    }{
        # Moves:
        \;
        # If player and pit share a room, say so.
        ."You fell into the pit."4Q
    }if
    # If player and wumpus share a room, say so.
    # NB If the player walks into a room with the pit and the wumpus,
    # the `or` favours the pit death.
    \."You were killed by the wumpus."4Q@or
    # Save the topmost element of the stack for output if we break the loop. Loop if it's falsy.
    :n!
}do
# Ditch the junk.
];
Peter Taylor
fonte
Abordagem algébrica agradável! Porém, há um pequeno erro: 10/@3%=tenta acessar o quarto elemento de uma matriz de comprimento 3 se a entrada for 35.
Dennis
@ Dennis, sim, percebi depois que fui para a cama. Não posso pensar em várias maneiras de corrigi-lo, tudo custando 2.
Peter Taylor
Você pode recuperar um char com 9/3%@3%=.
Dennis
Atualmente, tenho 7 personagens com uma reestruturação mais drástica. Mas esse 1 caractere em 9/vez de 10/ainda funciona, então obrigado.
Peter Taylor
5

Wumpus , 384 - 129 (cadeias) = ​​255 bytes

1SDL2vSD70L?.;;3AL1a!?,9)".supmuw a llems uoY"99+1.
II5x?,&WC2.           L2a!?,9)".ezeerb a leef uoY"93*2.
L1a!,FCFC[&WCL1a!?,"!supm",AW#16#[(=]?.;;l(&o1.
    ?,".uoy eta ",".gnih","uw eht dellik uoY"#22&oN@
     #15#L2a!?. ,"supmu","tyna tih t'ndid worra ruoY"#31&oND";"4L1a!?.;;L1xSUL1xSD=F-#81~4~?.;;;CCWC=F-#97~4~?.;;;2.
 ,"nto the pit."|       "w ehT"l&oN@
 |"i llef uoY"l2-&oN@

Experimente online! (É claro que o TIO não faz muito sentido, porque você não pode usar o programa de maneira interativa e, assim que o programa ficar sem instruções no STDIN, ele lerá 0 0, o que equivale a 3 4, então você acabará atire flechas até que o Wumpus se mova para lá ou o mate.)

Ao executar isso localmente, verifique se o avanço de linha após o segundo número de cada entrada é liberado (porque o Wumpus precisa dele para determinar que o número acabou). No Powershell, de alguma forma, preciso inserir mais um caractere após o avanço de linha para fazê-lo funcionar (não importa qual caractere, mas usei apenas o avanço de linha duplo para teste).

Há muito espaço para jogar golfe ainda mais, mas experimentar layouts completamente novos leva um tempo. A pontuação final também depende muito das strings reais que eu uso, porque em uma linguagem 2D, uma string de N bytes tende a custar mais do que N bytes de código fonte, porque impõe restrições significativas ao layout do código, e você frequentemente precisa dividi-lo em várias seções (com aspas duplas adicionais). No extremo, se eu reduzisse cada string a uma única letra (e os -129 a -12), provavelmente estaria economizando uma tonelada de bytes.

Explicação

Primeiro um aviso: apesar do nome do idioma, ele não foi projetado para facilitar a implementação do Hunt the Wumpus . Em vez disso, primeiro projetei a linguagem em torno do tema dos triângulos, acabei com uma estrutura de dados icosaédrica e decidi chamá-la de Wumpus por causa disso.

Então, sim, enquanto o Wumpus é principalmente baseado em pilha, ele também possui 20 registros, dispostos em torno das faces de um icosaedro. Isso significa que obtemos uma estrutura de dados para representar o mapa gratuitamente. A única coisa que não podemos fazer com facilidade é encontrar rostos específicos no icosaedro; portanto, para procurá-los, precisamos "rolar o d20" até terminar no rosto que procuramos. (É possível fazer isso de uma maneira determinística, mas isso levaria muito mais bytes.) A busca por rostos como esse termina quase com certeza (ou seja, com probabilidade 1); portanto, a pesquisa em andamento para sempre não é uma preocupação na prática).

O código acima é uma versão em golf desta primeira implementação com um layout mais limpo:

1SDL2vSD70L?.;;2.  < Setup; jumps to third line which starts the main loop

3AL1a! ?,".supmuw a llems uoY"#19&oN03.          < This section checks the player's surroundings.
        L2a!?,".ezeerb a leef uoY"#18&oN04.
             AW(=12[?.;;7.

    }&WC#11.                                     < This section reads the input. The top branch moves, the bottom branch shoots
II5x^                                              and kills or moves the wumpus.
     {FCFC[&WCL1a !?,"!supmuw eht dellik uoY"#22&oN@
                    ".gnihtyna tih t'ndid worra ruoY"#31&oND#59 9L1a!?.;;L1xSUL1xSD=F-#82~9~?.;;;CCWC=F-#98~9~?.;;;#11.

L1a!?,".uoy eta supmuw ehT"#19&oN@               < This section checks whether the player dies.
     L2a!?,".tip eht otni llef uoY"#22&oN@         Otherwise, we return back to the third line.
          2.

Como o golfe envolvia principalmente a compactação do layout, explicarei esta versão por enquanto (até adicionar quaisquer truques que vão além da reestruturação do código).

Vamos começar com o código de instalação:

1SDL2vSD70L?.;;2.

Inicialmente, todas as faces são definidas como 0 . Codificaremos o wumpus configurando o bit 1 da face correspondente e o poço configurando o bit 2. Dessa forma, os dois podem estar na mesma sala. A posição do jogador não será registrada no icosaedro, mas sempre estará ativa (apenas um dos 20 registros está ativo por vez).

1S     Store a 1 in the initially active face to put the wumpus there.
D      Roll the d20. Applies a uniformly random rotation to the icosahedron.
L2vS   Load the value of that face (in case it's the wumpus's), set the 2-bit
       and store the result back on that face.

Agora precisamos encontrar um rosto vazio aleatório para colocar o jogador.

D      Roll the D20.
70     Push 7 and 0 which are the coordinates of the D in the program.
L      Load the value of the current face.
?.     If that value is non-zero (i.e. the active face has either the
       wumpus or the pit), jump back to the D to reroll the die.
;;2.   Otherwise, discard the 0 and the 7 and jump to (0, 2), which is
       the beginning of the main loop.

Esta próxima seção verifica o ambiente do jogador e imprime os avisos apropriados:

3AL1a! ?,".supmuw a llems uoY"#19&oN03.
        L2a!?,".ezeerb a leef uoY"#18&oN04.
             AW(=12[?.;;7.

Este é um loop que percorremos três vezes. Cada vez que olhamos para o vizinho certo, imprima a (s) corda (s) apropriada (s) se houver risco e gire o icosaedro em 120 °.

3    Push a 3 as a loop counter.
A    Tip the icosahedron onto the NW neighbour of the active face, which
     will be used to represent the right-hand room.
L1a  Extract the 1-bit of the value on that face.
!?,  If that value is zero, strafe to the next line, otherwise continue.

  ".supmuw a llems uoY"#19&oN03.
     Print "You smell a wumpus.", a linefeed and then jump to the next line.

L2a  Extract the 2-bit of the value on that face.
!?,  If that value is zero, strafe to the next line, otherwise continue.

  ".ezeerb a leef uoY"#18&oN04.
     Print "You feel a breeze.", a linefeed and then jump to the next line.
A    Tip back to the original active room (where the player is).
W    Rotate the icosahedron by 120°, so that the next iteration checks
     another neighbour.
(=   Decrement the loop counter and duplicate it.
12   Push 1, 2, the coordinates of the cell after the 3 (the loop counter).
[    Pull up one copy of the loop counter.
?.   If it's non-zero, jump to the beginning of the loop, otherwise continue.
;;7. Discard the 2 and the 1 and jump to (0, 7), which reads the player's
     input for this turn.

A próxima seção lê dois números do jogador e, em seguida, move o jogador ou dispara uma flecha. O primeiro é trivial, o segundo menos. O principal problema para disparar a flecha é o caso em que ela falha. Nesse caso, a) precisamos procurar o wumpus para movê-lo e, em seguida, b) retornar à sala do jogador e a orientação correta do icosaedro (para que "voltar" permaneça "voltar"). Essa é a parte mais cara de todo o programa.

    }&WC#11.
II5x^
     {FCFC[&WCL1a !?,"!supmuw eht dellik uoY"#22&oN@
                    ".gnihtyna tih t'ndid worra ruoY"#31&oND#59 9L1a!?.;;L1xSUL1xSD=F-#82~9~?.;;;CCWC=F-#98~9~?.;;;#11.

O ponto de entrada para esta seção é o Ià esquerda.

II   Read the integers from STDIN.
5x   XOR the second one with 5.
^    Turn either left or right, depending on the previous result. If the
     second input is 4, XORing with 5 gives 1 and the IP turns right.
     Otherwise, we get 0 and the IP turns left.

If the player entered 5, move:

}    Turn right so that the IP moves east again.
&W   If the room indicator is X, rotate the icosahedron by X*120°. This
     puts the target room south of the active face (where the back room
     normally is).
C    Tip the icosahedron onto the southern face. This moves the player there.
     Due to the way tipping works, the formerly active face will now be
     the southern neighbour, i.e. correctly at the back of the player.
#11. Jump to (0, 11), the final section which checks whether the player
     stepped into the pit or onto the wumpus.

If the player entered 4, move:

{    Turn left so that the IP moves east again.
F    Store the active face index (the player's position) on the stack.
CFC  Also store the face index of the southern neighbour (the back room)
     on the stack, so that we can recover the correct orientation if
     we need to.
[    Pull up the player's room choice.
&WC  Tip the icosahedron onto the corresponding face (same as for the move action)
L1a  Extract the 1-bit of the value on that face to check whether the arrow
     hit the wumpus.
!?,  If that value is zero, strafe to the next line, otherwise continue.

  "!supmuw eht dellik uoY"#22&oN@
     Print "You killed the wumpus.", a linefeed, and terminate the program.

".gnihtyna tih t'ndid worra ruoY"#31&oN
     Print "Your arrow didn't hit anything." and a linefeed.

This next bit is a loop which searches for the wumpus:

D    Roll the d20. The easiest way to search for the wumpus is to look at
     random faces.
#59 9
     Push 59 and 9, the coordinates of the beginning of this loop.
L1a  Extract the 1-bit of the value on the current face.
!?.  If that value is zero, jump back to the beginning of this loop to
     try another face, otherwise continue.
;;   Discard the 9 and the 59.
L1xS Unset the 1-bit of the current face to remove the wumpus there.
U    Tip the icosahedron onto a random neighbouring face. This moves us
     to a random adjacent room.
L1xS Set the 1-bit of the current face to put the wumpus there.

This next bit contains two loops which get us back to the player's room
with the correct orientation. We do this by first searching for the room
at the player's back, and then looking through its neighbours to find the
player's room.

D    Roll the d20.
=F-  Duplicate the back room index and subtract the current face index.
#82~9~
     Push 82 and 9 and pull up the difference we just computed.
?.   If the difference is non-zero (we've got the wrong room), jump back
     to the D to try again. Otherwise continue.
;;;  We've found the back room. Discard the 9, the 82 and the back room index.
C    Tip the icosahedron onto the southern face (one of the candidate
     neighbours which might be the player's room).
CWC  This begins the loop that searches for the player's room. Tip onto
     the back room, rotate by 120°, tip back. This cycles through the
     neighbours of the back room, while keeping the active face on those
     neighbours.
=F-  Duplicate the player's room index and subtract the current face index.
#98~9~
     Push 98 and 9 and pull up the difference we just computed.
?.   If the difference is non-zero (we've got the wrong room), jump back
     to the CWC to try again. Otherwise continue.
;;;  We've found the player's room and since we entered from the back room
     via C, we've also got the correct orientation. Discard the 9, the 98
     and the player's room index.
#11. Jump to (0, 11), the final section which checks whether the player
     stepped into the pit or onto the wumpus.

Ufa, essa foi a parte mais difícil. Agora só precisamos verificar se o jogador morre e, caso contrário, começa novamente no loop principal:

L1a!?,".uoy eta supmuw ehT"#19&oN@
     L2a!?,".tip eht otni llef uoY"#22&oN@
          2.

A estrutura desta seção é essencialmente idêntica à estrutura que usamos ao verificar o ambiente do jogador: verificamos o bit 1 da face atual (a sala do jogador) e, se estiver configurada, imprimimos The wumpus ate you.e encerramos o programa. Caso contrário, verificamos os 2 bits e está definido que imprimimos You fell into the pit.e encerramos o programa. Caso contrário, alcançamos o 2.que retorna ao início do loop principal (nas coordenadas (0, 2)).

Martin Ender
fonte
1

awk - grande

Isso não saiu tão curto quanto eu esperava, mas adotei uma abordagem um pouco diferente para lidar com o gráfico, por isso estou postando a versão não-destruída de qualquer maneira.

Aproveitei o fato de que um icosaedro (poliedro de 20 lados) sob rotações preservando a orientação é isomórfico ao grupo alternado de grau 5 (5 permutações de elementos com um número par de ciclos de comprimento par). Em seguida, escolho duas permutações com comprimento de ciclo 5 como "esquerda" e "direita" e escolho uma permutação com comprimento de ciclo 2 como "voltar". Usando isso, construo o gráfico de uma sala percorrendo o caminho hamiltoniano (2xRRRLLLRLRL, usando 3xRB em cada sala para capturar as três direções possíveis).

function meta(z,a,b,c,d) {
    if(z==COMPOSE) {
        split(a,c,"");
        split(b,d,"");
        return c[d[1]]c[d[2]]c[d[3]]c[d[4]]c[d[5]];
    }
    if(z==WALK) {
        split(R" "R" "R" "L" "L" "L" "R" "L" "R" "L,c);
        for(b = 1; b <= 20; b++) {
            map[a] = b;
            a = meta(COMPOSE,meta(COMPOSE,a,R),B);
            map[a] = b;
            a = meta(COMPOSE,meta(COMPOSE,a,R),B);
            map[a] = b;
            a = meta(COMPOSE,meta(COMPOSE,a,R),B);
            a = meta(COMPOSE, a, c[b % 10 + 1]);
        }
    }
    if(z==TEST) {
        a = map[meta(COMPOSE,U,L)];
        b = map[meta(COMPOSE,U,R)];
        c = map[meta(COMPOSE,U,B)];
        if(a==W||b==W||c==W) print "You smell the wumpus";
        if(a==P||b==P||c==P) print "You feel a breeze";
        if(map[U]==W) {
            print "You have been eaten by the wumpus";
            exit;
        }
        if(map[U]==P) {
            print "You have fallen into a bottomless pit";
            exit;
        }
    }
    if(z==ARROWTEST) {
        if(A==W) {
            print "You have slain the wumpus!";
            exit;
        } else {
            for(a in p) if(map[a]==W) break;
            W=map[meta(COMPOSE,a,v[int(rand()*3)+1])];
        }
    }
}

BEGIN {
    COMPOSE = 0;
    WALK = 1;
    TEST = 2;
    ARROWTEST = 3;
    L = 35214;
    R = 35421;
    B = 35142;
    split(R" "L" "B,V);
    meta(WALK,L);
    W = int(rand()*19)+2;
    P = int(rand()*19)+2;
    U = L;
    meta(TEST);
}

{
    d=int($0/10);
    m=$0%10;
    if(m==5) U = meta(COMPOSE,U,V[d]);
    else if(m==4) {
        A = map[meta(COMPOSE,U,V[d])];
        meta(ARROWTEST);
    }
    meta(TEST);
}
laindir
fonte