Zindan İnşaat Seti


19

Çocukken Intellivision oyunu Advanced Dungeons and Dragons: Treasure of Tarmin'i oynadım . 3 boyutlu grafikler sizi şok edici gerçekçilikle birinci şahıs bakış açısına sokar:

Şaşırtıcı Şekilde Gerçekçi 3 Boyutlu Grafikler

Ama sonra bir C-64 aldım. Ekranda imleç yaparak, rengi Ctrl tuşu ve bir rakamla ayarlayarak ve istediğim herhangi bir yere semboller koyarak 40x25 karakter ızgarasını çizebildim (neden bunu bashyapmama izin vermiyorsun?) . Karakter setinde üçgen bileşenler ve katı blok bileşenler vardı. Bu yüzden, bir kişinin bu ortam üzerinden bir ızgarada perspektifinin bir görüntüsünü nasıl üretebileceğini anlayabildim.

Neredeyse otuz yıllık spekülasyonu, bu hafta "Zindan İnşaat Seti" hakkında spiral ciltli defter kağıdında buldum:

resim açıklamasını buraya girin

( GÜNCELLEME : Dikkatli okuyucular bunun eğimli parçalar üzerinde bir arada durmadığını fark edecektir. Düzeltilen sayılar aşağıda verilmiştir.)

Tarmin Hazinesi bir ızgarada oynamasına rağmen, duvarlar sadece ızgara karelerinin kenarlarında vardı . Baytların ne olduğunu öğrendikten sonra, haritayı bayttan çıkarırsam ... haritadaki her karenin her bir kenarı için dört olası duruma sahip olabileceğini fark ettim:

  1. engellenmemiş
  2. Duvar
  3. Kapı
  4. Başka bir şey?

(Dün geceye kadar) yazmak için hiç uğraşmadım. Başkalarının denemesinin eğlenceli olabileceğini düşündüm.

Bu yüzden göreviniz benim karakterimi düzelten bir karakter modu tabanlı labirent oluşturucuyu uygulamak (düzeltildi !!) spesifikasyonumu ... ama 2013 teknolojilerini kullanmak.

Giriş

Teknik özellikler kapılar için oluşturmayı tanımlamadığından, sadece seçeneklerin duvar ve duvar olmadığını varsayarız. Basit olması için, girdiniz şuna benzeyen dizelerden oluşan bir haritadır:

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

Bu 5x5'lik bir harita olurdu. Sol üst köşede (1,1) West ve North duvar seti vardır. Sağ alt köşenin (5,5) dış Skısmı veE ast duvar seti bulunmaktadır.

Harita navigasyonu olmadan bu daha az eğlencelidir. Yani, en azından, oyuncunuzu kuzeye bakacak şekilde (1,1) yerleştirin ve onlara şunları sunun:

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

Her adımda, dizüstü bilgisayar kağıdı özellikleri tarafından tanımlanan şekilde birinci şahıs perspektifinin 16x15'lik bir görüntüsünü alın. Saymak zorunda kalmamak için, üç mesafedeki düz duvarların boyutu:

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

Eğimli duvarların sınırlayıcı boyutları:

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

