Dungeon-Baukasten

19

Als Kind habe ich das Intellivision-Spiel Advanced Dungeons and Dragons: Treasure of Tarmin gespielt . Die 3-D-Grafik versetzt Sie in eine Perspektive aus der ersten Person mit schockierendem Realismus:

Erschreckend realistische 3D-Grafik

Aber dann habe ich einen C-64 bekommen. Und ich konnte auf dem 40x25-Zeichenraster zeichnen, indem ich über den Bildschirm flog, die Farbe mit der Strg-Taste und einer Ziffer einstellte und Symbole an einer beliebigen Stelle platzierte (warum bashließ ich das nicht zu?) . Der Zeichensatz hatte dreieckige Komponenten und feste Blockkomponenten. So konnte ich überlegen, wie man durch dieses Medium eine Darstellung seiner Perspektive in einem Raster erzeugen könnte.

Ich habe diese Woche die fast drei Jahrzehnte alte Spezifikation über "Dungeon Construction Set" in spiralgebundenem Notizbuchpapier gefunden:

Bildbeschreibung hier eingeben

( UPDATE : Vorsichtige Leser werden bemerken, dass dies bei den schrägen Teilen nicht ganz zusammenhält. Die korrigierten Zahlen sind unten angegeben.)

Obwohl Treasure of Tarmin auf einem Gitter gespielt wurde, existierten die Wände nur an den Rändern von Gitterfeldern. Nachdem ich gelernt hatte, was Bytes sind, wurde mir klar, dass jedes Quadrat auf der Karte vier mögliche Zustände für jeden seiner Ränder haben könnte, wenn ich die Karte aus Bytes zusammensetze:

  1. Ungehindert
  2. Mauer
  3. Tür
  4. Etwas anderes?

Ich bin nie dazu gekommen, es zu schreiben (bis letzte Nacht). Ich dachte, es könnte Spaß machen, wenn andere es versuchen.

Ihre Aufgabe ist es also, einen auf dem Zeichenmodus basierenden Labyrinth-Renderer zu implementieren, der meine (korrigierte !!) Spezifikation implementiert, aber die Technologien von 2013 verwendet.

Eingang

Da die Spezifikation kein Rendering für Türen definiert, gehen wir einfach davon aus, dass die einzigen Optionen Wand-und-nicht-Wand sind. Der Einfachheit halber handelt es sich bei Ihrer Eingabe um eine Karte, die aus folgenden Zeichenfolgen besteht:

WN.. .N.. .N.. .N.. .N.E
W... .... .... ..S. ...E
W... .N.E W... .N.. ...E
W... .... .... .... ...E
W.S. ..S. ..S. ..S. ..SE

Das wäre eine 5x5 Karte. In der oberen linken Ecke (1,1) ist die West- und North-Wand eingestellt. In der unteren rechten Ecke (5,5) ist die Säußere und die äußere EWand festgelegt.

Ohne Kartennavigation macht das deutlich weniger Spaß. Stellen Sie Ihren Player also mindestens auf (1,1) nach Norden und bieten Sie ihm Folgendes an:

[F]orward, [B]ackward, turn [L]eft, turn [R]ight or [Q]uit?

Geben Sie bei jedem Schritt eine 16x15-Anzeige der Perspektive der ersten Person aus, wie in der Notebook-Papierspezifikation definiert. Damit Sie nicht zählen müssen, sind die Abmessungen der flachen Wände in den drei Abständen wie folgt:

14x13  (directly in front of you; e.g. wall is in same cell)
8x7    (one step away)
6x5    (two steps away)

Die Begrenzungsgrößen der schrägen Wände sind:

1x15   (your direct left or right; e.g. wall is in same cell)
3x13   (one step away)
1x7    (two steps away)

Klarstellungen

  • Benachbarte Zellen stimmen möglicherweise nicht mit geteilten Wänden überein. Die Südkante eines Quadrats könnte also eine Mauer sein, während die Nordkante des Quadrats südlich davon frei wäre. Im ursprünglichen Design betrachtete ich dies als ein Merkmal: Es ermöglicht interessante Ideen wie Einweg-Türen ... oder unsichtbare Wände, die erst erscheinen, nachdem Sie sie passiert haben. Befolgen Sie für diese Vereinfachung die gleiche Regel: Achten Sie beim Navigieren und Rendern nur auf den Kantenstatus in der Zelle , die Ihnen in der Blickrichtung am nächsten liegt .

  • Die Ansicht ist mit "Schattierung" viel besser. Wechseln Sie also für Ihre vollständigen Blöcke entweder Unicode 2593 ▓ und 2591 ░ oder verwenden Sie Xund, +wenn Ihre Implementierung ASCII ist.

  • Unicode-Dreieck-Zeichen (25E2 ◢, 25E3 ◣, 25E4 ◤, 25E5 ◥) sind zum Zeichnen etwas lahm. Abgesehen davon, dass sie keine schattierten Varianten haben, strecken sie oft nur die Breite des Zeichens und nicht die volle Höhe ... selbst bei Schriften mit fester Breite. Sie können ganze Blöcke oder Schrägstriche oder etwas von Ihrer Wahl an den Stellen zeichnen, an denen ich Diagonalen haben wollte. Interessante kreative Lösungen, die Farbe einbeziehen und diese Zeichen anstelle von Schattierungen verwenden, werden geschätzt.

  • Sie können davon ausgehen, dass die äußersten Wände den Spielbereich begrenzen, sodass Sie sich nicht darum kümmern müssen, etwas außerhalb des Labyrinths zu rendern. Alle Wände, die weiter von Ihnen entfernt sind als die Spezifikation, werden ignoriert und lassen nur leeren Raum.

  • Die Schattierung der Wand, die Sie direkt vor sich sehen, wenn Sie bei (1,1) nach Norden blicken, sollte DUNKEL sein. Alternative Schattierung benachbarter Wände auf der Karte, sodass eine helle Wand niemals an eine dunkle Wand stoßen würde, wenn alle Wände vorhanden wären.

  • Eine C-64-Implementierung, die tatsächlich das tut, was ich ursprünglich beabsichtigt hatte ... mit den diagonalen Zeichen und allem ... wird jedes andere Eingabekriterium übertreffen. :-)

Beispiele

Für die oben angegebene Beispielkarte ...

