Poluj na Wumpusa

39

Kiedy byłem chłopcem, dzieciaki wędrowały do ​​sklepów komputerowych i grały w Hunt the Wumpus, dopóki nas nie wyrzucili. To była prosta gra, programowalna na domowych komputerach z połowy lat 70. XX wieku, na maszynach tak podstawowych, że zamiast mikroprocesorów wielkości kurcząt, myślę, że niektóre z nich prawdopodobnie miały tam prawdziwe kurczęta.

Wywołajmy minioną epokę, odtwarzając grę na nowoczesnym sprzęcie.

  1. Gracz zaczyna w losowym pokoju na mapie dwudziestościennej (w ten sposób w sumie jest 20 pokoi połączonych ze sobą jak twarze dwudziestościanu, a każdy pokój ma dokładnie trzy wyjścia).

  2. Wumpus rozpoczyna się w losowo wybranym innym pokoju. Wumpus śmierdzi, a jego zapach można wykryć w dowolnym z trzech pomieszczeń sąsiadujących z jego lokalizacją, chociaż kierunek zapachu jest niemożliwy do ustalenia przez gracza. Gra informuje tylko, że „czujesz wumpusa”.

  3. Gracz nosi łuk i nieskończoną liczbę strzał, które może wystrzelić w dowolnym momencie do pokoju przed sobą. Jeśli wumpus jest w tym pokoju, umiera, a gracz wygrywa. Jeśli wumpusa nie było w tym pokoju, jest zaskoczony i porusza się losowo do dowolnego z trzech pokoi połączonych z jego bieżącą lokalizacją.

  4. Jeden losowo wybrany pokój (na pewno nie jest to pokój, w którym gracz się rozpoczyna) zawiera dół bez dna. Jeśli gracz znajduje się w dowolnym pomieszczeniu przylegającym do dołu, odczuwa powiew wiatru, ale nie ma pojęcia, z jakich drzwi pochodzi wiatr. Jeśli wejdzie do pokoju z jamą, umiera, a Wumpus wygrywa. Wumpus nie ma wpływu na jamę.

  5. Jeśli gracz wejdzie do pokoju Wumpusa lub jeśli Wumpus wejdzie do pokoju gracza, Wumpus wygra.

  6. Gracz określa kierunek, w którym patrzy, za pomocą liczby (1 = prawa, 2 = lewa, 3 = wstecz), a następnie akcji (4 = strzał w strzałę, 5 = idź w określonym kierunku).

  7. Dla celów punktacji każdy ciąg gry („Czujesz powiew”, „Czujesz wumpus”, „Twoja strzała niczego nie trafił” itp.) Można uznać za jeden bajt. Nie nadużywaj tego, aby ukryć kod gry w tekście; służy tylko do interakcji z odtwarzaczem.

  8. Poświęć 10% swojego bajtu na wdrożenie megabatów, które zaczynają się w losowym pokoju innym niż gracz (chociaż mogą dzielić pokój z wumpusem i / lub jamą). Jeśli gracz wejdzie do pokoju z nietoperzami, nietoperze przeniosą gracza do innego losowo wybranego pokoju (na pewno nie będzie to pokój z jamą lub wumpusem), zanim odlecą do swojej nowej, losowej lokalizacji. W trzech pokojach sąsiadujących z nietoperzami słychać skrzypienie, ale gracz nie otrzymuje informacji o tym, z którego pomieszczenia pochodzi dźwięk.

  9. Poświęć 35% liczby bajtów na wdrożenie interfejsu graficznego, który pokazuje mapę dwudziestościenną i pewnego rodzaju informację, jaką gracz dotychczas posiadał na temat lokalizacji dołu, wumpusa i nietoperzy (jeśli dotyczy), w stosunku do gracz. Oczywiście, jeśli wumpus się poruszy lub gracz zostanie poruszony przez nietoperze, mapa musi zostać odpowiednio zresetowana.

  10. Najniższa liczba bajtów, po skorygowaniu, wygrywa.

Podstawowy kod źródłowy wersji gry (niekoniecznie zgodny z powyższymi regułami, a w każdym razie całkowicie nie golfowym) można znaleźć na stronie tej stronie i prawdopodobnie w innych.

