Set Konstruksi Bawah Tanah

19

Ketika saya masih kecil, saya memainkan game Intellivision, Dungeons and Dragons: Treasure of Tarmin . Grafik 3-D menempatkan Anda pada sudut pandang orang pertama dengan realisme yang mengejutkan:

Grafik 3-D Yang Sangat Realistis

Tapi kemudian saya mendapat C-64. Dan saya dapat menggambar pada grid karakter 40x25 dengan mengitari layar, mengatur warna dengan tombol Ctrl dan angka, dan menempatkan simbol di mana pun saya inginkan (mengapa tidak bashmembiarkan saya melakukan itu?) . Set karakter memiliki komponen segitiga dan komponen blok padat. Jadi saya bisa memikirkan bagaimana seseorang dapat menghasilkan rendering perspektif seseorang dalam kotak melalui media itu.

Saya menemukan spek yang hampir berumur tiga dekade, di kertas notebook berbentuk spiral, tentang "Dungeon Construction Set" minggu ini:

masukkan deskripsi gambar di sini

( PEMBARUAN : Pembaca yang cermat akan melihat bahwa ini tidak cukup melekat pada bagian yang miring. Angka yang diperbaiki disediakan di bawah ini.)

Meskipun Harta Karun Tarmin dimainkan pada kotak, dinding hanya ada di tepi kotak kotak. Setelah mempelajari apa itu byte, saya menyadari bahwa jika saya membuat peta dari byte ... maka setiap kotak pada peta dapat memiliki empat status yang mungkin untuk masing-masing tepi:

  1. Tidak terhalang
  2. Dinding
  3. Pintu
  4. Sesuatu yang lain

Saya tidak pernah sempat menulisnya (sampai semalam). Saya pikir mungkin menyenangkan bagi orang lain untuk mencoba.

Jadi tugas Anda adalah mengimplementasikan penyaji maze berbasis karakter-mode yang mengimplementasikan spesifikasi (terkoreksi !!) saya ... tetapi menggunakan teknologi tahun 2013.

Memasukkan

Karena spek tidak menentukan rendering untuk pintu, kami hanya akan berasumsi bahwa satu-satunya pilihan adalah dinding dan bukan dinding. Untuk kesederhanaan, input Anda adalah peta yang terdiri dari garis-garis string yang terlihat seperti ini:

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

Itu akan menjadi peta 5x5. Sudut kiri atas (1,1) memiliki West dan Ndinding. Sudut kanan bawah (5,5) memiliki set dinding South dan East.

Ini jauh lebih tidak menyenangkan tanpa navigasi peta. Jadi minimal, tempatkan pemain Anda di (1,1) menghadap ke utara dan tawarkan kepada mereka:

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

Pada setiap langkah, tampilkan tampilan 16x15 dari perspektif orang pertama, sebagaimana ditentukan oleh spesifikasi kertas notebook. Agar Anda tidak harus menghitung, ukuran dinding datar pada tiga jarak adalah:

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

Ukuran pembatas dinding miring adalah:

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

Klarifikasi

  • Sel yang berdekatan mungkin tidak setuju tentang dinding bersama. Jadi tepi selatan pada sebuah bujur sangkar mungkin adalah sebuah tembok, sedangkan tepi utara pada bujur sangkar di selatan itu tidak terhalang. Dalam desain aslinya saya menganggap ini sebagai fitur: itu memungkinkan ide-ide menarik seperti pintu satu arah ... atau dinding tak terlihat yang hanya muncul setelah Anda melewati mereka. Untuk penyederhanaan ini, ikuti aturan yang sama: untuk navigasi dan rendering, perhatikan hanya status tepi pada sel yang terdekat dengan Anda ke arah yang Anda hadapi .

  • Tampilannya jauh lebih baik dengan "shading". Jadi untuk blok penuh Anda, alternatifkan Unicode 2593 ▓ dan 2591 ░, atau gunakan Xdan +jika implementasi Anda adalah ASCII.

  • Karakter segitiga Unicode (25E2 ◢, 25E3 ◣, 25E4 ◤, 25E ​​◥) agak timpang untuk menggambar ini. Selain tidak memiliki varian teduh, mereka sering hanya merentangkan lebar karakter dan tidak tinggi penuh ... bahkan dalam font lebar tetap. Anda dapat menggambar blok penuh atau memangkas karakter atau sesuatu yang Anda pilih di tempat yang saya inginkan diagonal. Solusi kreatif yang menarik yang menggabungkan warna dan menggunakan karakter-karakter ini daripada menghargai.

  • Anda dapat mengasumsikan dinding terluar diatur untuk mengikat area bermain, sehingga Anda tidak perlu khawatir membuat apa pun di luar labirin. Dinding yang lebih jauh dari Anda daripada spec diabaikan dan hanya meninggalkan ruang kosong.

  • Bayangan dinding yang Anda lihat langsung di depan Anda jika menghadap Utara di (1,1) harus DARK. Bernaung alternatif di dinding yang berdekatan di peta, sehingga jika semua dinding ada maka dinding yang terang tidak akan pernah berbatasan dengan dinding yang gelap.

  • Implementasi C-64 yang benar-benar melakukan apa yang awalnya saya maksudkan ... dengan karakter diagonal dan semuanya ... akan mengalahkan kriteria entri lainnya. :-)

