Jage den Wumpus


39

Als ich ein Junge war, gingen die Kinder in Computerläden und spielten Hunt the Wumpus, bis die Angestellten uns rausschmissen. Es war ein einfaches Spiel, das auf den Heimcomputern der Mitte der 1970er Jahre programmiert werden konnte. Die Maschinen waren so rudimentär, dass ich glaube, dass einige von ihnen anstelle von Mikroprozessoren in Chicklet-Größe echte Chicklets enthielten.

Erinnern wir uns an diese vergangene Ära, indem wir das Spiel auf moderner Hardware reproduzieren.

  1. Der Spieler startet in einem zufälligen Raum auf einer Ikosaeder-Karte (es gibt also insgesamt 20 Räume, die wie die Gesichter eines Ikosaeders miteinander verbunden sind und jeder Raum hat genau drei Ausgänge).

  2. Der Wumpus startet in einem zufällig ausgewählten anderen Raum. Der Wumpus stinkt und sein Geruch kann in jedem der drei Räume neben seinem Standort festgestellt werden, obwohl der Spieler die Richtung des Geruchs nicht bestimmen kann. Das Spiel meldet nur "Sie riechen einen Wumpus."

  3. Der Spieler trägt einen Bogen und unendlich viele Pfeile, die er jederzeit in den Raum vor sich schießen kann. Befindet sich der Wumpus in diesem Raum, stirbt er und der Spieler gewinnt. Wenn sich der Wumpus nicht in diesem Raum befand, wird er erschrocken und bewegt sich zufällig in einen der drei Räume, die mit seiner aktuellen Position verbunden sind.

  4. Ein zufällig ausgewählter Raum (der garantiert nicht der Raum ist, in dem der Spieler beginnt) enthält eine bodenlose Grube. Befindet sich der Spieler in einem Raum neben der Grube, spürt er eine Brise, bekommt aber keine Ahnung, von welcher Tür die Brise kam. Wenn er mit der Grube in den Raum geht, stirbt er und Wumpus gewinnt. Der Wumpus bleibt von der Grube unberührt.

  5. Betritt der Spieler den Raum des Wumpus oder betritt der Wumpus den Raum des Spielers, gewinnt der Wumpus.

  6. Der Spieler gibt die Richtung an, in die er schaut, mit einer Zahl (1 = rechts, 2 = links, 3 = zurück) und einer Aktion (4 = Pfeil abschießen, 5 = in die angegebene Richtung gehen).

  7. Aus Gründen der Punktzahl kann jede Spielfolge ("Sie spüren eine Brise", "Sie riechen einen Wumpus", "Ihr Pfeil hat nichts getroffen" usw.) als ein Byte angesehen werden. Kein Missbrauch, um den Spielcode im Text zu verstecken. Dies ist nur für die Interaktion mit dem Spieler.

  8. Ziehe 10% deiner Byteanzahl ab, um Megabats zu implementieren, die in einem zufälligen Raum beginnen, der sich vom Spieler unterscheidet (obwohl sie einen Raum mit dem Wumpus und / oder der Grube teilen können). Wenn der Spieler den Raum mit den Fledermäusen betritt, tragen die Fledermäuse den Spieler in einen anderen zufällig ausgewählten Raum (der garantiert nicht der Raum mit der Grube oder dem Wumpus ist), bevor sie zu ihrem eigenen neuen zufälligen Ort fliegen. In den drei Räumen neben den Fledermäusen ist ein Quietschen zu hören, dem Spieler wird jedoch nicht mitgeteilt, aus welchem ​​Raum der Ton kommt.

  9. Ziehen Sie 35% Ihrer Byteanzahl ab, um eine grafische Oberfläche zu implementieren, die die Ikosaeder-Karte und eine Art Anzeige der Informationen enthält, die der Spieler bisher über die Position der Grube, des Wumpus und der Fledermäuse (falls zutreffend) im Verhältnis zu hat der Spieler. Wenn sich der Wumpus bewegt oder der Spieler von den Fledermäusen bewegt wird, muss die Karte natürlich entsprechend zurückgesetzt werden.

  10. Die niedrigste angepasste Byteanzahl gewinnt.

BASIC-Quellcode für eine Version des Spiels (die nicht unbedingt den oben genannten Regeln entspricht und auf jeden Fall völlig ungolfed ist) finden Sie auf dieser Website und wahrscheinlich auch auf anderen.


Einige Erklärungen: 3. Wenn der Wumpus nicht in diesem Raum war, ist er erschrocken und bewegt sich in einen der DREI Räume. Wenn Sie also einen Pfeil abfeuern und verfehlen, kann der Wumpus kommen und Sie töten, oder? Und der Wumpus bewegt sich nur, wenn er erschrocken ist, sonst bleibt er einfach stehen? 6. Ich verstehe, dass die Richtung des Spielers von dem Raum abhängt, aus dem er gekommen ist. Wenn er also aus dem Süden käme, wären seine Optionen 1.Nordosten 2.Nordwesten 3.Südosten, und wenn er aus dem Norden käme, wäre es umgekehrt. Auch Ihre Regeln scheinen einfacher / golfer zu sein als das Referenzprogramm (das ich noch nicht im Detail untersucht habe.) Bin ich richtig?
Level River St

Argh! Ich kann nicht finden , keine Bilder des dualen Graphen eines Ikosaeders überall im Netz.
Jack M