Bei (1,3) nach Süden:

               /
              /+
             /X+
            /XX+
           /XXX+
+++++XXXXXX+XXX+
+++++XXXXXX+XXX+
+++++XXXXXX+XXX+
+++++XXXXXX+XXX+
+++++XXXXXX+XXX+
           \XXX+
            \XX+
             \X+
              \+
               \

Bei (3,2) nach Süden:

                      /* blank line */        
X             /
X            /+
X           /++
X           +++
X           +++
X           +++
X           +++
X           +++
X           +++
X           +++
X           \++
X            \+
X             \
                      /* blank line */

Bei (3,2) nach Osten:

                      /* blank line */        
              / 
             /X 
            /XX 
            XXX 
+++++XXXXXX+XXX+
+++++XXXXXX+XXX+
+++++XXXXXX+XXX+
+++++XXXXXX+XXX+
+++++XXXXXX+XXX+
            XXX 
            \XX 
             \X 
              \ 
                      /* blank line */        

Bei (2,3) nach Norden:

               /
 ++++++++++++++X
 ++++++++++++++X
 ++++++++++++++X
 ++++++++++++++X
X++++++++++++++X
X++++++++++++++X
X++++++++++++++X
X++++++++++++++X
X++++++++++++++X
 ++++++++++++++X
 ++++++++++++++X
 ++++++++++++++X
 ++++++++++++++X
               \