Contohnya

Untuk peta contoh yang diberikan di atas ...

Di (1,3) menghadap ke selatan:

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

Di (3,2) menghadap ke selatan:

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

Di (3,2) menghadap ke timur:

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

Di (2,3) menghadap ke utara:

               /
 ++++++++++++++X
 ++++++++++++++X
 ++++++++++++++X
 ++++++++++++++X
X++++++++++++++X
X++++++++++++++X
X++++++++++++++X
X++++++++++++++X
X++++++++++++++X
 ++++++++++++++X
 ++++++++++++++X
 ++++++++++++++X
 ++++++++++++++X
               \
Rebmu
sumber
1
Saya sarankan menjadikan ini sebagai tantangan kode - golf akan terlalu sulit dibaca dan sulit: P
Gagang Pintu
1
@ Doorknob Jangan biarkan itu membodohi Anda ... itu sebenarnya tidak terlalu sulit. Ada petunjuk yang cukup bagus dengan daftar 3 ukuran pembatas. Dan apa itu golf selain tantangan yang diselesaikan dan kemudian dikupas? :-) Tetapi saya akan membiarkan orang memilih bagaimana mereka ingin menyelesaikannya ... NP
Dr. Rebmu
Bisakah Anda jelaskan dua kolom Xs dalam pandangan Anda 3, 2menghadap ke selatan?
jazzpi
Terutama yang di sisi kanan. Saya mengerti mengapa yang kiri ada di sana. Tapi yang benar sepertinya melanggar Klarifikasi # 1.
jazzpi
@jazzpi Ups, Anda benar, peta yang saya pasang tidak perlu mematuhi klarifikasi 1! Sudah selesai dilakukan dengan baik. Tetap. (Saya akan menempatkan tembok selatan yang hilang di versi saya sendiri di beberapa titik rupanya ... tapi bagus untuk memiliki test case dalam sampel ... jadi mari kita tinggalkan tembok selatan!)
Dr. Rebmu

Jawaban:

10

Commodore 64 Basic

Sobat, itu menyenangkan. Dan sulit. C64 Basic hampir tidak dapat dibatalkan, Anda bahkan tidak dapat menggunakan printdebugging karena layar sudah diambil untuk rendering penjara bawah tanah. Anda tahu Anda bersenang-senang saat menulis kode suka 55250 goto 55110. Dijkstra akan membunuhku.

Program ini menggunakan dua warna, dan karakter diagonal.

Tak perlu dikatakan saya tidak golf itu. Ia mengatakan tantangan kode sekarang. Ini 7183 byte jika Anda tertarik.

Ini lambat - pada kecepatan default, dibutuhkan beberapa detik untuk membuat adegan. Ukuran peta maksimum adalah 10 kali 10 tetapi dapat diubah dengan mengedit baris 120.

Saya telah mengembangkan dan menguji ini menggunakan emulator VICE . Kode di bawah ini ditampilkan dalam ASCII, sehingga itu berarti PETSCII bergeser . Namun, saat memasukkan peta, Anda harus menggunakan PETSCII tanpa pergeseran .

Tangkapan layar: Tangkapan layar

Kode:

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

Gambar kaset: unduh di sini .

Contohnya:

contoh

