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.
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 include
s no programa rev 0 e permite o int
tipo 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 \f
o 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 puts
substituída por uma macro printf
e código adicional que deve ser executado quando a mensagem exibida é colocada dentro de printf
argumentos (vantagem da face que printf não imprime os últimos argumentos se não houver especfificadores de formato suficientes na cadeia de formato). if
substituído por||
Cálculo da nova posição do player / wumpus colocada dentro da nova macro.
Ganhar / perder mensagens colocadas fora do while
loop. if
substituí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 printf
e o cálculo da posição do wumpus em uma única expressão, mas eu optei por uma das minhas. printf
retorna 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 a
contém as informações da posição do wumpus será redefinida.)
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==1
e 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 LT
substituídos TL
pela tabela acima, por exemplo.
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 int
inserir 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.