Caza el wumpus

39

Cuando era chico, los niños entraban a las tiendas de informática y jugaban a Hunt the Wumpus hasta que el personal nos echó. Era un juego simple, programable en las computadoras domésticas de mediados de la década de 1970, máquinas tan rudimentarias que en lugar de microprocesadores del tamaño de un chicklet, creo que algunos de ellos probablemente tenían chicklets reales allí.

Evocamos esa época pasada reproduciendo el juego en hardware moderno.

  1. El jugador comienza en una habitación aleatoria en un mapa icosaédrico (por lo tanto, hay 20 habitaciones en total, conectadas entre sí como las caras de un icosaedro, y cada habitación tiene exactamente tres salidas).

  2. El wumpus comienza en una habitación diferente seleccionada al azar. El wumpus apesta, y su olor se puede detectar en cualquiera de las tres habitaciones adyacentes a su ubicación, aunque la dirección del olor es imposible de determinar para el jugador. El juego informa solo "hueles un wumpus".

  3. El jugador lleva un arco y un número infinito de flechas, que puede disparar en cualquier momento en la habitación frente a él. Si el wumpus está en esa habitación, muere y el jugador gana. Si el wumpus no estaba en esa habitación, se sobresalta y se mueve aleatoriamente a cualquiera de las tres habitaciones conectadas a su ubicación actual.

  4. Una habitación seleccionada al azar (garantizada para no ser la habitación en la que comienza el jugador) contiene un pozo sin fondo. Si el jugador está en cualquier habitación adyacente al hoyo, siente una brisa, pero no tiene idea de qué puerta proviene la brisa. Si entra en la habitación con el pozo, muere y gana Wumpus. El wumpus no se ve afectado por el pozo.

  5. Si el jugador entra en la habitación del wumpus, o si el wumpus entra en la habitación del jugador, el wumpus gana.

  6. El jugador especifica la dirección que está mirando con un número (1 = derecha, 2 = izquierda, 3 = atrás), y luego una acción (4 = disparar una flecha, 5 = caminar en la dirección especificada).

  7. En aras de la puntuación, cada cadena de juego ("Sientes una brisa", "Hueles un wumpus", "Tu flecha no golpeó nada", etc.) puede considerarse un byte. No abusar de esto para ocultar el código del juego en el texto; Esto es solo para interactuar con el jugador.

  8. Deduzca el 10% de su recuento de bytes para implementar megabates, que comienzan en una habitación aleatoria diferente del jugador (aunque pueden compartir una habitación con el wumpus y / o el hoyo). Si el jugador entra en la habitación con los murciélagos, los murciélagos lo llevarán a otra habitación seleccionada al azar (garantizado que no sea la habitación con el hoyo o el wumpus dentro), antes de volar a su propia ubicación aleatoria. En las tres habitaciones adyacentes a los murciélagos, se puede escuchar un chirrido, pero el jugador no recibe información sobre de qué habitación proviene el sonido.

  9. Deduzca el 35% de su conteo de bytes para implementar una interfaz gráfica que muestre el mapa icosaédrico y algún tipo de indicación de la información que el jugador tiene hasta ahora sobre la ubicación del hoyo, el wumpus y los murciélagos (si corresponde), en relación con el jugador. Obviamente, si el wumpus se mueve o el jugador es movido por los murciélagos, el mapa debe reiniciarse en consecuencia.

  10. El conteo de bytes más bajo, según el ajuste, gana.

El código fuente BÁSICO para una versión del juego (que no se ajusta necesariamente a las reglas anteriores y, en cualquier caso, no tiene nada de golf) se puede encontrar en este sitio web y probablemente en otros.