Dr. Rebmu
quelle
1
Ich schlage vor, dies zu einer Code-Herausforderung zu machen - ein Golf wäre viel zu unleserlich und schwierig: P
Türklinke
1
@Doorknob Lass dich nicht täuschen ... es ist eigentlich gar nicht so schwer. Es gibt einen ziemlich guten Hinweis bei den Listen mit 3 Begrenzungsgrößen. Und was ist ein Golf, aber eine Herausforderung, die gelöst und dann reduziert wird? :-) Aber ich lasse die Leute entscheiden, wie sie es lösen wollen ... NP
Dr. Rebmu
Könnten Sie bitte die beiden Säulen von Xs aus Ihrer Sicht 3, 2nach Süden erklären ?
Jazzpi
Besonders der auf der rechten Seite. Ich verstehe, warum der Linke da ist. Aber der Richtige scheint gegen Klarstellung Nr. 1 zu verstoßen.
Jazzpi
@jazzpi Hoppla, du hast recht, die Karte, die ich aufstellte, muss der Klärung 1 gehorchen! Gut gemacht. Fest. (Ich würde die fehlende Südwand anscheinend irgendwann in meine eigene Version
einbauen

Antworten:

10

Commodore 64 Basic

Mann, das hat Spaß gemacht. Und schwer. C64 Basic ist fast nicht fehlerfrei, Sie können nicht einmal das printDebuggen verwenden, da der Bildschirm bereits zum Rendern des Dungeons verwendet wird. Sie wissen, dass Sie Spaß haben, wenn Sie Code wie schreiben 55250 goto 55110. Dijkstra wird mich töten.

Das Programm verwendet zwei Farben und diagonale Zeichen.

Unnötig zu erwähnen, dass ich nicht Golf gespielt habe. Schließlich heißt es jetzt Code-Challenge . Es sind 7183 Bytes, wenn Sie interessiert sind.

Es ist langsam - bei der Standardgeschwindigkeit dauert es einige Sekunden, bis die Szene gerendert ist. Die maximale Kartengröße beträgt 10 x 10, sie kann jedoch durch Bearbeiten der Zeile 120 geändert werden.

Ich habe dies mit dem VICE- Emulator entwickelt und getestet . Der folgende Code wird in ASCII angezeigt, dh verschobenes PETSCII. Bei der Eingabe der Karte sollten Sie jedoch unverschobenes PETSCII verwenden .

Bildschirmfoto: Bildschirmfoto

Code:

10 rem c64 dungeon construction set.
20 rem enter using lowercase mode
99 rem DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD
100 rem initialisation
110 poke 53272,21
115 poke 53280,0
120 dim m%(10,10)
121 dim di$(3),wa$(1),ma%(2,2)
122 di$(0)="north"
123 di$(1)="east "
124 di$(2)="south"
125 di$(3)="west "
126 wa$(1)="-wall"
127 wa$(0)="     "

130 x=0:y=0:di=0:xs=0:ys=0:wa=0
134 rem read map
135 print "input map"
140 l$="":input l$
150 if len(l$)=0 goto 250
160 cz=0
170 for i=1 to len(l$)
180   c$=mid$(l$,i,1)
190   if c$="n" then cz=cz or 8
200   if c$="e" then cz=cz or 4
205   if c$="s" then cz=cz or 2
210   if c$="w" then cz=cz or 1
215   if c$=" " then m%(x,y)=cz:cz=0:x=x+1
220   if x>=xs then xs=x
225 next
230 m%(x,y)=cz:x=0:y=y+1
240 goto 140
250 rem come from 150
260 print chr$(147)
265 ys=y:xs=xs+1
270 x=0:y=0

500 rem loop
510 gosub 1000: rem status
515 gosub 2000: rem render
520 gosub 55000: rem input
530 goto 500

1000 rem display current (x,y) value
1010 sx=5
1020 sy=17
1030 sl$="    "
1035 sw=14
1040 gosub 63900
1050 cz=m%(x,y)
1060 sx=5:sl$=".":if cz and 8 then sl$="n"
1065 gosub 63900
1070 sx=6:sl$=".":if cz and 4 then sl$="e"
1075 gosub 63900
1080 sx=7:sl$=".":if cz and 2 then sl$="s"
1085 gosub 63900
1090 sx=8:sl$=".":if cz and 1 then sl$="w"
1095 gosub 63900
1100 return

2000 rem render dungeon
2010 rem DDDDDDDDDDDDDD
2020 rem clear area
2030 sw=14:sz=32
2040 for sy=0 to 15
2050   for sx=0 to 16
2060      gosub 63950
2070   next
2080 next
2090 rem find cells / reorient sw
2100 rem store in ma% - we're at (0,1)
2110 sx=x:sy=y
2113 co=di+x+y and 1
2115 for ty=0 to 2
2120    gosub 59800:rem left/right sx/sy
2125    ma%(1,ty)=0
2126    if sx>=0 and sy>=0 and sx<xs and sy<ys then ma%(1,ty)=m%(sx,sy)
2130    ma%(0,ty)=rl
2140    ma%(2,ty)=rr
2150    gosub 59900:rem advance
2160 next
2170 rem draw back walls
2180 sa=ma%(1,2):gosub 59700
2190 if rf=0 goto 2245
2195 sw=14-11*co:sz=160
2200 for sy=5 to 9
2210    for sx=5 to 10
2220       gosub 63950
2230    next
2240 next
2245 sw=3:if co=1 then sw=14
2250 for de=0 to 2 step 2 
2260    sa=ma%(de,2):gosub 59700
2270    if rf=0 goto 2350
2280    for sx=de*5.5 to 4+de*5.5
2290       for sy=5 to 9
2300          gosub 63950
2310       next
2340    next 
2350 next
2360 rem 1,2 left wall
2370 sa=ma%(1,2):gosub 59700
2380 if rl=0 goto 2430
2390 sx=4:sz=160
2400 for sy=5 to 9:gosub 63950:next
2410 sy=4:sz=223:gosub 63950
2420 sy=10:sz=105:gosub 63950
2430 rem 1,2 right wall
2440 if rr=0 goto 2490
2450 sx=11:sz=160
2460 for sy=5 to 9:gosub 63950:next
2470 sy=4:sz=233:gosub 63950
2480 sy=10:sz=95:gosub 63950
2490 rem 1,1 back wall
2500 sa=ma%(1,1):gosub 59700
2510 sz=160
2520 sw=14:if co=1 then sw=3
2520 if rf=0 goto 2580
2530 for sy=4 to 10
2540    for sx=4 to 11
2550       gosub 63950
2560    next
2570 next
2580 rem (0-2),1 back walls
2590 sw=14:if co=1 then sw=3
2600 for de=0 to 2 step 2
2610    sa=ma%(de,1):gosub 59700
2620    if rf=0 goto 2680
2630    for sx=de*6 to 3+de*6
2640       for sy=4 to 10
2650          gosub 63950
2660       next
2670    next
2680 next 
2690 rem 1,1 left side wall
2700 sw=14:if co=1 then sw=3
2710 sa=ma%(1,1):gosub 59700
2720 if rl=0 goto 2760
2730 for sx=1 to 3
2735   sy=sx:sz=223:gosub 63950
2736   sy=14-sx:sz=105:gosub 63950
2737   sz=160
2740   for sy=1+sx to 13-sx:gosub 63950:next
2750 next
2760 rem 1,1 right side wall
2770 if rr=0 goto 2850
2780 for qx=1 to 3
2790   sx=15-qx
2800   sy=qx:sz=233:gosub 63950
2810   sy=14-qx:sz=95:gosub 63950
2820   sz=160
2830   for sy=1+qx to 13-qx:gosub 63950:next
2840 next
2850 rem 0,1 back wall
2860 sa=ma%(1,0):gosub 59700
2870 if rf=0 goto 2930
2880 for sy=1 to 13
2890   for sx=1 to 14
2900     gosub 63950
2910   next
2920 next
2930 rem (0,2)-0 back walls
2940 sw=3:if co=1 then sw=14
2950 for de=0 to 2 step 2
2960   sa=ma%(de,0):gosub 59700
2970   if rf=0 goto 3000
2980   sx=de*7.5
2990   for sy=1 to 13:gosub 63950:next
3000 next
3010 rem (1,0) left side wall
3020 sa=ma%(1,0):gosub 59700
3030 if rl=0 goto 3080
3040 sx=0:sy=0:sz=223:gosub 63950
3050 sy=14:sz=105:gosub 63950
3060 sz=160
3070 for sy=1 to 13:gosub 63950:next
3080 rem (1,0) right side wall
3085 if rr=0 goto 3130
3090 sx=15:sy=0:sz=233:gosub 63950
3100 sy=14:sz=95:gosub 63950
3110 sz=160
3120 for sy=1 to 13:gosub 63950:next
3130 rem done
3140 return

55000 rem ask for prompt & handle input
55010 sx=0:sy=20:gosub 63850
55013 print "at";x+1;y+1;"going ";di$(di);" size";xs;ys;wa$(wa)
55020 print "{f}rwd {b}kwd {l}eft {r}ight {q}uit"
55030 input c$
55040 if c$="q" goto 63999
55050 if c$="f" then dm=1:goto 55100
55060 if c$="b" then dm=-1:goto 55100
55070 if c$="l" then di=(di-1)and 3
55080 if c$="r" then di=(di+1)and 3
55090 return
55100 goto 55200:rem check walls
55110 if di=0 then y=y-dm
55120 if di=1 then x=x+dm
55130 if di=2 then y=y+dm
55140 if di=3 then x=x-dm
55145 wa=0
55146 if y>=ys then y=0
55147 if y<0   then y=ys-1
55148 if x>=xs then x=0
55149 if x<0   then x=xs-1
55150 return
55200 rem check walls
55205 cz=m%(x,y)
55207 if dm=-1 goto 55280
55210 if (di=0) and (cz and 8) goto 55260
55220 if (di=1) and (cz and 4) goto 55260
55230 if (di=2) and (cz and 2) goto 55260
55240 if (di=3) and (cz and 1) goto 55260
55250 goto 55110
55260 wa=1
55270 return : rem wall in the way
55280 rem backward
55290 if (di=2) and (cz and 8) goto 55260
55300 if (di=3) and (cz and 4) goto 55260
55310 if (di=0) and (cz and 2) goto 55260
55320 if (di=1) and (cz and 1) goto 55260
55330 goto 55110

59700 rem return front/back/left/right
59710 rem given sa and d
59720 sn=0:if sa and 8 then sn=1
59725 se=0:if sa and 4 then se=1
59730 ss=0:if sa and 2 then ss=1
59735 zw=0:if sa and 1 then zw=1
59740 if di=0 then rf=sn:rr=se:rb=ss:rl=zw
59745 if di=1 then rf=se:rr=ss:rb=zw:rl=sn
59750 if di=2 then rf=ss:rr=zw:rb=sn:rl=se
59755 if di=3 then rf=zw:rr=sn:rb=se:rl=ss
59760 return

59800 rem return left/right from sx/sy/d
59810 if di=0 then ly=sy:ry=sy:lx=sx-1:rx=sx+1
59820 if di=1 then lx=sx:rx=sx:ly=sy-1:ry=sy+1
59830 if di=2 then ly=sy:ry=sy:lx=sx+1:rx=sx-1
59840 if di=3 then lx=sx:rx=sx:ly=sy+1:ry=sy-1
59850 rl=0:rr=0
59860 if lx<0 or lx>=xs or ly<0 or ly>=ys goto 59880
59870 rl=m%(lx,ly)
59880 if rx<0 or rx>=xs or ry<0 or ry>=ys goto 59895
59890 rr=m%(rx,ry)
59895 return

59900 rem step forward
59910 if di=0 then sy=sy-1:rem N
59920 if di=1 then sx=sx+1:rem E
59930 if di=2 then sy=sy+1:rem S
59940 if di=3 then sx=sx-1:rem W
59950 return

63850 rem set cursor position
63851 rem sx=x sy=y
63860 poke 781,sy
63870 poke 782,sx
63880 poke 783,0
63890 sys 65520
63895 return

63900 rem write str to screen
63901 rem sl$ = string
63910 gosub 63850
63920 print sl$;
63930 return

63950 rem write chr to screen
63951 rem sx = x coordinate
63952 rem sy = y coordinate
63953 rem sz = character code
63954 rem sw = color
63950 sv=sx+sy*40
63960 poke 1024+sv,sz
63970 poke 55296+sv,sw
63980 return

63998 rem quit program
63999 print chr$(147):end

Bandbild: hier herunterladen .

Die Beispiele:

Beispiele

Marinus
quelle
1
OH MEIN GOTT. Wenn andere dies zum Teufel lösen wollen, großartig ... aber Sie haben das Kopfgeld per Definition der Trumpfkarte in der Herausforderung gewonnen. Ich war versucht, aus Nostalgiegründen einen Emulator herauszuholen, aber ich fand es produktiver, ihn in Rot zu schreiben, um zu sehen, wie gut der Compiler-Cross-Compiler mithalten kann. Quelle dafür . Ich werde es Rebmu-ify und es irgendwann veröffentlichen ... aber das Kopfgeld liegt bei Ihnen! Großer Applaus.
Dr. Rebmu
1
Auch RE: Dijkstra, er hat ein lustiges Zitat über die Unsterblichkeit : "Ich meine, wenn Sie in 10 Jahren etwas schnelles und schmutziges tun, stellen Sie sich plötzlich vor, dass ich über Ihre Schultern schaue und zu sich selbst sage: 'Dijkstra würde Ich hätte das nicht gemocht. Das wäre genug Unsterblichkeit für mich. " Also ich denke er hat seinen Wunsch bekommen! :-)
Dr. Rebmu
@ Dr.Rebmu: danke für das Kopfgeld! Ich brauchte buchstäblich den ganzen Tag, um zu schreiben :)
Marinus
10