1
@steveverrill Ja, wenn du es erschreckst, kann es kommen und dich töten. Wenn Sie es nicht erschrecken, bewegt es sich nicht. Es gibt viele Variationen des Spiels; In vielen Versionen können Pfeile zum Beispiel um dich kreisen und dich töten. Ich habe das gekürzt.
Michael Stern

3
@JackM Die Karte der Flächen eines Ikosaeders ist identisch mit der Karte der Eckpunkte eines Dodekaeders, und diese Grafik ist leicht zu finden. Versuchen Sie beispielsweise wolframalpha.com/input/?i=DodecahedralGraph+edgerules oder den entsprechenden Mathematica-Befehl GraphData ["DodecahedralGraph", "EdgeRules"]. In beiden Fällen erhalten Sie {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 Nein, "zurück" bedeutet, sich umzudrehen und den Weg zurückzugehen, den Sie gekommen sind. Wenn Sie zweimal "Zurück" drücken, landen Sie dort, wo Sie angefangen haben. Frühere Spielzustände müssen nicht gespeichert werden.
Michael Stern

Antworten:


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

Die Bewertung wird erhalten, indem die Byteanzahl (290) genommen wird, die Anzahl der für die Interaktion mit dem Benutzer (6) verwendeten Zeichenfolgen addiert wird und die kombinierte Länge dieser Zeichenfolgen subtrahiert wird (133). Die Zeilenvorschübe sind Teil der Zeichenfolgen und tragen zur Bytezahl bei.

Meilensteine

  1. Portierte professorfish's Antwort von Bash nach GolfScript. Ergebnis: 269

  2. Hat auf die Vorschläge von Peter Taylor in den Kommentaren reagiert. Ergebnis: 250

  3. Peter Taylor überarbeitete meinen gesamten Code und half mir, die Nachschlagetabelle zu komprimieren. Prüfungsergebnis: 202

  4. Die Nachschlagetabelle benachbarter Räume wurde durch einen mathematischen Ansatz ersetzt. Prüfungsergebnis: 182

  5. Refactored Input, Output und die Funktion, die den mathematischen Ansatz unterstützt. Prüfungsergebnis: 163

Ein großes Dankeschön geht an Peter Taylor für all seine Hilfe.

Wie es funktioniert

Die 20 Räume werden als Eckpunkte eines Dodekaeders dargestellt, denen auf folgende Weise Nummern von 0 bis 19 zugewiesen wurden:

Dodekaedrisches Diagramm

Um die an Raum N angrenzenden Räume zu finden und im Uhrzeigersinn zu ordnen, müssen wir vier Fälle betrachten:

  • Wenn N ≤ 0 mod 4 (blaue Eckpunkte) ist, sind der angrenzende Raum 19 - N , N + 2 mod 20 und N - 2 mod 20 .

  • Wenn N ≤ 1 mod 4 (grüne Eckpunkte) ist, sind der angrenzende Raum 19 - N , N - 4 mod 20 und N + 4 mod 20 .

  • Wenn N ≤ 2 mod 4 (gelbe Eckpunkte) ist, sind der angrenzende Raum 19 - N , N - 2 mod 20 und N + 2 mod 20 .

  • Wenn N ≤ 3 mod 4 (rote Eckpunkte), ist der angrenzende Raum, 19 - N , N + 4 mod 20 und 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
Sie können 1 in Qmit speichern 19rand 97+; 2 in @mit 97%3*&>..., eine weitere 1 durch Inlining Qals {19rand 97+}2*:,\:H, ein paar durch Ersetzen |durch *, was oft der beste Weg ist, um eine if. Bhat keinen Zweck, und ich denke, ein paar weitere Variablen könnten durch die Verwendung des Stacks beseitigt werden.
Peter Taylor

1
Ich habe vergessen, einen weiteren häufigen Trick zu erwähnen: die Basisumwandlung für Nachschlagetabellen. Sie können die 62 Zeichen für die Adjazenzliste durch eine Zeichenfolge mit 33 Zeichen gefolgt von ersetzen 256base 20base(und möglicherweise auch einige +/- 97 Zeichen entfernen ). Der einzige Nachteil ist, dass nicht druckbare Zeichen erforderlich sind.
Peter Taylor

1
Ich habe weitere 13 gespart, indem ich umgestaltet habe, um eine idiomatischere GS zu erhalten (hauptsächlich unter Verwendung des Stacks anstelle von Variablen). und es gibt weitere 10 auf Kosten der weniger hübschen Ausgabe. Abgesehen von der in meinem vorherigen Kommentar erwähnten Komprimierung der Lookup-Tabelle.
Peter Taylor

1
Gar nicht, ich habe es genossen. Ich bin nur enttäuscht, dass der Lookup-Table-Ansatz so viel besser ist als der mathematischere, den ich beabsichtigte. Übrigens denke ich, dass Ihre aktuelle Version einen kleinen Fehler hat, denn wenn Sie einen Pfeil abfeuern, den Wumpus verpassen und in Ihr Zimmer schießen, wird nur ausgegeben, You were killed by the wumpusohne dass der Pfeil fehlt. Deshalb habe ich in der nicht-hübschen Version angehängt.
Peter Taylor

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

15

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

Im Folgenden sehen Sie einen Durchgang, der zeigt, dass Sie bei korrektem Spiel immer gewinnen können (vorausgesetzt, Sie beginnen nicht direkt neben einer Gefahr). Der Spieler fühlt eine Brise, dreht sich um und macht eine vollständige Schleife gegen den Uhrzeigersinn. Da er genau 5 Züge braucht, um wieder eine Brise zu spüren, kennt er das Loch zu seiner Rechten und kommt so weit wie möglich weg. Wenn er den Wumpus riecht und nicht weiß, ob er rechts oder links ist, dreht er sich um und macht eine Schleife im Uhrzeigersinn. Er braucht 5 Züge, um den Wumpus wieder zu riechen, also weiß er, dass er links ist und schießt mit Sicherheit.

Wenn er sich anders herum geschleift hätte, hätte er den Wumpus früher gefunden und gewusst, dass er sich in die gleiche Richtung drehte.

Bildbeschreibung hier eingeben

REV1 C (GCC auf Cygwin), 431-35% 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!",)}}

Zeilenumbrüche zur Verdeutlichung hinzugefügt. Die Änderungen von Rev 0 sind wie folgt:

Ein großes Dankeschön an @Dennis für die Empfehlung des GCC-Compilers für den Cygwin Linux-Emulator für Windows. Für diesen Compiler ist das includes im Rev. 0-Programm nicht erforderlich , und der Standardtyp intfür Variablen main.ist zulässig. Dies ist ein lebensverändernder Golftipp!

Wenn Sie zusätzlich unter Linux arbeiten \f, bewegt sich der Cursor nach unten, ohne dass ein Wagenrücklauf ausgeführt wird (im Gegensatz zu Windows, in dem nur ein druckbares Symbol erstellt wird). Dadurch konnte die printf-Anweisung, mit der die Karte gedruckt wird, erheblich verkürzt werden

Einige zusätzliche Tipps von Dennis in den Kommentaren und einen von mir: Zustandsänderung beim Prüfen, ob der Pfeil auf den Wumpus trifft: if(q==w)> if(q-w)(..else .. ist umgekehrt)

Zusätzliches Grafikdisplay mit den Informationen, die der Spieler darüber kennt, wo ein Wumpus riecht / eine Brise spürt, dass er den 35% -Bonus beansprucht. (Ich habe die alte Debug-Version gelöscht, in der die genaue Position von Wumpus und Loch angegeben ist. Sie ist im Bearbeitungsverlauf zu sehen.)

REV2 C (GCC auf Cygwin), 389-35% Bonus = 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");}

Nochmals vielen Dank an Dennis für die Überarbeitung meines Codes:

Zeichenkonstante m[]durch Literale ersetzt (Ich wusste nicht, dass Sie ein Literal indizieren können.)

Seeding von Zufallszahlen mit Stapelvariablen (systemabhängig, einige Systeme setzen die Speicherzuordnung als Sicherheitsmaßnahme zufällig fest.)

Makro mit putsersetzt durch ein Makro mit printfund zusätzlichem Code, der ausgeführt werden muss, wenn die angezeigte Nachricht in printfArgumenten platziert wird (Vorteil, dass printf die letzten Argumente nicht druckt, wenn nicht genügend Formatspezifizierer in der Formatzeichenfolge vorhanden sind.) ifersetzt durch||

Berechnung der neuen Position des Spielers / Wumpus innerhalb des neuen Makros.

Nachrichten außerhalb der whileSchleife gewinnen / verlieren . ifersetzt durch bedingten Operator.

Verwendung des bedingten Operators in der Linie zum Schießen des Pfeils. Wenn der Spieler ausfällt, müssen Sie sowohl eine Nachricht drucken als auch die Wumpusposition anpassen. Dennis bot ein paar Möglichkeiten an, printfdie Wumpus-Position in einem Ausdruck zu kombinieren und zu berechnen, aber ich habe mich für einen eigenen entschieden. printfgibt die Anzahl der gedruckten Zeichen, die Your arrow didn't hit anything\nalso 31 (11111 binär.) 31&Q(w)==Q(w).

Mein anderer Beitrag zu dieser Bearbeitung war die Beseitigung einiger unnötiger Klammern.

Ausgabe

Hier hat der Spieler bereits gefunden, wo sich der Wumpus befindet, entscheidet sich jedoch für eine gründliche Untersuchung, um herauszufinden, wo sich auch die Grube befindet. Im Gegensatz zu meiner alten Debug-Version, in der angezeigt wurde, wo sich Wumpus und Pit während des Spiels befanden, werden hier nur die Räume angezeigt, in denen der Spieler den Wumpus (2) oder beides (3) besucht und eine Brise gespürt hat (1). (Wenn der Spieler einen Pfeil schießt und verfehlt, wird die Variable amit den Wumpuspositionsinformationen zurückgesetzt.)

Bildbeschreibung hier eingeben

ICOSAHEDRON-VERTRETUNG

Hinweis: Dieser Abschnitt basiert auf Version 1

Mein Star-Feature! Mein Code enthält kein Diagramm. Informationen zur Funktionsweise finden Sie auf der folgenden Weltkarte. Jeder Punkt auf dem Ikosaeder kann durch einen Breitengrad 0-3 und einen Längengrad 0-4 (oder eine einzelne Zahl) dargestellt werden long*4+lat. Die auf der Karte markierte Längengradlinie verläuft nur durch die Flächen mit dem Längengrad Null, und die Breitengradlinie verläuft durch die Mitte der Gesichter mit dem Breitengrad Null.

Der Spieler kann sich an drei möglichen Achsen orientieren, die durch die folgenden Symbole dargestellt werden: Nord-Süd- -Nordost-Südwest \- Nordost -Südost /. In jedem Raum steht ihm auf jeder dieser Achsen genau ein Ausgang zur Verfügung. In der angezeigten Anzeige macht der Player eine vollständige Schleife im Uhrzeigersinn. Es ist im Allgemeinen einfach, anhand der Spielermarkierung zu erkennen, woher er kam und wohin er gehen darf.

Der eine Fall, der für das nicht eingeweihte Auge etwas schwierig ist, ist der vierte. Wenn Sie eine Neigung in einer dieser Polarreihen sehen, ist der Spieler von der Polarzelle gekommen, die dem äußeren Ende der Neigung am nächsten liegt, und zeigt im Allgemeinen zum Äquator. Der Spieler ist also nach Südosten ausgerichtet und hat folgende Optionen: 15 (SÜD, die Zelle rechts) 25 (Nordosten, die Zelle oben) oder 35 (Nordwesten, die Zelle unten).

Im Grunde genommen ordne ich das Ikosaeder einem 5x4-Raster zu, wobei die Zellen in der Reihenfolge, in der sie gedruckt werden, von 19 bis 0 nummeriert sind. Der Zug erfolgt durch Addieren oder Subtrahieren der aktuellen Position, abhängig vom Breitengrad und der Richtung des Spielers, gemäß der folgenden Tabelle.

Wenn der Spieler vom Boden (Westen) des Bretts absteigt, kehrt er auf die Oberseite (Osten) zurück und umgekehrt, sodass seine Position modulo 20 eingenommen wird. Im Allgemeinen werden die Züge durch Hinzufügen von ASCII 80 in m [] codiert. P) zu dem Rohwert hinzu, der die unten gezeigten Zeichen angibt, aber grundsätzlich kann ein beliebiges Vielfaches von 20 hinzugefügt werden, ohne die Operation zu beeinflussen.

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