Michael Stern
fuente
Algunas aclaraciones: 3. si el wumpus no estaba en esa habitación, se asusta y se mueve a una de las TRES habitaciones ... así que si disparas una flecha y fallas, el wumpus puede venir y matarte, ¿verdad? ¿Y el wumpus solo se moverá si se asusta, de lo contrario, simplemente se queda quieto? 6. Entiendo que el rumbo del jugador está determinado por la sala de donde vino. Entonces, si viniera del sur, sus opciones serían 1.noreste 2.odoeste 3.o sur y si viniera del norte sería lo contrario. Además, sus reglas parecen más simples / golfistas que el programa de referencia (que aún no he investigado en detalle). ¿Estoy en lo correcto?
Level River St
Argh! No puedo encontrar ninguna imagen del gráfico dual de un icosaedro en ninguna parte de la red.
Jack M
1
@steveverrill Sí, si lo asustas, puede venir y matarte. Si no lo asusta, no se mueve. Hay muchas variaciones en el juego; muchas versiones permiten que las flechas circulen y te maten, por ejemplo. Lo he resuelto.
Michael Stern
3
@JackM, el mapa de las caras de un icosaedro es idéntico al mapa de vértices de un dodecaedro, y ese gráfico se encuentra fácilmente. Pruebe, por ejemplo, wolframalpha.com/input/?i=DodecahedralGraph+edgerules o el comando equivalente de Mathematica GraphData ["DodecahedralGraph", "EdgeRules"]. De cualquier manera obtienes {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 No, "atrás" implica darse la vuelta y caminar de regreso por donde viniste. Si presionas "atrás" dos veces, terminas donde empezaste. No es necesario almacenar estados de juego anteriores.
Michael Stern

Respuestas:

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])=

La puntuación se obtiene tomando el recuento de bytes (290), sumando el número de cadenas utilizadas para la interacción con el usuario (6) y restando la longitud combinada de esas cadenas (133). Los avances de línea son parte de las cadenas y contribuyen al recuento de bytes.

Hitos

  1. Portado respuesta de professorfish de Bash a GolfScript. Puntuación: 269

  2. Actuó sobre las sugerencias de Peter Taylor en los comentarios. Puntuación: 250

  3. Peter Taylor refactorizó todo mi código y me ayudó a comprimir la tabla de búsqueda. Puntuación: 202

  4. Se reemplazó la tabla de búsqueda de habitaciones adyacentes con un enfoque matemático. Puntuación: 182

  5. Entrada, salida y función refactorizadas que apoyan el enfoque matemático. Puntuación: 163

Un gran "¡Gracias!" Va a Peter Taylor por toda su ayuda.

Cómo funciona

Las 20 habitaciones se representan como los vértices de un dodecaedro, a los que se les ha asignado números del 0 al 19 de la siguiente manera:

Gráfico dodecaédrico

Para encontrar las habitaciones adyacentes a la habitación N y ordenarlas en el sentido de las agujas del reloj, tenemos que considerar cuatro casos:

  • Si N ≡ 0 mod 4 (vértices azules), la habitación adyacente es 19 - N , N + 2 mod 20 y N - 2 mod 20 .

  • Si N ≡ 1 mod 4 (vértices verdes), la habitación adyacente es 19 - N , N - 4 mod 20 y N + 4 mod 20 .

  • Si N ≡ 2 mod 4 (vértices amarillos), la habitación adyacente es 19 - N , N - 2 mod 20 y N + 2 mod 20 .

  • Si N ≡ 3 mod 4 (vértices rojos), la habitación adyacente es 19 - N , N + 4 mod 20 y 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
fuente
1
Puede guardar 1 Qcon 19rand 97+; 2 @con 97%3*&>..., otro 1 al alinear Qcomo {19rand 97+}2*:,\:H, algunos al reemplazar |con *, que a menudo es la mejor manera de hacer un if. Bno sirve para nada, y creo que algunas variables más podrían eliminarse mediante el uso de la pila.
Peter Taylor
1
Olvidé mencionar otro truco frecuente: conversión de base para tablas de búsqueda. Puede reemplazar los 62 caracteres de la lista de adyacencia con una cadena de 33 caracteres seguida de 256base 20base(y probablemente también eliminar algunos +/- 97). El único inconveniente es que requerirá caracteres no imprimibles.
Peter Taylor
1
He guardado otros 13 refactorizando para ser GS más idiomático (principalmente usando la pila en lugar de variables); y hay 10 adicionales a costa de hacer que la salida sea menos bonita. Eso es aparte de la compresión de la tabla de búsqueda mencionada en mi comentario anterior.
Peter Taylor
1
En absoluto, lo he disfrutado. Estoy decepcionado de que el enfoque de la tabla de búsqueda sea mucho mejor que el más matemático que tenía la intención de usar. Por cierto, creo que su versión actual tiene un pequeño error, porque si dispara una flecha, falla y asusta al wumpus en su habitación, entonces solo sale You were killed by the wumpussin mencionar la flecha que falta. Es por eso que estaba agregando en la versión no bonita.
Peter Taylor
1
2*2+=>)2*
Peter Taylor
15

