REV0 C ++ (Visual Studio su 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!");}}
Di seguito è riportato un playthrough, a dimostrazione del fatto che (purché non si inizi proprio accanto a un pericolo) con il gioco corretto è sempre possibile vincere. Il giocatore sente una brezza, gira indietro e fa un giro completo in senso antiorario. Mentre gli occorrono esattamente 5 mosse per sentire di nuovo la brezza, conosce il buco alla sua destra e si allontana il più possibile. Allo stesso modo, quando sente l'odore del wumpus, non sapendo se è giusto o sinistro, si gira indietro e fa un giro in senso orario. Gli occorrono 5 mosse per annusare di nuovo il wumpus, quindi sa che è a sinistra e spara con certezza.
Se avesse fatto un giro nell'altro modo, avrebbe trovato prima il wumpus e avrebbe saputo che era nella stessa direzione in cui stava girando.
REV1 C (GCC su Cygwin), 431-35% di bonus = 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!",)}}
Newline aggiunti per chiarezza. Le modifiche da Rev 0 sono le seguenti:
Un grande ringraziamento a @Dennis per aver raccomandato il compilatore GCC sull'emulatore Cygwin Linux per Windows. Questo compilatore non richiede le include
s nel programma rev 0 e consente il int
tipo predefinito per variabili emain.
questo è un consiglio di golf che cambia la vita!
Inoltre, l'esecuzione in Linux significa che \f
il cursore si sposta verso il basso senza fare un ritorno a capo (diversamente da Windows dove produce solo un simbolo stampabile). Ciò ha consentito un accorciamento considerevole dell'istruzione printf che stampa la scheda
Diversi consigli aggiuntivi di Dennis nei commenti e uno dei miei: cambio di condizione quando si controlla se la freccia ha colpito il wumpus: if(q==w)
>if(q-w)
(..else .. è invertito)
Aggiunta di un display grafico che mostra le informazioni che il giocatore conosce su dove si sente un wumpus / si sente una brezza richiedere il bonus del 35%. (Ho eliminato la vecchia versione di debug di questo che mostrava la posizione esatta del wumpus e del buco. Può essere visto nella cronologia delle modifiche.)
REV2 C (GCC su Cygwin), bonus 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");}
Grazie ancora a Dennis per il refactoring del mio codice:
Costante del carattere m[]
sostituita con valori letterali (non sapevo che si potesse indicizzare un valore letterale).
Semina di numeri casuali con variabile stack (a seconda del sistema, alcuni sistemi randomizzano l'allocazione della memoria come misura di sicurezza).
La macro viene puts
sostituita con una macro con printf
e un codice aggiuntivo che deve essere eseguito quando il messaggio visualizzato viene inserito all'interno degli printf
argomenti (vantaggio della faccia che printf non stampa gli ultimi argomenti se non ci sono abbastanza specificatori di formato nella stringa di formato.)if
sostituito da||
Calcolo della nuova posizione del giocatore / wumpus inserito nella nuova macro.
Vincere / perdere messaggi posizionati all'esterno del while
loop. if
sostituito dall'operatore condizionale.
Uso dell'operatore condizionale in linea per il tiro della freccia. Se il giocatore non riesce, ciò richiede sia la stampa di un messaggio sia la regolazione della posizione wumpus. Dennis ha offerto un paio di modi per combinare printf
e calcolare la posizione di Wumpus in un'unica espressione, ma io ne ho scelto uno mio. printf
restituisce il numero di caratteri stampati, che per Your arrow didn't hit anything\n
il 31 (11111 binario.) Quindi, 31&Q(w)==Q(w)
.
Il mio altro contributo a questa modifica è stato l'eliminazione di alcune parentesi non necessarie.
Produzione
Qui il giocatore ha già trovato dove si trova il Wumpus, ma sceglie di fare un'approfondita esplorazione per scoprire esattamente dove si trova anche la fossa. A differenza della mia vecchia versione di debug che mostrava dove si trovavano il wumpus e il pit nel corso del gioco, questo mostra solo le stanze in cui il giocatore ha visitato e ha sentito una brezza (1) annusare il wumpus (2) o entrambi (3). (Se il giocatore lancia una freccia e perde, la variabile a
contenente le informazioni sulla posizione wumpus viene ripristinata.)
RAPPRESENTAZIONE ICOSAHEDRON
Nota: questa sezione si basa sul rev 1
La mia caratteristica principale! Non c'è un grafico nel mio codice. Per spiegare come funziona, vedere la mappa del mondo di seguito. Qualsiasi punto sull'icosaedro può essere rappresentato da una latitudine 0-3 e una longitudine 0-4 (o un singolo numero long*4+lat
,.) La linea della longitudine segnata sulla mappa passa solo attraverso quelle facce con longitudine zero e la linea della latitudine passa attraverso il centro delle facce con latitudine zero.
Il giocatore può essere orientato su 3 assi possibili, rappresentati dai simboli come segue: nord-sud-nord- -
est-sud-ovest \
nord-sud-est /
. In una data stanza ha esattamente un'uscita su ciascuno di questi assi a sua disposizione. Nel display mostrato il giocatore esegue un ciclo completo in senso orario. In genere è facile identificarsi dalla marcatura del giocatore da dove proviene, e quindi dove gli è permesso andare.
L'unico caso un po 'difficile per l'occhio non iniziato è il quarto. Quando vedi un'inclinazione in una di queste file polari, il giocatore proviene dalla cellula polare più vicina all'estremità esterna dell'inclinazione ed è generalmente rivolto verso l'equatore. Quindi il giocatore è rivolto a sud-est e le sue opzioni sono: 15 (SUD, la cella a destra) 25 (nord-est, la cella in alto) o 35 (nord-ovest, la cella in basso).
Quindi, fondamentalmente, mappa l'icosaedro su una griglia 5x4, con le celle numerate da 19 a 0 nell'ordine in cui sono stampate. La mossa viene effettuata aggiungendo o sottraendo dalla posizione corrente, a seconda della latitudine e della direzione del giocatore, secondo la tabella seguente.
Se il giocatore esce dalla parte inferiore (ovest) del tabellone, torna sul lato superiore (est) e viceversa, quindi la sua posizione viene presa modulo 20. Generalmente le mosse sono codificate in m [] aggiungendo ascii 80 ( P
) al valore grezzo che fornisce i caratteri mostrati di seguito, ma in linea di principio è possibile aggiungere qualsiasi multiplo di 20 senza influire sull'operazione.
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
L'input del giocatore (diviso per 10 per rimuovere la seconda cifra) viene aggiunto alla sua direzione attuale e preso modulo 3 per ottenere la sua nuova direzione. Funziona bene nella maggior parte dei casi. Tuttavia, c'è un problema quando si trova in una stanza polare e si sposta verso il palo. Sarà chiaro quando piega la mappa in basso che se lascia la stanza di fronte a "nord-est" entrerà nella nuova piazza di fronte a "sud-est", quindi una correzione deve essere fatta. Questo viene fatto in linea e=(d+i/10)*m[p%4]%3;
dalla moltiplicazione per m[p%4]
. I primi quattro valori di m [] sono selezionati in modo tale che, oltre alla loro funzione sopra, abbiano anche la caratteristica m[1]%3==m[2]%3==1
e m[0]%3==m[3]%3==2
. Questo lascia la direzione da solo per le stanze equatoriali e applica la correzione necessaria per le stanze polari.
Il tempo logico per eseguire la correzione sarebbe dopo lo spostamento. Tuttavia, per salvare i personaggi viene fatto prima del trasloco. Pertanto alcuni valori in m [] devono essere trasposti. Ad esempio, gli ultimi 2 caratteri sono LT
invece TL
quelli indicati nella tabella sopra.
CODICE NON CONSOLIDATO
questo è il codice rev 1, che è meno offuscato rispetto alla rev 2.
Questo funzionerà su GCC / Linux. Ho incluso nei commenti il codice aggiuntivo necessario per farlo funzionare su Visual Studio / Windows. È una grande differenza!
//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!",)
}
}
QUESTIONI E CURIOISITÀ
Ho approfittato del punto menzionato da @professorfish, se l'umpus e il pit iniziano in punti casuali, non è necessario che il giocatore inizi in un posto casuale. Il giocatore inizia sempre nella stanza 19 esposta a nord.
Capisco che poiché il wumpus è "non influenzato dalla fossa", il wumpus può iniziare o entrare nella stanza in cui si trova la fossa. In generale questo semplifica le cose tranne per un punto. Non ho una variabile specifica per indicare che il gioco è finito; è finita quando il giocatore coincide con il wumpus o il pit. Quindi quando il giocatore vince visualizzo il messaggio vincente ma sposto la fossa sul giocatore per uscire dal circuito! Non riesco a mettere il giocatore nella fossa poiché potrebbe esserci il wumpus e riceverei un messaggio sul wumpus che non voglio.
Il programma rev0 ha funzionato perfettamente in Visual Studio, ma l'IDE ha detto "stack danneggiato attorno alla variabile i" all'uscita. Questo perché scanf sta provando a inserire un errore int
in un char.
Dennis segnalato a causa della sua macchina Linux. Comunque è stato risolto usando il tipo corretto nella rev 1.
La linea per la visualizzazione della scheda in rev 0 è goffa e appare leggermente diversa su altre piattaforme. In Rev 1 ha una rivisitazione di questa riga che utilizza un carattere di formattazione \ f e quindi non ha bisogno di caratteri di formattazione all'inizio della stampa. Questo lo rende più breve, ma \ f non funziona in Windows.printf(" %c%c%c")
mezzo% c è visualizzato il carattere stampabile. L'ultimo% c è ASCII 0 o ASCII 10 (\ n, newline con ritorno a capo in Windows.) Non sembra esserci alcun carattere in Windows che funzioni nella console, che scenderà su una riga senza dare un ritorno a capo. Se non ci fosse, non avrei bisogno del primo c% (ASCII 0 o ASCII 9 prima del carattere di latitudine 1. Le schede sono notoriamente indefinite nel loro comportamento.) Lo spazio iniziale migliora la formattazione (avvicina i caratteri di latitudine 3 e 2 al carattere di latitudine 1 .)