Chasser le wumpus

39

Quand j'étais jeune garçon, les enfants se promenaient dans les magasins d'informatique et jouaient à Hunt the Wumpus jusqu'à ce que le personnel nous expulse. C’était un jeu simple, programmable sur les ordinateurs personnels du milieu des années 70, des machines tellement rudimentaires qu’au lieu de microprocesseurs de la taille d’un poussin, certains d’entre eux avaient probablement de vrais poussins.

Évoquons cette époque révolue en reproduisant le jeu sur du matériel moderne.

  1. Le joueur commence dans une salle au hasard sur une carte icosaédrique (il y a donc 20 salles au total, connectées les unes aux autres comme des faces d'icosaèdre, et chaque salle possède exactement trois sorties).

  2. Le wumpus commence dans une pièce différente choisie au hasard. Le wumpus pue et son odeur peut être détectée dans l’une des trois salles adjacentes à son emplacement, bien que la direction de l’odeur soit impossible à déterminer. Le jeu rapporte seulement "tu sens un wumpus."

  3. Le joueur porte un arc et un nombre infini de flèches, qu’il peut tirer à tout moment dans la pièce devant lui. Si le wumpus est dans cette pièce, il meurt et le joueur gagne. Si le wumpus n'était pas dans cette pièce, il est surpris et se déplace de manière aléatoire dans l'une des trois pièces connectées à son emplacement actuel.

  4. Une salle choisie au hasard (garantie de ne pas être la salle dans laquelle le joueur commence) contient un puits sans fond. Si le joueur se trouve dans une pièce adjacente à la fosse, il ressent une brise, mais ne sait pas de quelle porte provient la brise. S'il entre dans la pièce avec la fosse, il meurt et Wumpus gagne. Le wumpus n'est pas affecté par la fosse.

  5. Si le joueur entre dans la chambre du wumpus ou si le wumpus entre dans la chambre du joueur, le wumpus gagne.

  6. Le joueur spécifie la direction à laquelle il fait face avec un chiffre (1 = droite, 2 = gauche, 3 = arrière), puis une action (4 = tire une flèche, 5 = marche dans la direction spécifiée).

  7. Par souci de marquer, chaque séquence de jeu ("Vous sentez une brise," "Vous sentez un wumpus", "Votre flèche n'a rien touché", etc.) peut être considérée comme un octet. Pas besoin d'en abuser pour cacher le code du jeu dans le texte; c'est juste pour interagir avec le joueur.

  8. Déduisez 10% de votre nombre d'octets pour la mise en œuvre de mégabats, qui commencent dans une pièce différente du joueur (bien qu'ils puissent partager une pièce avec le wumpus et / ou le pit). Si le joueur entre dans la salle avec les chauves-souris, les chauves-souris le mèneront dans une autre pièce choisie au hasard (il lui sera garanti de ne pas être la pièce contenant la fosse ou le wumpus) avant de s'envoler vers son nouvel emplacement aléatoire. Dans les trois salles adjacentes aux chauves-souris, on peut les entendre grincer, mais le joueur ne reçoit aucune information sur la pièce d'où provient le son.

  9. Déduisez 35% de votre nombre d'octets pour implémenter une interface graphique qui montre la carte icosaédrique et une sorte d'indication des informations dont dispose le joueur jusqu'à présent sur l'emplacement de la fosse, du wumpus et des chauves-souris (le cas échéant), par rapport à le joueur. Évidemment, si le wumpus se déplace ou si le joueur est déplacé par les chauves-souris, la carte doit être réinitialisée en conséquence.

  10. Le plus petit nombre d'octets, ajusté, gagne.

Le code source BASIC pour une version du jeu (non nécessairement conforme aux règles ci-dessus et, en tout état de cause, totalement non-golfée) peut être trouvé sur ce site et probablement sur d'autres.

