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
\
X
w swoim widoku 3, 2
skierowanym na południe?