ダンジョン建設セット


19

子供の頃、私はIntellivisionゲームAdvanced Dungeons and Dragons:Treasure of Tarminをプレイしました。3Dグラフィックスにより、衝撃的なリアリズムを備えた、一人称視点の視点が得られます。

驚くほどリアルな3Dグラフィックス

しかし、その後、C-64を手に入れました。そして、画面の周りにカーソルを移動し、Ctrlキーと数字で色を設定し、好きな場所に記号を配置することで、40x25の文字グリッドに描画することができました(なぜそうさせないのbashですか?)。文字セットには、三角形のコンポーネントとソリッドブロックのコンポーネントがありました。だから私は、その媒体を介してグリッドに自分の視点のレンダリングを生成する方法を推論することができました。

今週、「ダンジョンコンストラクションセット」について、スパイラルバインドされたノート用紙で、約3年前の仕様を見つけました。

ここに画像の説明を入力してください

更新:注意深く読んでいる人は、これが傾斜部分で完全には結びつかないことに気付くでしょう。修正された数値を以下に示します。)

Treasure of Tarminはグリッド上でプレイされましたが、壁はグリッドの正方形のにのみ存在していました。バイトが何であるかを学んだ後、バイトでマップを作成すると、マップ上の各正方形がそのエッジごとに4つの可能な状態を持つことができることに気付きました。

  1. 遮るものがない
  2. ドア
  3. 他に何か?

(昨夜まで)私はそれを書くことはできませんでした。他の人が試すのは楽しいかもしれないと思った。

したがって、あなたのタスクは、私の(修正された!!)仕様を実装する文字モードベースの迷路レンダラーを実装することですが、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)にはWestおよびNorthの壁セットがあります。右下隅(5,5)には、S外側とE外側の壁が設定されています。

これは、マップナビゲーションがないとかなり面白くありません。したがって、少なくとも、プレーヤーを北向きの(1,1)に置き、提供します。

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

各ステップで、ノートブックの紙の仕様で定義されているように、一人称視点の16x15ディスプレイを出力します。数える必要がないように、3つの距離にある平らな壁のサイズは次のとおりです。

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)

明確化

  • 隣接するセルは、共有された壁について意見が異なる場合があります。そのため、正方形の南端は壁になり、南の正方形の北端は遮られなくなります。元のデザインでは、これを機能と考えました。一方通行のドアのような興味深いアイデアを許可します。この単純化のために、同じルールに従います。ナビゲーションとレンダリングでは、あなたが向いている方向であなたに最も近いセルのエッジステータスにのみ注意を払ってください

  • ビューは、「シェーディング」の方がはるかに優れています。したがって、完全なブロックについては、Unicode 2593▓と2591░を交互に使用するか、実装がASCIIの場合に使用X+ます。

  • Unicodeの三角形の文字(25E2◢、25E3◣、25E4◤、25E5◥)は、これを描くには少し不自由です。影付きのバリアントがないことに加えて、固定幅のフォントであっても、文字の幅だけが伸び、全高は伸びないことがよくあります。対角線が必要な場所に、ブロックを完全に描いたり、キャラクターをスラッシュしたり、好きなものを描いたりできます。色を組み込み、シェーディングの代わりにこれらの文字を使用する興味深い創造的なソリューションは高く評価されています。

  • 一番外側の壁がプレイエリアを囲むように設定されていると仮定できます。そのため、迷路の外で何かをレンダリングすることを心配する必要はありません。仕様よりもあなたから遠く離れている壁は無視され、空のスペースを残します。

  • (1,1)で北を向いている場合、正面に見える壁の陰影は暗いはずです。マップ内の隣接する壁の代替シェーディング。すべての壁が存在する場合、明るい壁が暗い壁に接することはありません。

  • 私が本来意図したことを実際に行う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
ドアノブ

1
@Doorknobだまされてはいけません...それは実際にはそれほど難しくありません。3つの境界サイズのリストには、かなり良いヒントがあります。そして、ゴルフとは何ですか?しかし解決された後、削減された課題は何ですか?:-)しかし、私は人々が彼らがそれを解決したい方法を選ぶことができます... NP
レブム博士13

南向きのX視点での2つの列について説明してください3, 2
jazzpi

特に右側のもの。なぜ左のものがあるのか​​わかります。しかし、正しいものは明確化#1に違反しているようです。
jazzpi

@jazzpiおっと、そうだね、私が立てた地図は説明1に従う必要がある!よくやった。一定。 (私はある時点で行方不明の南壁を自分のバージョンに置いていたようですが...サンプルにテストケースを入れておくと良いので、南壁を外してみましょう!)
Dr. Rebmu

回答:


10

コモドール64ベーシック

男、それは楽しかった。そして難しい。C64 Basicはほぼ間違いなくprint、ダンジョンをレンダリングするために画面がすでに使用されているため、デバッグさえ使用できません。のようなコードを書いているとき、あなたは楽しんでいるのを知っています55250 goto 55110。ダイクストラは私を殺します。