(Warum bashlass ich das nicht tun?)

Ich musste jetzt einfach.

Bash, 12743 Zeichen

#!/bin/bash
IFS=
declare -a term
typeset -i term[0] term[1]
IFS=' ' read -a term <<< `stty size`
front[0]='\e[2;2H██████████████
\e[3;2H██████████████
\e[4;2H██████████████
\e[5;2H██████████████
\e[6;2H██████████████
\e[7;2H██████████████
\e[8;2H██████████████
\e[9;2H██████████████
\e[10;2H██████████████
\e[11;2H██████████████
\e[12;2H██████████████
\e[13;2H██████████████
\e[14;2H██████████████'
front[1]='\e[5;5H████████
\e[6;5H████████
\e[7;5H████████
\e[8;5H████████
\e[9;5H████████
\e[10;5H████████
\e[11;5H████████'
front[2]='\e[6;6H██████
\e[7;6H██████
\e[8;6H██████
\e[9;6H██████
\e[10;6H██████'
lfront[0]='\e[2;1H█
\e[3;1H█
\e[4;1H█
\e[5;1H█
\e[6;1H█
\e[7;1H█
\e[8;1H█
\e[9;1H█
\e[10;1H█
\e[11;1H█
\e[12;1H█
\e[13;1H█
\e[14;1H█'
lfront[1]='\e[5;1H████
\e[6;1H████
\e[7;1H████
\e[8;1H████
\e[9;1H████
\e[10;1H████
\e[11;1H████'
lfront[2]='\e[6;1H█████
\e[7;1H█████
\e[8;1H█████
\e[9;1H█████
\e[10;1H█████'
rfront[0]='\e[2;16H█
\e[3;16H█
\e[4;16H█
\e[5;16H█
\e[6;16H█
\e[7;16H█
\e[8;16H█
\e[9;16H█
\e[10;16H█
\e[11;16H█
\e[12;16H█
\e[13;16H█
\e[14;16H█'
rfront[1]='\e[5;13H████
\e[6;13H████
\e[7;13H████
\e[8;13H████
\e[9;13H████
\e[10;13H████
\e[11;13H████'
rfront[2]='\e[6;12H█████
\e[7;12H█████
\e[8;12H█████
\e[9;12H█████
\e[10;12H█████'
left[0]='\e[1;1H▙
\e[2;1H█
\e[3;1H█
\e[4;1H█
\e[5;1H█
\e[6;1H█
\e[7;1H█
\e[8;1H█
\e[9;1H█
\e[10;1H█
\e[11;1H█
\e[12;1H█
\e[13;1H█
\e[14;1H█
\e[15;1H▛'
left[1]='\e[2;2H▙
\e[3;2H█▙
\e[4;2H██▙
\e[5;2H███
\e[6;2H███
\e[7;2H███
\e[8;2H███
\e[9;2H███
\e[10;2H███
\e[11;2H███
\e[12;2H██▛
\e[13;2H█▛
\e[14;2H▛'
left[2]='\e[5;5H▙
\e[6;5H█
\e[7;5H█
\e[8;5H█
\e[9;5H█
\e[10;5H█
\e[11;5H▛'
right[0]='\e[1;16H▟
\e[2;16H█
\e[3;16H█
\e[4;16H█
\e[5;16H█
\e[6;16H█
\e[7;16H█
\e[8;16H█
\e[9;16H█
\e[10;16H█
\e[11;16H█
\e[12;16H█
\e[13;16H█
\e[14;16H█
\e[15;16H▜'
right[1]='\e[2;13H  ▟
\e[3;13H ▟█
\e[4;13H▟██
\e[5;13H███
\e[6;13H███
\e[7;13H███
\e[8;13H███
\e[9;13H███
\e[10;13H███
\e[11;13H███
\e[12;13H▜██
\e[13;13H ▜█
\e[14;13H  ▜'
right[2]='\e[5;12H▟
\e[6;12H█
\e[7;12H█
\e[8;12H█
\e[9;12H█
\e[10;12H█
\e[11;12H▜'

echo -e "\e[2J"

# Read map
typeset -i cout
cout=0
echo "Please input your map!"
echo "Please input the next row (or leave it blank if you're finished!)"
read input

declare -A map

typeset -i xlen ylen
ylen=0

until [ -z $input ]
do
    IFS=' ' read -a inputmap <<< "$input"
    xlen=${#inputmap[*]}
    let ylen++
    for index in "${!inputmap[@]}"
    do
        typeset -i map[$index,$cout]
        map[$index,$cout]=0
        el=${inputmap[index]}
        if [[ $el == W??? ]]
        then
            let "map[$index,$cout]|=1"
        fi
        if [[ $el == ?N?? ]]
        then
            let "map[$index,$cout]|=2"
        fi
        if [[ $el == ??S? ]]
        then
            let "map[$index,$cout]|=4"
        fi
        if [[ $el == ???E ]]
        then
            let "map[$index,$cout]|=8"
        fi
    done
    echo "Please input the next row (or leave it blank if you're finished!)"
    read input
    cout+=1
done

echo -ne "\e[2J"

typeset -i dir x y
dir=0
x=0
y=0

move() {
    if ((dir == 0)) && ( ((${map[$x,$y]} & 2)) || ((y == 0)) )
    then
        return 1
    elif ((dir == 1)) && ( ((${map[$x,$y]} & 8)) || (($x == $xlen)) )
    then
        return 1
    elif ((dir == 2)) && ( ((${map[$x,$y]} & 4)) || ((y == $ylen)) )
    then
        return 1
    elif ((dir == 3)) && ( ((${map[$x,$y]} & 1)) || ((x == 0)) )
    then
        return 1
    fi
    x=$1
    y=$2
}

input=

until [[ $input == [qQ] ]]
do
    if [[ $input == [DlL] ]]
    then
        let dir-=1
        if (( dir == -1 ))
        then
            dir=3
        fi
    elif [[ $input == [CrR] ]]
    then
        let dir+=1
        if (( dir == 4 ))
        then
            dir=0
        fi
    elif [[ $input == [AfF] ]]
    then
        if (( dir == 0 ))
        then
            move $x $(( y-1 ))
        elif (( dir == 1 ))
        then
            move $(( x+1 )) $y
        elif (( dir == 2 ))
        then
            move $x $(( y+1 ))
        elif (( dir == 3 ))
        then
            move $(( x-1 )) $y
        fi
    elif [[ $input == [bB] ]]
    then
        if (( dir == 0 ))
        then
            dir=2
            move $x $(( y+1 ))
            dir=0
        elif (( dir == 1 ))
        then
            dir=3
            move $(( x-1 )) $y
            dir=1
        elif (( dir == 2 ))
        then
            dir=0
            move $x $(( y-1 ))
            dir=2
        elif (( dir == 3 ))
        then
            dir=1
            move $(( x+1 )) $y
            dir=3
        fi
    fi
    echo -ne "\e[2J"
    echo -ne "\e[16;1Hd=$dir; x=$x; y=$y\e[48;5;29m"
    for (( y2=1; y2 <= 15; y2++ ))
    do
        echo -ne "\e[$y2;16H\e[1K"
    done
    if (( dir == 0 ))
    then
        for (( y2=(y-2); y2 <= y; y2++ ))
        do
            if (( y2 < 0 )); then continue; fi
            let i=y-y2
            if (( x > 0 )) && (( ${map[$((x-1)),$y2]} & 2 ))
            then
                if (( ((x-1) + y2) & 1 ))
                then
                    echo -ne "\e[38;5;40m"
                else
                    echo -ne "\e[38;5;28m"
                fi
                echo -ne ${lfront[$i]}
            fi
            if (( (x+1) < xlen )) && (( ${map[$((x+1)),$y2]} & 2 ))
            then
                if (( ((x-1) + y2) & 1 ))
                then
                    echo -ne "\e[38;5;40m"
                else
                    echo -ne "\e[38;5;28m"
                fi
                echo -ne ${rfront[$i]}
            fi
            if (( ${map[$x,$y2]} & 1 ))
            then
                if (( (x + y2) & 1 ))
                then
                    echo -ne "\e[38;5;28m"
                else
                    echo -ne "\e[38;5;40m"
                fi
                echo -ne ${left[$i]}
            fi
            if (( ${map[$x,$y2]} & 8 ))
            then
                if (( (x + y2) & 1 ))
                then
                    echo -ne "\e[38;5;28m"
                else
                    echo -ne "\e[38;5;40m"
                fi
                echo -ne ${right[$i]}
            fi
            if (( ${map[$x,$y2]} & 2 ))
            then
                if (( (x + y2) & 1 ))
                then
                    echo -ne "\e[38;5;40m"
                else
                    echo -ne "\e[38;5;28m"
                fi
                echo -ne ${front[$i]}
            fi
        done
    elif (( dir == 1 ))
    then
        for (( x2=x+2; x2 >= x; x2-- ))
        do
            if (( x2 > 16 )) || (( x2 >= xlen )); then continue; fi
            let i=x2-x
            if (( y > 0 )) && (( ${map[$x2,$((y-1))]} & 8 ))
            then
                if (( (x2 + (y-1)) & 1 ))
                then
                    echo -ne "\e[38;5;28m"
                else
                    echo -ne "\e[38;5;40m"
                fi
                echo -ne ${lfront[$i]}
            fi
            if (( (y+1) < ylen )) && (( ${map[$x2,$((y+1))]} & 8 ))
            then
                if (( (x2 + (y-1)) & 1 ))
                then
                    echo -ne "\e[38;5;28m"
                else
                    echo -ne "\e[38;5;40m"
                fi
                echo -ne ${rfront[$i]}
            fi
            if (( ${map[$x2,$y]} & 2 ))
            then
                if (( (x2 + y) & 1 ))
                then
                    echo -ne "\e[38;5;40m"
                else
                    echo -ne "\e[38;5;28m"
                fi
                echo -ne ${left[$i]}
            fi
            if (( ${map[$x2,$y]} & 4 ))
            then
                if (( (x2 + y) & 1 ))
                then
                    echo -ne "\e[38;5;40m"
                else
                    echo -ne "\e[38;5;28m"
                fi
                echo -ne ${right[$i]}
            fi
            if (( ${map[$x2,$y]} & 8 ))
            then
                if (( (x2 + y) & 1 ))
                then
                    echo -ne "\e[38;5;28m"
                else
                    echo -ne "\e[38;5;40m"
                fi
                echo -ne ${front[$i]}
            fi
        done
    elif (( dir == 2 ))
    then
        for (( y2=(y+2); y2 >= y; y2-- ))
        do
            if (( y2 > 15 )) || (( y2 >= ylen )); then continue; fi
            let i=y2-y
            if (( x > 0 )) && (( ${map[$((x-1)),$y2]} & 4 ))
            then
                if (( ((x-1) + y2) & 1 ))
                then
                    echo -ne "\e[38;5;40m"
                else
                    echo -ne "\e[38;5;28m"
                fi
                echo -ne ${rfront[$i]}
            fi
            if (( (x+1) < xlen )) && (( ${map[$((x+1)),$y2]} & 4 ))
            then
                if (( ((x+1) + y2) & 1 ))
                then
                    echo -ne "\e[38;5;40m"
                else
                    echo -ne "\e[38;5;28m"
                fi
                echo -ne ${lfront[$i]}
            fi
            if (( ${map[$x,$y2]} & 8 ))
            then
                if (( (x + y2) & 1 ))
                then
                    echo -ne "\e[38;5;28m"
                else
                    echo -ne "\e[38;5;40m"
                fi
                echo -ne ${left[$i]}
            fi
            if (( ${map[$x,$y2]} & 1 ))
            then
                if (( (x + y2) & 1 ))
                then
                    echo -ne "\e[38;5;28m"
                else
                    echo -ne "\e[38;5;40m"
                fi
                echo -ne ${right[$i]}
            fi
            if (( ${map[$x,$y2]} & 4 ))
            then
                if (( (x + y2) & 1 ))
                then
                    echo -ne "\e[38;5;40m"
                else
                    echo -ne "\e[38;5;28m"
                fi
                echo -ne ${front[$i]}
            fi
        done
    elif (( dir == 3 ))
    then
        for (( x2=(x-2); x2 <= x; x2++ ))
        do
            if (( x2 < 0 )); then continue; fi
            let i=x-x2
            if (( y > 0 )) && (( ${map[$x2,$((y-1))]} & 1 ))
            then
                if (( (x2 + (y-1)) & 1 ))
                then
                    echo -ne "\e[38;5;28m"
                else
                    echo -ne "\e[38;5;40m"
                fi
                echo -ne ${rfront[$i]}
            fi
            if (( (y+1) < ylen )) && (( ${map[$x2,$((y+1))]} & 1 ))
            then
                if (( (x2 + (y+1)) & 1 ))
                then
                    echo -ne "\e[38;5;28m"
                else
                    echo -ne "\e[38;5;40m"
                fi
                echo -ne ${lfront[$i]}
            fi
            if (( ${map[$x2,$y]} & 4 ))
            then
                if (( (x2 + y) & 1 ))
                then
                    echo -ne "\e[38;5;40m"
                else
                    echo -ne "\e[38;5;28m"
                fi
                echo -ne ${left[$i]}
            fi
            if (( ${map[$x2,$y]} & 2 ))
            then
                if (( (x2 + y) & 1 ))
                then
                    echo -ne "\e[38;5;40m"
                else
                    echo -ne "\e[38;5;28m"
                fi
                echo -ne ${right[$i]}
            fi
            if (( ${map[$x2,$y]} & 1 ))
            then
                if (( (x2 + y) & 1 ))
                then
                    echo -ne "\e[38;5;28m"
                else
                    echo -ne "\e[38;5;40m"
                fi
                echo -ne ${front[$i]}
            fi
        done
    fi
    echo -ne "\e[0m"
    echo -ne "\e[${term[0]};0H[F]orward, [B]ackward, turn [L]eft, turn [R]ight or [Q]uit?"
    read -n 1 input
done

echo

Bitte denken Sie daran, dass dies so ziemlich das erste war, was ich getan habe bash, als nur ein paar Befehle zusammenzupfeifen. Es wäre wahrscheinlich um einiges reduzierbar, wenn ich nicht alle Wände fest codiert hätte, aber es schien einfacher zu sein. Es hat überhaupt keine Konsistenz. Das Byte-Format für jedes Quadrat wird auf schreckliche Weise ausgewählt. Aber es funktioniert.

Ich habe sogar Unterstützung für die Bewegung durch die Pfeiltasten hinzugefügt :)

Dies sind einige Screenshots für die Beispieleingabe (Beachten Sie, dass meine Karte bei (0 | 0) beginnt):

0 | 0, nach Norden ausgerichtet 0 | 2, nach Süden ausgerichtet 2 | 1 nach Osten 2 | 1 nach Süden 1 | 2 nach Norden

Abgesehen von der vierten sehen sie alle wie die Muster aus (siehe meinen Kommentar zum OP).

Diese Screenshots wurden mit urxvt v9.15 mit 256-Farben-Unterstützung aufgenommen. Auf einem 88-Farben-Terminal würde es wahrscheinlich ziemlich beschissen aussehen, und Terminals ohne Unicode-Unterstützung funktionieren überhaupt nicht. Die Schriftart, die ich verwendete, war Source Code Pro von Adobe.

jazzpi
quelle
1
Haha, in Bash und auch in Farbe! Nett. Sie hatten vollkommen recht mit dieser Mauer, anscheinend hatte ich sie irgendwann in meinem Programm "behoben". Also habe ich es wieder gelöst. :-) Danke für den Fang!
Dr. Rebmu
3

Hier ist meine Version in Python 3. Es ist so etwas wie 3k-Zeichen und könnte mit ein wenig Aufwand ein bisschen kleiner werden (es gibt viele Leerzeichen, die entfernt werden könnten, um damit zu beginnen).

Derzeit werden +X/\Zeichen als Zeichnungszeichen verwendet, es ist jedoch für das Zeichnen mit Unicode-Zeichen eingerichtet, wenn Sie eine Schriftart mit fester Breite verwenden, die diese ordnungsgemäß darstellt. Es unterstützt die Verwendung separater Kacheln für die abgewinkelten Teile der unterschiedlich gefärbten Wände, obwohl ich diese Funktion nicht verwende. Außerdem können Sie Decken-, Boden- und "entfernte" Kacheln bereitstellen und verschiedene verwenden, wenn der Spieler nach Osten oder Westen gegen Norden oder Süden zeigt. Leider sah das nie sehr gut aus, also sollten wahrscheinlich alle leer sein (oder so etwas wie fest ).

Leider hatte ich auf meinem Windows 7-System eine schreckliche Zeit damit, eine nichtproportionale Schriftart mit dem vollständigen Satz von Blockzeichen (z . B. und ) zu finden. Die meisten von denen, die ich gefunden habe, konnten aus cmdirgendeinem Grund nicht in der Konsole verfügbar gemacht werden (vielleicht, weil sie nicht perfekt monospaced sind?). Wenn Sie der Meinung sind, dass Ihre Konsole funktionsfähiger ist, verwenden Sie den alternativen Zeichensatz, den ich oben in der Datei auskommentiert habe, was auch mit nur zwei Farben nicht schlecht aussieht. Es hat Decken und Böden und größtenteils transparente Wände ausgefüllt.

Der Code:

from itertools import product as p
r=range
cs=r"+X//\\//\\      " #" ░▛▛▜▜▟▟▙▙██████"
shapes=[(1,[(x,y,0)for x,y in p(r(5),r(5,10))]),
        (0,[(x,y,0)for x,y in p(r(5,11),r(5,10))]),
        (1,[(x,y,0)for x,y in p(r(11,16),r(5,10))]),
        (1,[(4,4,4),(4,10,6)]+[(4,y,0)for y in r(5,10)]),
        (1,[(11,4,2),(11,10,8)]+[(11,y,0)for y in r(5,10)]),
        (0,[(x,y,0)for x,y in p(r(4),r(4,11))]),
        (1,[(x,y,0)for x,y in p(r(4,12),r(4,11))]),
        (0,[(x,y,0)for x,y in p(r(12,16),r(4,11))]),
        (0,[(1,1,4),(2,2,4),(3,3,4),(1,13,6),(2,12,6),(3,11,6)]+
           [(x,y,0)for x,y in p(r(1,4),r(2,14)) if x<y<14-x]),
        (0,[(14,1,2),(13,2,2),(12,3,2),(14,13,8),(13,12,8),(12,11,8)]+
           [(x,y,0)for x,y in p(r(12,15),r(2,14)) if 15-x<y<x-1]),
        (1,[(0,y,0) for y in r(1,14)]),
        (0,[(x,y,0) for x,y in p(r(1,15),r(1,14))]),
        (1,[(15,y,0) for y in r(1,14)]),
        (1,[(0,0,4),(0,14,6)]+[(0,y,0)for y in r(1,14)]),
        (1,[(15,0,2),(15,14,8)]+[(15,y,0) for y in r(1,14)])]
def rr(s):
    for r in s:print("".join(r))
def dw(s,a,p,d):
    for i,r in enumerate(s):r[:]=cs[10+i//5*2+d%2]*16
    for w,(pl,sh) in zip(a,shapes):
        if w:
            for x,y,c in sh:
                s[y][x]=cs[c+(p+d+pl)%2]
dx=[1,0,-1,0]
def ga(x,y,d,m):
    fx=dx[d];fy=lx=dx[d-1];ly=dx[d-2]
    return [m[y+2*fy+ly][x+2*fx+lx][d],m[y+2*fy][x+2*fx][d],
            m[y+2*fy-ly][x+2*fx-lx][d],m[y+2*fy][x+2*fx][d-1],
            m[y+2*fy][x+2*fx][d-3],m[y+fy+ly][x+fx+lx][d],
            m[y+fy][x+fx][d],m[y+fy-ly][x+fx-lx][d],
            m[y+fy][x+fx][d-1],m[y+fy][x+fx][d-3],
            m[y+ly][x+lx][d],m[y][x][d],
            m[y-ly][x-lx][d],m[y][x][d-1],m[y][x][d-3]]
def rd():
    l=input();
    while l!="":
        if "\n" in l:yield from l.split("\n")
        else:yield l
        l=input()
def rm():
    m=[[[d in s for d in"ESWN"]for s in r.strip().split()]+[[1]*4]*2
       for r in rd()]
    return m+[[[1]*4 for _ in m[0]]]*2
def cl():print("\n"*30)
def gl():
    print("Enter map, followed by a blank line.")
    x=y=0;d=3;m=rm();mv="";s=[[""]*16 for _ in r(15)]
    while True:
        cl();dw(s,ga(x,y,d,m),x+y,d);rr(s)
        print("X:",x+1,"Y:",y+1,"Facing:","ESWN"[d])
        if mv:print("Last move:",mv)
        mv=input("[FBLRQ]? ").upper()
        if mv=="F":
            if not m[y][x][d]:x+=dx[d];y+=dx[d-1]
            else:mv+=" (Blocked)"
        elif mv=="B":
            if not m[y][x][d-2]:x+=dx[d-2];y+=dx[d-3]
            else:mv+=" (Blocked)"
        elif mv=="L":d=(d-1)%4
        elif mv=="R":d=(d+1)%4
        elif mv=="Q":break
        else:mv="I didn't understand %r."%mv
gl()

Der Zeichensatz wird oben in der Datei angegeben. Die Reihenfolge der Zeichen ist:

  1. gerade Paritätswand
  2. ungerade Paritätswand
  3. Gleichmäßige Parität im rechten oberen Wandwinkel (z. B. /mit einer Wand darunter)
  4. ungerade Parität oberen rechten Wandwinkel
  5. Gerade Parität oberer linker Wandwinkel
  6. ungerade Parität oberen linken Wandwinkel
  7. Gleichmäßige Parität unter dem rechten Wandwinkel
  8. ungerade Parität unteren rechten Wandwinkel
  9. Gerade Parität unterer linker Wandwinkel
  10. ungerade Parität unteren linken Wandwinkel
  11. Decke mit Blick auf E / W
  12. Decke mit Blick auf N / S
  13. Horizont nach O / W (die Mitte des Bildschirms, wenn keine Wände vorhanden sind)
  14. Horizont mit Blick auf N / S
  15. Boden mit Blick auf E / W
  16. Boden mit Blick auf N / S

Es gibt 15 Wände, die möglicherweise vom Spiel in einem Muster wie diesem gerendert werden müssen (mit VAngabe der Position und des Blickwinkels des Spielers):

_ _ _
_|_|_ 
_|_|_
 |V|

Die von den 15 Wänden verwendeten Kacheln werden in der shapesListe definiert . Es ist eine Liste von 2 Tupeln. Der erste Wert des Tupels gibt die "Parität" der Wand an, wobei angegeben wird 0, dass diese mit den gleichen Zeichen wie eine Wand direkt vor dem Zeichen gezeichnet werden soll und 1dass es sich um das Alternativmuster (z . B. +vs X) handeln soll. Der zweite Wert ist eine Liste von x,y,tTupeln, die die Bildschirmkoordinaten und den Kachelindex eines Pixels angeben (Wände, die mit ungerader Parität gerendert werden, werden 1zu jedem dieser Indizes hinzugefügt). Die Formen sind nach Abstand geordnet, sodass die ersten drei die senkrechten Wände, zwei Kacheln vor dem Zeichen, gefolgt von den beiden parallelen Wänden, zwei Kacheln vor dem Zeichen usw. darstellen.

Die Funktionen sind:

  • rr: "Rendern" des Bildschirms (durch Drucken der Kacheln im Bildschirmpuffer).
  • dw: "Wände zeichnen" in einen bereitgestellten Bildschirmpuffer. Hierbei wird der Maleralgorithmus verwendet, sodass die am weitesten entfernten Wände zuerst gezeichnet werden und möglicherweise von näheren verdeckt werden.
  • ga: "get area" gibt eine Liste mit Booleschen Werten zurück, die angibt, welche Wände für eine bestimmte Kartenposition und Ausrichtung undurchsichtig sind.
  • rd: "read", ein Generator, der die Karte liest und die Linien liefert. Dies ist nur erforderlich, weil die IDLE-Konsole seltsame Dinge tut, wenn Sie mehrzeilige Eingaben einfügen, anstatt jeweils eine Zeile einzugeben.
  • rm: "Karte lesen" parst die Karte in eine verschachtelte Liste von Booleschen Werten, die mit m[y][x][d]( d=0als Osten und d=1als Süden) indiziert sind . Außerdem werden zwei Zeilen und zwei Spalten mit Auffüllquadraten hinzugefügt, um Indexfehler im anderen Code zu vermeiden.
  • cl: "lösche" die Ausgabe (indem du genug Zeilenumbrüche schreibst, um die alte Ansicht von der Oberseite der meisten Konsolen zu scrollen).
  • gl: "game loop", wo die Eingabe gesammelt wird und das oben genannte Zeug aufgerufen wird.

Ein paar "Screenshots":

Die Ausgangsposition:

\               
+XXXXXXXXXXXXXX+
+XXXXXXXXXXXXXX+
+XXXXXXXXXXXXXX+
+XXXXXXXXXXXXXX+
+XXXXXXXXXXXXXX+
+XXXXXXXXXXXXXX+
+XXXXXXXXXXXXXX+
+XXXXXXXXXXXXXX+
+XXXXXXXXXXXXXX+
+XXXXXXXXXXXXXX+
+XXXXXXXXXXXXXX+
+XXXXXXXXXXXXXX+
+XXXXXXXXXXXXXX+
/               
X: 1 Y: 1 Facing: N
[FBLRQ]? 

Blick entlang der Nordwand:

\               
X\              
X+\             
X++\            
X+++\           
X+++X           
X+++X           
X+++X           
X+++X           
X+++X           
X+++/           
X++/            
X+/             
X/              
/               
X: 1 Y: 1 Facing: E
Last move: R
[FBLRQ]? 

Einige Aufnahmen, die Ihren Beispielen entsprechen (beachten Sie, dass die leeren ersten Zeilen vom Stapelüberlauf abgeschnitten werden und in der Programmausgabe enthalten sind):

X             / 
X            /+ 
X           /++ 
X           +++ 
X           +++ 
X           +++ 
X           +++ 
X           +++ 
X           +++ 
X           +++ 
X           \++ 
X            \+ 
X             \ 

X: 3 Y: 2 Facing: S
Last move: F
[FBLRQ]? 

Und:

              / 
             /X 
            /XX 
            XXX 
+++++XXXXXX+XXX+
+++++XXXXXX+XXX+
+++++XXXXXX+XXX+
+++++XXXXXX+XXX+
+++++XXXXXX+XXX+
            XXX 
            \XX 
             \X 
              \ 

X: 3 Y: 2 Facing: E
Last move: L
[FBLRQ]? 

Hier ist eine der seltsamsten Ansichten auf der mitgelieferten Karte, da die Wand parallel zu unserer Ansicht dieselbe Farbe hat wie die senkrechte Wand, die dahinter herausragt:

 \              
 +\             
 ++\            
++++        ++++
++++        ++++
++++        ++++
++++        ++++
++++        ++++
++++        ++++
++++        ++++
 ++/            
 +/             
 /              

X: 3 Y: 4 Facing: N
Last move: R
[FBLRQ]? 

So würde der Bereich der letzten Aufnahme von oben aussehen:

_   _
 |
  V
Blckknght
quelle
Schön hinzugefügte Analysen und Diagramme! Hm, diese Wände haben auch bei der letzten in meiner Implementierung die gleiche Farbe. Guter Punkt zum Randfall. Ich hätte nicht gedacht, dass das passieren würde, aber das muss so sein. Schätze, es ist wie Kartenfärbung, und zwei Farben sind eigentlich nicht genug ...: - /
Dr. Rebmu