REV0 C ++ (Visual Studio en 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!");}}

A continuación se muestra un juego, que demuestra que (siempre que no comience justo al lado de un obstáculo) con el juego correcto, siempre puede ganar. El jugador siente una brisa, retrocede y realiza un bucle completo en sentido antihorario. Como le toma exactamente 5 movimientos para sentir una brisa nuevamente, conoce el agujero a su derecha y se aleja lo más posible. Del mismo modo, cuando huele el wumpus, sin saber si es correcto o izquierdo, se vuelve y hace un bucle en el sentido de las agujas del reloj. Le toma 5 movimientos para oler el wumpus nuevamente, por lo que sabe que está a la izquierda y dispara con certeza.

Si hubiera pasado por el otro lado, habría encontrado el wumpus antes y habría sabido que estaba en la misma dirección en la que giraba.

ingrese la descripción de la imagen aquí

REV1 C (CCG en Cygwin), 431-35% de bonificación = 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!",)}}

Nuevas líneas agregadas para mayor claridad. Los cambios de Rev 0 son los siguientes:

Muchas gracias a @Dennis por recomendar el compilador GCC en el emulador Cygwin Linux para Windows. Este compilador no requiere la includes en el programa rev 0, y permite el inttipo predeterminado para las variables y ¡ main.Este es un consejo de golf que cambia la vida!

Además, ejecutarse en Linux significa que \fhace que el cursor se mueva hacia abajo sin hacer un retorno de carro (a diferencia de Windows, donde solo produce un símbolo imprimible). Esto ha permitido un acortamiento considerable de la declaración printf que imprime el tablero

Varios consejos adicionales de Dennis en los comentarios, y uno de los míos: cambio de condición al verificar si la flecha golpeó el wumpus: if(q==w)> if(q-w)(..else .. se invierte)

Adición de una pantalla gráfica que muestra la información que el jugador sabe sobre dónde se huele un wumpus / se siente una brisa para reclamar el 35% de bonificación. (Eliminé la versión anterior de depuración de esto que mostraba la posición exacta del wumpus y el agujero. Se puede ver en el historial de edición).

REV2 C (CCG en Cygwin), 389-35% de bonificación = 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");}

Gracias nuevamente a Dennis por refactorizar mi código:

Constante de caracteres m[]reemplazada por literales (no sabía que podía indexar un literal).

Siembra de números aleatorios con variable de pila (depende del sistema, algunos sistemas aleatorizan la asignación de memoria como medida de seguridad).

Macro con putsreemplazado por una macro con printfun código adicional que debe ejecutarse cuando el mensaje que se muestra se coloca dentro de los printfargumentos (se aprovecha la cara de que printf no imprime los últimos argumentos si no hay suficientes especificadores de formato en la cadena de formato). ifreemplazado por||

Cálculo de la nueva posición del jugador / wumpus colocado dentro de la nueva macro.

Ganar / perder mensajes colocados fuera del whilebucle. ifreemplazado por operador condicional.

Uso del operador condicional en línea para disparar flechas. Si el jugador falla, esto requiere tanto imprimir un mensaje como ajustar la posición de wumpus. Dennis ofreció un par de formas de combinar printfy calcular la posición de wumpus en una sola expresión, pero me he ido con una propia. printfdevuelve el número de caracteres impresos, que para Your arrow didn't hit anything\nel 31 (11111 binario.) Por lo tanto, 31&Q(w)==Q(w).

Mi otra contribución a esta edición ha sido la eliminación de algunos corchetes innecesarios.

Salida

Aquí el jugador ya ha encontrado dónde está el Wumpus, pero elige hacer una exploración exhaustiva para descubrir exactamente dónde está el pozo también. A diferencia de mi versión anterior de depuración que mostraba dónde estaban el wumpus y el hoyo durante todo el juego, esto muestra solo las salas donde el jugador ha visitado y sintió una brisa (1) olía el wumpus (2) o ambos (3). (Si el jugador dispara una flecha y falla, la variable que acontiene la información de posición wumpus se restablece).

ingrese la descripción de la imagen aquí

REPRESENTACIÓN DE ICOSAEDRO

Nota: esta sección se basa en la rev 1

Mi característica estrella! No hay gráfico en mi código. Para explicar cómo funciona, vea el mapa mundial a continuación. Cualquier punto en el icosaedro se puede representar con una latitud 0-3 y una longitud 0-4 (o un solo número long*4+lat,.) La línea de longitud marcada en el mapa pasa solo a través de esas caras con longitud cero, y la línea de latitud pasa El centro de las caras con latitud cero.

El jugador puede orientarse en 3 ejes posibles, representados por los símbolos de la siguiente manera: norte-sur -noreste-suroeste \noroeste-sureste /. En cualquier habitación tiene exactamente una salida en cada uno de estos ejes disponibles para él. En la pantalla que se muestra, el jugador realiza un bucle completo en sentido horario. En general, es fácil de identificar desde el jugador que marca de dónde vino y, por lo tanto, a dónde se le permite ir.

El único caso que es un poco difícil para el ojo no iniciado es el cuarto. Cuando vea una inclinación en una de estas filas polares, el jugador ha venido de la celda polar más cercana al extremo exterior de la inclinación y se enfrenta generalmente al ecuador. Por lo tanto, el jugador está orientado al sureste y sus opciones son: 15 (SUR, la celda a la derecha) 25 (noreste, la celda superior) o 35 (noroeste, la celda inferior).

Entonces, básicamente mapeo el icosaedro a una cuadrícula de 5x4, con celdas numeradas del 19 al 0 en el orden en que se imprimen. El movimiento se realiza sumando o restando de la posición actual, según la latitud y dirección del jugador, según la tabla a continuación.

Si el jugador sale del fondo (oeste) del tablero, regresa al lado superior (este) y viceversa, por lo que su posición se toma en el módulo 20. Generalmente, los movimientos se codifican en m [] agregando ascii 80 ( P) al valor bruto que proporciona los caracteres que se muestran a continuación, pero en principio se puede agregar cualquier múltiplo de 20 sin afectar la operación.

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

La entrada del jugador (dividida por 10 para eliminar el segundo dígito) se agrega a su dirección actual y se toma el módulo 3 para obtener su nueva dirección. Esto funciona bien en la mayoría de los casos. Sin embargo, hay un problema cuando está en una habitación polar y se mueve hacia el poste. Al doblar el mapa a continuación, será claro que si sale de la habitación mirando hacia el "noreste", ingresará al nuevo cuadrado que mira hacia el "sureste", por lo que se debe hacer una corrección. Esto se hace en línea e=(d+i/10)*m[p%4]%3;por la multiplicación por m[p%4]. Los primeros cuatro valores de m [] se seleccionan de modo que, además de su función anterior, también tengan la característica m[1]%3==m[2]%3==1y m[0]%3==m[3]%3==2. Esto deja la dirección sola para las habitaciones ecuatoriales y aplica la corrección necesaria para las habitaciones polares.

El momento lógico para hacer la corrección sería después del movimiento. Sin embargo, para guardar personajes se hace antes del movimiento. Por lo tanto, ciertos valores en m [] deben transponerse. Entonces, los últimos 2 caracteres son en LTlugar de TLpor la tabla anterior, por ejemplo.

ingrese la descripción de la imagen aquí

CÓDIGO SIN GOLF

este es el código rev 1, que está menos ofuscado que rev 2.

Esto se ejecutará en GCC / Linux. He incluido en los comentarios el código adicional necesario para que se ejecute en Visual Studio / Windows. ¡Es una gran diferencia!

//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!",)
    }

}