Michael Stern
źródło
Kilka wyjaśnień: 3. jeśli wumpusa nie było w tym pokoju, jest zaskoczony i przenosi się do TRZY pokoi. Więc jeśli wystrzelisz strzałę i spudłujesz, wumpus może przyjść i cię zabić, prawda? A wumpus będzie się poruszał tylko, gdy będzie zaskoczony, w przeciwnym razie pozostanie na miejscu? 6. Rozumiem, że kierunek gracza zależy od pokoju, z którego przyszedł. Gdyby więc przybył z południa, jego opcje byłyby 1. północno-wschodnie 2. północno-zachodnie 3. południowe, a gdyby przybył z północy, byłoby odwrotnie. Także twoje zasady wydają się prostsze / bardziej golfowe niż program referencyjny (którego jeszcze nie zbadałem szczegółowo). Czy mam rację?
Level River St
Argh! Nie mogę znaleźć żadnych zdjęć podwójnego wykresem nigdzie dwudziestościanu w sieci.
Jack M
1
@steveverrill Tak, jeśli go wystraszysz, może przyjść i cię zabić. Jeśli go nie wystraszysz, nie będzie się ruszał. Istnieje wiele odmian gry; wiele wersji pozwala na przykład krążyć wokół siebie i zabija cię. Rozłożyłem to.
Michael Stern
3
@JackM mapa ścian dwudziestościanu jest identyczna z mapą wierzchołków dwunastościanu i ten wykres można łatwo znaleźć. Spróbuj na przykład wolframalpha.com/input/?i=DodecahedralGraph+edgerules lub równoważne polecenie Mathematica GraphData [„DodecahedralGraph”, „EdgeRules”]. Tak czy inaczej otrzymujesz {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 Nie, „powrót” oznacza odwrócenie się i powrót tak, jak przybyłeś. Jeśli dwukrotnie naciśniesz „wstecz”, skończysz w miejscu, w którym zacząłeś. Nie trzeba przechowywać wcześniejszych stanów gry.
Michael Stern

Odpowiedzi:

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

Wynik jest uzyskiwany przez pobranie liczby bajtów (290), dodanie liczby ciągów używanych do interakcji z użytkownikiem (6) i odjęcie łącznej długości tych ciągów (133). Kanały są częścią ciągów i przyczyniają się do zliczania bajtów.

Kamienie milowe

  1. Przeniesiono odpowiedź professorfish z Bash na GolfScript. Wynik: 269

  2. Działał zgodnie z sugestiami Petera Taylora zawartymi w komentarzach. Wynik: 250

  3. Peter Taylor przeredagował cały mój kod i pomógł mi skompresować tabelę wyszukiwania. Wynik: 202

  4. Zastąpiono tabelę wyszukiwania sąsiednich pokoi matematycznym podejściem. Wynik: 182

  5. Refaktoryzowane dane wejściowe, wyjściowe i funkcja wspierająca podejście matematyczne. Wynik: 163

Wielkie „Dziękuję!” Dla Petera Taylora za całą jego pomoc.

Jak to działa

20 pokoi jest reprezentowanych jako wierzchołki dwunastościanu, którym przypisano numery od 0 do 19 w następujący sposób:

Graf dodekaedryczny

Aby znaleźć pokoje przylegające do pokoju N i uporządkować je zgodnie z ruchem wskazówek zegara, musimy wziąć pod uwagę cztery przypadki:

  • Jeśli N ≡ 0 mod 4 (niebieskie wierzchołki), sąsiedni pokój to 19 - N , N + 2 mod 20 i N - 2 mod 20 .

  • Jeśli N ≡ 1 mod 4 (zielone wierzchołki), sąsiedni pokój to 19 - N , N - 4 mod 20 i N + 4 mod 20 .

  • Jeśli N ≡ 2 mod 4 (żółte wierzchołki), sąsiedni pokój to 19 - N , N - 2 mod 20 i N + 2 mod 20 .

  • Jeśli N ≡ 3 mod 4 (czerwone wierzchołki), sąsiednie pomieszczenie to 19 - N , N + 4 mod 20 i 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
źródło
1
Można zapisać w 1 Qz 19rand 97+; 2 w @z 97%3*&>..., kolejny 1 przez inline Qjak {19rand 97+}2*:,\:H, kilka zastępując |z *, który jest często najlepszym sposobem, aby zrobić if. Bnie ma żadnego sensu i myślę, że kilka innych zmiennych można by wyeliminować za pomocą stosu.
Peter Taylor
1
Zapomniałem wspomnieć o innej częstej sztuczce: konwersji podstawowej dla tabel odnośników. Możesz zastąpić 62 znaki dla listy sąsiedztwa ciągiem 33 znaków, po którym następuje 256base 20base(i prawdopodobnie również wyeliminować kilka +/- 97). Jedynym minusem jest to, że będzie wymagał znaków niedrukowalnych.
Peter Taylor
1
Zapisałem kolejne 13 , refaktoryzując się, by być bardziej idiomatycznym GS (głównie używając stosu zamiast zmiennych); i jest dodatkowe 10 kosztem zmniejszenia wydajności. To oprócz kompresji tabeli odnośników wspomnianej w poprzednim komentarzu.
Peter Taylor
1
W ogóle mi się podobało. Jestem po prostu rozczarowany, że podejście do tabeli odnośników jest o wiele lepsze niż bardziej matematyczne, którego zamierzałem użyć. BTW, myślę, że twoja obecna wersja ma mały błąd, ponieważ jeśli wystrzelisz strzałę, spudłujesz i zaskoczysz wumpusa w swoim pokoju, wtedy wyjdzie tylko You were killed by the wumpusbez żadnej wzmianki o strzale. Właśnie dlatego dołączałem w nie ładnej wersji.
Peter Taylor,
1
2*2+=>)2*
Peter Taylor
15

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

