Jako dziecko grałem w grę Intellivision Advanced Dungeons and Dragons: Treasure of Tarmin . Trójwymiarowa grafika pozwala spojrzeć z perspektywy pierwszej osoby z szokującym realizmem:
Ale potem dostałem C-64. I mogłem rysować na siatce znaków 40x25, kursorami po ekranie, ustawiając kolor za pomocą klawisza Ctrl i cyfry oraz umieszczając symbole gdziekolwiek chciałem (dlaczego nie bash
mogę tego zrobić?) . Zestaw znaków miał komponenty trójkątne i bryłowe. Byłem więc w stanie zrozumieć, w jaki sposób można wygenerować rendering własnej perspektywy w siatce za pośrednictwem tego medium.
Znalazłem prawie trzy dekady specyfikacji, w oprawionym spiralnie papierze do notatników, na temat „Zestawu konstrukcyjnego lochów” w tym tygodniu:
( AKTUALIZACJA : Uważni czytelnicy zauważą, że to nie do końca trzyma się pochyłych części. Poprawione liczby podano poniżej.)
Chociaż Treasure of Tarmin grano na siatce, ściany istniały tylko na krawędziach kwadratowych pól. Po zapoznaniu się z bajtami zdałem sobie sprawę, że jeśli zrobię mapę z bajtów ... to każdy kwadrat na mapie może mieć cztery możliwe stany dla każdej krawędzi:
- Bez przeszkód
- Ściana
- Drzwi
- Coś innego?
Nigdy nie zabrałem się do pisania tego (do ostatniej nocy). Pomyślałem, że fajnie byłoby spróbować innych.
Twoim zadaniem jest zaimplementowanie renderera labiryntu opartego na trybie znakowym, który implementuje moją (poprawioną !!) specyfikację ... ale przy użyciu technologii z 2013 roku.
Wejście
Ponieważ specyfikacja nie definiuje renderowania dla drzwi, zakładamy, że jedynymi opcjami są ściany i ściany. Dla uproszczenia dane wejściowe to mapa złożona z linii ciągów, które wyglądają tak:
WN.. .N.. .N.. .N.. .N.E
W... .... .... ..S. ...E
W... .N.E W... .N.. ...E
W... .... .... .... ...E
W.S. ..S. ..S. ..S. ..SE
To byłaby mapa 5 x 5. Lewy górny róg (1,1) ma ustawiony W
est i N
prawą ścianę. Prawy dolny róg (5,5) ma S
ustawiony outh i E
ast wall.
Jest to znacznie mniej przyjemne bez nawigacji na mapie. Więc przynajmniej ustaw swojego gracza w pozycji (1,1) twarzą na północ i zaoferuj mu:
[F]orward, [B]ackward, turn [L]eft, turn [R]ight or [Q]uit?
Na każdym kroku wydrukuj obraz 16x15 z perspektywy pierwszej osoby, zgodnie ze specyfikacją papieru do notebooków. Aby nie musieć liczyć, rozmiar płaskich ścian w trzech odległościach wynosi:
14x13 (directly in front of you; e.g. wall is in same cell)
8x7 (one step away)
6x5 (two steps away)
Graniczne rozmiary skośnych ścian to:
1x15 (your direct left or right; e.g. wall is in same cell)
3x13 (one step away)
1x7 (two steps away)
Wyjaśnienia
Sąsiednie komórki mogą nie zgadzać się co do wspólnych ścian. Zatem południowa krawędź kwadratu może być ścianą, podczas gdy północna krawędź kwadratu na południe od niego będzie niezakłócona. W oryginalnym projekcie uznałem to za cechę: pozwala na ciekawe pomysły, takie jak drzwi jednokierunkowe ... lub niewidoczne ściany, które pojawiają się dopiero po ich przejściu. W celu uproszczenia postępuj zgodnie z tą samą zasadą: podczas nawigacji i renderowania zwracaj uwagę tylko na stan krawędzi w komórce najbliżej Ciebie w kierunku, w którym patrzysz .
Widok jest znacznie lepszy dzięki „cieniowaniu”. Tak więc dla pełnych bloków naprzemiennie używaj Unicode 2593 ▓ i 2591 ░ lub użyj
X
i+
jeśli twoją implementacją jest ASCII.Trójkątne znaki Unicode (25E2 ◢, 25E3 ◣, 25E4 ◤, 25E5 ◥) są nieco kiepskie przy rysowaniu tego. Oprócz braku cieniowanych wariantów, często rozciągają tylko szerokość znaku, a nie pełną wysokość ... nawet czcionkami o stałej szerokości. Możesz narysować pełne bloki lub ukośne postacie lub coś innego w miejscach, w których chciałem mieć przekątne. Doceniamy ciekawe kreatywne rozwiązania, które wykorzystują kolor i wykorzystują te postacie zamiast cieniowania.
Możesz założyć, że skrajne ściany są ustawione tak, aby ograniczały obszar gry, więc nie musisz się martwić o renderowanie czegokolwiek poza labiryntem. Wszelkie ściany bardziej oddalone od ciebie są ignorowane i po prostu zostawiają puste miejsce.
Cieniowanie ściany, którą widzisz bezpośrednio przed sobą, jeśli skierowane jest na północ w punkcie (1,1), powinno być CIEMNE. Naprzemienne cieniowanie sąsiednich ścian na mapie, tak że gdyby wszystkie ściany były obecne, jasna ściana nigdy nie przylegałaby do ciemnej ściany.
Implementacja C-64, która faktycznie robi to, co pierwotnie zamierzałem ... z ukośnymi znakami i wszystkim ... przebije każde inne kryterium wejścia. :-)
Przykłady
Dla przykładowej mapy podanej powyżej ...
W punkcie (1,3) skierowanym na południe:
/
/+
/X+
/XX+
/XXX+
+++++XXXXXX+XXX+
+++++XXXXXX+XXX+
+++++XXXXXX+XXX+
+++++XXXXXX+XXX+
+++++XXXXXX+XXX+
\XXX+
\XX+
\X+
\+
\
W punkcie (3,2) skierowanym na południe:
/* blank line */
X /
X /+
X /++
X +++
X +++
X +++
X +++
X +++
X +++
X +++
X \++
X \+
X \
/* blank line */
W punkcie (3,2) skierowanym na wschód:
/* blank line */
/
/X
/XX
XXX
+++++XXXXXX+XXX+
+++++XXXXXX+XXX+
+++++XXXXXX+XXX+
+++++XXXXXX+XXX+
+++++XXXXXX+XXX+
XXX
\XX
\X
\
/* blank line */
W punkcie (2,3) skierowanym na północ:
/
++++++++++++++X
++++++++++++++X
++++++++++++++X
++++++++++++++X
X++++++++++++++X
X++++++++++++++X
X++++++++++++++X
X++++++++++++++X
X++++++++++++++X
++++++++++++++X
++++++++++++++X
++++++++++++++X
++++++++++++++X
\
źródło
X
w swoim widoku3, 2
skierowanym na południe?Odpowiedzi:
Commodore 64 Basic
Człowieku, było fajnie. I twarde. C64 Basic jest prawie niemożliwy do debugowania, nie można nawet użyć
print
debugowania, ponieważ ekran jest już zajęty do renderowania lochu. Wiesz, że dobrze się bawisz, pisząc kod55250 goto 55110
. Dijkstra mnie zabije.Program wykorzystuje dwa kolory i znaki ukośne.
Nie trzeba dodawać, że nie grałem w golfa. W końcu mówi teraz wyzwanie kodowe . Jeśli jesteś zainteresowany, to 7183 bajtów.
Jest powolny - przy domyślnej prędkości renderowanie sceny zajmuje kilka sekund. Maksymalny rozmiar mapy to 10 na 10, ale można to zmienić edytując linię 120.
Opracowałem i przetestowałem to za pomocą emulatora VICE . Poniższy kod jest wyświetlany w ASCII, więc oznacza to przesunięty PETSCII. Jednak podczas wprowadzania mapy należy korzystać z nieprzesuniętego PETSCII .
Zrzut ekranu:
Kod:
Obraz na taśmie: pobierz tutaj .
Przykłady:
źródło
Właśnie musiałem teraz.
Bash, 12743 znaków
Proszę pamiętać, że to pierwsza rzecz, którą zrobiłem
bash
, to więcej niż połączenie kilku poleceń razem. Prawdopodobnie można by go znacznie zmniejszyć, gdybym nie kodował wszystkich ścian, ale wydawało się to łatwiejsze. Nie ma żadnej konsekwencji. Format bajtu dla każdego kwadratu jest wybierany w straszny sposób. Ale to działa.Dodałem nawet obsługę ruchu za pomocą klawiszy strzałek :)
Oto kilka zrzutów ekranu dla przykładowego wejścia (Uwaga: moja mapa zaczyna się od (0 | 0)):
Oprócz czwartej wszystkie one również wyglądają jak próbki (patrz mój komentarz do PO).Te zrzuty ekranu zostały zrobione na urxvt v9.15 z obsługą 256 kolorów, prawdopodobnie wyglądałoby to dość bzdury na terminalu w 88 kolorach, a terminale bez obsługi Unicode w ogóle nie działają. Czcionka, której użyłem, to Source Code Pro firmy Adobe.
źródło
Oto moja wersja w Pythonie 3. To coś w rodzaju postaci 3k i przy odrobinie wysiłku może być nieco mniejsze (na początek można usunąć wiele białych znaków).
Obecnie używa
+X/\
jako znaków do rysowania, ale jest skonfigurowany do rysowania ze znakami Unicode, jeśli masz czcionkę o stałej szerokości, która będzie renderować je poprawnie. Obsługuje stosowanie oddzielnych kafelków dla kątowych części różnych ścian, chociaż nie używam tej funkcji. Pozwala także na umieszczenie sufitu, podłogi i „odległych” kafelków, a także możesz użyć różnych, gdy gracz jest skierowany na wschód lub zachód kontra północ lub południe. Niestety, to nigdy nie wyglądało zbyt dobrze, więc prawdopodobnie wszystkie powinny być puste (lub coś solidnego, jak█
).Niestety, na moim systemie Windows 7 miałem okropny czas, próbując znaleźć czcionkę o stałej szerokości z pełnym zestawem znaków blokowych (np.
▜
I▟
). Większość z tych, które znalazłem, nie mogła zostać udostępniona wcmd
konsoli z jakiegoś powodu (być może dlatego, że nie są idealnie rozmieszczone w jednej przestrzeni ?). Jeśli uważasz, że twoja konsola jest bardziej funkcjonalna, spróbuj użyć alternatywnego zestawu znaków, który skomentowałem w górnej części pliku, który nie wygląda źle nawet przy dwóch kolorach. Ma wypełnione sufity i podłogi, a przeważnie przezroczyste ściany.Kod:
Zestaw znaków jest określony u góry pliku. Kolejność znaków to:
/
ze ścianą poniżej)Istnieje 15 ścian, które mogą wymagać renderowania przez grę, w podobny sposób (ze
V
wskazaniem pozycji gracza i łuku widzenia):Kafelki używane przez 15 ścian są zdefiniowane na
shapes
liście. To lista 2-krotek. Pierwsza wartość krotki wskazuje na „parzystość” ściany, ze0
wskazaniem, że powinna być narysowana tymi samymi znakami co ściana bezpośrednio przed znakiem i1
wskazując, że powinien to być alternatywny wzór (np.+
VsX
). Druga wartość to listax,y,t
krotek wskazująca współrzędne ekranu i indeks jednego piksela (ściany renderowane z nieparzystą parzystością zostaną1
dodane do każdego z tych indeksów). Kształty są uporządkowane według odległości, więc pierwsze trzy reprezentują prostopadłe ściany dwie płytki przed postacią, a następnie dwie równoległe ściany dwie płytki przed sobą i tak dalej.Funkcje to:
rr
: „render” ekran (drukując kafelki w buforze ekranu).dw
: „rysuj ściany” do dostarczonego bufora ekranu. Korzysta z algorytmu malarzy, więc najbardziej odległe ściany są rysowane jako pierwsze i mogą zostać zasłonięte przez bliższe.ga
: „get area” zwraca listę wartości boolowskich wskazujących, które ściany są nieprzezroczyste dla danej pozycji mapy i które są zwrócone.rd
: „read”, generator, który czyta mapę, uzyskując linie. Jest to potrzebne tylko dlatego, że konsola IDLE robi dziwne rzeczy, gdy wklejasz wieloliniowe dane wejściowe zamiast wpisywać jedną linię na raz.rm
: „czytaj mapę”, analizuje mapę w zagnieżdżoną listę wartości logicznych, indeksowaną wedługm[y][x][d]
(zd=0
zachowaniem Wschodu id=1
Południa). Dodaje również dwa wiersze i dwie kolumny kwadratów wypełnienia, aby uniknąć błędów indeksu w drugim kodzie.cl
: „wyczyść” wynik (pisząc wystarczającą liczbę nowych wierszy, aby przewinąć stary widok z góry większości konsol).gl
: „pętla gry”, w której gromadzone są dane wejściowe i wywoływane są powyższe elementy.Kilka „zrzutów ekranu”:
Pozycja wyjściowa:
Patrząc wzdłuż północnej ściany:
Kilka ujęć pasujących do twoich przykładów (uwaga, puste pierwsze linie są odcinane przez przepełnienie stosu, są one w wyniku programu):
I:
Oto jeden z dziwniejszych widoków na dostarczonej mapie, ponieważ ściana równoległa do naszego widoku jest tego samego koloru, co wystająca za nią prostopadła ściana:
Oto jak wyglądałby obszar ostatniego strzału z góry:
źródło