CUESTIONES Y CURIOISIDADES

He aprovechado el punto mencionado por @professorfish, si el wumpus y el hoyo comienzan en lugares aleatorios, no hay necesidad de que el jugador comience en un lugar aleatorio. El jugador siempre comienza en la sala 19 mirando hacia el norte.

Entiendo que como el wumpus "no se ve afectado por el hoyo", el wumpus puede comenzar o entrar a la habitación donde está el hoyo. En general, esto simplifica las cosas excepto por un punto. No tengo una variable específica que indique que el juego ha terminado; termina cuando el jugador coincide con el wumpus o el hoyo. Entonces, cuando el jugador gana, muestro el mensaje ganador, ¡pero muevo el pozo hacia el jugador para salir del círculo! No puedo poner al jugador en el pozo ya que el wumpus podría estar allí y recibiría un mensaje sobre el wumpus que no quiero.

El programa rev0 funcionó perfectamente en Visual Studio, pero el IDE dijo que "la pila se corrompió alrededor de la variable i" al salir. Esto se debe a que scanf está tratando de poner un Dennis inten un char.comportamiento incorrecto en su máquina Linux debido a esto. De todos modos, se soluciona mediante el uso del tipo correcto en rev 1.

La línea para mostrar el tablero en rev 0 es torpe y parece ligeramente diferente en otras plataformas. En printf(" %c%c%c")el medio% c se muestra el carácter imprimible. El último% c es ASCII 0 o ASCII 10 (\ n, nueva línea con retorno de carro en Windows). Parece que no hay ningún carácter en Windows que funcione en la consola, que bajará una línea sin dar un retorno de carro. Si lo hubiera, no necesitaría el primer c% (ASCII 0 o ASCII 9 antes del carácter de latitud 1. Las pestañas están notoriamente indefinidas en su comportamiento). El espacio inicial mejora el formato (coloca los caracteres de latitud 3 y 2 más cerca del carácter de latitud 1). .) Rev 1 tiene una revisión de esta línea que utiliza un carácter de avance de formulario \ f y, por lo tanto, no necesita caracteres de formato al comienzo de printf. Esto lo hace más corto, pero el \ f no funciona en Windows.