Poniżej znajduje się przegląd gry, pokazujący, że (pod warunkiem, że nie zaczniesz tuż przy ryzyku) z prawidłową grą, zawsze możesz wygrać. Gracz odczuwa powiew wiatru, zawraca i wykonuje pełną pętlę przeciwnie do ruchu wskazówek zegara. Ponieważ zajmuje mu dokładnie 5 ruchów, aby znów poczuć powiew wiatru, zna dziurę po swojej prawej stronie i oddala się jak najdalej. Podobnie, gdy wącha wumpusa, nie wiedząc, czy jest on prawy czy lewy, odwraca się i wykonuje pętlę zgodnie z ruchem wskazówek zegara. Ponowne wyczucie wumpusa zajmuje mu 5 ruchów, więc wie, że jest po lewej stronie i strzela z pewnością.

Gdyby zapętlił w drugą stronę, szybciej znalazłby wumpusa i wiedziałby, że jest w tym samym kierunku, w którym się obraca.

wprowadź opis zdjęcia tutaj

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

Dodano nowe linie dla przejrzystości. Zmiany w stosunku do wersji 0 są następujące:

Ogromne podziękowania dla @Dennis za zalecenie kompilatora GCC w emulatorze Cygwin Linux dla systemu Windows. Ten kompilator nie wymaga includes w programie rev 0 i pozwala na domyślny inttyp zmiennych i main.To jest zmieniająca życie wskazówka golfowa!

Dodatkowo uruchomienie w systemie Linux powoduje, że \fkursor przesuwa się w dół bez powrotu karetki (w przeciwieństwie do systemu Windows, w którym po prostu wyświetla symbol do wydrukowania). Pozwoliło to na znaczne skrócenie instrukcji printf drukującej tablicę

Kilka dodatkowych wskazówek od Dennisa w komentarzach i jedna z moich: zmiana stanu podczas sprawdzania, czy strzałka uderzyła w wumpusa: if(q==w)> if(q-w)(..else .. jest odwrócona)

Dodanie graficznego wyświetlacza pokazującego informacje, które gracz wie o tym, gdzie pachnie wumpus / czuje się bryza, aby otrzymać 35% premii. (Usunąłem starą wersję tego debugowania, która pokazywała dokładną pozycję wumpusa i dziury. Można to zobaczyć w historii edycji).

REV2 C (GCC na Cygwin), bonus 389-35% = 252,85

#define Q(N) (N+"QTLOQMQOSOLT"[N%4*3+e])%20
#define P printf(
i,d,e,a,b;main(){int p=19,q=srand(&p),h=rand()%p,w=rand()%p;
while(p-h&&p-w){
  for(e=3;e--;){q=Q(p);q-w||P"You smell a wumpus\n",a|=2<<p);q-h||P"You feel a breeze\n",b|=1<<p);}
  for(i=20;i--;)P"%c%c",i-p?48+(a>>i&2)+(b>>i&1):"-\\/"[d],"\n \v "[i%4]);
  scanf("%d",&i);e=(d+i/9)*"edde"[p%4]%3;q=Q(p);
  if(i%5){e=rand()%3;w=q-w?P"Your arrow didn't hit anything\n",a=0)&Q(w):(p=20);}
  else p=q,d=e;
}
P p-20?p-w?"YOU FELL IN A HOLE!\n":"THE WUMPUS GOT YOU!\n":"YOU KILLED THE WUMPUS!\n");}

Jeszcze raz dziękuję Dennisowi za refaktoryzację mojego kodu:

Stała Char m[]zastąpiona literałami (nie wiedziałem, że można indeksować literał.)

Ziarno liczb losowych zmienną stosową (zależne od systemu, niektóre systemy losowo przydzielają pamięć jako środek bezpieczeństwa).

Makro z putszastąpionym makrem z printfdodatkowym kodem, który należy wykonać, gdy wyświetlany komunikat umieszcza się wewnątrz printfargumentów (zaletą tej strony jest to, że printf nie drukuje kilku ostatnich argumentów, jeśli w ciągu formatu nie ma wystarczającej liczby specyfikatorów formatu). ifzastąpione przez||

Obliczanie nowej pozycji gracza / wumpus umieszczonej w nowym makrze.

Wygrywaj / przegrywaj wiadomości umieszczone poza whilepętlą. ifzastąpiony przez operatora warunkowego.

Zastosowanie operatora warunkowego w linii do strzelania strzałą. Jeśli gracz nie trafi, wymaga to zarówno wydrukowania wiadomości, jak i dostosowania pozycji wumpus. Dennis zaproponował kilka sposobów łączenia printfi obliczania pozycji wumpusa w jednym wyrażeniu, ale wybrałem jeden z moich własnych. printfzwraca liczbę wydrukowanych znaków, czyli Your arrow didn't hit anything\n31 (11111 binarnie.) Tak więc 31&Q(w)==Q(w).

Mój inny wkład w tę edycję polegał na wyeliminowaniu niepotrzebnych nawiasów.

Wydajność

Tutaj gracz już odkrył, gdzie jest Wumpus, ale decyduje się na dokładną eksplorację, aby dowiedzieć się, gdzie dokładnie jest dół. W przeciwieństwie do mojej starej wersji debugowania, która pokazywała, gdzie wumpus i pit były w trakcie gry, pokazuje to tylko pokoje, w których gracz odwiedził i poczuł powiew (1), który wyczuł wumpus (2) lub oba (3). (Jeśli gracz strzela strzałą i chybia, zmienna azawierająca informacje o pozycji wumpus zostaje zresetowana.)