Açıklamalar

  • Bitişik hücreler ortak duvarlar konusunda aynı fikirde olmayabilir. Yani bir karenin güney kenarı bir duvar olabilir, güneydeki karenin kuzey kenarı engellenmez. Orijinal tasarımda bunu bir özellik olarak gördüm: tek yönlü kapılar gibi ilginç fikirlere izin veriyor ... ya da sadece içinden geçtikten sonra görünen görünmez duvarlara izin veriyor. Bu basitleştirme için aynı kuralı izleyin: gezinme ve oluşturma için yalnızca size en yakın hücredeki yüzleştiğiniz yöndeki kenar durumuna dikkat edin .

  • Görünümü "gölgeleme" ile çok daha iyi. Yani tam bloklar, alternatif ya Unicode 2593 ▓ ve 2591 ░ veya kullanım için Xve +eğer uygulama ASCII olduğunu.

  • Unicode üçgen karakterler (25E2 ◢, 25E3 ◣, 25E4 ◤, 25E5 ◥) bunu çizmek için biraz topal. Gölgeli varyantlara sahip olmanın yanı sıra, sabit genişlikli yazı tiplerinde bile genellikle karakterin genişliğini ve tam yüksekliğini uzatmazlar. Çapraz bloklar istediğim yerlerde tam bloklar çizebilir, karakterleri ya da seçtiğiniz bir şeyi kesebilirsiniz. Renkleri birleştiren ve gölgelendirme yerine bu karakterleri kullanan ilginç yaratıcı çözümler takdir.

  • En dış duvarların oyun alanını sınırlamak için ayarlandığını varsayabilirsiniz, bu yüzden labirent dışında bir şey oluşturma konusunda endişelenmenize gerek yoktur. Spesifikasyondan daha uzakta olan tüm duvarlar göz ardı edilir ve sadece boş alan bırakın.

  • (1,1) 'de kuzeye bakacak olursanız, doğrudan önünüzde gördüğünüz duvarın gölgesi KOYU olmalıdır. Haritadaki bitişik duvarlarda alternatif gölgeleme, böylece tüm duvarlar mevcutsa, açık renkli bir duvar asla karanlık bir duvara dayanmaz.

  • Aslında istediğim şeyi yapan bir C-64 uygulaması ... diyagonal karakterler ve hepsi ... diğer giriş kriterlerini gölgede bırakacak. :-)

Örnekler

Yukarıda verilen örnek harita için ...

(1,3) güneye bakarken:

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

(3,2) güneye bakarken:

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

(3,2) doğuda:

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

(2,3) kuzeye bakarken:

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

1
Ben bu bir kod meydan okuma yapmak öneririz - bir golf çok okunmaz ve zor olurdu: P
Kapı Tokmağı

1
@Doorknob Seni kandırmasına izin verme ... aslında o kadar da zor değil. 3 sınırlayıcı boyut listesi ile oldukça iyi bir ipucu var. Ve bir golf nedir ama çözülen ve sonra çözülen bir meydan okuma nedir? :-) Ama insanların bunu nasıl çözmek istediklerini seçmelerine izin vereceğim ... NP
Dr. Rebmu

Güneye bakan Xbakış açısında iki s sütununu açıklar 3, 2mısınız?
jazzpi

Özellikle sağ taraftaki. Solun neden orada olduğunu anlıyorum. Ancak doğru olan 1 numaralı açıklamayı ihlal ediyor gibi görünüyor.
jazzpi

@jazzpi Hata! Haklısın, eklediğim haritanın açıklığa uyması gerekiyor 1! Aferin. Sabit. (Görünüşe göre bir noktada eksik güney duvarını kendi versiyonuma
koyardım

Yanıtlar:


10

Commodore 64 Temel

Adamım, eğlenceliydi. Ve zor. C64 Basic neredeyse tartışılamaz, zindan işlemek printiçin ekran zaten alınmış olduğundan hata ayıklamayı bile kullanamazsınız . Gibi kod yazarken eğlendiğinizi biliyorsunuz55250 goto 55110 . Dijkstra beni öldürecek.

Program iki renk ve köşegen karakterler kullanır.

Tabii ki ben golf yoktu. Sonuçta şimdi kod meydan okuma diyor . Bu var 7183 Eğer ilgileniyorsanız bayt.

Yavaş - varsayılan hızda sahneyi oluşturması birkaç saniye sürer. Maksimum harita boyutu 10'dan 10'a kadardır ancak 120 numaralı satır düzenlenerek değiştirilebilir.

Bunu VICE emülatörünü kullanarak geliştirdim ve test ettim . Aşağıdaki kod ASCII'de görüntülenir, yani kaydırılmış PETSCII anlamına gelir . Haritayı girerken Ancak, kullanmanız gereken elenmemiş PETSCII .

Ekran görüntüsü: Ekran görüntüsü

Kod:

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

Teyp resmi: buradan indirin .

Örnekler:

örnekler


1
AMAN TANRIM. Başkaları bunu heck için çözmek istiyorsa, harika ... ama meydan okumadaki kozun tanımıyla ödülünü kazandınız. Bir emülatör çıkarmak ve nostalji nedenleriyle yapmak için cazip davrandım, ancak derleyicinin çapraz derleyicisinin ne kadar iyi olabileceğini görmek için Kırmızı renkte yazmanın daha verimli olduğunu düşündüm . bunun için kaynak . Ben Rebmu-ify ve bir noktada göndereceğim ... ama ödül senin! Büyük alkış.
Dr. Rebmu

1
Ayrıca, RE: Dijkstra, ölümsüzlük üzerine komik bir alıntısı var : "Yani, 10 yıl sonra, hızlı ve kirli bir şey yaptığınızda, birdenbire omuzlarınıza baktığımı ve kendinize şunu söylediğimi hayal ediyorsunuz: Dijkstra bunu sevmedim ', bu benim için yeterli ölümsüzlük olurdu. " Sanırım onun dileğini yerine getirdi! :-)
Dr.Rebmu

