던전 건설 세트


19

내가 어렸을 때, 나는 Intellivision 게임 Advanced Dungeons and Dragons : Treasure of Tarmin을했다 . 3D 그래픽을 사용하면 충격적인 사실감으로 1 인칭 시점을 볼 수 있습니다.

놀랍도록 사실적인 3D 그래픽

그러나 나는 C-64를 얻었다. 그리고 화면 주위를 커서로 움직이고 Ctrl 키와 숫자로 색상을 설정하고 원하는 곳에 기호를두면 (왜 그렇게하지 bash않을까요?) 40x25 문자 그리드에 그릴 수있었습니다 . 문자 집합에는 삼각형 구성 요소와 솔리드 블록 구성 요소가있었습니다. 그래서 나는 그 매체를 통해 그리드에서 자신의 원근을 렌더링하는 방법을 통해 추론 할 수있었습니다.

나는 이번 주에 "던전 건설 세트"에 관한 거의 3 십년 전의 나선 묶인 공책에서 발견했다.

여기에 이미지 설명을 입력하십시오

( 업데이트 :주의 깊은 독자들은 이것이 기울어 진 부분에 잘 맞지 않는 것을 알 수 있습니다. 수정 된 숫자는 아래에 제공됩니다.)

타민의 보물은 격자에서 재생되었지만 벽 은 격자 사각형 의 가장자리 에만 존재했습니다 . 바이트가 무엇인지 알았을 때, 맵을 바이트로 만들면 맵의 각 사각형이 각 가장자리에 대해 네 가지 가능한 상태를 가질 수 있음을 깨달았습니다.

  1. 막힘
  2. 다른 것?

나는 (어제 밤까지) 그것을 작성하지 않았다. 다른 사람들이 시도하는 것이 재미있을 것이라고 생각했습니다.

따라서 귀하의 작업은 2013 년 기술을 사용하여 내 (수정 된) 사양 을 구현하는 문자 모드 기반 미로 렌더러를 구현 하는 것입니다.

입력

스펙은 문에 대한 렌더링을 정의하지 않으므로 벽과 벽이 아닌 유일한 옵션 만 가정합니다. 간단히하기 위해 입력은 다음과 같은 문자열 줄로 구성된 맵입니다.

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

5x5 맵입니다. 왼쪽 상단 (1,1)는이 W추정치와 N노스 벽 세트를. 오른쪽 하단 (5,5)에는 S바깥 쪽 E벽 과 벽이 있습니다.

지도 내비게이션이 없으면 재미가 떨어집니다. 따라서 최소한 플레이어를 북쪽을 향한 (1,1)에 놓고 다음을 제공하십시오.

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

각 단계에서 노트북 용지 사양에 정의 된대로 1 인칭 시점의 16x15 디스플레이를 출력합니다. 계산하지 않아도되도록 세 거리의 평평한 벽의 크기는 다음과 같습니다.

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

경사 벽의 경계 크기는 다음과 같습니다.

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

설명

  • 인접한 셀은 공유 벽에 대해 동의하지 않을 수 있습니다. 따라서 정사각형의 남쪽 가장자리는 벽이 될 수 있지만 정사각형의 남쪽 가장자리는 막히지 않습니다. 원래 디자인에서 나는 이것을 특징으로 생각했습니다. 단방향 문이나 보이지 않는 벽과 같은 흥미로운 아이디어를 허용합니다. 이 단순화를 위해 동일한 규칙을 따르십시오. 탐색 및 렌더링의 경우, 사용자가 직면 한 방향으로 가장 가까운 셀의 가장자리 상태에만주의하십시오 .

  • "음영"으로보기가 훨씬 좋습니다. 따라서 전체 블록의 경우 유니 코드 2593 ▓ 및 2591 alternate를 대체하거나 사용 X하고 +구현이 ASCII 인 경우 사용 하십시오.

  • 유니 코드 삼각형 문자 (25E2, 25E3 ◣, 25E4 ◤, 25E5 ◥)는이를 그리기위한 약간 불충분합니다. 음영 처리 된 변형이없는 것 외에도 고정 너비 글꼴에서도 문자의 너비 만 늘리고 전체 높이는 늘리지 않습니다. 내가 원하는 대각선에 전체 블록이나 슬래시 문자 또는 선택한 것을 그릴 수 있습니다. 색상을 통합하고 음영 대신 이러한 문자를 사용하는 흥미로운 창의적인 솔루션.

  • 가장 바깥 쪽 벽이 연주 영역을 경계로 설정되어 있다고 가정 할 수 있으므로 미로 외부에 아무것도 렌더링하지 않아도됩니다. 사양보다 멀리 떨어진 벽은 무시되고 빈 공간 만 남습니다.

  • (1,1)에서 북쪽을 향하면 정면에서 보이는 벽의 음영은 DARK 여야합니다. 맵의 인접한 벽에 다른 음영을 적용하여 모든 벽이 존재하는 경우 밝은 벽은 절대 어두운 벽과 접하지 않습니다.

  • 내가 원래 의도했던 것을 대각선 문자와 모두로 실제로 수행하는 C-64 구현은 다른 진입 기준보다 우선합니다. :-)