wprowadź opis zdjęcia tutaj

REPREZENTACJA ICOSAHEDRON

Uwaga: ta sekcja oparta jest na wersji 1

Moja funkcja gwiazdy! W moim kodzie nie ma wykresu. Aby wyjaśnić, jak to działa, zobacz mapę świata poniżej. Dowolny punkt na dwudziestościanie może być reprezentowany przez szerokość 0–3 i długość 0–4 (lub pojedynczą liczbę long*4+lat.) Linia długości oznaczona na mapie przechodzi tylko przez te twarze o długości zero, a linia szerokości przechodzi przez środek twarzy o zerowej szerokości geograficznej.

Gracz może być zorientowany na 3 możliwych osiach, reprezentowanych następującymi symbolami: północ-południe -północny wschód-południowy zachód \północny zachód-południowy wschód /. W każdym pokoju ma dokładnie jedno wyjście na każdej z dostępnych mu osi. Na pokazanym ekranie odtwarzacz wykonuje pełną pętlę zgodnie z ruchem wskazówek zegara. Generalnie łatwo jest zidentyfikować na podstawie oznaczenia gracza, skąd pochodził, a zatem gdzie może się udać.

Jedynym przypadkiem, który jest nieco trudny dla niewtajemniczonego oka, jest czwarty. Kiedy zobaczysz nachylenie w jednym z tych biegunowych rzędów, gracz wyszedł z komórki polarnej najbliższej zewnętrznego końca skosu i jest skierowany ogólnie w kierunku równika. Tak więc gracz jest skierowany na południowy wschód, a jego opcje to: 15 (POŁUDNIOWA, komórka po prawej) 25 (północno-wschodnia, komórka powyżej) lub 35 (północno-zachodnia, komórka poniżej).

Zasadniczo odwzorowuję dwudziestościan na siatkę 5x4, z komórkami ponumerowanymi od 19 do 0 w kolejności, w jakiej są drukowane. Ruch wykonuje się przez dodanie lub odjęcie bieżącej pozycji, w zależności od szerokości i kierunku gracza, zgodnie z tabelą poniżej.

Jeśli gracz zejdzie z dolnej (zachodniej) planszy, wraca na górną (wschodnią) stronę i odwrotnie, więc jego pozycja jest przejmowana modulo 20. Zasadniczo ruchy są kodowane do m [] przez dodanie ascii 80 ( P) do wartości surowej, podając znaki przedstawione poniżej, ale zasadę można dodać dowolną wielokrotność 20 bez wpływu na operację.

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

Dane wejściowe gracza (podzielone przez 10, aby usunąć drugą cyfrę) są dodawane do jego bieżącego kierunku i pobierane modulo 3, aby uzyskać jego nowy kierunek. To działa dobrze w większości przypadków. Jednak jest problem, gdy jest on w pokoju polarnym i zbliża się do bieguna. Podczas składania mapy poniżej będzie jasne, że jeśli opuści pokój skierowany na „północny wschód”, wejdzie na nowy kwadrat skierowany na „południowy wschód”, więc należy dokonać korekty. Odbywa się to w linii e=(d+i/10)*m[p%4]%3;przez pomnożenie przez m[p%4]. Pierwsze cztery wartości m [] są wybrane w taki sposób, że oprócz ich powyższej funkcji mają także charakterystykę m[1]%3==m[2]%3==1i m[0]%3==m[3]%3==2. Pozostawia to kierunek samemu dla pomieszczeń równikowych i stosuje niezbędną korektę dla pomieszczeń polarnych.

Logicznym czasem na dokonanie korekty byłby ruch. Jednak aby zapisać postacie, należy to zrobić przed ruchem. Dlatego niektóre wartości wm [] muszą zostać przetransponowane. Ostatnie 2 znaki są na przykład LTzamiast w TLpowyższej tabeli.

wprowadź opis zdjęcia tutaj

KOD NIEGOLFOWANY

jest to kod rev 1, który jest mniej zaciemniony niż rev 2.

To będzie działać na GCC / Linux. W komentarzach umieściłem dodatkowy kod potrzebny do uruchomienia go w Visual Studio / Windows. To duża różnica!

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

}

ZAGADNIENIA I CIEKAWOSTKI

Wykorzystałem punkt wspomniany przez @professorfish, jeśli wumpus i pit startują w losowych miejscach, nie ma potrzeby, aby gracz zaczynał w losowym miejscu. Gracz zawsze zaczyna od pokoju 19 skierowanego na północ.

Rozumiem, że ponieważ wumpus jest „niewrażliwy na otchłań”, wumpus może rozpocząć lub wejść do pokoju, w którym znajduje się otchłań. Zasadniczo upraszcza to sprawy z wyjątkiem jednego punktu. Nie mam konkretnej zmiennej wskazującej, że gra się skończyła; kończy się, gdy gracz zbiega się z wumpusem lub pitem. Kiedy więc gracz wygrywa, wyświetlam komunikat o zwycięskiej wygranej, ale przesuwam dół do gracza, aby wyjść z pętli! Nie mogę wsadzić gracza do dołu, ponieważ może tam być Wumpus i dostanę wiadomość o Wumpusie, której nie chcę.

