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.
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 include
s w programie rev 0 i pozwala na domyślny int
typ zmiennych i main.
To jest zmieniająca życie wskazówka golfowa!
Dodatkowo uruchomienie w systemie Linux powoduje, że \f
kursor 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 puts
zastąpionym makrem z printf
dodatkowym kodem, który należy wykonać, gdy wyświetlany komunikat umieszcza się wewnątrz printf
argumentó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). if
zastąpione przez||
Obliczanie nowej pozycji gracza / wumpus umieszczonej w nowym makrze.
Wygrywaj / przegrywaj wiadomości umieszczone poza while
pętlą. if
zastą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 printf
i obliczania pozycji wumpusa w jednym wyrażeniu, ale wybrałem jeden z moich własnych. printf
zwraca liczbę wydrukowanych znaków, czyli Your arrow didn't hit anything\n
31 (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 a
zawierająca informacje o pozycji wumpus zostaje zresetowana.)
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==1
i 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 LT
zamiast w TL
powyższej tabeli.
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ć int
w 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 .)