Die Eingabe des Spielers (geteilt durch 10, um die zweite Ziffer zu entfernen) wird zu seiner aktuellen Richtung addiert und modulo 3 genommen, um seine neue Richtung zu erhalten. Dies funktioniert in den meisten Fällen einwandfrei. Es gibt jedoch ein Problem, wenn er sich in einem Polarraum befindet und sich auf die Stange zubewegt. Wenn Sie die Karte darunter falten, wird klar, dass er, wenn er den Raum nach "Nordosten" verlässt, das neue Quadrat nach "Südosten" betritt, sodass eine Korrektur vorgenommen werden muss. Dies geschieht in der Zeile e=(d+i/10)*m[p%4]%3;durch Multiplikation mit m[p%4]. Die ersten vier Werte von m [] werden so gewählt, dass sie zusätzlich zu ihrer obigen Funktion auch die Charakteristik m[1]%3==m[2]%3==1und haben m[0]%3==m[3]%3==2. Dies lässt die Richtung für die Äquatorialräume allein und wendet die notwendige Korrektur für Polarräume an.

Die logische Zeit für die Korrektur wäre nach dem Umzug. Das Speichern von Zeichen erfolgt jedoch vor dem Verschieben. Daher müssen bestimmte Werte in m [] transponiert werden. So sind die letzten 2 Zeichen LTanstelle der TLobigen Tabelle zum Beispiel.