Rev0program działał doskonale w studiu wizualnym, ale IDE powiedział „stos uszkodzony wokół zmiennej i” przy wyjściu. To dlatego, scanf próbuje umieścić intw char.Dennis podano nieprawidłowe zachowanie na swoim komputerze z systemem Linux z tego powodu. W każdym razie jest to naprawione przez użycie poprawnego typu w wersji 1.

Linia do wyświetlania planszy w wersji 0 jest niezdarna i wygląda nieco inaczej na innych platformach. W Rev 1 ma rewizję tej linii, która używa znaku \ f formfeed i dlatego nie potrzebuje znaku formatu na początku printf. To sprawia, że ​​jest krótszy, ale \ f nie działa w systemie Windows.printf(" %c%c%c") środku% c jest wyświetlany znak do wydrukowania. Ostatni% c to ASCII 0 lub ASCII 10 (\ n, nowa linia ze znakiem powrotu karetki w systemie Windows.) Wygląda na to, że w systemie Windows nie ma znaku, który działałby w konsoli, który przejdzie w dół linii bez podania znaku powrotu karetki. Gdyby tak było, nie potrzebowałbym pierwszej c% (ASCII 0 lub ASCII 9 przed znakiem szerokości geograficznej 1. Zakładki są notorycznie niezdefiniowane w swoim zachowaniu). Wiodące miejsce poprawia formatowanie (umieszcza znaki szerokości 3 i 2 bliżej znaku szerokości 1 .)

Level River St
źródło
1
Uwielbiam ten opis.
Michael Stern
Nie jestem pewien, czy to ze względu na modyfikacje musiałem uczynić go skompilować z GCC na platformie Linux (usunąć pierwszy obejmują zastąpienie scanf_sze scanfi to stdio.hjeśli mogę skompilować jak C ++ Rater niż C), ale to nie dość pracy dla mnie. Na przykład, jeśli pójdę w lewo, a potem w prawo na początku ( 15 35), znajdę się w innym pokoju niż ten, w którym zacząłem.
Dennis,
@Dennis wyśledziłem źródło błędu przy wyjściu. to scanf_s (podobno bezpieczny!), który „psuje stos wokół zmiennej i”, gdy próbuje umieścić w char, 32-bitową liczbę całkowitą. Pierwszą rzeczą, którą zasugerowałem, jest sprawdzenie typu używanego przez scanf dla „% d” i zmiana zmiennej i na ten typ. Otrzymuję poprawną odpowiedź bez błędu wyjścia dla znaku, poprawną odpowiedź bez błędu wyjścia dla int i złą odpowiedź z typem Microsoft __int64 (równoważne długie długie, chyba że wstawię „% lld”.) Również uruchomiłeś wersja bez golfa i czy miałeś jakieś problemy z wyświetlaczem?
Level River St
@steveverrill: Tak, wypróbowałem obie wersje. Rodzaj ijest rzeczywiście problemem. Strona człowiek mówi: „ d zestawienia ewentualnie podpisane całkowitą dziesiętną; Następny wskaźnik musi być wskaźnik do int .” Zmiana typu sprawia, że ​​działa dobrze.
Dennis
@steveverrill: Nie wiem, jak VS radzi sobie z różnymi rzeczami, ale jeśli kompilujesz z GCC (jako C, a nie C ++), możesz zapisać wiele znaków. Żaden z includes jest potrzebne, jeśli zastąpi NULLsię 0i scanf_sz scanf, nie trzeba intwcześniej maini można poruszać ii dpoza main (domyślnie one inti są inicjowane 0). Ponadto, można określić p=19,h=rand()%p,w=rand()%p, wymienić m[]się *mi powinno być możliwe do zdefiniowania makra dla wszystkich instancji if(...==...)puts(...);.
Dennis
9

GolfScript, 269 znaków

{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

Zauważ, że 163 odjęto od liczby znaków dla zakodowanych ciągów. Jeśli chcesz, aby wynik debugowania wskazywał numery pokojów, dodaj następujący wiersz zaraz po pierwszym wystąpieniu ^:

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

Przykładowa sesja (z dodatkowym wyjściem debugowania):

  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
źródło
Oto pierwszy działający kod. Wracam później, by trochę pograć w golfa.
Howard
Mój kod jest obecnie o 1 znak dłuższy. Szukam dalszego sposobu gry w golfa!
Timtech
Nie chodzi o to, że potrzebujesz mojej pomocy, ale możesz zapisać 14 znaków, definiując {puts}:|;, 5 znaków, zastępując je Ri za Wpomocą -i >(pozwala wyeliminować otaczające spacje) i 9 znaków, upuszczając '> 'print(nie wydaje się, żeby pytanie to wymagało).
Dennis
@Dennis Dziękuję. Z pewnością zrealizuję niektóre z twoich sugestii.
Howard
9

JavaScript (ECMAScript 6) - 2197 1759 -45% = 967,45 znaków

Prawie skończyłem grać w golfa ...

Zawiera GUI z mapą Icosahedral i Mega-Bats dla pełnych bonusów.

Wumpus GUI

  • Każdy pokój ma 4 przyciski: X( dół ); B(Mega-Bat); W(Wumpus); iP (Ty).
  • Twoja aktualna lokalizacja ma kolor niebieski.
  • Przyciski mają kolor czerwony, jeśli reprezentowany obiekt może znajdować się w tym miejscu, i zielony, jeśli zdecydowanie nie znajduje się w tym miejscu.
  • WiP można klikać tylko w pokojach sąsiadujących z bieżącą lokalizacją.
  • Jeśli wygrasz, tło zmieni kolor na zielony, a jeśli umrzesz, tło zmieni kolor na czerwony.

Kod:

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
źródło
Otrzymujesz 1066 bez ECMA 6 za pomocą kompilatora zamknięcia.
AMK
Zastanawiałem się, o ile łatwiej byłoby, gdybyś miał graficzną reprezentację, która pomogłaby ustalić, gdzie są rzeczy. 1+, ale to trochę zbyt łatwe :)
Sylwester
9

