Caccia il Wumpus


39

Quando ero un ragazzo, i bambini andavano nei negozi di computer e giocavano a Hunt the Wumpus fino a quando il personale non ci cacciava. Era un gioco semplice, programmabile sui computer di casa a metà degli anni '70, macchine così rudimentali che invece di microprocessori delle dimensioni di un chicklet, penso che alcuni di loro probabilmente contenessero dei veri chicklet.

Evociamo quell'epoca passata riproducendo il gioco su hardware moderno.

  1. Il giocatore inizia in una stanza a caso su una mappa icosaedrica (quindi ci sono 20 stanze in totale, collegate tra loro come le facce di un icosaedro, e ogni stanza ha esattamente tre uscite).

  2. Il wumpus inizia in una stanza diversa selezionata casualmente. Il wumpus puzza e il suo odore può essere rilevato in una qualsiasi delle tre stanze adiacenti alla sua posizione, sebbene la direzione dell'odore sia impossibile da determinare per il giocatore. Il gioco riporta solo "si sente l'odore di un wumpus".

  3. Il giocatore porta un arco e un numero infinito di frecce, che può sparare in qualsiasi momento nella stanza di fronte a lui. Se il wumpus è in quella stanza, muore e il giocatore vince. Se il wumpus non era in quella stanza, è sorpreso e si sposta casualmente in una delle tre stanze collegate alla sua posizione attuale.

  4. Una stanza, selezionata casualmente (garantita per non essere la stanza in cui il giocatore inizia) contiene una fossa senza fondo. Se il giocatore si trova in una stanza adiacente alla buca, sente una brezza, ma non ha idea di quale porta provenga la brezza. Se entra nella stanza con la fossa, muore e vince il wumpus. Il wumpus non è interessato dalla fossa.

  5. Se il giocatore entra nella stanza del wumpus, o se il wumpus entra nella stanza del giocatore, il wumpus vince.

  6. Il giocatore specifica la direzione che sta affrontando con un numero (1 = destra, 2 = sinistra, 3 = indietro), quindi un'azione (4 = lancia una freccia, 5 = cammina nella direzione specificata).

  7. Per motivi di punteggio, ogni stringa di gioco ("Ti senti un gioco da ragazzi", "Senti l'odore di un wumpus", "La tua freccia non ha colpito nulla", ecc.) Può essere considerata un byte. Nessun abuso per nascondere il codice di gioco nel testo; questo è solo per interagire con il giocatore.

  8. Dedurre il 10% del conteggio dei byte per l'implementazione di megabat, che iniziano in una stanza casuale diversa dal giocatore (sebbene possano condividere una stanza con il wumpus e / o il pit). Se il giocatore entra nella stanza con i pipistrelli, i pipistrelli porteranno il giocatore in un'altra stanza selezionata casualmente (garantita per non essere la stanza con la fossa o il wumpus in essa), prima di volare verso la propria nuova posizione casuale. Nelle tre stanze adiacenti ai pipistrelli, possono essere sentiti cigolare, ma al giocatore non vengono fornite informazioni sulla stanza da cui proviene il suono.

  9. Dedurre il 35% del conteggio dei byte per l'implementazione di un'interfaccia grafica che mostri la mappa icosaedrica e un qualche tipo di indicazione delle informazioni che il giocatore ha finora sulla posizione della fossa, del wumpus e dei pipistrelli (se applicabile), relativamente a il giocatore. Ovviamente, se il wumpus si muove o il giocatore viene mosso dai pipistrelli, la mappa deve resettarsi di conseguenza.

  10. Vince il conteggio dei byte più basso, come regolato.

Il codice sorgente BASIC per una versione del gioco (non necessariamente conforme alle regole precedenti e, in ogni caso, completamente non registrato) può essere trovato su questo sito Web e probabilmente su altri.


Alcuni chiarimenti: 3. se il wumpus non era in quella stanza, è sorpreso e si sposta in una delle TRE stanze .. quindi se lanci una freccia e manchi, il wumpus potrebbe venire e ucciderti, giusto? E il wumpus si muoverà solo se spaventato, altrimenti rimane semplicemente messo? 6. Comprendo che l'intestazione del giocatore è determinata dalla stanza da cui proviene. Quindi, se venisse da sud, le sue opzioni sarebbero: 1.northeast 2.northwest 3.south e se venisse da nord sarebbe il contrario. Inoltre le tue regole sembrano più semplici / più golfistiche rispetto al programma di riferimento (di cui non ho ancora studiato in dettaglio). Ho ragione?
Level River St