Bildbeschreibung hier eingeben

UNGOLFED CODE

Dies ist Rev. 1 Code, der weniger verschleiert ist als Rev. 2.

Dies wird unter GCC / Linux ausgeführt. Ich habe in den Kommentaren den zusätzlichen Code angegeben, der für die Ausführung unter Visual Studio / Windows erforderlich ist. Das ist ein großer Unterschied!

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

}

FRAGEN UND NEUGIERIGKEITEN

Ich habe den von @professorfish erwähnten Punkt ausgenutzt. Wenn der Wumpus und die Grube an zufälligen Orten beginnen, muss der Spieler nicht an zufälligen Orten beginnen. Der Spieler startet immer in Raum 19 nach Norden.

Ich verstehe, dass, da der Wumpus "von der Grube nicht betroffen" ist, der Wumpus beginnen oder den Raum betreten kann, in dem sich die Grube befindet. Im Allgemeinen vereinfacht dies die Dinge bis auf einen Punkt. Ich habe keine spezifische Variable, die anzeigt, dass das Spiel beendet ist. es ist vorbei, wenn der Spieler mit dem Wumpus oder der Grube zusammenfällt. Wenn der Spieler gewinnt, zeige ich die Gewinnnachricht an, aber bewege die Grube zum Spieler, um aus der Schleife herauszukommen! Ich kann den Spieler nicht in die Box setzen, da der Wumpus dort sein könnte und ich eine Nachricht über den Wumpus erhalten würde, die ich nicht möchte.

Das rev0-Programm funktionierte perfekt in Visual Studio, aber die IDE meldete beim Beenden "Stapel um Variable i beschädigt". Dies ist darauf zurückzuführen, dass scanf versucht, intein von char.Dennis gemeldetes falsches Verhalten auf seinem Linux-Computer zu erzeugen . Auf jeden Fall wird es durch Verwendung des richtigen Typs in Rev. 1 behoben.