위에 주어진 샘플 맵의 경우 ...

남쪽을 향한 (1,3)에서 :

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

남쪽을 향한 (3,2)에서 :

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

동쪽을 향한 (3,2)에서 :

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

북쪽을 향한 (2,3)에서 :

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

1
나는 이것을 코드 도전으로 만드는 것을 제안한다 -골프는 너무 읽을 수없고 어려울 것이다 : P
Doorknob

1
@Doorknob 당신을 바보하게하지 마십시오 ... 그것은 실제로 그렇게 어려운 것은 아닙니다. 3 가지 경계 크기 목록에 대한 좋은 힌트가 있습니다. 그리고 골프는 무엇이고 해결하고 해결해야 할 도전은 무엇입니까? :-)하지만 사람들이 어떻게 해결하고 싶은지 고르도록하겠습니다 ... NP
Dr. Rebmu

남쪽 X3, 2바라 보는 관점에서 s 의 두 열을 설명해 주시겠습니까?
재즈 피

특히 오른쪽에 있습니다. 나는 왼쪽이 왜 있는지 알 수 있습니다. 그러나 올바른 것은 명확화 # 1을 위반하는 것 같습니다.
재즈 피

@jazzpi 죄송합니다. 제가 올린지도는 설명 1을 준수해야합니다! 잘 했어. 결정된. (어떤 시점에서 빠진 남쪽 벽을 내 자신의 버전에 넣었을 것입니다 ...하지만 샘플에 테스트 케이스를 두는 것이 좋습니다 ... 남쪽 벽을 나가자!)
Dr. Rebmu

답변:


10

코모도어 64 기본

남자, 재미 있었어. 그리고 열심히. C64 Basic은 거의 논쟁의 여지가 없으며, print던전을 렌더링하기 위해 화면이 이미 만들어져 있기 때문에 디버깅을 사용할 수도 없습니다 . 와 같은 코드를 작성할 때 재미 있다는 것을 알고 있습니다 55250 goto 55110. Dijkstra가 나를 죽일 것입니다.

이 프로그램은 두 가지 색상과 대각선 문자를 사용합니다.

말할 것도없이 나는 골프를하지 않았다. 결국 코드 챌린지 라고 합니다. 그것은의 7183 당신이 관심이 있다면 바이트.

속도가 느립니다. 기본 속도에서는 장면을 렌더링하는 데 몇 초가 걸립니다. 최대지도 크기는 10 x 10이지만 편집 선 120을 통해 변경할 수 있습니다.

VICE 에뮬레이터를 사용하여 이것을 개발하고 테스트했습니다 . 아래 코드는 ASCII로 표시되므로 PETSCII가 이동 했음을 의미 합니다. 그러나 맵을 입력 할 때는 시프트되지 않은 PETSCII 를 사용해야합니다 .

스크린 샷 : 스크린 샷

암호:

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

테이프 이미지 : 여기에서 다운로드하십시오 .

예를 들면 다음과 같습니다.

예


1
세상에 다른 사람들이 이것을 해결하기를 원한다면 대단합니다 ...하지만 도전에서 트럼프 카드의 정의로 현상금을 얻었습니다. 나는 에뮬레이터를 꺼내고 향수를 불러 일으키려는 유혹을 받았지만 컴파일러 크로스 컴파일러가 얼마나 잘 유지할 수 있는지 보려면 빨간색 으로 작성하는 것이 더 생산적이라고 생각했습니다 . 그 소스 . 나는 그것을 Rebmu-ify 하고 어떤 시점에 그것을 게시 할 것입니다 ...하지만 현상금은 당신입니다! 큰 박수.
Dr. Rebmu

