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.
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 include
s en el programa rev 0, y permite el int
tipo predeterminado para las variables y ¡ main.
Este es un consejo de golf que cambia la vida!
Además, ejecutarse en Linux significa que \f
hace 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 puts
reemplazado por una macro con printf
un código adicional que debe ejecutarse cuando el mensaje que se muestra se coloca dentro de los printf
argumentos (se aprovecha la cara de que printf no imprime los últimos argumentos si no hay suficientes especificadores de formato en la cadena de formato). if
reemplazado por||
Cálculo de la nueva posición del jugador / wumpus colocado dentro de la nueva macro.
Ganar / perder mensajes colocados fuera del while
bucle. if
reemplazado 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 printf
y calcular la posición de wumpus en una sola expresión, pero me he ido con una propia. printf
devuelve el número de caracteres impresos, que para Your arrow didn't hit anything\n
el 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 a
contiene la información de posición wumpus se restablece).
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==1
y 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 LT
lugar de TL
por la tabla anterior, por ejemplo.
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 int
en 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.