marinus
sumber
1
OH TUHAN. Jika orang lain ingin menyelesaikan ini untuk itu, bagus ... tapi Anda telah memenangkan hadiah dengan definisi kartu truf dalam tantangan. Saya tergoda untuk mengeluarkan emulator dan melakukannya untuk alasan nostalgia, tetapi berpikir lebih produktif untuk menuliskannya dalam warna Merah untuk melihat seberapa baik kompilator lintas-kompiler dapat bertahan. sumber untuk itu . Saya akan Rebmu-ify dan mempostingnya di beberapa titik ... tapi hadiahnya adalah milikmu! Tepuk tangan meriah.
Dr. Rebmu
1
Juga, RE: Dijkstra, ia memiliki kutipan lucu tentang keabadian : "Maksud saya, jika 10 tahun dari sekarang, ketika Anda melakukan sesuatu yang cepat dan kotor, Anda tiba-tiba membayangkan bahwa saya melihat ke atas bahu Anda dan berkata kepada diri sendiri 'Dijkstra akan tidak menyukai ini ', well, itu akan cukup keabadian bagi saya. " Jadi saya kira dia mendapatkan keinginannya! :-)
Dr. Rebmu
@ Dr.Rebmu: terima kasih untuk hadiahnya! Ini membuat saya benar-benar sepanjang hari untuk menulis :)
marinus
10

(mengapa tidak bashmembiarkan saya melakukan itu?)

Aku harus melakukannya sekarang.

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

Harap diingat bahwa ini adalah hal pertama yang saya lakukan dengan bashlebih dari sekadar menyatukan beberapa perintah. Mungkin akan dapat direduksi dengan cukup banyak jika saya tidak melakukan hardcode pada semua dinding, tetapi sepertinya lebih mudah. Tidak ada konsistensi sama sekali. Format byte untuk setiap kotak dipilih dengan cara yang mengerikan. Tapi itu berhasil.

Saya bahkan menambahkan dukungan untuk pergerakan melalui tombol panah :)

Ini adalah beberapa tangkapan layar untuk input sampel (Perhatikan bahwa peta saya mulai dari (0 | 0)):

0 | 0, menghadap ke utara 0 | 2, menghadap ke selatan 2 | 1, menghadap ke timur 2 | 1, menghadap ke selatan 1 | 2, menghadap ke utara

Terlepas dari yang keempat, mereka semua terlihat seperti sampel juga (lihat komentar saya di OP).

Cuplikan layar ini diambil pada urxvt v9.15 dengan dukungan 256 warna, mungkin akan terlihat sangat jelek di terminal 88 warna, dan terminal tanpa dukungan unicode tidak berfungsi sama sekali. Font yang saya gunakan adalah Source Code Pro oleh Adobe.

jazzpi
sumber
1
Haha, dalam bash, dan dalam warna juga! Bagus. Anda benar-benar benar tentang tembok itu, rupanya saya pernah "memperbaikinya" di program saya. Jadi saya un-memperbaikinya. :-) Terima kasih atas tangkapannya!
Dr. Rebmu
3

Ini versi saya, dengan Python 3. Ini kira-kira seperti karakter 3k dan bisa menjadi sedikit lebih kecil dengan sedikit usaha (ada banyak ruang putih yang bisa dihapus, untuk memulainya).

Saat ini digunakan +X/\sebagai karakter gambarnya, tetapi sudah diatur untuk menggambar dengan karakter Unicode jika Anda memiliki font lebar tetap yang akan membuatnya dengan benar. Ini mendukung penggunaan ubin terpisah untuk bagian miring dari dinding yang berbeda warna, meskipun saya tidak menggunakan fitur itu. Ini juga memungkinkan Anda memberikan ubin langit-langit, lantai dan "jauh", dan Anda dapat menggunakan yang berbeda ketika pemain menghadap ke timur atau barat vs utara atau selatan. Sayangnya ini tidak pernah terlihat sangat bagus, jadi mungkin semua ini harus kosong (atau sesuatu yang solid, seperti ).

Sayangnya, pada sistem Windows 7 saya, saya memiliki waktu yang mengerikan mencoba menemukan font monospace dengan set lengkap karakter blok (misalnya dan ). Sebagian besar yang saya temukan tidak dapat dibuat tersedia di cmdkonsol untuk beberapa alasan (mungkin karena mereka tidak sempurna monospace?). Jika menurut Anda konsol Anda lebih fungsional, coba gunakan rangkaian karakter alternatif yang saya komentari di dekat bagian atas file, yang tidak terlihat terlalu buruk bahkan hanya dengan dua warna. Itu telah mengisi langit-langit dan lantai dan sebagian besar dinding transparan.