1
또한 RE : Dijkstra는 불멸성에 대한 재미있는 인용문을 가지고 있습니다 . "지금부터 10 년이 지난 지금, 당신이 빠르고 더러운 일을하고있을 때, 나는 어깨 너머로보고 있다는 것을 갑자기 시각화하고 'Dijkstra는 "이것이 마음에 들지 않았습니다." 그래서 그는 그의 소원을 얻었다 고 생각합니다! :-)
Dr. Rebmu

@ Dr.Rebmu : 현상금에 감사드립니다! 이것은 문자 그대로 하루 종일 글을 써야했습니다 :)
marinus December

10

(왜 그렇게하지 bash못하게합니까?)

나는 지금 막 가야했다.

배쉬, 12743 자

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

이것이 내가 한 첫 번째 작업 bash은 몇 가지 명령을 함께 파이핑하는 것 이상이라는 것을 명심하십시오 . 모든 벽을 하드 코딩하지 않았다면 아마도 상당히 많이 줄일 수 있지만 더 쉬워 보였습니다. 일관성이 없습니다. 각 사각형의 바이트 형식은 끔찍한 방식으로 선택됩니다. 그러나 작동합니다.

나는 심지어 화살표 키를 통한 움직임에 대한 지원을 추가했습니다 :)

다음은 샘플 입력에 대한 스크린 샷입니다 (내지도는 (0 | 0)에서 시작합니다).

0 | 0, 북쪽을 향함 0 | 2, 남쪽을 향함 2 | 1, 동쪽을 향함 남쪽을 향한 2 | 1 1 | 2, 북쪽을 향함

네 번째 것 외에도 샘플 샘플처럼 보입니다 (OP에 대한 내 의견 참조).

이 스크린 샷은 256 색을 지원하는 urxvt v9.15에서 찍은 것으로, 88 색 터미널에서는 엉망이 될 수 있으며 유니 코드를 지원하지 않는 터미널은 전혀 작동하지 않습니다. 내가 사용한 글꼴 은 Adobe의 Source Code Pro 였습니다 .


1
Haha, bash와 색깔도! 좋은. 당신은 그 벽에 대해 완전히 옳았습니다. 분명히 나는 ​​어느 시점에서 내 프로그램에서 "고정"했습니다. 그래서 나는 그것을 고쳤다. :-) 캐치 주셔서 감사합니다!
Dr. Rebmu

3

다음은 Python 3의 내 버전입니다. 3k 문자와 비슷하며 약간의 노력으로 조금 더 작아 질 수 있습니다 (제거 할 수있는 많은 공백이 있습니다).

현재 +X/\그리기 문자로 사용되지만 너비가 고정 된 글꼴을 사용하여 올바르게 렌더링하는 경우 유니 코드 문자로 그리도록 설정되어 있습니다. 이 기능은 사용하지 않지만 서로 다른 벽의 각진 부분에 별도의 타일 사용을 지원합니다. 또한 천장, 바닥 및 "먼"타일을 제공 할 수 있으며 플레이어가 동쪽 또는 서쪽 대 북쪽 또는 남쪽을 향할 때 다른 타일을 사용할 수 있습니다. 아아, 이것은 결코 좋아 보지 않았으므로 아마도 모두 비어 있어야합니다 (또는 같은 단단한 것 ).

아아, 내 Windows 7 시스템에서 전체 블록 문자 세트 (예 : )가 있는 고정 폭 글꼴을 찾으려고 끔찍한 시간을 보냈습니다 . 내가 찾은 대부분은 cmd어떤 이유로 콘솔 에서 사용할 수 없었습니다 (아마도 완전히 공백이 아니기 때문일 수 있습니다). 콘솔이 더 기능적이라고 생각되면 파일 상단 근처에서 주석 처리 한 대체 문자 세트를 사용해보십시오. 단 두 가지 색상으로도 나쁘지 않습니다. 그것은 천장과 바닥과 대부분 투명한 벽으로 채워져 있습니다.

코드:

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