Argh! Non riesco a trovare nessuna foto del doppio grafico di un icosaedro da nessuna parte sulla rete.
Jack M,

1
@steveverrill Sì, se lo spaventi, potrebbe venire e ucciderti. Se non lo spaventi, non si muove. Ci sono molte varianti nel gioco; molte versioni consentono alle frecce di circondarsi e ucciderti, per esempio. L'ho eliminato.
Michael Stern,

3
@JackM la mappa delle facce di un icosaedro è identica alla mappa dei vertici di un dodecaedro e quel grafico è facilmente reperibile. Prova ad esempio wolframalpha.com/input/?i=DodecahedralGraph+edgerules o l'equivalente comando Mathematica GraphData ["DodecahedralGraph", "EdgeRules"]. Ad ogni modo ottieni {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, "indietro" implica girarsi e tornare indietro nel modo in cui sei venuto. Se premi "indietro" due volte, finirai da dove hai iniziato. Non è necessario memorizzare stati di gioco precedenti.
Michael Stern,

Risposte:


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

Il punteggio si ottiene prendendo il conteggio dei byte (290), aggiungendo il numero di stringhe utilizzate per l'interazione con l'utente (6) e sottraendo la lunghezza combinata di tali stringhe (133). Gli avanzamenti di riga fanno parte delle stringhe e contribuiscono al conteggio dei byte.

Pietre miliari

  1. Attacchi su La risposta di professorfish da Bash a GolfScript. Punteggio: 269

  2. Agito suggerimenti di Peter Taylor nei commenti. Punteggio: 250

  3. Peter Taylor mio intero codice e mi ha aiutato a comprimere la tabella di ricerca. Punteggio: 202

  4. Sostituito la tabella di ricerca delle stanze adiacenti con un approccio matematico. Punteggio: 182

  5. Input, output refactored e funzione a supporto dell'approccio matematico. Punteggio: 163

Un grande "Grazie!" Va a Peter Taylor per tutto il suo aiuto.

Come funziona

Le 20 stanze sono rappresentate come vertici di un dodecaedro, a cui sono stati assegnati numeri da 0 a 19 nel modo seguente:

Grafico dodecaedrico

Per trovare le stanze adiacenti alla stanza N e ordinarle in senso orario, dobbiamo considerare quattro casi:

  • Se N ≡ 0 mod 4 (vertici blu), la stanza adiacente è 19 - N , N + 2 mod 20 e N - 2 mod 20 .

  • Se N ≡ 1 mod 4 (vertici verdi), la stanza adiacente è 19 - N , N - 4 mod 20 e N + 4 mod 20 .

  • Se N ≡ 2 mod 4 (vertici gialli), la stanza adiacente è 19 - N , N - 2 mod 20 e N + 2 mod 20 .

  • Se N ≡ 3 mod 4 (vertici rossi), la stanza adiacente è, 19 - N , N + 4 mod 20 e N - 4 mod 20 .

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