Level River St
fuente
1
Me encanta el reportaje.
Michael Stern el
No estoy seguro de si es debido a las modificaciones que tuve que hacer para compilarlo con GCC en Linux (elimine la primera inclusión, reemplace scanf_scon scanfe incluir stdio.hsi compilo como calificador C ++ que C), pero no funciona del todo para yo. Por ejemplo, si voy a la izquierda, luego vuelvo a la derecha al principio ( 15 35), estoy en una habitación diferente a la que comencé.
Dennis
@ Dennis He rastreado la fuente del error al salir. Es el scanf_s (¡supuestamente seguro!) que "corrompe la pila alrededor de la variable i" cuando intenta poner lo que supongo que es un entero de 32 bits en un carácter. Entonces, lo primero que sugeriría es verificar el tipo que scanf usa para un "% d" y cambiar la variable i a ese tipo. Obtengo la respuesta correcta con error de salida para char, la respuesta correcta sin error de salida para int y la respuesta incorrecta con el tipo de Microsoft __int64 (equivalente a long long, a menos que ponga "% lld"). También ha ejecutado la versión no golfista y tuviste algún problema con la pantalla?
Level River St
@steveverrill: Sí, había probado ambas versiones. El tipo de ihecho es el problema. La página de manual dice: " d Coincide con un entero decimal opcionalmente firmado; el siguiente puntero debe ser un puntero a int ". Cambiar el tipo hace que funcione bien.
Dennis
@steveverrill: No sé cómo VS maneja las cosas, pero si compila con GCC (como C, no C ++), puede guardar muchos caracteres. Ninguna de las inclusiones es necesaria si reemplaza NULLcon 0y scanf_scon scanf, no necesita intantes mainy puede moverse iy dfuera de main (se configuran por defecto inty se inicializan 0). Además, puede definir p=19,h=rand()%p,w=rand()%p, reemplazar m[]con *my debería ser posible definir una macro para todas las instancias 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

Tenga en cuenta que 163 se sustrajo del recuento de caracteres para las cadenas codificadas. Si desea una salida de depuración que indique los números de sala, agregue la siguiente línea justo después de la primera aparición de ^:

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

Una sesión de ejemplo (con salida de depuración 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
fuente
Aquí está el primer código de trabajo. Volviendo más tarde para jugar un poco más al golf.
Howard
Mi código tiene actualmente 1 carácter más. Estoy buscando cualquier forma posible de golf más lejos!
Timtech
No es que necesite mi ayuda, pero puede guardar 14 caracteres definiendo {puts}:|;, 5 caracteres reemplazando Ry Wcon -y >(permite eliminar espacios circundantes) y 9 caracteres soltando '> 'print(no parece ser requerido por la pregunta).
Dennis
@ Dennis Gracias. Seguramente implementaré algunas de sus sugerencias.
Howard
9

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

Casi terminado de jugar al golf ...

Incluye una GUI con un mapa icosaédrico y megabates para las bonificaciones completas.

Wumpus GUI

  • Cada habitación tiene 4 botones: X(el hoyo); B(el Mega-Murciélago); W(el Wumpus); y P(usted)
  • Su ubicación actual es de color azul.
  • Los botones son de color rojo si el objeto que representa podría estar en esa ubicación y verde si definitivamente no está en esa ubicación.
  • El Wy Plos botones se puede hacer clic sólo en las habitaciones adyacentes a la ubicación actual.
  • Si gana, el fondo se vuelve verde y si muere, el fondo se vuelve rojo.

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
fuente
Obtiene 1066 sin ECMA 6 usando el compilador de cierre.
AMK
Me preguntaba cuánto más fácil sería cuando tienes una representación gráfica para ayudar a deducir dónde están las cosas. 1+ pero es un poco demasiado fácil :)
Sylwester
9