문자 세트는 파일 상단 근처에 지정됩니다. 문자의 순서는 다음과 같습니다.

  1. 심지어 패리티 벽
  2. 홀수 패리티 벽
  3. 패리티 상단 오른쪽 벽 각도 (예 : /벽 아래)
  4. 홀수 패리티 오른쪽 상단 벽 각도
  5. 패리티 상단 왼쪽 벽 각도
  6. 홀수 패리티 왼쪽 상단 벽 각도
  7. 패리티 하단 오른쪽 벽 각도
  8. 홀수 패리티 오른쪽 아래 벽 각도
  9. 짝수 패리티 왼쪽 하단 벽 각도
  10. 홀수 패리티 왼쪽 하단 벽 각도
  11. E / W를 향한 천장
  12. 천장 직면 N / S
  13. 수평선이 E / W를 향함 (벽이없는 경우 화면 중앙)
  14. 수평선 대면 N / S
  15. 바닥면 E / W
  16. 바닥면 N / S

다음과 같은 패턴으로 게임에서 렌더링해야 할 15 개의 벽이 있습니다 ( V플레이어의 위치 및 시야각 표시).

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

15 개의 벽에 사용 된 타일이 shapes목록에 정의되어 있습니다. 2 개의 튜플 목록입니다. 튜플의 첫 번째 값은 벽의 "패리티"를 나타내며, 벽 0은 캐릭터 바로 앞에 벽과 동일한 문자로 그려야 1하며 대체 패턴 (예 : +vs X) 이어야한다는 것을 나타냅니다 . 두 번째 값은 x,y,t한 픽셀의 화면 좌표와 타일 인덱스를 나타내는 튜플 목록입니다 (홀수 패리티로 렌더링되는 벽 1은 이러한 각 인덱스에 추가됨). 모양은 거리에 따라 순서가 정해 지므로 처음 3 개는 캐릭터보다 2 타일 앞의 수직 벽, 2 개의 타일 2 개 앞의 타일 등을 나타냅니다.

기능은 다음과 같습니다.

  • rr: 화면을 "렌더링"합니다 (스크린 버퍼에 타일을 인쇄하여).
  • dw: 제공된 스크린 버퍼에 "벽 그리기". 이것은 painters 알고리즘을 사용하므로 가장 먼 벽이 먼저 그려지고 더 가까운 벽으로 덮여있을 수 있습니다.
  • ga: "get area"는 주어진 맵 위치와 마주 보는 벽이 불투명 한 부울 값 목록을 반환합니다.
  • rd: "읽기",지도를 읽고 선을 생성하는 생성기. IDLE의 콘솔은 한 번에 한 줄을 입력하지 않고 여러 줄 입력을 붙여 넣을 때 이상한 일을하기 때문에 필요합니다.
  • rm: "지도 읽기"는지도를 m[y][x][d]( d=0동쪽과 d=1남쪽으로) 색인화 된 부울 목록으로 구문 분석합니다 . 또한 다른 코드에서 인덱스 오류를 피하기 위해 두 행과 두 열의 패딩 사각형을 추가합니다.
  • cl: 출력을 "지우십시오"(대부분의 콘솔 상단에서 이전보기를 스크롤하기에 충분한 줄 바꿈을 작성하여).
  • gl: "game loop", 입력이 수집되고 위의 것들이 호출됩니다.

몇 가지 "스크린 샷":

시작 위치 :

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

북쪽 벽을 따라 보면 :

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

예제와 일치하는 몇 샷 (스택 오버플로로 첫 번째 빈 줄이 잘 리며 프로그램 출력에 있음) :

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

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

과:

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

제공된 맵의 낯선 뷰 중 하나는 뷰와 평행 한 벽이 그 뒤에 튀어 나오는 수직 벽과 같은 색이기 때문입니다.

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

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

마지막 샷 영역은 다음과 같습니다.

_   _
 |
  V

멋진 추가 분석 및 다이어그램! 흠, 그 벽은 내 구현에서 마지막 벽과 같은 색으로 감겨 있습니다. 가장자리 케이스에 대한 좋은 지적. 나는 그것이 일어날 것이라고 생각하지 않았지만 그것은 일종의해야합니다. 지도 채색과 같고 두 가지 색상으로는 충분하지 않습니다 ... :-/
Dr. Rebmu
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.