:n;:`

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

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

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

{19:|rand}2*0|

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

{

  [

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

    ,4%       # Push the room number modulus 4.

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

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

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

 }:^

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

  ;           # STACK: Strings Pit Wumpus Previous Current

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

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

  # Push two more messages and their respective triggers.

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

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

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

  {$?-1>*p}2*

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

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

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

  ={          # If the player shoots an arrow:

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

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

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

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

    if

  }{           # If the player moves:

    =@;        # Place him into the selected room.

  }if

  # STACK: Pit Wumpus Previous Current Invalid?

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

  .[|4$6$]?

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

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

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

}do      

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

])=

# GolfScript will execute “print n print”.

1
Puoi salvare 1 in Qcon 19rand 97+; 2 in @con 97%3*&>..., un ulteriore 1 in linea Qcome {19rand 97+}2*:,\:H, alcuni in sostituzione |con *, che è spesso il modo migliore per fare un if. Bnon serve a nulla, e penso che alcune variabili in più possano essere eliminate usando lo stack.
Peter Taylor,

1
Hai dimenticato di menzionare un altro trucco frequente: la conversione di base per le tabelle di ricerca. È possibile sostituire i 62 caratteri per l'elenco di adiacenza con una stringa di 33 caratteri seguita da 256base 20base(e probabilmente eliminare anche alcuni +/- 97). L'unico aspetto negativo è che richiederà caratteri non stampabili.
Peter Taylor,

1
Ho risparmiato altri 13 refactoring per essere GS più idiomatico (usando principalmente lo stack anziché le variabili); e c'è un ulteriore 10 al costo di rendere l'output meno bello. A parte la compressione della tabella di ricerca menzionata nel mio commento precedente.
Peter Taylor,

1
Niente affatto, mi è piaciuto. Sono solo deluso dal fatto che l'approccio della tabella di ricerca sia molto migliore di quello matematico che intendevo usare. A proposito, penso che la tua versione attuale abbia un piccolo bug, perché se spari una freccia, perdi e fai sussultare il wumpus nella tua stanza, allora emette solo You were killed by the wumpussenza menzionare la freccia mancante. Ecco perché stavo aggiungendo la versione non carina.
Peter Taylor,

1
2*2+=>)2*
Peter Taylor,

15

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.

inserisci qui la descrizione dell'immagine

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 includes nel programma rev 0 e consente il inttipo 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 putssostituita con una macro con printfe un codice aggiuntivo che deve essere eseguito quando il messaggio visualizzato viene inserito all'interno degli printfargomenti (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 whileloop. ifsostituito 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 printfe calcolare la posizione di Wumpus in un'unica espressione, ma io ne ho scelto uno mio. printfrestituisce il numero di caratteri stampati, che per Your arrow didn't hit anything\nil 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 acontenente le informazioni sulla posizione wumpus viene ripristinata.)

inserisci qui la descrizione dell'immagine

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 LTinvece TLquelli indicati nella tabella sopra.

inserisci qui la descrizione dell'immagine

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 intin 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 .)


1
Adoro il writeup.
Michael Stern,

Non sono sicuro che sia a causa delle modifiche che ho dovuto apportare per compilarlo con GCC su Linux (rimuovo il primo include, lo sostituisco scanf_scon scanfe include stdio.hse compilo come C ++ più di C), ma non funziona abbastanza per me. Ad esempio, se vado a sinistra, poi di nuovo a destra all'inizio ( 15 35), mi trovo in una stanza diversa da quella in cui ho iniziato.
Dennis

@Dennis Ho rintracciato l'origine dell'errore all'uscita. è lo scanf_s (apparentemente sicuro!) che "corrompe lo stack attorno alla variabile i" quando tenta di mettere in un carattere quello che presumo sia un numero intero a 32 bit. Quindi la prima cosa che suggerirei è controllare il tipo che scanf usa per un "% d" e cambiare la variabile i in quel tipo. Ottengo la risposta giusta senza errore di uscita per char, la risposta giusta senza errore di uscita per int e la risposta errata con il tipo Microsoft __int64 (equivalente long long, a meno che non inserisca "% lld".) Inoltre hai eseguito la versione ungolfed e hai avuto problemi con il display?
Level River St

@steveverrill: Sì, avevo provato entrambe le versioni. Il tipo di iè davvero il problema. La pagina man dice: " d Corrisponde a un intero decimale con segno facoltativo; il puntatore successivo deve essere un puntatore a int ." Cambiare il tipo lo fa funzionare bene.
Dennis,

@steveverrill: Non so come VS gestisca le cose, ma se si compila con GCC (come C, non C ++), è possibile salvare molti caratteri. Nessuna delle inclusioni è necessaria se si sostituisce NULLcon 0e scanf_scon scanf, non è necessario intprima maine si può spostare ie dal di fuori di main (sono predefiniti inte inizializzati 0). Inoltre, è possibile definire p=19,h=rand()%p,w=rand()%p, sostituire m[]con *me dovrebbe essere possibile definire una macro per tutte le istanze di if(...==...)puts(...);.
Dennis,

9

GolfScript, 269 caratteri

{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

Si noti che 163 è stato sottratto dal conteggio dei caratteri per le stringhe codificate. Se si desidera un output di debug che indichi i numeri delle stanze, aggiungere la seguente riga subito dopo la prima occorrenza di ^:

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

Una sessione di esempio (con output di debug aggiuntivo):

  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

Ecco il primo codice funzionante. Torneremo più tardi per qualche altro golf.
Howard,

Il mio codice è attualmente più lungo di 1 carattere. Sto cercando qualsiasi modo possibile per giocare a golf!
Timtech,

Non che hai bisogno del mio aiuto, ma puoi salvare 14 caratteri definendo {puts}:|;, 5 caratteri sostituendo Re Wcon -e >(consente di eliminare gli spazi circostanti) e 9 caratteri cadendo '> 'print(non sembra essere richiesto dalla domanda).
Dennis,

@Dennis Grazie. Sicuramente implementerò alcuni dei tuoi suggerimenti.
Howard

9

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

Golf quasi finito questo ...

Include una GUI con una mappa icosaedrica e mega pipistrelli per i bonus completi.

Wumpus GUI

  • Ogni stanza ha 4 pulsanti: X(la fossa); B(il Mega-Bat); W(il Wumpus); e P(tu).
  • La posizione corrente è di colore blu.
  • I pulsanti sono di colore rosso se l'oggetto che rappresenta potrebbe trovarsi in quella posizione e verde se sicuramente non si trova in quella posizione.
  • È possibile fare clic sui pulsanti We Psolo nelle stanze adiacenti alla posizione corrente.
  • Se vinci lo sfondo diventa verde e se muori lo sfondo diventa rosso.

Codice:

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)

Ottieni 1066 senza ECMA 6 usando il compilatore di chiusura.
AMK,

Mi chiedevo quanto sarebbe stato più semplice avere una rappresentazione grafica per dedurre dove sono le cose. 1+ ma è leggermente troppo facile :)
Sylwester

9

Bash, 365 (prima versione funzionante 726!)

CATTURARE CON GOLFSCRIPT?

@Dennis ha praticamente fatto tutto il golf per me. Grazie!

Il programma presuppone un input valido. L'immissione valida è la direzione scelta (1 per destra, 2 per sinistra, 3 per retro) seguita dalla tua azione (4 per sparare, 5 per camminare).

Qualche spiegazione

Normalmente faccio grandi spiegazioni dettagliate, ma questo è probabilmente un po 'troppo complicato per me essere disturbato.

Ogni vertice sul grafico del dodecaedro è codificato come una lettera (a = 1, b = 2, ... t = 20).

La posizione di partenza del giocatore è sempre 20 (e sono in piedi con le spalle a 18), dal momento che non ha importanza in sé, ma solo le posizioni relative del giocatore, del pit e del wumpus.

La variabile $pmemorizza la posizione del giocatore. $rmemorizza la posizione precedente del giocatore. $wè il wumpus e $h(H per il buco) è il pozzo.

Codice

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

Cronologia delle versioni

  1. Versione iniziale, 698 caratteri
  2. Corretto errore in cui "You feel a breeze" e "Youodore the wumpus" non possono essere visualizzati contemporaneamente; salvato 39 caratteri rendendo la generazione di numeri casuali una funzione.
  3. Ricorda che il wumpus si muove se spari e manchi. 726 caratteri.
  4. Fatto grep -oEuna variabile. Hai salvato 5 caratteri.
  5. Fatto [a-z]{3}una variabile. Hai salvato 3 caratteri.
  6. Fatto echouna variabile. Hai salvato 5 caratteri.
  7. Ha agito sulla maggior parte dei suggerimenti di @Dennis. 72 caratteri salvati.
  8. Aggiunti tutti i suggerimenti rimanenti. Salvato 68 caratteri.
  9. Hai salvato 2 caratteri dal suggerimento di @DigitalTrauma.
  10. Risolto un bug importante in cui si poteva sparare al wumpus solo se era sulla destra. Stesso numero di personaggi.
  11. Espansione dei parametri usata per radere via 2 caratteri usando $m.
  12. Rasato un sacco di caratteri abbandonando greped essendo leggermente più sensibile.
  13. Definito Ccome una funzione di ricerca regexp da utilizzare in istruzioni if ​​e Ecome una funzione che stampa "Hai ucciso il wumpus" ed esce.
  14. Salvato 1 carattere con riorganizzazione "if statement".
  15. Hai salvato molti caratteri eliminandoli d e rimosso le parentesi non necessarie.
  16. Bug corretti. Aggiunti molti caratteri :(
  17. RISPARMIO MOARR ( http://xkcd.com/1296/ )
  18. Un'altra delle idee di @Dennis (salvataggio di alcuni caratteri) e il mio furbo (ab) uso dell'irregolarità (salvataggio di 1 carattere).
  19. Correzione di stile per q ().
  20. aggiunto nuovamente l'output corretto

Esecuzione del campione

"In:" è inserito, "Out: è emesso".

Il giocatore si aggira per un po ', sente l'odore del wumpus e spara. Mancano, e i wumpus entrano nella loro stanza e li mangiano.

In: 15

In: 15

In: 25

In: 25

In: 15

Fuori: senti l'odore del wumpus

In: 14

Fuori: ti sei perso

Fuori: il wumpus ti ha mangiato


1
Penso che puoi ridurre il tuo codice di almeno 100 byte. 1. exitè solo un byte più lungo di g=1ed elimina la necessità di testare istruzioni gdiverse da zero e alcune elif. 2. È possibile utilizzare al ((i==35))posto di [ $i = 35 ]e ...&&...anziché if ... then ... fi. 3. q(){ L=({a..s});$j ${L[RANDOM%19]};}ed n=`$k $w$m<<<$d`;w=${n:RANDOM%2+1:1}entrambi salvano pochi byte.
Dennis,

1
Sostituisci while :;do... donecon for((;;);{... }per un risparmio di 3 caratteri
Digital Trauma

1
@professorfish: Penso che una funzione funzionerebbe meglio dell'attuale approccio string-grep-cut. Ad esempio, d(){ x=npoemfgnshtoksblbtckpdpljqniorelgfhkbqraicadjaghimsmjtqecrdf;s=${x:3*30#$1-30:3};}ti permetterà di sostituire le definizioni di se ncon d $pe d $w. Se, inoltre, definisce u=${s#*$r}$s(e di regolare le definizioni di le fdi conseguenza), non sarà necessario $ke $mpiù. Salva 83 byte, credo. Inoltre, lo spazio in q ()non è richiesto.
Dennis,

1
@professorfish: E puoi salvare 3 byte aggiuntivi definendo c(){ [[ $1 =~ $2 ]];}e sostituendo, ad esempio, la penultima riga con c $r $b||{ $j You missed;d $w;w=${s:RANDOM%2+1:1};}.
Dennis,

1
@professorfish: utilizzando la funzione che ho suggerito dovrebbe essere più breve di 3 byte. Puoi salvare 106 byte aggiuntivi sostituendo le quattro righe dopo b=$pcon d $p;u=u${s#*$r}$sle righe dopo 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};}e eliminandole 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 raggiunto la versione della tabella di ricerca di Dennis, dalla quale prende in prestito un bel po '. La cosa interessante di questa versione è che non ha una tabella di ricerca per il layout della stanza.

Le 60 simmetrie di rotazione di un icosaedro sono isomorfe al gruppo alternato su 5 lettere, A_5. Dopo aver provato tutti i tipi di approcci per rappresentare il gruppo in modo compatto, sono tornato al più semplice: ogni elemento è una permutazione della parità pari. Il gruppo può essere generato da due generatori in più di un modo: l'approccio che sto adottando utilizza i generatori 3e 3 1. Questi ci permettono di generare 1 = 3 3 1, 2 = 3 3 1 3 1e 3 = 3.

Osserva che la direzione 3corrisponde a un elemento dell'ordine 2, perché dopo aver attraversato la porta dietro di te, quella porta è di nuovo dietro di te. Direzione1 corrisponde a un elemento dell'ordine 5, camminando attorno a un vertice dell'icosaedro. (Allo stesso modo elemento 2). E la combinazione 3 1è di ordine 3, poiché ruota attorno alle stanze adiacenti a quella che inizia dietro di te.

Quindi stiamo cercando una permutazione dell'ordine 2 per rappresentare la direzione 3 e una permutazione dell'ordine 5 per rappresentare la direzione 1tale che 3 1è dell'ordine 3.

Ci sono 15 permutazioni dell'ordine 2 in A_5 e per ognuna ci sono 8 permutazioni candidate per 1(e quindi per 3 1). C'è un'ovvia attrazione [4 3 2 1 0]per 3: invertire un array è giusto -1%. Delle sue possibili permutazioni di accompagnamento3 1 che ho scelto [0 1 3 4 2], che ammette un'implementazione abbastanza breve come [~@].

Ungolfed

# Generate the 60 permutations by repeated application of `3 1` and `3`
[5:C,]{{.{[~@]}:>~.{-1%}:<~}%.&}8*
# Remove [0 1 2 3 4] and its equivalence class (apply 3 (3 1)^n 3 for n in 0,1,2)
({[.<><.<><]}:F~-
# Shuffle the remaining 57 options to select random starting points for wumpus and pit
# Note that this introduces a slight bias against them being in the same room,
# but it's still possible.
{99rand}$~
# Start player at [0 1 2 3 4]
5,
{
    # Stack: Pit Wumpus Player
    .<
    # The adjacent rooms to the player are Player<> , Player<>> , and Player<>>>
    # If the wumpus is in one of those rooms, say so.
    {
        >."You smell a wumpus.\n"4
        {
            # ... X str off
            $F@?~!!*
            # ... str off$F X ?~!! *
            # Which means that we leave either str (if off$ and X are equivalent)
            # or the empty string on the stack
        }:Q~
        {print}:,~
    }3*
    # Ditto for the pit
    {>."You feel a breeze.\n"5Q,}3*
    # Read one line from STDIN.
    '"#{'C):C';STDIN.gets()}"'++~~
    # Stack: Pit Wumpus Player Player< Input
    # Find the room corresponding to the specified direction.
    :&9/{>}*&
    # Stack: Pit Wumpus Player TargetRoom Input
    5%{
        # Shoots:
        "You killed the wumpus."3Q
        {
            \<{>}3rand*\ # Move the wumpus to an adjacent room
            "Your arrow didn't hit anything.\n", # Inform
            0 # Continue
        }
        or
    }{
        # Moves:
        \;
        # If player and pit share a room, say so.
        ."You fell into the pit."4Q
    }if
    # If player and wumpus share a room, say so.
    # NB If the player walks into a room with the pit and the wumpus,
    # the `or` favours the pit death.
    \."You were killed by the wumpus."4Q@or
    # Save the topmost element of the stack for output if we break the loop. Loop if it's falsy.
    :n!
}do
# Ditch the junk.
];

Bel approccio algebrico! C'è un piccolo bug però: 10/@3%=tenta di accedere al quarto elemento di un array di lunghezza 3 se l'input è 35.
Dennis,

@Dennis, sì, mi sono reso conto dopo essere andato a letto. Mi vengono in mente vari modi per risolverlo, che costano tutti 2.
Peter Taylor,

Puoi riavere un carattere con 9/3%@3%=.
Dennis,

Sono attualmente 7 caratteri con una ristrutturazione più drastica. Ma quel 1 carattere 9/invece di 10/funziona ancora, quindi grazie.
Peter Taylor,

5

Wumpus , 384 - 129 (stringhe) = 255 byte

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@

Provalo online! (Naturalmente, TIO non ha molto senso, perché non è possibile utilizzare il programma in modo interattivo lì, e una volta che il programma ha esaurito le istruzioni su STDIN leggerà0 0 , il che equivale a 3 4, quindi finirai scoccare frecce finché il Wumpus non si sposta lì o ti uccide.)

Quando lo esegui localmente, assicurati che l'alimentazione di riga dopo il secondo numero di ogni input venga svuotata (poiché Wumpus ne ha bisogno per determinare che il numero è finito). In Powershell, in qualche modo ho bisogno di inserire un altro personaggio dopo l'avanzamento di riga per farlo funzionare (non importa quale personaggio, ma ho usato solo doppi avanzamenti di riga per i test).

C'è ancora molto spazio per giocare a golf, ma provare layout completamente nuovi richiede un po 'di tempo. Il punteggio finale dipende anche molto dalle stringhe effettive che utilizzo, perché in un linguaggio 2D, una stringa di N byte tende a costarti più di N byte del codice sorgente, perché pone vincoli significativi sul layout del codice e spesso è necessario dividerlo in più sezioni (incorrendo in doppie virgolette aggiuntive). All'estremo, se riducessi ogni stringa a una singola lettera (e da -129 a -12), probabilmente risparmierei una tonnellata di byte.

Spiegazione

Innanzitutto un disclaimer: nonostante il nome della lingua, non è stato progettato per rendere particolarmente facile l' implementazione di Hunt the Wumpus . Invece, per prima cosa ho disegnato il linguaggio attorno al tema dei triangoli, ho finito con una struttura di dati icosaedrici e ho deciso di chiamarlo Wumpus per questo.

Quindi sì, mentre Wumpus è principalmente basato su stack, ha anche 20 registri che sono disposti attorno alle facce di un icosaedro. Ciò significa che otteniamo una struttura di dati per rappresentare la mappa gratuitamente. L'unica cosa che non possiamo fare facilmente è trovare volti specifici sull'icosaedro, quindi per cercarli, dobbiamo "tirare il d20" fino a quando non finiamo sul volto che stiamo cercando. (È possibile farlo in modo deterministico, ma ciò richiederebbe molti più byte.) La ricerca di volti come questo termina quasi sicuramente (cioè con probabilità 1), quindi la ricerca in esecuzione per sempre non è un problema nella pratica).

Il codice sopra è una versione golfata di questa prima implementazione con un layout più igienico:

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.

Dal momento che il golf ha comportato principalmente la compressione del layout, per ora spiegherò solo questa versione (fino a quando non aggiungerò trucchi per il golf che vanno oltre la ristrutturazione del codice).

Cominciamo con il codice di installazione:

1SDL2vSD70L?.;;2.

Inizialmente, tutte le facce sono impostate su 0 . Codificheremo il wumpus impostando il 1-bit della faccia corrispondente e il pit impostando il 2-bit. In questo modo, possono entrambi trovarsi nella stessa stanza. La posizione del giocatore non verrà registrata sull'icosaedro, ma sarà sempre attiva (solo uno dei 20 registri è attivo alla volta).

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.

Ora dobbiamo trovare una faccia vuota a caso per inserire il giocatore.

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.

Questa sezione successiva controlla l'ambiente circostante il giocatore e stampa gli avvisi appropriati:

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

Questo è un ciclo che eseguiamo 3 volte. Ogni volta, guardiamo il vicino giusto, stampiamo le stringhe appropriate se c'è un pericolo e quindi ruotiamo l'icosaedro di 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 sezione successiva legge due numeri dal giocatore e quindi sposta il giocatore o lancia una freccia. Il primo è banale, il secondo meno. Il problema principale per lanciare la freccia è il caso in cui manca. In quel caso a) dobbiamo andare a cercare il wumpus per spostarlo, e quindi b) tornare nella stanza del giocatore e il corretto orientamento dell'icosaedro (in modo che "back" rimanga "back"). Questa è la parte più costosa dell'intero programma.

    }&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.

Il punto di accesso a questa sezione è Ia sinistra.

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.

Accidenti, quella era la parte difficile. Ora dobbiamo solo verificare se il giocatore muore e in caso contrario ricominciare il ciclo principale:

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

La struttura di questa sezione è essenzialmente identica alla struttura che abbiamo usato per controllare l'ambiente circostante il giocatore: controlliamo l'1 bit della faccia corrente (la stanza del giocatore) e se è impostato stampiamo The wumpus ate you.e terminiamo il programma. Altrimenti, controlliamo il 2-bit ed è impostato stampiamo You fell into the pit.e terminiamo il programma. Altrimenti raggiungiamo il punto 2.che salta all'inizio del ciclo principale (alle coordinate (0, 2)).


1

awk - grande

Questo non è risultato breve come avevo sperato, ma ho adottato un approccio leggermente diverso nel trattare il grafico, quindi sto pubblicando comunque la versione non golfata.

Ho approfittato del fatto che un icosaedro (poliedro a 20 facce) sotto rotazioni che preserva l'orientamento è isomorfo al gruppo alternato di grado 5 (permutazioni a 5 elementi con un numero pari di cicli di lunghezza pari). Scelgo quindi due permutazioni con durata del ciclo 5 come "sinistra" e "destra", e scelgo una permutazione con durata del ciclo 2 come "indietro". Usando questi, costruisco il grafico da una stanza percorrendo il percorso Hamiltoniano (2xRRRLLLRLRL, usando 3xRB in ogni stanza per catturare le 3 direzioni possibili).

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);
}
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.