@ Dr.Rebmu: ödül için teşekkürler! Bu beni tam anlamıyla yazmak için bütün gün aldı :)
marinus

10

(neden bunu bashyapmama izin vermiyorsun?)

Sadece şimdi yapmam gerekiyordu.

Bash, 12743 karakter

#!/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

Lütfen bunun ilk yaptığım şey olduğunu unutmayın bash , sadece birkaç komutu birbirine bağlamaktan ibaret değildi. Tüm duvarları kodlamamış olsaydım muhtemelen çok fazla azaltılabilirdi, ama daha kolay görünüyordu. Hiç bir tutarlılığı yok. Her kare için bayt biçimi korkunç bir şekilde seçilir. Ama işe yarıyor.

Ok tuşlarıyla hareket desteği bile ekledim :)

Bunlar örnek giriş için bazı ekran görüntüleri (Haritamın (0 | 0) ile başladığını unutmayın):

0 | 0, kuzeye bakacak 0 | 2, güneye bakar 2 | 1, doğuya bakacak şekilde 2 | 1, güneye bakacak şekilde 1 | 2, kuzeye bakacak şekilde

Dördüncüsü dışında hepsi de örnek olanlara benziyor (OP hakkındaki yorumuma bakın).

Bu ekran görüntüleri 256 renk desteğiyle urxvt v9.15'te çekildi, muhtemelen 88 renkli terminalde oldukça saçma görünüyordu ve unicode desteği olmayan terminaller hiç çalışmıyor. Kullandığım yazı tipi Adobe tarafından geliştirilen Source Code Pro'dur .


1
Haha, bash ve renkli! Güzel. O duvarda tamamen haklıydın, görünüşe göre programımda bir noktada "düzelttim". Ben de çözdüm. :-) Yakalamak için teşekkürler!
Dr. Rebmu

3

İşte benim sürümüm, Python 3'te. 3k karakter gibi bir şey ve biraz çaba ile biraz daha küçük olabilir (başlangıçta kaldırılabilecek çok fazla beyaz alan var).

Şu anda +X/\çizim karakterleri olarak kullanılıyor, ancak bunları düzgün şekilde işleyecek sabit genişlikte bir yazı tipiniz varsa Unicode karakterlerle çizim yapacak şekilde ayarlanmıştır. Farklı renklere sahip duvarların açılı kısımları için ayrı karoların kullanılmasını destekler, ancak bu özelliği kullanmıyorum. Ayrıca tavan, zemin ve "uzak" fayanslar sağlamanıza izin verir ve oyuncu doğu veya batıya karşı kuzeye veya güneye bakarken farklı olanları kullanabilirsiniz. Ne yazık ki bu asla çok iyi görünmüyordu, bu yüzden muhtemelen bunların hepsi boş olmalıdır (veya sağlam bir şey gibi ).

Ne yazık ki, Windows 7 sistemimde blok karakterlerin tam setiyle (örneğin ve ) tek aralıklı bir yazı tipi bulmaya çalışırken korkunç bir zaman geçirdim . Bulduğum çoğu, bir cmdnedenden ötürü konsolda kullanılamıyor (belki de mükemmel bir şekilde tek boşluklu olmadıkları için?). Konsolunuzun daha işlevsel olduğunu düşünüyorsanız, dosyanın üst kısmına yakın bir yerde yorum yaptığım, sadece iki renkle bile çok kötü görünmeyen alternatif karakter kümesini kullanmayı deneyin. Tavanları ve tabanları ve çoğunlukla şeffaf duvarları doldurmuştur.