Kode:

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

Set karakter ditentukan di dekat bagian atas file. Urutan karakter adalah:

  1. bahkan tembok paritas
  2. dinding paritas aneh
  3. bahkan sudut dinding kanan atas paritas (mis. /dengan dinding di bawahnya)
  4. paritas aneh sudut kanan atas dinding
  5. bahkan paritas sudut kiri atas dinding
  6. paritas aneh sudut kiri atas dinding
  7. bahkan paritas sudut kanan bawah dinding
  8. paritas aneh sudut kanan bawah dinding
  9. bahkan paritas sudut kiri bawah dinding
  10. paritas aneh sudut kiri bawah dinding
  11. langit-langit menghadap E / W
  12. langit-langit menghadap N / S
  13. cakrawala menghadap ke E / W (bagian tengah layar jika tidak ada dinding)
  14. cakrawala menghadap N / S
  15. lantai menghadap ke E / W
  16. lantai menghadap N / S

Ada 15 dinding yang mungkin perlu dibuat oleh permainan, dalam pola seperti ini (dengan Vmenunjukkan posisi pemain dan sudut pandang):

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

Ubin yang digunakan oleh 15 dinding didefinisikan dalam shapesdaftar. Ini daftar 2-tupel. Nilai pertama tupel menunjukkan "paritas" dinding, dengan 0menunjukkan bahwa itu harus digambar dengan karakter yang sama dengan dinding langsung di depan karakter dan 1menunjukkan bahwa itu harus menjadi pola alternatif (misalnya +vs X). Nilai kedua adalah daftar x,y,ttupel yang menunjukkan koordinat layar dan indeks ubin satu piksel (dinding yang diberikan dengan paritas ganjil akan 1ditambahkan ke masing-masing indeks ini). Bentuknya disusun berdasarkan jarak, sehingga tiga yang pertama mewakili dinding tegak lurus dua ubin di depan karakter, diikuti oleh dua dinding paralel dua ubin di depan, dan seterusnya.

Fungsinya adalah:

  • rr: "render" layar (dengan mencetak ubin di buffer layar).
  • dw: "menggambar dinding" ke penyangga layar yang disediakan. Ini menggunakan algoritma pelukis, sehingga dinding yang paling jauh digambar terlebih dahulu dan mungkin ditutup oleh yang lebih dekat.
  • ga: "get area" mengembalikan daftar nilai boolean yang menunjukkan dinding mana yang buram untuk posisi dan posisi peta yang diberikan.
  • rd: "read", generator yang membaca peta, menghasilkan garis. Ini hanya diperlukan karena konsol IDLE melakukan hal-hal aneh ketika Anda menempelkan input multi-baris daripada memasukkan satu baris sekaligus.
  • rm: "baca peta", parsing peta ke daftar boolean bersarang, diindeks oleh m[y][x][d](dengan d=0menjadi Timur dan d=1Selatan). Ini juga menambahkan dua baris dan dua kolom kotak padding, untuk menghindari kesalahan indeks pada kode lainnya.
  • cl: "hapus" output (dengan menulis cukup baris baru untuk menggulir tampilan lama dari atas sebagian besar konsol).
  • gl: "game loop", tempat input dikumpulkan dan hal-hal di atas dipanggil.

Beberapa "tangkapan layar":

Posisi awal:

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

Melihat sepanjang dinding utara:

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

Beberapa gambar yang cocok dengan contoh Anda (perhatikan, baris pertama kosong sedang dipotong oleh Stack Overflow, mereka ada di output program):

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

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

Dan:

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

Berikut adalah salah satu pandangan orang asing di peta yang disediakan, karena dinding yang sejajar dengan pandangan kami berwarna sama dengan dinding tegak lurus yang menonjol di belakangnya:

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

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

Beginilah area bidikan terakhir akan terlihat dari atas:

_   _
 |
  V
Blckknght
sumber
Analisis dan diagram yang ditambahkan dengan bagus! Hm, dinding-dinding itu akhirnya menjadi warna yang sama pada yang terakhir dalam implementasi saya juga. Poin bagus tentang kasing tepi. Saya tidak berpikir itu akan terjadi, tapi itu semacam keharusan. Kira itu seperti pewarnaan peta, dan dua warna sebenarnya tidak cukup ...: - /
Dr. Rebmu