Michael Stern
la source
Quelques éclaircissements: 3. si le wumpus n'était pas dans cette pièce, il est surpris et se déplace dans l'une des TROIS chambres .. alors si vous tirez une flèche et ratez, le wumpus peut venir vous tuer, n'est-ce pas? Et le wumpus ne bougera que s'il est surpris, sinon il reste en place? 6. Je comprends que le cap du joueur est déterminé par la pièce d'où il vient. Donc, s'il venait du sud, ses options seraient 1. nord-ouest 2. nord-ouest 3. sud et s'il venait du nord, ce serait l'inverse. De plus, vos règles semblent plus simples / plus simples que le programme de référence (que je n’ai pas encore étudié en détail.) Ai-je raison?
Level River St
Argh! Je ne trouve aucune image du double graphe d'un icosaèdre sur le net.
Jack M
1
@steveverrill Oui, si vous l'effrayez, cela risque de vous tuer. Si vous ne l'effrayez pas, ça ne bouge pas. Il y a beaucoup de variations sur le jeu; de nombreuses versions permettent aux flèches de tourner autour de vous et de vous tuer, par exemple. Je l'ai éliminé.
Michael Stern
3
@JackM la carte des faces d'un icosaèdre est identique à la carte des sommets d'un dodécaèdre et ce graphique est facile à trouver. Essayez par exemple wolframalpha.com/input/?i=DodecahedralGraph+edgerules ou la commande équivalente Mathematica GraphData ["DodecahedralGraph", "EdgeRules"]. De toute façon, vous obtenez {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 le
2
@JackM Non, "retour" implique de faire demi-tour et de revenir dans la voie empruntée. Si vous appuyez deux fois sur «retour», vous vous retrouvez là où vous avez commencé. Pas besoin de stocker les états de jeu précédents.
Michael Stern

Réponses:

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

Le score est obtenu en prenant le nombre d'octets (290), en ajoutant le nombre de chaînes utilisées pour l'interaction avec l'utilisateur (6) et en soustrayant la longueur combinée de ces chaînes (133). Les sauts de ligne font partie des chaînes et contribuent au nombre d'octets.

Les jalons

  1. Ported profanish réponse de Bash à GolfScript. Score: 269

  2. A suivi les suggestions de Peter Taylor dans les commentaires. Score: 250

  3. Peter Taylor a restructuré tout mon code et m'a aidé à compresser la table de consultation. Score: 202

  4. Remplacement de la table de consultation des salles adjacentes par une approche mathématique. Score: 182

  5. L'entrée, la sortie et la fonction refactorisées supportant l'approche mathématique. Score: 163

Un grand merci à Peter Taylor pour toute son aide.

Comment ça marche

Les 20 salles sont représentées comme les sommets d'un dodécaèdre, et portent les numéros 0 à 19 de la manière suivante:

Graphique dodécaédrique

Pour trouver les pièces adjacentes à la pièce N et les commander dans le sens des aiguilles d'une montre, nous devons considérer quatre cas:

  • Si N ≡ 0 mod 4 (sommets bleus), la salle adjacente est 19 - N , N + 2 mod 20 et N - 2 mod 20 .

  • Si N ≡ 1 mod 4 (sommets verts), la salle adjacente est 19 - N , N - 4 mod 20 et N + 4 mod 20 .

  • Si N ≡ 2 mod 4 (sommets jaunes), la salle adjacente est 19 - N , N - 2 mod 20 et N + 2 mod 20 .

  • Si N ≡ 3 mod 4 (sommets rouges), la pièce adjacente est 19 - N , N + 4 mod 20 et N - 4 mod 20 .

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

:n;:`

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

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

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

{19:|rand}2*0|

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

{

  [

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

    ,4%       # Push the room number modulus 4.

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

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

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

 }:^

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

  ;           # STACK: Strings Pit Wumpus Previous Current

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

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

  # Push two more messages and their respective triggers.

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

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

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

  {$?-1>*p}2*

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

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

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

  ={          # If the player shoots an arrow:

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

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

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

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

    if

  }{           # If the player moves:

    =@;        # Place him into the selected room.

  }if

  # STACK: Pit Wumpus Previous Current Invalid?

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

  .[|4$6$]?

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

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

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

}do      

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

])=

# GolfScript will execute “print n print”.
Dennis
la source
1
Vous pouvez économiser 1 dans Qavec 19rand 97+; 2 @avec 97%3*&>..., un autre 1 par inline Qque {19rand 97+}2*:,\:H, quelques en remplaçant |avec *, ce qui est souvent la meilleure façon de faire une if. Bne sert à rien, et je pense que quelques autres variables pourraient être éliminées en utilisant la pile.
Peter Taylor
1
J'ai oublié de mentionner un autre truc fréquent: la conversion de base pour les tables de recherche. Vous pouvez remplacer les 62 caractères de la liste de contiguïté par une chaîne de 33 caractères suivie de 256base 20base(et probablement aussi éliminer quelques +/- 97). Le seul inconvénient est que cela nécessitera des caractères non imprimables.
Peter Taylor
1
J'en ai économisé 13 autres en refacturant pour qu'il soit plus générique que GS (en utilisant principalement la pile plutôt que des variables); et il y en a 10 supplémentaires au prix de rendre la sortie moins jolie. Cela ne tient pas compte de la compression de la table de consultation mentionnée dans mon précédent commentaire.
Peter Taylor
1
Pas du tout, j'ai bien aimé. Je suis juste déçu que l'approche de la table de consultation soit tellement meilleure que celle plus mathématique que j'avais l'intention d'utiliser. BTW, je pense que votre version actuelle a un petit bug, parce que si vous tirez une flèche, manquez, et surprenez le wumpus dans votre pièce, alors il ne restituera que la sortie You were killed by the wumpussans mention de la flèche manquante. C'est pourquoi j'ai ajouté dans la version non-jolie.
Peter Taylor
1
2*2+=>)2*
Peter Taylor
15

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

Vous trouverez ci-dessous un scénario de jeu montrant que si vous jouez correctement, vous pouvez toujours gagner (à condition que vous ne commenciez pas juste à côté d'un obstacle). Le joueur ressent une brise, fait demi-tour et fait une boucle complète dans le sens antihoraire. Comme il lui faut exactement 5 coups pour sentir à nouveau une brise, il connaît le trou à sa droite et s’éloigne le plus possible. De même, quand il sent le wumpus, ne sachant pas si c'est à droite ou à gauche, il se retourne et fait une boucle dans le sens des aiguilles d'une montre. Cela lui prend 5 coups pour sentir à nouveau le wumpus, alors il sait que c'est à gauche et tire avec certitude.

S'il avait bouclé dans l'autre sens, il aurait retrouvé le wumpus plus tôt et aurait su que c'était dans la même direction qu'il se retournait.

entrez la description de l'image ici

REV1 C (GCC sur Cygwin), bonus 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!",)}}

Nouvelles lignes ajoutées pour plus de clarté. Les modifications par rapport à la révision 0 sont les suivantes:

Un grand merci à @Dennis pour avoir recommandé le compilateur GCC sur l'émulateur Cygwin Linux pour Windows. Ce compilateur ne requiert pas le includes dans le programme rev 0, et il autorise le inttype par défaut pour les variables et main.c’est un conseil de golf qui change la vie!

De plus, tourner sous Linux signifie que \fle curseur descendra sans faire de retour chariot (contrairement à Windows où il ne produit qu'un symbole imprimable.) Cela a permis de raccourcir considérablement l'instruction printf imprimant le tableau.

Plusieurs conseils supplémentaires de Dennis dans les commentaires, et un des miens: changement de condition lors de la vérification si la flèche touche le wumpus: if(q==w)> if(q-w)(..else .. est inversé)

Ajout d'un affichage graphique montrant les informations que le joueur sait sur l'endroit où un wumpus est senti / une brise semble réclamer le bonus de 35%. (J'ai supprimé l'ancienne version de débogage de celle-ci, qui indiquait la position exacte du wumpus et du trou. Elle peut être vue dans l'historique d'édition.)

REV2 C (GCC sur Cygwin), bonus 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");}

Merci encore à Dennis pour refactoriser mon code:

Constante de caractère m[]remplacée par des littéraux (je ne savais pas que vous pouviez indexer un littéral.)

Amorçage de nombres aléatoires avec une variable de pile (dépendant du système, certains systèmes randomisent l'allocation de mémoire comme mesure de sécurité)

Macro à putsremplacé par une macro avec printfun code supplémentaire à exécuter lorsque le message affiché est placé à l'intérieur d' printfarguments (avantage de la face que printf n'imprime pas les derniers arguments s'il n'y a pas suffisamment de spécificateurs de format dans la chaîne de format.) ifremplacé par||

Calcul de la nouvelle position du joueur / wumpus placé dans la nouvelle macro.

Messages gagnants / perdus placés en dehors de la whileboucle. ifremplacé par l'opérateur conditionnel.

Utilisation de l'opérateur conditionnel en ligne pour la flèche de tir. Si le joueur manque, cela nécessite à la fois d’imprimer un message et d’ajuster la position de Wumpus. Dennis a proposé plusieurs façons de combiner printfet de calculer la position de wumpus en une seule expression, mais je suis parti avec l'une des miennes. printfrenvoie le nombre de caractères imprimés, qui , pour Your arrow didn't hit anything\nse 31 (11111 binaire). Donc, 31&Q(w)==Q(w).

Mon autre contribution à cette vérification a été l’élimination de certains crochets inutiles.

Sortie

Ici, le joueur a déjà trouvé où se trouve le Wumpus, mais choisit de faire une exploration approfondie pour savoir exactement où se trouve la fosse. Contrairement à mon ancienne version de débogage qui indiquait où se trouvaient le wumpus et la fosse tout au long du jeu, elle ne montre que les pièces où le joueur s’est rendu et a senti une brise (1) sentait le wumpus (2) ou les deux (3). (Si le joueur tire une flèche et manque, la variable acontenant les informations de position de wumpus est réinitialisée.)

entrez la description de l'image ici

REPRÉSENTATION ICOSAHEDRON

Remarque: cette section est basée sur la rév. 1

Mon film vedette! Il n'y a pas de graphique dans mon code. Pour expliquer son fonctionnement, voir la carte du monde ci-dessous. Tout point de l'icosaèdre peut être représenté par une latitude comprise entre 0 et 3 et une longitude comprise entre 0 et 4 (ou par un seul chiffre long*4+lat,.) le centre des faces avec une latitude zéro.

Le joueur peut être orienté sur 3 axes possibles, représentés par les symboles suivants: nord-sud -nord-sud-sud-ouest \nord-ouest-sud-est /. Dans n'importe quelle pièce, il dispose exactement d'une sortie sur chacun de ces axes. Sur l’affichage indiqué, le lecteur effectue une boucle complète dans le sens des aiguilles d’une montre. Il est généralement facile d'identifier le joueur qui marque d'où il vient et donc où il est autorisé à aller.

Le quatrième cas est un peu difficile pour l'œil non initié. Lorsque vous voyez une inclinaison dans l'une de ces rangées polaires, le joueur vient de la cellule polaire la plus proche de l'extrémité extérieure de l'inclinaison et fait généralement face à l'équateur. Ainsi, le joueur fait face au sud-est et ses options sont: 15 (SUD, la cellule à droite) 25 (nord-est, la cellule ci-dessus) ou 35 (nord-ouest, la cellule ci-dessous.)

Donc, fondamentalement, je mappe l'icosaèdre sur une grille 5x4, avec les cellules numérotées de 19 à 0 dans l'ordre dans lequel elles sont imprimées. Le déplacement est effectué en ajoutant ou en soustrayant de la position actuelle, en fonction de la latitude et de la direction du joueur, conformément au tableau ci-dessous.

Si le joueur quitte le bord inférieur (ouest) du plateau, il revient sur le côté supérieur (est) et inversement, sa position est donc prise modulo 20. Généralement, les mouvements sont codés dans m [] en ajoutant ascii 80 ( P) à la valeur brute donnant les caractères montrés ci-dessous, mais en principe, tout multiple de 20 peut être ajouté sans affecter le fonctionnement.

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'entrée du joueur (divisée par 10 pour supprimer le deuxième chiffre) est ajoutée à sa direction actuelle et prise en modulo 3 pour obtenir sa nouvelle direction. Cela fonctionne bien dans la majorité des cas. Cependant, il y a un problème lorsqu'il se trouve dans une pièce polaire et se dirige vers le pôle. En pliant la carte ci-dessous, il est clair que s’il quitte la pièce face au "nord-est", il entrera dans la nouvelle place faisant face au "sud-est"; une correction doit donc être effectuée. Cela se fait dans la ligne e=(d+i/10)*m[p%4]%3;par la multiplication par m[p%4]. Les quatre premières valeurs de m [] sont sélectionnées de telle sorte que, en plus de leur fonction ci-dessus, elles possèdent également la caractéristique m[1]%3==m[2]%3==1et m[0]%3==m[3]%3==2. Cela laisse la direction seule pour les salles équatoriales et applique la correction nécessaire pour les salles polaires.

Le moment logique pour faire la correction serait après le déménagement. Cependant, pour sauvegarder les personnages, cela est fait avant le déplacement. Par conséquent, certaines valeurs dans m [] doivent être transposées. Ainsi, les 2 derniers caractères sont LTau lieu de TLpar la table ci-dessus par exemple.

entrez la description de l'image ici

CODE UNGOLFED

c'est le code rev 1, qui est moins obscurci que le rev 2.

Cela fonctionnera sous GCC / Linux. J'ai inclus dans les commentaires le code supplémentaire nécessaire pour le faire fonctionner sous Visual studio / Windows. C'est une grande différence!

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

}

QUESTIONS ET CURIOISITES

J'ai profité du point mentionné par @professorfish, si le wumpus et la fosse commencent à des endroits aléatoires, il n’est pas nécessaire que le joueur démarre dans un endroit aléatoire. Le joueur commence toujours dans la salle 19 face au nord.

Je comprends que, le wumpus n'étant "pas affecté par la fosse", il peut commencer ou entrer dans la pièce où se trouve la fosse. En général, cela simplifie les choses sauf pour un point. Je n'ai pas de variable spécifique pour indiquer que le jeu est terminé; c'est fini quand le joueur coïncide avec le wumpus ou la fosse. Ainsi, lorsque le joueur gagne, j'affiche le message gagnant mais déplace le stand au joueur pour qu'il sorte de la boucle! Je ne peux pas mettre le joueur dans la fosse car le wumpus est peut-être là et je voudrais recevoir un message sur le wumpus que je ne veux pas.

Le rev0program fonctionnait parfaitement dans Visual Studio, mais l’EDI disait "pile corrompue autour de la variable i" à la sortie. En effet , scanf tente de mettre inten char.Dennis ont signalé un comportement incorrect sur sa machine Linux à cause de cela. Quoi qu’il en soit, il est corrigé par l’utilisation du type correct dans rev 1.

La ligne d'affichage du tableau dans rev 0 est maladroite et semble légèrement différente sur d'autres plates-formes. Au printf(" %c%c%c")milieu,% c est le caractère imprimable affiché. Le dernier% c est ASCII 0 ou ASCII 10 (\ n, nouvelle ligne avec retour chariot dans Windows.) Il semble qu’aucun caractère de Windows fonctionnant dans la console ne passe sur une ligne sans donner de retour chariot. Si c'était le cas, je n'aurais pas besoin du premier c% (onglet ASCII 0 ou ASCII 9 avant le caractère de latitude 1. Les onglets sont notoirement non définis dans leur comportement.) .) La rév. 1 a une révision de cette ligne qui utilise un caractère de saut de page et ne nécessite par conséquent aucun caractère de format au début de printf. Cela le rend plus court, mais le \ f ne fonctionne pas sous Windows.

Level River St
la source
1
J'aime l'écriture.
Michael Stern
Je ne sais pas si c'est à cause des modifications que j'ai dû faire pour le compiler avec GCC sous Linux (supprimer le premier include, le remplacer scanf_spar scanfet l'inclure stdio.hsi je compile en tant qu'évaluateur C ++ plutôt que C), mais cela ne fonctionne pas très bien pour moi. Par exemple, si je vais à gauche, puis à droite au début ( 15 35), je suis dans une pièce différente de celle où j'ai commencé.
Dennis
@ Dennis J'ai retrouvé la source de l'erreur à la sortie. c'est le scanf_s (censé être sûr!) qui "corrompt la pile autour de la variable i" quand il essaie de mettre ce que je suppose est un entier de 32 bits dans un caractère. Donc, la première chose que je suggère est de vérifier le type utilisé par scanf pour un "% d" et de changer la variable i en ce type. Je reçois la bonne réponse avec erreur de sortie pour char, la bonne réponse sans erreur de sortie pour int et la mauvaise réponse avec le type Microsoft __int64 (équivalent long, à moins que je mette "% lld".) Aussi, exécutez-vous également la version non-golfée et avez-vous eu des problèmes avec l'affichage?
Level River St
@steveverrill: Oui, j'avais essayé les deux versions. Le type de iest en effet le problème. La page de manuel indique: " d Correspond à un entier décimal éventuellement signé; le pointeur suivant doit être un pointeur sur int ." Changer le type le fait fonctionner correctement.
Dennis
@steveverrill: Je ne sais pas comment VS gère les choses, mais si vous compilez avec GCC (en C, pas C ++), vous pouvez économiser beaucoup de caractères. Aucune des inclusions n'est nécessaire si vous remplacez NULLpar 0et scanf_savec scanf, vous n'avez pas besoin intavant mainet vous pouvez vous déplacer iet dsortir de main (elles intsont initialisées par défaut 0). En outre, vous pouvez définir p=19,h=rand()%p,w=rand()%p, remplacer m[]par *met il devrait être possible de définir une macro pour toutes les instances de if(...==...)puts(...);.
Dennis
9

GolfScript, 269 caractères

{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

Notez que 163 a été soustrait du nombre de caractères pour les chaînes codées en dur. Si vous voulez que la sortie de débogage indique les numéros de salle, ajoutez la ligne suivante juste après la première occurrence de ^:

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

Un exemple de session (avec une sortie de débogage supplémentaire):

  YOU 6->11
  DIRECTIONS [BRL] [6 7 16]
  PIT 7
  WUMPUS 5
You feel a breeze
25
  YOU 11->16
  DIRECTIONS [BRL] [11 17 15]
  PIT 7
  WUMPUS 5
35
  YOU 16->11
  DIRECTIONS [BRL] [16 6 7]
  PIT 7
  WUMPUS 5
You feel a breeze
15
  YOU 11->6
  DIRECTIONS [BRL] [11 10 1]
  PIT 7
  WUMPUS 5
15
  YOU 6->10
  DIRECTIONS [BRL] [6 15 5]
  PIT 7
  WUMPUS 5
You smell a wumpus
14
Your arrow didn't hit anything
  YOU 6->10
  DIRECTIONS [BRL] [6 15 5]
  PIT 7
  WUMPUS 0
25
  YOU 10->5
  DIRECTIONS [BRL] [10 14 0]
  PIT 7
  WUMPUS 0
You smell a wumpus
24
Your arrow hit the wumpus
Howard
la source
Voici le premier code de travail. Revenir plus tard pour un peu plus de golf.
Howard
Mon code est actuellement 1 caractère plus long. Je cherche tout moyen possible de jouer au golf plus loin!
Timtech
Non pas que vous ayez besoin de mon aide, mais vous pouvez économiser 14 caractères en définissant {puts}:|;, 5 caractères en remplaçant Ret Wavec -et >(permet d'éliminer les espaces environnants) et 9 caractères en sautant '> 'print(ne semble pas être requis par la question).
Dennis
@ Dennis Merci. Je vais sûrement mettre en œuvre certaines de vos suggestions.
Howard
9

JavaScript (ECMAScript 6) - 2197 1759 -45% = 967.45 Caractères

Presque fini de jouer au golf cette ...

Inclut une interface graphique avec une carte icosaededral et des méga-chauves-souris pour tous les bonus.

Wumpus GUI

  • Chaque pièce a 4 boutons: X(le Pit); B(le méga-bat); W(le Wumpus); et P(vous).
  • Votre position actuelle est colorée en bleu.
  • Les boutons sont colorés en rouge si l’objet qu’il représente pourrait se trouver à cet emplacement et en vert s’ils ne le sont certainement pas.
  • Les boutons Wet Pne peuvent être cliqués que dans les salles adjacentes à votre position actuelle.
  • Si vous gagnez, l'arrière-plan devient vert et si vous mourez, l'arrière-plan devient rouge.

Code:

P=x=>parseInt(x,36);Q=(y,a=4)=>[P(x)<<a for(x of y)];e=Q("def45c6di7ej1ai1bj2af3bf9dg8eh46b57a1gh0280390678ci9cj24g35h",0);X=Q("o6fl6afnik27bloscfaf");Y=Q("icp8i8t4jej4encjjan6");A='appendChild';C='createElement';W='width';H='height';G='background-color';L='disabled';I='innerHTML';N='className';D=document;R=Math.random;B=D.body;E=[];F=1<0;T=!F;Z="XBWP";s=D[C]('style');s.type='text/css';t='.A{position:absolute;left:25px;top:25px}.D{'+W+':50px;'+H+':50px}.D button{'+W+':25px;'+H+':25px;float:left}.R{'+G+':red}.G{'+G+':green}.B{'+G+':blue}';for(i in X)t+='#D'+i+'{left:'+X[i]+'px;top:'+Y[i]+'px}';s[A](D.createTextNode(t));D.head[A](s);c=D[C]('canvas');c[N]='A';c[W]=c[H]=500;B[A](c);x=c.getContext('2d');x.beginPath();d=(i,b,v)=>{for(j=0;j<3;j++){E[e[3*i+j]][b][L]=v}};a=(i,l,v)=>{t=F;for(j=0;j<3;j++)t=e[3*i+j]==l?T:t;if(t)M[v]++;b=E[i][v];b.c=-1;for(j=0;j<3;j++)E[e[3*i+j]][v].c+=t?1:-1;for(j of E)j[v][N]=j[v].c==M[v]?'R':'G';};M=[0,0,0];S=v=>{M[v]=0;for(i of E){i[v][N]='';i[v].c=0}};for(i in X){for(j=3*i;j<3*i+3;j++)x.moveTo(X[i],Y[i])|x.lineTo(X[e[j]],Y[e[j]]);B[A](v=D[C]('div'));v[N]='A D';v.id='D'+i;E[i]=[];for(j in Z){b=E[i][j]=v[A](D[C]('button'));b[L]=T;b.i=i;b.c=0;b[I]=Z[j];}E[i][4][O='onclick']=function(){d(P,2,T);d(P,3,T);if(this.i==W)c[N]+=' G';else{S(2);W=e[3*W+R()*3|0];if(W==P)c[N]+=' R';else{a(P,W,2);d(P,2,F);d(P,3,F)}}};E[i][3][O]=function(){d(P,2,T);d(P,3,T);E[P][3][N]='';P=this.i;if(W==P||Q==P){c[N]+=' R';return}else if(Z==P){j=P;do{P=R()*20|0}while(P==W||P==Q||P==j);do{Z=R()*20|0}while(Z==j||Z==P);S(1)}d(P,2,F);d(P,3,F);E[P][3][N]='B';a(P,Q,0);a(P,Z,1);a(P,W,2)}}x.stroke();P=R()*20|0;do{W=R()*20|0}while(W==P);do{Q=R()*20|0}while(Q==P);do{Z=R()*20|0}while(Z==P);E[P][3][N]='B';a(P,Q,0);a(P,Z,1);a(P,W,2);d(P,2,F);d(P,3,F)
MT0
la source
Vous obtenez 1066 sans ECMA 6 en utilisant le compilateur de fermeture.
AMK
Je me demandais comme il serait beaucoup plus facile de disposer d’une représentation graphique pour aider à déduire où se trouvent les choses. 1+ mais c'est un peu trop facile :)
Sylwester
9

Bash, 365 (première version de travail 726!)

ACCÉDER AU GOLFSCRIPT?

@ Dennis a essentiellement fait tout le golf pour moi. Merci!

Le programme suppose une entrée valide. Une entrée valide correspond à la direction que vous choisissez (1 à droite, 2 à gauche, 3 à l'arrière) suivie de votre action (4 à tirer, 5 à marcher).

Quelques explications

Je fais normalement de grosses explications verbeuses, mais c'est probablement un peu trop compliqué pour que je sois dérangé.

Chaque sommet du graphe du dodécaèdre est codé sous forme de lettre (a = 1, b = 2, ... t = 20).

La position de départ du joueur est toujours 20 (et ils se tiennent dos à 18), puisque cela n'a pas d'importance en soi, seules les positions relatives du joueur, des stands et des wumpus importent.

La variable $pstocke l'emplacement du joueur. $rstocke l'emplacement précédent du joueur. $west le wumpus et $h(H pour trou) est la fosse.

Code

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

Historique de la version

  1. Première version, 698 caractères
  2. Correction d'un bug où "Vous sentez une brise" et "Vous sentez le wumpus" ne peuvent pas s'afficher en même temps; enregistré 39 caractères en faisant de la génération de nombres aléatoires une fonction.
  3. Rappelez-vous que le wumpus se déplace si vous tirez et ratez. 726 caractères.
  4. Fait grep -oEune variable. Sauvegardé 5 caractères.
  5. Fait [a-z]{3}une variable. Sauvegardé 3 caractères.
  6. Fait echoune variable. Sauvegardé 5 caractères.
  7. Agi sur la plupart des suggestions de @Dennis. Sauvé 72 caractères.
  8. Ajout de toutes les suggestions restantes. Sauvé 68 caractères.
  9. Enregistré 2 caractères de la suggestion de @DigitalTrauma.
  10. Correction d'un bug majeur qui empêchait de tirer sur le wumpus s'il se trouvait à droite. Même nombre de caractères.
  11. Expansion de paramètre utilisée pour éliminer 2 caractères à l’aide de $m.
  12. Rasé beaucoup de personnages en abandonnant grepet en étant un peu plus sensible.
  13. Défini Ccomme une fonction de recherche d'expression rationnelle à utiliser dans les instructions if, et Ecomme une fonction affichant "Vous avez tué le wumpus" et sortant.
  14. Enregistré 1 caractère par réarrangement "si déclaration".
  15. Vous avez sauvé beaucoup de caractères en vous en débarrassant det en supprimant les crochets inutiles.
  16. Correction de bugs. Ajout de nombreux caractères :(
  17. MOARR SAVINGS ( http://xkcd.com/1296/ )
  18. Une autre des idées de @Dennis (économiser quelques caractères), et mon utilisation rusée (ind) de l’indirection (enregistrer un caractère).
  19. Correction de style pour q ().
  20. ré-ajouté la sortie appropriée

Échantillon échantillon

"In:" est entré, "Out: est sorti".

Le joueur erre un peu, sent le wumpus et tire. Ils manquent, et le wumpus entre dans leur chambre et les mange.

Dans: 15

Dans: 15

Dans: 25

Dans: 25

Dans: 15

Out: vous sentez le wumpus

Dans: 14

Out: tu as manqué

Out: le wumpus vous a mangé


la source
1
Je pense que vous pouvez raccourcir votre code d'au moins 100 octets. 1. exitest seulement un octet plus long que g=1et il élimine la nécessité de tester non-zéro get certaines elifdéclarations. 2. Vous pouvez utiliser ((i==35))au lieu de [ $i = 35 ]et ...&&...au lieu de if ... then ... fi. 3. q(){ L=({a..s});$j ${L[RANDOM%19]};}et les n=`$k $w$m<<<$d`;w=${n:RANDOM%2+1:1}deux économiser quelques octets.
Dennis
1
Remplacez while :;do... donepar for((;;);{... }pour une économie de 3 caractères
Digital Trauma
1
@professorfish: Je pense qu'une fonction fonctionnerait mieux que l'approche actuelle string-grep-cut. Par exemple,d(){ x=npoemfgnshtoksblbtckpdpljqniorelgfhkbqraicadjaghimsmjtqecrdf;s=${x:3*30#$1-30:3};} vous permettra de remplacer les définitions de set navec d $pet d $w. Si vous définissez en outre u=${s#*$r}$s(et d' ajuster les définitions de let en fconséquence), vous aurez pas besoin $ket $mplus. Enregistre 83 octets, je pense. En outre, l'espace q ()n'est pas requis.
Dennis
1
@professorfish: Et vous pouvez enregistrer 3 octets supplémentaires en définissant c(){ [[ $1 =~ $2 ]];}et en remplaçant, par exemple, l'avant-dernière ligne par c $r $b||{ $j You missed;d $w;w=${s:RANDOM%2+1:1};}.
Dennis
1
@professorfish: L'utilisation de la fonction suggérée devrait être plus courte de 3 octets. Vous pouvez économiser 106 octets supplémentaires en remplaçant les quatre lignes après par b=$pavec d $p;u=u${s#*$r}$s, les lignes après read ipar 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};}et en vous en débarrassant 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];

Enfin rattrapé la version de la table de recherche de Dennis, à laquelle il emprunte un peu. La chose intéressante à propos de cette version est qu’elle n’a pas de table de recherche pour la disposition de la salle.

Les 60 symétries de rotation d'un icosaèdre sont isomorphes du groupe alternant sur 5 lettres, A_5. Après avoir essayé toutes sortes d'approches pour représenter le groupe de manière compacte, je suis revenu à la plus simple: chaque élément est une permutation de parité paire. Le groupe peut être généré à partir de deux générateurs de plus d'une façon: l'approche que je prends utilise les générateurs 3et 3 1. Ceux - ci nous permettent de générer 1 = 3 3 1, 2 = 3 3 1 3 1et 3 = 3.

Observez que la direction 3correspond à un élément d'ordre 2, car après avoir franchi la porte derrière vous, cette porte est à nouveau derrière vous. La direction 1correspond à un élément d'ordre 5, en contournant un sommet de l'icosaèdre. (Élément similaire 2). Et la combinaison 3 1est d'ordre 3, car elle fait le tour des pièces adjacentes à celle qui commence derrière vous.

Nous cherchons donc une permutation de l’ordre 2 pour représenter la direction 3et une permutation de l’ordre 5 pour représenter la direction 1telle qu’elle 3 1soit de l’ordre 3.

Il y a 15 permutations d'ordre 2 dans A_5, et pour chacune il y a 8 permutations candidates pour 1(et donc pour 3 1). Il y a un intérêt évident [4 3 2 1 0]pour 3: inverser un tableau, c'est juste -1%. 3 1J'ai choisi parmi les permutations possibles de mon compagnon [0 1 3 4 2], qui admet une implémentation assez courte comme [~@].

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.
];
Peter Taylor
la source
Belle approche algébrique! Il existe cependant un bogue mineur: 10/@3%=tente d'accéder au quatrième élément d'un tableau de longueur 3 si l'entrée est 35.
Dennis
@ Dennis, oui, j'ai réalisé après mon coucher. Je peux penser à différentes façons de le réparer, le tout pour un coût de 2.
Peter Taylor
Vous pouvez récupérer un personnage avec 9/3%@3%=.
Dennis
Je suis actuellement 7 caractères avec une restructuration plus drastique. Mais ce caractère de 9/au lieu de 10/fonctionne toujours, alors merci.
Peter Taylor
5

Wumpus , 384 - 129 (chaînes) = 255 octets

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@

Essayez-le en ligne!(Bien sûr, TIO n’a pas beaucoup de sens, parce que vous ne pouvez pas utiliser le programme de manière interactive là-bas, et une fois que le programme est à court d’instructions sur STDIN, il lira 0 0, ce qui équivaut à 3 4, de sorte que vous finirez par tirant des flèches jusqu'à ce que le Wumpus se déplace là-bas ou vous tue.)

Lorsque vous exécutez cette opération localement, assurez-vous que le saut de ligne après le deuxième numéro de chaque entrée est purgé (car Wumpus en a besoin pour déterminer si le nombre est dépassé). Dans Powershell, je dois en quelque sorte entrer un caractère supplémentaire après le saut de ligne pour que cela fonctionne (peu importe le personnage, mais je viens d'utiliser un double saut de ligne pour les tests).

Il y a beaucoup de place pour jouer au golf plus loin, mais essayer de nouvelles configurations prend du temps. Le score final dépend aussi beaucoup des chaînes que j’utilise, car dans un langage 2D, une chaîne de N octets a tendance à vous coûter plus cher que N octets de code source, car elle impose des contraintes importantes à la structure du code et besoin de le scinder en plusieurs sections (ce qui entraîne des guillemets doubles). À la fin, si je réduisais chaque chaîne à une seule lettre (et les -129 à -12), je économiserais probablement une tonne d'octets.

Explication

Tout d’abord une clause de non-responsabilité: malgré le nom de la langue, elle n’a pas été conçue pour rendre particulièrement simple l’ application de Hunt the Wumpus . Au lieu de cela, j'ai d'abord conçu le langage autour du thème des triangles, puis une structure de données icosaédrique et décidé de l'appeler Wumpus pour cette raison.

Donc oui, bien que Wumpus soit principalement basé sur des piles, il possède également 20 registres disposés autour des faces d’un icosaèdre. Cela signifie que nous obtenons une structure de données pour représenter la carte gratuitement. La seule chose que nous ne pouvons pas facilement faire est de trouver des visages spécifiques sur l'icosahédron. Par conséquent, pour les rechercher, nous devons "lancer le d20" jusqu'à obtenir le visage que nous recherchons. (Il est possible de le faire de manière déterministe, mais cela prendrait beaucoup plus d'octets.) La recherche de visages comme celle-ci se termine presque sûrement (c'est-à-dire avec la probabilité 1), de sorte que la recherche exécutée pour toujours n'est pas une préoccupation dans la pratique).

Le code ci-dessus est une version golfée de cette première implémentation avec une mise en page plus saine:

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.

Étant donné que le jeu consiste principalement à compresser la présentation, je vais simplement expliquer cette version pour l'instant (jusqu'à ce que j'ajoute des astuces qui vont au-delà de la restructuration du code).

Commençons par le code d'installation:

1SDL2vSD70L?.;;2.

Initialement, toutes les faces sont définies sur 0 . Nous allons coder le wumpus en définissant le bit 1 de la face correspondante, et la fosse en définissant le 2 bits. De cette façon, ils peuvent tous deux être dans la même pièce. La position du joueur ne sera pas du tout enregistrée sur l'icosaèdre, mais restera toujours active (un seul des 20 registres est actif à la fois).

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.

Nous devons maintenant trouver un visage vide au hasard dans lequel placer le joueur.

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.

Cette section suivante vérifie l'environnement du joueur et affiche les avertissements appropriés:

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

C'est une boucle que nous parcourons 3 fois. À chaque fois, nous regardons le voisin de droite, imprimons la ou les chaînes appropriées s'il y a un danger, puis faisons pivoter l'icosaèdre de 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 section suivante lit deux chiffres du joueur puis déplace le joueur ou tire une flèche. Le premier est trivial, le dernier moins. Le problème principal pour tirer la flèche est le cas où il manque. Dans ce cas, nous devons a) rechercher le wumpus pour le déplacer, puis b) retourner dans la chambre du joueur et indiquer la bonne orientation de l'icosaèdre (pour que "retour" reste "en arrière"). C'est la partie la plus chère de l'ensemble du programme.

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

Le point d’entrée de cette section est le Ià gauche.

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.

Ouf, c'était la partie difficile. Il ne reste plus qu’à vérifier si le joueur meurt et à recommencer par la boucle principale:

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

La structure de cette section est essentiellement identique à celle utilisée lors de la vérification de l'environnement du joueur: nous vérifions le bit du visage actuel (la chambre du joueur) et, le cas échéant, imprimons The wumpus ate you.et mettons fin au programme. Sinon, on vérifie le 2-bit et c'est réglé nous imprimons You fell into the pit.et terminons le programme. Sinon, nous atteignons le 2.qui revient au début de la boucle principale (aux coordonnées (0, 2)).

Martin Ender
la source
1

awk - grand

Cela n’a pas été aussi bref que je l’espérais, mais j’ai adopté une approche légèrement différente de la gestion du graphique; j’ai donc publié la version non-golfée.

J'ai profité du fait qu'un icosaèdre (polyèdre à 20 côtés) avec une orientation préservant les rotations est isomorphe au groupe alternant du degré 5 (permutations à 5 éléments ayant un nombre pair de cycles de longueurs égales). Je choisis ensuite deux permutations avec la longueur de cycle 5 en "gauche" et "à droite", et je choisis une permutation avec la longueur de cycle 2 en "arrière". En utilisant ceux-ci, je construis le graphique d'une pièce en marchant sur le chemin hamiltonien (2xRRRLLLRLRL, en utilisant 3xRB dans chaque pièce pour capturer les 3 directions possibles).

function meta(z,a,b,c,d) {
    if(z==COMPOSE) {
        split(a,c,"");
        split(b,d,"");
        return c[d[1]]c[d[2]]c[d[3]]c[d[4]]c[d[5]];
    }
    if(z==WALK) {
        split(R" "R" "R" "L" "L" "L" "R" "L" "R" "L,c);
        for(b = 1; b <= 20; b++) {
            map[a] = b;
            a = meta(COMPOSE,meta(COMPOSE,a,R),B);
            map[a] = b;
            a = meta(COMPOSE,meta(COMPOSE,a,R),B);
            map[a] = b;
            a = meta(COMPOSE,meta(COMPOSE,a,R),B);
            a = meta(COMPOSE, a, c[b % 10 + 1]);
        }
    }
    if(z==TEST) {
        a = map[meta(COMPOSE,U,L)];
        b = map[meta(COMPOSE,U,R)];
        c = map[meta(COMPOSE,U,B)];
        if(a==W||b==W||c==W) print "You smell the wumpus";
        if(a==P||b==P||c==P) print "You feel a breeze";
        if(map[U]==W) {
            print "You have been eaten by the wumpus";
            exit;
        }
        if(map[U]==P) {
            print "You have fallen into a bottomless pit";
            exit;
        }
    }
    if(z==ARROWTEST) {
        if(A==W) {
            print "You have slain the wumpus!";
            exit;
        } else {
            for(a in p) if(map[a]==W) break;
            W=map[meta(COMPOSE,a,v[int(rand()*3)+1])];
        }
    }
}

BEGIN {
    COMPOSE = 0;
    WALK = 1;
    TEST = 2;
    ARROWTEST = 3;
    L = 35214;
    R = 35421;
    B = 35142;
    split(R" "L" "B,V);
    meta(WALK,L);
    W = int(rand()*19)+2;
    P = int(rand()*19)+2;
    U = L;
    meta(TEST);
}

{
    d=int($0/10);
    m=$0%10;
    if(m==5) U = meta(COMPOSE,U,V[d]);
    else if(m==4) {
        A = map[meta(COMPOSE,U,V[d])];
        meta(ARROWTEST);
    }
    meta(TEST);
}
laindir
la source