Die Zeile für die Anzeige des Boards in Rev. 0 ist unbeholfen und sieht auf anderen Plattformen etwas anders aus. In printf(" %c%c%c")der Mitte% c wird das druckbare Zeichen angezeigt. Das letzte% c ist entweder ASCII 0 oder ASCII 10 (\ n, Zeilenumbruch mit Wagenrücklauf in Windows.) In Windows scheint es kein Zeichen zu geben, das in der Konsole funktioniert und das eine Zeile ohne Wagenrücklauf abfährt. Wenn es das gäbe, bräuchte ich nicht das erste c% (ASCII 0- oder ASCII 9-Tab vor dem 1. Breitengrad-Zeichen. Tabs sind in ihrem Verhalten notorisch undefiniert.) Das führende Leerzeichen verbessert die Formatierung (bringt die 3. und 2. Breite näher an das 1. Breitengrad-Zeichen .) Rev 1 enthält eine Überarbeitung dieser Zeile, die ein \ f-Formatvorschubzeichen verwendet und daher am Anfang des Ausdrucks kein Formatzeichen benötigt. Das macht es kürzer, aber das \ f funktioniert nicht in Windows.


1
Ich liebe den Artikel.
Michael Stern

Ich bin nicht sicher, ob es an den Änderungen liegt, die ich vornehmen musste, um es mit GCC unter Linux zu kompilieren (entfernen Sie das erste Include, ersetzen Sie es scanf_smit scanfund schließen Sie es ein, stdio.hwenn ich als C ++ Rater als C kompiliere), aber es funktioniert nicht ganz für mich. Zum Beispiel, wenn ich nach links gehe, dann wieder rechts am Anfang ( 15 35), bin ich in einem anderen Raum als dem, in dem ich angefangen habe.
Dennis

@ Tennis Ich habe die Fehlerquelle beim Beenden aufgespürt. Es ist das scanf_s (angeblich sicher!), das den Stapel um die Variable i "beschädigt", wenn es versucht, eine 32-Bit-Ganzzahl in ein Zeichen zu setzen, was ich annehme. Als erstes würde ich vorschlagen, den Typ zu überprüfen, den scanf für ein "% d" verwendet, und die Variable i in diesen Typ zu ändern. Ich erhalte die richtige Antwort ohne Exit-Fehler für char, die richtige Antwort ohne Exit-Fehler für int und die falsche Antwort mit dem Microsoft-Typ __int64 (äquivalent lang lang, es sei denn, ich habe "% lld" eingegeben.) Haben Sie auch ausgeführt die ungolfed version und hattest du irgendwelche probleme mit dem display?
Level River St

@steveverrill: Ja, ich hatte beide Versionen ausprobiert. Die Art iist in der Tat das Problem. Die Manpage sagt: " d Entspricht einer optional vorzeichenbehafteten Dezimalzahl; der nächste Zeiger muss ein Zeiger auf int sein ." Durch Ändern des Typs funktioniert es einwandfrei.
Dennis

@steveverrill: Ich weiß nicht, wie VS mit Dingen umgeht, aber wenn Sie mit GCC kompilieren (als C, nicht C ++), können Sie eine Menge Zeichen sparen. Keines der enthält erforderlich, wenn Sie ersetzen NULLmit 0und scanf_smit scanf, die Sie nicht brauchen , intbevor mainund Sie können sich bewegen iund dder Haupt außen (sie standardmäßig auf intund werden initialisiert 0). Sie können auch definieren p=19,h=rand()%p,w=rand()%p, ersetzen m[]durch *mund es sollte möglich sein, ein Makro für alle Instanzen von zu definieren if(...==...)puts(...);.
Dennis

9

GolfScript, 269 Zeichen

{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

Beachten Sie, dass 163 von der Zeichenanzahl für die hartcodierten Zeichenfolgen abgezogen wurde. Wenn Sie eine Debug-Ausgabe unter Angabe der Raumnummern wünschen, fügen Sie die folgende Zeile direkt nach dem ersten Auftreten von hinzu ^:

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

Eine Beispielsitzung (mit zusätzlicher Debug-Ausgabe):

  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

Hier ist der erste Arbeitscode. Ich komme später wieder, um noch mehr Golf zu spielen.
Howard

Mein Code ist derzeit 1 Zeichen länger. Ich bin auf der Suche nach einer Möglichkeit, weiter Golf zu spielen!
Timtech

Nicht, dass Sie meine Hilfe brauchen, aber Sie können 14 Zeichen sparen, indem Sie definieren {puts}:|;, 5 Zeichen durch Ersetzen von Rund Wmit -und >(umliegende Leerzeichen können entfernt werden) und 9 Zeichen durch Löschen '> 'print(scheint in der Frage nicht erforderlich zu sein).
Dennis

@ Tennis Danke. Ich werde sicherlich einige Ihrer Vorschläge umsetzen.
Howard

9

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

Fast fertig mit Golfen ...

Enthält eine grafische Benutzeroberfläche mit einer Ikosaeder-Karte und Mega-Fledermäusen für die vollen Boni.

Wumpus GUI

  • Jedes Zimmer hat 4 Knöpfe: X(die Grube); B(der Mega-Bat); W(der Wumpus); und P(Sie).
  • Ihr aktueller Standort ist blau gefärbt.
  • Die Schaltflächen sind rot, wenn sich das Objekt, das sie darstellen, an dieser Stelle befinden könnte, und grün, wenn es sich definitiv nicht an dieser Stelle befindet.
  • Die Schaltflächen Wund Pkönnen nur in den Räumen neben Ihrem aktuellen Standort angeklickt werden.
  • Wenn Sie gewinnen, wird der Hintergrund grün und wenn Sie sterben, wird der Hintergrund rot.

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)