Bash, 365 (¡primera versión de trabajo 726!)

¿Ponerse al día con GOLFSCRIPT?

@Dennis básicamente ha hecho todo el golf por mí. ¡Gracias!

El programa asume una entrada válida. La entrada válida es la dirección que elija (1 para la derecha, 2 para la izquierda, 3 para la espalda) seguida de su acción (4 para disparar, 5 para caminar).

Alguna explicación

Normalmente hago grandes explicaciones detalladas, pero esto es probablemente demasiado complicado para que me moleste.

Cada vértice en el gráfico del dodecaedro se codifica como una letra (a = 1, b = 2, ... t = 20).

La posición inicial del jugador es siempre 20 (y están de espaldas a 18), ya que eso no importa en sí mismo, solo importan las posiciones relativas del jugador, hoyo y wumpus.

La variable $palmacena la ubicación del jugador. $ralmacena la ubicación anterior del jugador. $wes el wumpus y $h(H para el hoyo) es el hoyo.

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};};}

Historial de versiones

  1. Lanzamiento inicial, 698 caracteres
  2. Se corrigió un error en el que "Sientes una brisa" y "Hueles el wumpus" no se pueden mostrar al mismo tiempo; ahorró 39 caracteres al hacer que la generación de números aleatorios sea una función.
  3. Recordó que el wumpus se mueve si disparas y fallas. 726 caracteres.
  4. Hecho grep -oEuna variable. Guardado 5 caracteres.
  5. Hecho [a-z]{3}una variable. Guardado 3 caracteres.
  6. Hecho echouna variable. Guardado 5 caracteres.
  7. Actuó sobre la mayoría de las sugerencias de @Dennis. Guardado 72 caracteres.
  8. Se agregaron todas las sugerencias restantes. Guardado 68 caracteres.
  9. Guardado 2 caracteres de la sugerencia de @DigitalTrauma.
  10. Se corrigió un error importante en el que solo puedes disparar al wumpus si está a la derecha. El mismo recuento de personajes.
  11. Se usó la expansión de parámetros para eliminar 2 caracteres usando $m.
  12. Afeitó muchos caracteres abandonando grepy siendo un poco más sensible.
  13. Definido Ccomo una función de búsqueda regexp para usar en declaraciones if, y Ecomo una función que imprime "Has matado al wumpus" y sale.
  14. Se ha guardado 1 carácter mediante la reordenación de "declaración if".
  15. Ahorró muchos caracteres al eliminarlos dy eliminó los corchetes innecesarios.
  16. Errores arreglados. Se agregaron muchos caracteres :(
  17. AHORRO DE MOARR ( http://xkcd.com/1296/ )
  18. Otra de las ideas de @Dennis (guardar algunos caracteres), y mi uso astuto (ab) de la indirección (guardar 1 carácter).
  19. Corrección de estilo para q ().
  20. re-agregado de salida adecuada

Ejecución de la muestra

"In:" es entrada, "Out: es salida".

El jugador deambula un poco, huele el wumpus y dispara. Echan de menos, y el wumpus entra en su habitación y se los come.

En: 15

En: 15

En: 25

En: 25

En: 15

Fuera: hueles el wumpus

En: 14

Fuera: te perdiste

Fuera: El wumpus te comió


fuente
1
Creo que puede hacer que su código sea al menos 100 bytes más corto. 1. exites solo un byte más largo que g=1y elimina la necesidad de realizar pruebas de no-cero gy algunas elifdeclaraciones. 2. Puede usar en ((i==35))lugar de [ $i = 35 ]y en ...&&...lugar de if ... then ... fi. 3. q(){ L=({a..s});$j ${L[RANDOM%19]};}y n=`$k $w$m<<<$d`;w=${n:RANDOM%2+1:1}ambos guardan unos pocos bytes.
Dennis
1
Reemplace while :;do... donecon for((;;);{... }para un ahorro de 3 caracteres
Digital Trauma
1
@professorfish: creo que una función funcionaría mejor que el enfoque actual string-grep-cut. Por ejemplo, d(){ x=npoemfgnshtoksblbtckpdpljqniorelgfhkbqraicadjaghimsmjtqecrdf;s=${x:3*30#$1-30:3};}le permitirá reemplazar las definiciones de sy ncon d $py d $w. Si además define u=${s#*$r}$s(y ajusta las definiciones de ly en fconsecuencia), no necesitará $ky $mmás. Ahorra 83 bytes, creo. Además, el espacio q ()no es obligatorio.
Dennis
1
@professorfish: Y puede guardar 3 bytes adicionales definiendo c(){ [[ $1 =~ $2 ]];}y reemplazando, por ejemplo, la penúltima línea con c $r $b||{ $j You missed;d $w;w=${s:RANDOM%2+1:1};}.
Dennis
1
@professorfish: el uso de la función que sugerí debería ser 3 bytes más corto. Puede guardar 106 bytes adicionales reemplazando las cuatro líneas después b=$pcon d $p;u=u${s#*$r}$s, las líneas después read icon 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};}y deshaciéndose de ellas 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 se puso al día con la versión de la tabla de búsqueda de Dennis, de la que toma un poco prestado. Lo interesante de esta versión es que no tiene una tabla de búsqueda para el diseño de la sala.

Las 60 simetrías rotacionales de un icosaedro son isomorfas al grupo alterno en 5 letras, A_5. Después de intentar todo tipo de enfoques para representar al grupo de manera compacta, he vuelto al más simple: cada elemento es una permutación de paridad pareja. El grupo puede generarse a partir de dos generadores en más de una forma: el enfoque que estoy tomando utiliza los generadores 3y 3 1. Estos nos permiten generar 1 = 3 3 1, 2 = 3 3 1 3 1y 3 = 3.

Observe que la dirección 3corresponde a un elemento de orden 2, porque después de pasar por la puerta detrás de usted, esa puerta está detrás de usted nuevamente. La dirección 1corresponde a un elemento de orden 5, caminando alrededor de un vértice del icosaedro. (Del mismo modo elemento 2). Y la combinación 3 1es de orden 3, ya que recorre las habitaciones adyacentes a la que comienza detrás de usted.

Por lo tanto, estamos buscando una permutación de orden 2 para representar la dirección 3y una permutación de orden 5 para representar una dirección 1tal que 3 1sea ​​de orden 3.

Hay 15 permutaciones de orden 2 en A_5, y para cada una hay 8 permutaciones candidatas para 1(y por lo tanto para 3 1). Hay una atracción obvia [4 3 2 1 0]para 3: revertir una matriz es justo -1%. De sus posibles permutaciones complementarias 3 1que he elegido [0 1 3 4 2], que admite una implementación bastante corta como [~@].

Sin golf

# 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
fuente
Buen enfoque algebraico! Sin embargo, hay un error menor: 10/@3%=intenta acceder al cuarto elemento de una matriz de longitud 3 si la entrada es 35.
Dennis
@ Dennis, sí, me di cuenta después de acostarme. Puedo pensar en varias formas de arreglarlo, todas cuestan 2.
Peter Taylor
Puedes recuperar un char con 9/3%@3%=.
Dennis
Actualmente tengo 7 caracteres con una reestructuración más drástica. Pero ese 1 char de en 9/lugar de 10/todavía funciona, así que gracias.
Peter Taylor
5

Wumpus , 384-129 (cadenas) = ​​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@

Pruébalo en línea! (Por supuesto, TIO no tiene mucho sentido, porque no puede usar el programa de forma interactiva allí, y una vez que el programa se quede sin instrucciones en STDIN, leerá 0 0, lo que es equivalente a 3 4, por lo que terminará disparando flechas hasta que el Wumpus se mueva allí o te mate).

Cuando ejecute esto localmente, asegúrese de que el salto de línea después del segundo número de cada entrada se vacíe (porque Wumpus lo necesita para determinar que el número ha terminado). En Powershell, de alguna manera necesito ingresar un carácter más después del salto de línea para que funcione (no importa qué carácter, pero solo usé saltos de línea dobles para las pruebas).

Hay mucho espacio para jugar al golf aún más, pero probar diseños completamente nuevos lleva un tiempo. La puntuación final también depende mucho de las cadenas reales que uso, porque en un lenguaje 2D, una cadena de N bytes tiende a costarle más de N bytes de código fuente, ya que impone restricciones significativas en el diseño del código, y a menudo necesita dividirlo en varias secciones (incurriendo en comillas dobles adicionales). En el extremo, si redujera cada cadena a una sola letra (y -129 a -12), probablemente estaría ahorrando una tonelada de bytes.

Explicación

Primero un descargo de responsabilidad: A pesar del nombre de la lengua, se no se diseña para hacer la implementación de la caza del Wumpus particularmente fácil. En cambio, primero diseñé el lenguaje en torno al tema de los triángulos, terminé con una estructura de datos icosaédricas y decidí llamarlo Wumpus por eso.

Entonces, sí, aunque Wumpus se basa principalmente en la pila, también tiene 20 registros que están dispuestos alrededor de las caras de un icosaedro. Eso significa que obtenemos una estructura de datos para representar el mapa de forma gratuita. Lo único que no podemos hacer fácilmente es encontrar caras específicas en el icosaedro, por lo que para buscarlas, necesitamos "rodar el d20" hasta que terminemos en la cara que estamos buscando. (Es posible hacer esto de una manera determinista, pero eso requeriría muchos más bytes). La búsqueda de caras como esta termina casi seguramente (es decir, con probabilidad 1), por lo que la búsqueda que se ejecuta para siempre no es una preocupación en la práctica).

El código anterior es una versión de golf de esta primera implementación con un diseño más sano:

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.

Dado que el golf consistió principalmente en comprimir el diseño, explicaré esta versión por ahora (hasta que agregue cualquier truco de golf que vaya más allá de la reestructuración del código).

Comencemos con el código de configuración:

1SDL2vSD70L?.;;2.

Inicialmente, todas las caras se establecen en 0 . Codificaremos el wumpus configurando el 1 bit de la cara correspondiente, y el pozo configurando el 2 bits. De esta manera, ambos pueden estar en la misma habitación. La posición del jugador no se registrará en el icosaedro, sino que siempre estará activa (solo uno de los 20 registros está activo a la 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.

Ahora necesitamos encontrar una cara vacía al azar para colocar al jugador.

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.

La siguiente sección verifica los alrededores del jugador e imprime las advertencias apropiadas:

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

Este es un ciclo que ejecutamos 3 veces. Cada vez, miramos al vecino correcto, imprimimos la (s) cadena (s) apropiada (s) si hay un peligro y luego giramos el icosaedro 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.

La siguiente sección lee dos números del jugador y luego mueve al jugador o dispara una flecha. El primero es trivial, el último menos. El problema principal para disparar la flecha es el caso en que falla. En ese caso, a) necesitamos ir a buscar el wumpus para moverlo, y luego b) regresar a la habitación del jugador y la orientación correcta del icosaedro (para que "atrás" permanezca "atrás"). Esta es la parte más cara de todo el 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.

El punto de entrada a esta sección es el Ide la izquierda.

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.

Uf, esa fue la parte difícil. Ahora solo tenemos que comprobar si el jugador muere y, de lo contrario, comenzar de nuevo desde el bucle principal:

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

La estructura de esta sección es esencialmente idéntica a la estructura que utilizamos al verificar los alrededores del jugador: verificamos el 1 bit de la cara actual (la sala del jugador) y si está configurado imprimimos The wumpus ate you.y terminamos el programa. De lo contrario, verificamos los 2 bits y está configurado, imprimimos You fell into the pit.y terminamos el programa. De lo contrario, llegamos al 2.que salta al principio del bucle principal (en las coordenadas (0, 2)).

Martin Ender
fuente
1

awk - grande

Esto no resultó tan corto como esperaba, pero tomé un enfoque ligeramente diferente para lidiar con el gráfico, así que estoy publicando la versión sin golf.

Aproveché el hecho de que un icosaedro (poliedro de 20 lados) bajo rotaciones que preserva la orientación es isomorfo al grupo alterno de grado 5 (permutaciones de 5 elementos que tienen un número par de ciclos de longitud par). Luego elijo dos permutaciones con la longitud del ciclo 5 como "izquierda" y "derecha", y elijo una permutación con la longitud del ciclo 2 como "atrás". Con estos, construyo el gráfico de una habitación caminando por el camino de Hamilton (2xRRRLLLRLRL, usando 3xRB en cada habitación para capturar las 3 direcciones posibles).

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
fuente