Kod:

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()

Karakter kümesi dosyanın üst kısmında belirtilir. Karakterlerin sırası:

  1. eşit parite duvarı
  2. garip parite duvar
  3. eşit parite sağ üst duvar açısı (örneğin /altında bir duvar ile)
  4. tek parite sağ üst duvar açısı
  5. eşit parite sol üst duvar açısı
  6. tek parite sol üst duvar açısı
  7. eşit parite sağ alt duvar açısı
  8. tek parite sağ alt duvar açısı
  9. eşit parite alt sol duvar açısı
  10. tek parite sol alt duvar açısı
  11. tavana bakan E / W
  12. tavan kaplama N / S
  13. ufuk E / W (duvar yoksa ekranın ortası)
  14. ufuk yönü N / S
  15. zemine bakan E / W
  16. zemine bakan N / S

Oyun tarafından Vişlenmesi gereken 15 duvar vardır, böyle bir desende ( oyuncunun pozisyonunu ve görüş açısını gösterir):

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

15 duvar tarafından kullanılan karolar shapeslistede tanımlanmıştır . 2 tuplun bir listesi. Tupun ilk değeri, duvarın "paritesini" belirtir, 0doğrudan karakterin önündeki bir duvarla aynı karakterlerle çizilmesi 1gerektiğini ve bunun alternatif desen (ör. +Vs X) olması gerektiğini gösterir . İkinci değer, bir x,y,tpikselin ekran koordinatlarını ve karo dizinini gösteren tupleslerin bir listesidir ( tek parite ile oluşturulan duvarlar 1bu dizinlerin her birine eklenmiş olacaktır ). Şekiller mesafeye göre sıralanır, bu nedenle ilk üçü, karakterin iki karo önündeki dik duvarları, ardından iki paralel duvar iki ön karo, vb.

Fonksiyonlar:

  • rr: ekranı "render" (ekran tamponundaki kutucukları yazdırarak).
  • dw: sağlanan bir ekran arabelleğine "duvar çizin". Bu ressam algoritmasını kullanır, bu yüzden en uzak duvarlar önce çizilir ve daha yakın olanlarla örtülebilir.
  • ga: "get area", belirli bir harita konumu ve yüz için hangi duvarların opak olduğunu gösteren boole değerlerinin bir listesini döndürür.
  • rd: "read", haritayı okuyan, satırları veren bir jeneratör. Bu, yalnızca IDLE'nin konsolu, her seferinde bir satır girmek yerine çok satırlı girişleri yapıştırdığınızda garip şeyler yaptığı için gereklidir.
  • rm: "haritayı oku", haritayı m[y][x][d]( d=0Doğu ve d=1Güney olarak) dizine eklenmiş, iç içe bir booleans listesine ayrıştırır . Ayrıca, diğer koddaki dizin hatalarını önlemek için iki satır ve iki sütun dolgu karesi ekler.
  • cl: çıktıyı "temizle" (eski görünümü çoğu konsolun üstünden kaydırmak için yeterli satır yazarak).
  • gl: "oyun döngüsü", burada girdi toplanır ve yukarıdaki şeyler çağrılır.

Birkaç "ekran görüntüsü":

Başlangıç ​​pozisyonu:

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

Kuzey duvarı boyunca bakıldığında:

\               
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]? 

Örneklerinizle eşleşen birkaç çekim (not, boş ilk satırlar Stack Overflow tarafından kesiliyor, program çıktısındalar):

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

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

Ve:

              / 
             /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]? 

Görüşümüze paralel duvar, arkasındaki yapışkan duvarla aynı renkte olduğundan, verilen haritadaki yabancı görünümlerden biri:

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

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

Son çekim alanı yukarıdan nasıl görünecekti:

_   _
 |
  V

Güzel eklenen analiz ve diyagramlar! Hm, bu duvarlar benim uygulamamda da aynı renkte duruyor. Kenar kasası hakkında iyi bir nokta. Bunun olacağını sanmıyordum, ama böyle olması gerekiyor. Sanırım bu harita boyama gibi ve iki renk aslında yeterli değil ...: - /
Dr. Rebmu
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.