Mit dem Closure Compiler erhalten Sie 1066 ohne ECMA 6.
AMK

Ich habe mich gefragt, wie viel einfacher es wäre, wenn Sie eine grafische Darstellung hätten, um herauszufinden, wo sich die Dinge befinden. 1+ aber es ist etwas zu einfach :)
Sylwester

9

Bash, 365 (erste Arbeitsversion 726!)

FANGEN SIE MIT GOLFSCRIPT?

@Dennis hat im Grunde das ganze Golfen für mich erledigt. Vielen Dank!

Das Programm geht von einer gültigen Eingabe aus. Die gültige Eingabe ist die von Ihnen gewählte Richtung (1 für rechts, 2 für links, 3 für hinten), gefolgt von Ihrer Aktion (4 zum Schießen, 5 zum Gehen).

Einige Erklärungen

Normalerweise erkläre ich ausführlich, aber das ist wahrscheinlich ein bisschen zu kompliziert, als dass ich mich darum kümmern könnte.

Jeder Scheitelpunkt im Dodekaedergraphen wird als Buchstabe kodiert (a = 1, b = 2, ... t = 20).

Die Startposition des Spielers ist immer 20 (und sie stehen mit dem Rücken zu 18), da dies an sich keine Rolle spielt, sondern nur die relativen Positionen des Spielers, der Grube und des Wumpus.

Die Variable $pspeichert den Standort des Players. $rSpeichert den vorherigen Standort des Players. $wist der Wumpus und $h(H für Loch) ist die Grube.

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