Bash, 365 (pierwsza działająca wersja 726!)

ŁĄCZENIE Z GOLFSCRIPT?

@Dennis w zasadzie zrobił dla mnie całą grę w golfa. Dzięki!

Program zakłada prawidłowe wejście. Prawidłowym wprowadzeniem jest wybrany kierunek (1 dla prawej, 2 dla lewej, 3 dla tyłu), a następnie twoja akcja (4, aby strzelać, 5, aby iść).

Niektóre wyjaśnienia

Zwykle robię duże szczegółowe objaśnienia, ale jest to prawdopodobnie zbyt skomplikowane, żebym się tym przejmował.

Każdy wierzchołek na wykresie dwunastościanu jest kodowany jako litera (a = 1, b = 2, ... t = 20).

Pozycja początkowa gracza wynosi zawsze 20 (i stoją plecami do 18), ponieważ to nie ma znaczenia samo w sobie, liczą się tylko względne pozycje gracza, pit i wumpus.

Zmienna $pprzechowuje lokalizację gracza. $rprzechowuje poprzednią lokalizację gracza. $wjest wumpus i$h (H dla dziury) to dół.

Kod

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

Historia wersji

  1. Pierwsze wydanie, 698 znaków
  2. Naprawiono błąd, w którym „Czujesz powiew” i „Czujesz wumpus” nie może wyświetlać się jednocześnie; zapisano 39 znaków, tworząc funkcję generowania liczb losowych.
  3. Pamiętał, że wumpus porusza się, jeśli strzelasz i chybiasz. 726 znaków.
  4. Zrobiłem grep -oEzmienną. Zapisano 5 znaków.
  5. Zrobiłem [a-z]{3}zmienną. Zapisano 3 znaki.
  6. Zrobiłem echozmienną. Zapisano 5 znaków.
  7. Działał na podstawie większości sugestii @Dennis. Zapisano 72 znaki.
  8. Dodano wszystkie pozostałe sugestie. Zapisano 68 znaków.
  9. Zapisano 2 znaki z sugestii @DigitalTrauma.
  10. Naprawiono poważny błąd, w którym można strzelać do Wumpusa tylko wtedy, gdy jest on po prawej stronie. Ta sama liczba znaków.
  11. Wykorzystano rozszerzenie parametrów, aby zgolić 2 znaki przy użyciu $m.
  12. Ogoliłem wiele znaków, porzucając grepi będąc nieco bardziej rozsądnym.
  13. Zdefiniowany Cjako funkcja wyszukiwania wyrażenia regularnego do użycia w instrukcjach if oraz Ejako funkcja wypisująca „Zabiłeś wumpusa” i wychodząca.
  14. Zapisano 1 znak dzięki przegrupowaniu „if statement”.
  15. Pozbyłem się wielu znaków d i usuwając niepotrzebne nawiasy.
  16. Naprawione błędy. Dodano wiele znaków :(
  17. OSZCZĘDNOŚCI MOARR ( http://xkcd.com/1296/ )
  18. Kolejny z pomysłów @Dennis (oszczędność kilku znaków) i moje sprytne (ab) użycie pośrednictwa (oszczędność 1 znaku).
  19. Poprawka stylu dla q ().
  20. ponownie dodano poprawne wyjście

Przykładowy przebieg

Wprowadzane jest „In:”, „Out: is output”.

Gracz błąka się trochę, wącha wumpusa i strzela. Tęsknią, a wumpus wchodzi do ich pokoju i je je.

W: 15

W: 15

W: 25

W: 25

W: 15

Out: Czujesz wumpus

W: 14

Out: Przegapiłeś

Out: Wumpus zjadł cię


źródło
1
Myślę, że możesz skrócić swój kod o co najmniej 100 bajtów. 1. exitjest tylko jeden bajt dłuższy g=1i eliminuje potrzebę testowania niezerowych gi niektórych elifinstrukcji. 2. Możesz używać ((i==35))zamiast [ $i = 35 ]i ...&&...zamiast if ... then ... fi. 3. q(){ L=({a..s});$j ${L[RANDOM%19]};}i n=`$k $w$m<<<$d`;w=${n:RANDOM%2+1:1}oba oszczędzają kilka bajtów.
Dennis,
1
Wymienić while :;do... donez for((;;);{... }dla oszczędności 3 char
Cyfrowego uraz
1
@professorfish: Myślę, że funkcja działałaby lepiej niż obecne podejście do cięcia ciągów znaków. Na przykład d(){ x=npoemfgnshtoksblbtckpdpljqniorelgfhkbqraicadjaghimsmjtqecrdf;s=${x:3*30#$1-30:3};}pozwoli ci zastąpić definicje si nprzy pomocy d $pi d $w. Jeśli ponadto określić u=${s#*$r}$s(i dostosować definicje li fOdpowiednio), nie trzeba będzie $ki $mjuż. Myślę, że oszczędza 83 bajty. Również miejsce w q ()nie jest wymagane.
Dennis
1
@professorfish: Możesz zaoszczędzić 3 dodatkowe bajty, definiując c(){ [[ $1 =~ $2 ]];}i zastępując np. drugą do ostatniej linii c $r $b||{ $j You missed;d $w;w=${s:RANDOM%2+1:1};}.
Dennis
1
@professorfish: Korzystanie z funkcji, którą zasugerowałem, powinno być 3 bajty krótsze. Możesz zaoszczędzić dodatkowe 106 bajtów, zastępując po cztery linie b=$pz d $p;u=u${s#*$r}$slinie po read iz 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};}i pozbycie się 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];

W końcu dogonił wersję tabeli odnośników Dennisa, z której sporo się pożycza. Ciekawą rzeczą w tej wersji jest to, że nie ma tabeli odnośników do układu pokoju.

W 60 o symetrii obrotowej o dwudziestościanu są izomorficzne z grupy przemiennego 5 liter, A_5. Po wypróbowaniu wszystkich podejść do zwięzłego reprezentowania grupy wróciłem do najprostszego: każdy element jest permutacją parzystej parzystości. Grupę można wygenerować z dwóch generatorów na więcej niż jeden sposób: moje podejście wykorzystuje generatory 3i 3 1. To pozwoli nam na generowanie 1 = 3 3 1, 2 = 3 3 1 3 1i3 = 3 .

Zauważ, że kierunek 3odpowiada elementowi rzędu 2, ponieważ po przejściu przez drzwi za tobą, drzwi te znów są za tobą. Kierunek 1odpowiada elementowi rzędu 5, idąc wokół wierzchołka dwudziestościanu. (Podobnie element 2). A kombinacja 3 1jest rzędu 3, ponieważ krąży wokół pokoi sąsiadujących z pokojem, który zaczyna się za tobą.

Szukamy więc permutacji rzędu 2 do reprezentowania kierunku 3i permutacji rzędu 5 do reprezentowania kierunku 1takiego, który 3 1jest rzędu 3.

W A_5 jest 15 permutacji rzędu 2, a dla każdej z nich jest 8 permutacji kandydujących na 1(a więc na 3 1). Jest oczywistym atrakcją [4 3 2 1 0]dla 3: odwrócenie tablicę tylko -1%. Z możliwych możliwych kombinacji permutacji 3 1wybrałem [0 1 3 4 2], co dopuszcza dość krótką implementację jako [~@].

Bez golfa

# 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
źródło
Ładne podejście algebraiczne! Jest jednak drobny błąd: 10/@3%=próbuje uzyskać dostęp do czwartego elementu tablicy o długości 3, jeśli dane wejściowe są 35.
Dennis
@Dennis, tak, zdałem sobie sprawę po tym, jak poszedłem spać. Mogę wymyślić różne sposoby naprawienia tego problemu, wszystko kosztuje 2.
Peter Taylor
Możesz odzyskać jeden char 9/3%@3%=.
Dennis
Obecnie mam 7 rang i nieco bardziej drastyczna restrukturyzacja. Ale ten 1 znak 9/zamiast 10/nadal działa, więc dzięki.
Peter Taylor
5

Wumpus , 384 - 129 (łańcuchy) = 255 bajtów

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@

Wypróbuj online! (Oczywiście TIO nie ma większego sensu, ponieważ nie można tam używać programu interaktywnie, a gdy program skończy się instrukcjami na STDIN, przeczyta 0 0, co jest równoważne 3 4, więc skończysz strzelanie strzałami, dopóki Wumpus się tam nie przeniesie lub nie zabije.)

Uruchamiając to lokalnie, upewnij się, że wysunięcie linii po drugiej liczbie każdego wejścia zostaje opróżnione (ponieważ Wumpus potrzebuje go do ustalenia, że ​​liczba się skończyła). W Powershell muszę jakoś wprowadzić jeszcze jeden znak po wysunięciu wiersza, aby działał (nie ma znaczenia, który znak, ale do testowania użyłem tylko podwójnego wysuwu).

Jest jeszcze dużo miejsca na grę w golfa, ale wypróbowanie zupełnie nowych układów zajmuje trochę czasu. Ostateczny wynik zależy również od rzeczywistych ciągów, których używam, ponieważ w języku 2D ciąg N bajtów kosztuje więcej niż N bajtów kodu źródłowego, ponieważ nakłada znaczne ograniczenia na układ kodu, a ty często trzeba go podzielić na wiele sekcji (co wiąże się z dodatkowymi podwójnymi cudzysłowami). Na końcu, jeśli zredukowałbym każdy ciąg do jednej litery (i -129 do -12), prawdopodobnie zaoszczędziłbym tonę bajtów.

Wyjaśnienie

Najpierw zrzeczenie się odpowiedzialności: pomimo nazwy języka nie zostało ono zaprojektowane tak, aby wdrożenie Hunt the Wumpus było szczególnie łatwe. Zamiast tego po raz pierwszy zaprojektowałem język wokół tematu trójkątów, skończyłem z dwudziestościenną strukturą danych i dlatego nazwałem go Wumpus.

Tak, chociaż Wumpus opiera się głównie na stosie, ma również 20 rejestrów rozmieszczonych wokół powierzchni dwudziestościanu. Oznacza to, że otrzymujemy strukturę danych do reprezentowania mapy za darmo. Jedyne, czego nie możemy zrobić łatwo, to znaleźć określone twarze w dwudziestościanie, więc aby je wyszukać, musimy „rzucać d20”, aż skończymy na szukanej twarzy. (Można to zrobić w sposób deterministyczny, ale zajęłoby to o wiele więcej bajtów.) Wyszukiwanie takich twarzy kończy się prawie na pewno (tj. Z prawdopodobieństwem 1), więc wyszukiwanie trwające wiecznie nie jest problemem w praktyce).

Powyższy kod jest wersją tej pierwszej implementacji w wersji golfowej z rozsądniejszym układem:

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.

Ponieważ gra w golfa polegała głównie na kompresji układu, na razie wyjaśnię tę wersję (dopóki nie dodam żadnych sztuczek golfowych wykraczających poza restrukturyzację kodu).

Zacznijmy od kodu instalacyjnego:

1SDL2vSD70L?.;;2.

Początkowo wszystkie twarze są ustawione na 0 . Zakodujemy wumpus ustawiając 1-bit odpowiedniej powierzchni, a dół ustawiając 2-bit. W ten sposób oboje mogą znajdować się w tym samym pokoju. Pozycja gracza nie zostanie w ogóle zapisana na dwudziestościanie, zamiast tego zawsze będzie aktywną twarzą (tylko jeden z 20 rejestrów jest aktywny jednocześnie).

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.

Teraz musimy znaleźć losową pustą twarz, aby umieścić gracza.

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.

Następna sekcja sprawdza otoczenie gracza i drukuje odpowiednie ostrzeżenia:

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

Jest to pętla, przez którą przebiegamy 3 razy. Za każdym razem, gdy patrzymy na prawego sąsiada, drukujemy odpowiednie łańcuchy, jeśli istnieje zagrożenie, a następnie obracamy dwudziestościan o 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.

Następna sekcja odczytuje z gracza dwie liczby, a następnie albo przesuwa gracza, albo strzela strzałą. Pierwszy jest trywialny, drugi mniej. Głównym problemem przy strzelaniu do strzały jest przypadek, w którym tęskni. W takim przypadku a) musimy poszukać wumpusa, aby go przenieść, a następnie b) wrócić do pokoju gracza i prawidłowej orientacji dwudziestościanu (tak, aby „powrót” pozostał „z powrotem”). To najdroższa część całego programu.

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

Punkt wejścia do tej sekcji znajduje Isię po lewej stronie.

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.

Uff, to była trudna część. Teraz musimy tylko sprawdzić, czy gracz umiera, i w przeciwnym razie zacząć od głównej pętli:

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

Struktura tej sekcji jest zasadniczo identyczna ze strukturą, której użyliśmy podczas sprawdzania otoczenia gracza: sprawdzamy 1-bit bieżącej twarzy (pokój gracza) i jeśli jest ustawiona, drukujemy The wumpus ate you.i kończymy program. W przeciwnym razie sprawdzamy 2-bit i po jego ustawieniu drukujemy You fell into the pit.i kończymy program. W przeciwnym razie dochodzimy do tego, 2.który przeskakuje z powrotem na początek głównej pętli (na współrzędnych (0, 2)).

Martin Ender
źródło
1

awk - duży

Nie okazało się to tak krótkie, jak się spodziewałem, ale podjąłem nieco inne podejście do wykresu, więc i tak publikuję wersję bez golfa.

Skorzystałem z faktu, że dwudziestościan (wielościenny wielościan) pod rotacjami zachowujący orientację jest izomorficzny w stosunku do naprzemiennej grupy stopnia 5 (permutacje 5-elementowe o parzystej liczbie cykli o parzystej długości). Następnie wybieram dwie permutacje o długości cyklu 5 jako „lewą” i „prawą”, i wybieram jedną permutację o długości cyklu 2 jako „wstecz”. Korzystając z nich, buduję wykres z jednego pokoju, idąc ścieżką hamiltonowską (2xRRRLLLRLRL, używając 3xRB w każdym pokoju, aby uchwycić 3 możliwe kierunki).

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
źródło