このプログラムは、2色と対角文字を使用します。

言うまでもなく、私はゴルフをしませんでした。結局のところ、今はコードチャレンジだということです。それはだ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化して、ある時点で投稿します...しかし、賞金はあなたのものです!大きな拍手。
レブム博士13

1
また、RE:ダイクストラ、彼が持っている不死の面白い引用を「私は意味、今から10年後ならば、あなたが何かを迅速かつ汚れをやっているとき、あなたは突然私はあなたの肩の上に探していますことを視覚化し、あなた自身「ダイクストラなりに言います「これが好きじゃなかった」、まあ、それは私にとって十分な不滅です。」 だから彼は彼の願いを得たと思います!:
レブム博士13

@ Dr.Rebmu:賞金に感謝します!これは文字通り1日中書くのにかかりました:)
マリヌス

10

(なぜそれをbashさせないのですか?)

私は今しなければならなかった。

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、北向き

4番目のもの以外は、すべてサンプルのように見えます(OPに関する私のコメントを参照)。

これらのスクリーンショットは、256色をサポートするurxvt v9.15で撮影されたもので、おそらく88色の端末ではまったく見た目が悪く、Unicodeサポートのない端末はまったく機能しません。使用したフォントは、AdobeのSource Code Proでした。


1
ハハ、バッシュ、そして色も!いいね あなたはその壁について完全に正しかったようです。どうやら私はプログラムのある時点で「修正」していました。 だから私はそれを修正しました。:-)キャッチしてくれてありがとう!
レブム博士13

3

Python 3の私のバージョンは次のとおりです。3k文字のようなもので、少しの労力で少し小さくすることができます(最初に削除できる空白がたくさんあります)。

現在+X/\、描画文字として使用していますが、Unicode文字を適切にレンダリングする固定幅フォントがある場合は、Unicode文字で描画するように設定されています。私はその機能を使用していませんが、異なる色の壁の角度のある部分に別々のタイルを使用することをサポートします。また、天井、床、および「離れた」タイルを提供できます。プレーヤーが東または西と北または南を向いている場合は、異なるタイルを使用できます。残念ながら、これは決して見栄えがよくなかったので、おそらくこれらすべてが空白(またはのような堅固なもの)である必要があります。

残念ながら、私のWindows 7システムでは、ブロック文字(およびなど)の完全なセットを含む等幅フォントを見つけるのに苦労しました。私が見つけたもののほとんどは、cmd何らかの理由でコンソールで利用可能にすることができませんでした(おそらくそれらが完全に等幅ではないためでしょうか?)。コンソールがより機能的であると思われる場合は、ファイルの上部近くでコメントアウトした代替文字セットを使用してみてください。2色でも見た目は悪くありません。それは天井と床とほとんど透明な壁を埋めました。

コード:

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)であることを示します。2番目の値は、x,y,t1つのピクセルの画面座標とタイルインデックスを示すタプルのリストです(奇数パリティでレンダリングされた壁は1、これらの各インデックスに追加されます)。形状は距離順に並べられているため、最初の3つはキャラクターの2タイル先の垂直な壁を表し、2タイル先の2つの平行な壁が続きます。

機能は次のとおりです。

  • rr:(画面バッファー内のタイルを印刷することにより)画面を「レンダリング」します。
  • dw:指定された画面バッファーに「壁を描く」。これはペインターアルゴリズムを使用するため、最も遠い壁が最初に描画され、近くの壁で覆われる可能性があります。
  • ga:「get area」は、指定されたマップの位置と向きに対してどの壁が不透明であるかを示すブール値のリストを返します。
  • rd: "read"、マップを読み取り、ラインを生成するジェネレーター。IDLEのコンソールは、一度に1行ずつ入力するのではなく、複数行の入力を貼り付けると奇妙なことを行うため、これが必要なだけです。
  • rm:「マップを読み取る」、マップを解析して、ネストされたブール値のリストにインデックスを付けますm[y][x][d]d=0東とd=1南)。また、他のコードでのインデックスエラーを回避するために、2行2列のパディングスクエアを追加します。
  • cl:出力を「クリア」します(ほとんどのコンソールの上部から古いビューをスクロールするのに十分な改行を書き込むことにより)。
  • gl:「ゲームループ」。入力が収集され、上記のものが呼び出されます。

いくつかの「スクリーンショット」:

開始位置:

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

例に一致するショットがいくつかあります(空白の最初の行はStack Overflowによって切り取られているため、プログラムの出力に含まれています)。

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

ビューに平行な壁は、背後に突き出ている垂直な壁と同じ色なので、提供されたマップの見知らぬビューの1つを次に示します。

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

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

上からラストショットの領域は次のようになります。

_   _
 |
  V

分析と図表を追加しました!うーん、これらの壁は私の実装の最後の壁でも同じ色になります。エッジケースについての良い点。私はそれが起こるとは思いませんでしたが、そうする必要があります。地図の色付けのようなもので、実際には2色では十分ではありません...:-/
レブム博士13
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.