Versionsgeschichte

  1. Erstveröffentlichung: 698 Zeichen
  2. Es wurde ein Fehler behoben, durch den "Sie spüren eine Brise" und "Sie riechen den Wumpus" nicht gleichzeitig angezeigt werden konnten. 39 Zeichen gespart, indem die Zufallszahlengenerierung zu einer Funktion gemacht wurde.
  3. Denken Sie daran, dass sich der Wumpus bewegt, wenn Sie schießen und verfehlen. 726 Zeichen.
  4. Hergestellt grep -oEeine Variable. 5 Zeichen gespeichert.
  5. Hergestellt [a-z]{3}eine Variable. 3 Zeichen gespeichert.
  6. Hergestellt echoeine Variable. 5 Zeichen gespeichert.
  7. Hat auf die meisten Vorschläge von @Dennis reagiert. 72 Zeichen gespeichert.
  8. Alle verbleibenden Vorschläge hinzugefügt. 68 Zeichen gespeichert.
  9. 2 Zeichen aus @DigitalTraumas Vorschlag gespeichert.
  10. Es wurde ein schwerwiegender Fehler behoben, bei dem der Wumpus nur dann abgeschossen werden kann, wenn er sich rechts befindet. Gleiche Zeichenanzahl.
  11. Verwendete Parametererweiterung, um mit 2 Zeichen zu sparen $m.
  12. Viele Zeichen wurden durch Abziehen grepund etwas mehr Sinn rasiert .
  13. Definiert Cals reguläre Suchfunktion, die in if-Anweisungen verwendet werden soll, und Eals Funktion, die "Sie haben den Wumpus getötet" ausgibt und beendet.
  14. 1 Zeichen durch "if statement" -Umlagerung gespeichert.
  15. Es wurden viele Zeichen gespart, indem dunnötige Klammern entfernt wurden.
  16. Behobene Fehler. Viele Zeichen hinzugefügt :(
  17. MOARR SAVINGS ( http://xkcd.com/1296/ )
  18. Eine weitere Idee von @Dennis (ein paar Zeichen sparen) und meine listige (ab) Verwendung von Indirektion (1 Zeichen sparen).
  19. Style Fix für q ().
  20. Die korrekte Ausgabe wurde wieder hinzugefügt

Probelauf

"In:" wird eingegeben, "Out: wird ausgegeben".

Der Spieler läuft ein bisschen umher, riecht den Wumpus und schießt. Sie vermissen, und der Wumpus kommt in ihr Zimmer und isst sie.

In: 15

In: 15

In: 25

In: 25

In: 15

Raus: Du riechst den Wumpus

In: 14

Out: Du hast verpasst

Raus: Der Wumpus hat dich gefressen


1
Ich denke, Sie können Ihren Code mindestens 100 Bytes kürzer machen. 1. exitist nur ein Byte länger als g=1und erübrigt das Testen auf Nicht-Null gund einige elifAnweisungen. 2. Sie können ((i==35))anstelle von [ $i = 35 ]und ...&&...anstelle von verwenden if ... then ... fi. 3. q(){ L=({a..s});$j ${L[RANDOM%19]};}und n=`$k $w$m<<<$d`;w=${n:RANDOM%2+1:1}beide sparen ein paar Bytes.
Dennis

1
Ersetzen Sie while :;do... donedurch for((;;);{..., }um 3 Zeichen zu sparen
Digitales Trauma

1
@professorfish: Ich denke, eine Funktion würde besser funktionieren als der aktuelle String-Grep-Cut-Ansatz. Beispielsweise d(){ x=npoemfgnshtoksblbtckpdpljqniorelgfhkbqraicadjaghimsmjtqecrdf;s=${x:3*30#$1-30:3};}können Sie die Definitionen von sund ndurch d $pund ersetzen d $w. Wenn Sie außerdem u=${s#*$r}$sdie Definitionen von lund fentsprechend definieren (und anpassen ), brauchen Sie $kund $mnicht mehr. Spart 83 Bytes, denke ich. Auch der Platz in q ()ist nicht erforderlich.
Dennis

1
@professorfish: Und Sie können 3 zusätzliche Bytes einsparen, c(){ [[ $1 =~ $2 ]];}indem Sie zB die vorletzte Zeile mit definieren und ersetzen c $r $b||{ $j You missed;d $w;w=${s:RANDOM%2+1:1};}.
Dennis

1
@professorfish: Die von mir vorgeschlagene Funktion sollte 3 Byte kürzer sein. Sie können 106 zusätzliche Bytes einsparen, indem Sie die vier Zeilen nach b=$pmit d $p;u=u${s#*$r}$s, die Zeilen nach read imit 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};}und loswerden ersetzen 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];

Endlich mit Dennis 'Lookup-Table-Version, von der es sich einiges leiht, mithalten können. Das Interessante an dieser Version ist, dass es keine Nachschlagetabelle für die Raumaufteilung gibt.

Die 60 Rotationssymmetrien eines Ikosaeders sind isomorph zu der alternierenden Gruppe von 5 Buchstaben A_5. Nachdem ich alle Arten von Ansätzen zur kompakten Darstellung der Gruppe ausprobiert habe, bin ich auf die einfachste zurückgekehrt: Jedes Element ist eine Permutation mit gerader Parität. Die Gruppe kann auf mehrere Arten aus zwei Generatoren generiert werden: Der von mir verfolgte Ansatz verwendet die Generatoren 3und 3 1. Diese ermöglichen es uns zu erzeugen 1 = 3 3 1, 2 = 3 3 1 3 1und 3 = 3.

Beachten Sie, dass die Richtung 3einem Element der Ordnung 2 entspricht, denn nachdem Sie durch die Tür hinter sich gegangen sind, befindet sich diese Tür wieder hinter Ihnen. Die Richtung 1entspricht einem Element der Ordnung 5, das um einen Scheitelpunkt des Ikosaeders läuft. (Ähnliches Element 2). Und die Kombination 3 1ist in der Größenordnung 3, da sie die Räume umrundet, die an den Raum angrenzen, der hinter Ihnen beginnt.

Wir suchen also nach einer Permutation der Ordnung 2, um die Richtung darzustellen, 3und einer Permutation der Ordnung 5, um die Richtung darzustellen 1, 3 1die der Ordnung 3 entspricht.

Es gibt 15 Permutationen der Ordnung 2 in A_5, und für jede gibt es 8 mögliche Permutationen für 1(und damit für 3 1). Es gibt eine offensichtliche Anziehungskraft [4 3 2 1 0]für 3: Das Umkehren eines Arrays ist gerecht -1%. Von seinen möglichen Companion-Permutationen 3 1habe ich gewählt [0 1 3 4 2], was eine recht kurze Implementierung als zulässt [~@].

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.
];

Schöner algebraischer Ansatz! Es gibt jedoch einen kleinen Fehler: Es wird 10/@3%=versucht, auf das vierte Element eines Arrays der Länge 3 zuzugreifen, wenn die Eingabe lautet 35.
Dennis

@Dennis, ja, mir wurde klar, nachdem ich ins Bett gegangen war. Ich kann mir verschiedene Möglichkeiten vorstellen, das Problem zu beheben, die alle 2 kosten.
Peter Taylor

Sie können ein Zeichen mit zurückbekommen 9/3%@3%=.
Dennis

Ich bin derzeit 7 Zeichen mit etwas drastischeren Umstrukturierungen. Aber das 1 Zeichen von 9/anstatt 10/noch funktioniert, also danke.
Peter Taylor

5

Wumpus , 384 - 129 (Zeichenfolgen) = 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@

Probieren Sie es online! (Natürlich macht TIO nicht viel Sinn, da Sie das Programm dort nicht interaktiv verwenden können und wenn die Anweisungen für STDIN nicht mehr ausreichen, wird es lesen 0 0, was äquivalent ist 3 4, sodass Sie am Ende enden werden schieße Pfeile, bis der Wumpus sich dorthin bewegt oder dich tötet.)

Wenn Sie dies lokal ausführen, stellen Sie sicher, dass der Zeilenvorschub nach der zweiten Nummer jeder Eingabe gelöscht wird (da Wumpus ihn benötigt, um festzustellen, dass die Nummer überschritten ist). In Powershell muss ich nach dem Zeilenvorschub noch ein Zeichen eingeben, damit es funktioniert (egal welches Zeichen, aber ich habe nur doppelte Zeilenvorschübe zum Testen verwendet).

Es gibt viel Platz zum Golfspielen, aber das Ausprobieren völlig neuer Layouts dauert eine Weile. Die endgültige Bewertung hängt auch stark von den tatsächlichen Zeichenfolgen ab, die ich verwende. In einer 2D-Sprache kostet eine Zeichenfolge mit N Bytes in der Regel mehr als N Bytes Quellcode, da dies das Codelayout erheblich einschränkt, und zwar häufig Es muss in mehrere Abschnitte unterteilt werden (zusätzliche doppelte Anführungszeichen). Am äußersten Ende würde ich wahrscheinlich eine Tonne Bytes sparen, wenn ich jede Zeichenfolge auf einen einzelnen Buchstaben (und die -129 bis -12) reduzieren würde.

Erläuterung

Zunächst wird ein Haftungsausschluss: Trotz des Namens der Sprache, wurde es nicht zu machen Umsetzung entworfen Hunt Wumpus besonders einfach. Stattdessen entwarf ich zuerst die Sprache rund um das Thema Dreiecke, endete mit einer ikosaedrischen Datenstruktur und beschloss, sie deswegen Wumpus zu nennen.

Also ja, während Wumpus hauptsächlich stapelbasiert ist, hat es auch 20 Register, die um die Flächen eines Ikosaeders angeordnet sind. Das heißt, wir bekommen eine Datenstruktur, um die Karte kostenlos darzustellen. Das Einzige, was wir nicht so einfach machen können, ist, bestimmte Gesichter auf dem Ikosaeder zu finden. Um nach ihnen zu suchen, müssen wir den "d20" rollen, bis wir auf dem Gesicht landen, das wir suchen. (Es ist möglich, dies deterministisch zu tun, aber das würde viel mehr Bytes in Anspruch nehmen.) Die Suche nach Gesichtern wie diesem endet fast sicher (dh mit Wahrscheinlichkeit 1), sodass die Suche, die für immer läuft, in der Praxis kein Problem darstellt.

Der obige Code ist eine Golf-Version dieser ersten Implementierung mit einem klareren Layout:

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.

Da das Golfen hauptsächlich das Komprimieren des Layouts beinhaltete, erkläre ich diese Version erst einmal (bis ich irgendwelche Golftricks hinzufüge, die über die Umstrukturierung des Codes hinausgehen).

Beginnen wir mit dem Setup-Code:

1SDL2vSD70L?.;;2.

Anfangs sind alle Gesichter auf 0 gesetzt . Wir codieren den Wumpus, indem wir das 1-Bit der entsprechenden Fläche und den Pit, indem wir das 2-Bit setzen. Auf diese Weise können sich beide im selben Raum befinden. Die Position des Spielers wird überhaupt nicht im Ikosaeder aufgezeichnet, sondern ist immer aktiv (es ist immer nur eines der 20 Register aktiv).

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.

Jetzt müssen wir ein zufälliges leeres Gesicht finden, um den Spieler hinein zu setzen.

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.

Dieser nächste Abschnitt prüft die Umgebung des Players und druckt die entsprechenden Warnungen aus:

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

Dies ist eine Schleife, die wir dreimal durchlaufen. Jedes Mal, wenn wir auf den rechten Nachbarn schauen, drucken wir die entsprechende (n) Zeichenfolge (n), wenn eine Gefahr besteht, und drehen dann das Ikosaeder um 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.

Der nächste Abschnitt liest zwei Zahlen vom Spieler und bewegt dann entweder den Spieler oder schießt einen Pfeil. Ersteres ist trivial, letzteres weniger. Das Hauptproblem beim Schießen des Pfeils ist der Fall, in dem er verfehlt. In diesem Fall müssen wir a) nach dem Wumpus suchen, um ihn zu bewegen, und dann b) in den Raum des Spielers zurückkehren und die richtige Ausrichtung des Ikosaeders feststellen (so dass "zurück" "zurück" bleibt). Dies ist der teuerste Teil des gesamten Programms.

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

Der Einstiegspunkt zu diesem Abschnitt befindet sich Iauf der linken Seite.

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.

Puh, das war der schwierige Teil. Jetzt müssen wir nur noch prüfen, ob der Spieler stirbt und ansonsten über die Hauptschleife starten:

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

Die Struktur dieses Abschnitts ist im Wesentlichen identisch mit der Struktur, die wir beim Überprüfen der Umgebung des Spielers verwendet haben: Wir überprüfen das 1-Bit des aktuellen Gesichts (des Raums des Spielers), und wenn es gesetzt ist, drucken wir The wumpus ate you.das Programm und beenden es. Andernfalls überprüfen wir das 2-Bit und setzen es, wir drucken You fell into the pit.und beenden das Programm. Ansonsten erreichen wir die, 2.die an den Anfang der Hauptschleife zurückspringt (bei Koordinaten (0, 2)).


1

awk - groß

Dies fiel nicht so kurz aus, wie ich es mir erhofft hatte, aber ich ging etwas anders mit dem Graphen um, also poste ich die ungolfed Version trotzdem.

Ich habe die Tatsache ausgenutzt, dass ein Ikosaeder (20-seitiges Polyeder) unter Wahrung der Orientierung bei Rotationen isomorph zu der alternierenden Gruppe des Grades 5 ist (5 Elementpermutationen mit einer geraden Anzahl von Zyklen mit gerader Länge). Ich wähle dann zwei Permutationen mit der Zykluslänge 5 als "left" und "right" und ich wähle eine Permutation mit der Zykluslänge 2 als "back". Mit diesen baue ich den Graphen aus einem Raum auf, indem ich den Hamilton-Pfad gehe (2xRRRLLLRLRL, mit 3xRB in jedem Raum, um die 3 möglichen Richtungen zu erfassen).

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);
}
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.