Bộ xây dựng ngục tối


19

Khi tôi còn là một đứa trẻ, tôi đã chơi trò chơi Intellivision Advanced Dungeons and Dragons: Treasure of Tarmin . Đồ họa 3 chiều đưa bạn vào góc nhìn góc nhìn thứ nhất với chủ nghĩa hiện thực gây sốc:

Đồ họa 3-D thực tế gây sốc

Nhưng sau đó tôi có một chiếc C-64. Và tôi đã có thể vẽ trên lưới ký tự 40x25 bằng cách nguyền rủa xung quanh màn hình, đặt màu bằng phím Ctrl và chữ số, và đặt các ký hiệu ở bất cứ đâu tôi muốn (tại sao tôi không bashcho phép tôi làm điều đó?) . Bộ ký tự có các thành phần hình tam giác và các thành phần khối rắn. Vì vậy, tôi đã có thể suy luận về cách người ta có thể tạo ra kết xuất phối cảnh của một người trong một lưới thông qua phương tiện đó.

Tôi đã tìm thấy thông số kỹ thuật gần ba thập kỷ này, trên tờ sổ tay có hình xoắn ốc, về "Bộ xây dựng ngục tối" trong tuần này:

nhập mô tả hình ảnh ở đây

( CẬP NHẬT : Người đọc cẩn thận sẽ nhận thấy rằng điều này không hoàn toàn kết hợp với các phần nghiêng. Các số chính xác được cung cấp dưới đây.)

Mặc dù Treasure of Tarmin được chơi trên lưới, các bức tường chỉ tồn tại trên các cạnh của hình vuông lưới. Tìm hiểu các byte là gì, tôi nhận ra rằng nếu tôi tạo bản đồ ra khỏi byte ... thì mỗi ô vuông trên bản đồ có thể có bốn trạng thái có thể có cho mỗi cạnh của nó:

  1. Không bị cản trở
  2. Tường
  3. Cửa
  4. Thứ gì khác?

Tôi chưa bao giờ đi xung quanh để viết nó (cho đến tối qua). Tôi nghĩ rằng nó có thể là niềm vui cho những người khác để thử.

Vì vậy, nhiệm vụ của bạn là triển khai trình kết xuất mê cung dựa trên chế độ nhân vật thực hiện (đã sửa !!) của tôi thông số kỹ thuật ... nhưng sử dụng các công nghệ của năm 2013.

Đầu vào

Bởi vì thông số kỹ thuật không xác định kết xuất cho cửa, chúng tôi sẽ chỉ giả sử các tùy chọn duy nhất là tường và không tường. Để đơn giản, đầu vào của bạn là một bản đồ bao gồm các chuỗi các chuỗi trông như thế này:

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

Đó sẽ là bản đồ 5x5. Góc trên bên trái (1,1) có bộ tường West và North. Góc dưới bên phải (5,5) có phần ngoài SE tường ngoài ast.

Điều này là ít thú vị hơn đáng kể với không có điều hướng bản đồ. Vì vậy, tối thiểu, đặt người chơi của bạn ở (1,1) hướng về phía bắc và cung cấp cho họ:

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

Ở mỗi bước, xuất ra màn hình 16x15 của phối cảnh ngôi thứ nhất, như được xác định bởi thông số giấy máy tính xách tay. Để giữ cho bạn khỏi phải đếm, kích thước của các bức tường phẳng ở ba khoảng cách là:

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

Các kích thước giới hạn của các bức tường nghiêng là:

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

Làm rõ

  • Các tế bào liền kề có thể không đồng ý về các bức tường được chia sẻ. Vì vậy, cạnh phía nam trên một hình vuông có thể là một bức tường, trong khi cạnh phía bắc trên hình vuông ở phía nam của nó sẽ không bị cản trở. Trong thiết kế ban đầu tôi coi đây là một tính năng: nó cho phép những ý tưởng thú vị như cửa một chiều ... hoặc những bức tường vô hình chỉ xuất hiện sau khi bạn đi qua chúng. Để đơn giản hóa này, hãy tuân theo quy tắc tương tự: để điều hướng và kết xuất, chỉ chú ý đến trạng thái cạnh trên ô gần bạn nhất theo hướng bạn đang đối mặt .

  • Chế độ xem tốt hơn rất nhiều với "bóng". Vì vậy, đối với các khối đầy đủ của bạn, hãy thay thế Unicode 2593 và 2591 hoặc sử dụng X+nếu triển khai của bạn là ASCII.

  • Các ký tự tam giác Unicode (25E2, 25E3, 25E4, 25E5) là một chút khập khiễng khi vẽ điều này. Bên cạnh việc không có bất kỳ biến thể bóng mờ nào, chúng thường chỉ kéo dài chiều rộng của ký tự và không có chiều cao đầy đủ ... ngay cả trong các phông chữ có chiều rộng cố định. Bạn có thể vẽ các khối đầy đủ hoặc dấu gạch chéo hoặc một cái gì đó bạn chọn ở những nơi tôi muốn đường chéo. Các giải pháp sáng tạo thú vị kết hợp màu sắc và sử dụng các ký tự này thay vì đánh giá cao.

  • Bạn có thể cho rằng các bức tường ngoài cùng được đặt để giới hạn khu vực chơi, vì vậy bạn không phải lo lắng về việc hiển thị bất cứ thứ gì bên ngoài mê cung. Bất kỳ bức tường nào cách xa bạn hơn thông số kỹ thuật đều bị bỏ qua và chỉ để lại khoảng trống.

  • Màu của bức tường bạn nhìn thấy ngay trước mặt nếu hướng về phía Bắc tại (1,1) phải là DARK. Bóng xen kẽ trên các bức tường liền kề trong bản đồ, sao cho nếu tất cả các bức tường đều có mặt thì một bức tường sáng sẽ không bao giờ có một bức tường tối.

  • Việc triển khai C-64 thực sự thực hiện những gì tôi dự định ban đầu ... với các ký tự chéo và tất cả ... sẽ vượt qua mọi tiêu chí nhập cảnh khác. :-)

Ví dụ

Đối với bản đồ mẫu được đưa ra ở trên ...

Tại (1,3) hướng về phía nam:

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

Tại (3,2) hướng về phía nam:

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

Tại (3,2) hướng về phía đông:

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

Tại (2,3) hướng về phía bắc:

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

1
Tôi đề nghị biến điều này thành một thử thách mã - một sân golf sẽ trở nên quá khó đọc và khó khăn: P
Doorknob

1
@Doorknob Đừng để nó đánh lừa bạn ... thực sự không khó lắm đâu. Có một gợi ý khá hay với danh sách 3 kích cỡ giới hạn. Và một golf là gì nhưng một thách thức được giải quyết và sau đó giảm xuống? :-) Nhưng tôi sẽ cho phép mọi người chọn cách họ muốn giải quyết ... NP
Tiến sĩ Rebmu

Bạn có thể vui lòng giải thích hai cột của Xs trong quan điểm của bạn về 3, 2phía nam?
jazzpi

Đặc biệt là một bên tay phải. Tôi thấy tại sao một bên trái là ở đó. Nhưng cái bên phải dường như vi phạm Làm rõ # 1.
nhạc jazz

@jazzpi Rất tiếc, bạn nói đúng, bản đồ tôi đưa lên cần phải tuân theo làm rõ 1! Làm tốt. Đã sửa. (Tôi đã đặt bức tường phía nam bị mất vào phiên bản của chính mình vào một lúc nào đó ... nhưng thật tốt khi có một trường hợp thử nghiệm trong mẫu ... vì vậy hãy bỏ bức tường phía nam ra!)
Tiến sĩ Rebmu

Câu trả lời:


10

Hàng hóa 64 cơ bản

Man, đó là niềm vui. Và khó. C64 Basic gần như không thể tải được, thậm chí bạn không thể sử dụng printgỡ lỗi vì màn hình đã được sử dụng để hiển thị dungeon. Bạn biết bạn đang vui vẻ khi bạn viết mã như thế nào 55250 goto 55110. Dijkstra sẽ giết tôi.

Chương trình sử dụng hai màu sắc và các ký tự chéo.

Không cần phải nói tôi đã không chơi nó. Nó nói rằng thách thức mã bây giờ, sau khi tất cả. Đó là 7183 byte nếu bạn quan tâm.

Nó chậm - ở tốc độ mặc định, phải mất vài giây để nó hiển thị cảnh. Kích thước bản đồ tối đa là 10 x 10 nhưng có thể thay đổi bằng cách chỉnh sửa dòng 120.

Tôi đã phát triển và thử nghiệm điều này bằng trình giả lập VICE . Mã dưới đây được hiển thị trong ASCII, do đó có nghĩa là đã thay đổi PETSCII. Tuy nhiên, khi nhập bản đồ, bạn nên sử dụng PETSCII chưa được chỉnh sửa .

Ảnh chụp màn hình: Ảnh chụp màn hình

Mã số:

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

Hình ảnh băng: tải về tại đây .

Các ví dụ:

ví dụ


1
CHÚA ƠI. Nếu những người khác muốn giải quyết vấn đề này, thật tuyệt ... nhưng bạn đã giành được tiền thưởng theo định nghĩa của con át chủ bài trong thử thách. Tôi đã cố gắng rút ra một trình giả lập và làm điều đó vì lý do hoài cổ, nhưng nghĩ rằng sẽ hiệu quả hơn khi viết nó bằng Red để xem trình biên dịch chéo có thể giữ tốt như thế nào. nguồn cho điều đó . Tôi sẽ Rebmu-ify và đăng nó vào một lúc nào đó ... nhưng tiền thưởng là của bạn! Tiếng vỗ tay lớn.
Bác sĩ Rebmu

1
Ngoài ra, RE: Dijkstra, anh ta có một câu nói hài hước về sự bất tử : "Ý tôi là, nếu 10 năm nữa, khi bạn đang làm gì đó nhanh và bẩn, bạn đột nhiên hình dung rằng tôi đang nhìn qua vai bạn và nói với chính mình 'Dijkstra sẽ không thích điều này ', tốt, điều đó sẽ đủ bất tử đối với tôi. " Vì vậy, tôi đoán anh ấy có ước muốn của mình! :-)
Bác sĩ Rebmu

@ Dr.Rebmu: cảm ơn vì tiền thưởng! Điều này khiến tôi mất cả ngày để viết :)
marinus 23/12/13

10

(tại sao không bashđể tôi làm điều đó?)

Tôi chỉ phải đến bây giờ.

Bash, 12743 ký tự

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

Xin lưu ý rằng đây là điều đầu tiên tôi làm với bash nó không chỉ là ghép một vài lệnh với nhau. Nó có thể giảm được khá nhiều nếu tôi không mã hóa tất cả các bức tường, nhưng có vẻ dễ hơn. Nó không có tính nhất quán nào. Định dạng byte cho mỗi ô vuông được chọn theo cách khủng khiếp. Nhưng nó đã có tác dụng.

Tôi thậm chí đã thêm hỗ trợ cho việc di chuyển thông qua các phím mũi tên :)

Đây là một số ảnh chụp màn hình cho đầu vào mẫu (Lưu ý rằng bản đồ của tôi bắt đầu tại (0 | 0)):

0 | 0, hướng về phía bắc 0 | 2, hướng về phía nam 2 | 1, quay mặt về hướng đông 2 | 1, hướng về phía nam 1 | 2, hướng về phía bắc

Ngoài cái thứ tư, tất cả chúng đều trông giống như mẫu thử (xem nhận xét của tôi về OP).

Những ảnh chụp màn hình này được chụp trên urxvt v9.15 với hỗ trợ 256 màu, nó có thể trông khá tào lao trên thiết bị đầu cuối 88 màu và các thiết bị đầu cuối không có hỗ trợ unicode hoàn toàn không hoạt động. Phông chữ tôi sử dụng là Source Code Pro của Adobe.


1
Haha, trong bash, và trong màu sắc quá! Tốt đẹp. Bạn đã hoàn toàn đúng về bức tường đó, rõ ràng tôi đã có lúc "sửa nó" trong chương trình của mình. Vì vậy, tôi đã sửa nó. :-) Cảm ơn bạn đã bắt!
Bác sĩ Rebmu

3

Đây là phiên bản của tôi, trong Python 3. Nó giống như 3 ký tự và có thể nhỏ hơn một chút với một chút nỗ lực (có rất nhiều khoảng trắng có thể được loại bỏ, để bắt đầu).

Hiện tại nó sử dụng +X/\làm ký tự vẽ, nhưng nó được thiết lập để vẽ bằng các ký tự Unicode nếu bạn có phông chữ có chiều rộng cố định sẽ hiển thị chúng đúng cách. Nó hỗ trợ sử dụng gạch riêng cho các phần góc của các bức tường được thu thập khác nhau, mặc dù tôi không sử dụng tính năng đó. Nó cũng cho phép bạn cung cấp trần, sàn và gạch "xa" và bạn có thể sử dụng các loại khác nhau khi người chơi quay mặt về hướng đông hoặc tây so với bắc hoặc nam. Than ôi điều này không bao giờ trông rất tốt, vì vậy có lẽ tất cả những thứ này nên để trống (hoặc một cái gì đó vững chắc, như ).

Than ôi, trên hệ thống Windows 7 của tôi, tôi đã có một khoảng thời gian khủng khiếp khi cố gắng tìm một phông chữ đơn cách với đầy đủ các ký tự khối (ví dụ ). Hầu hết những cái tôi tìm thấy không thể có sẵn trongcmd bảng điều khiển vì một số lý do (có lẽ vì chúng không hoàn toàn đơn cách?). Nếu bạn nghĩ rằng bảng điều khiển của bạn có nhiều chức năng hơn, hãy thử sử dụng bộ ký tự thay thế mà tôi đã nhận xét ở gần đầu tệp, trông không tệ lắm dù chỉ có hai màu. Nó đã lấp đầy trần nhà và sàn nhà và hầu hết các bức tường trong suốt.

Mật mã:

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

Bộ ký tự được chỉ định ở gần đầu tệp. Thứ tự của các ký tự là:

  1. thậm chí tường chẵn lẻ
  2. tường chẵn lẻ
  3. thậm chí chẵn góc trên cùng bên phải (ví dụ /với một bức tường bên dưới nó)
  4. lẻ góc trên cùng bên phải góc tường
  5. thậm chí chẵn góc trên cùng bên trái
  6. lẻ góc trên cùng bên trái góc tường
  7. thậm chí chẵn lẻ góc tường bên phải
  8. lẻ góc dưới cùng bên phải góc tường
  9. thậm chí chẵn lẻ góc tường bên trái
  10. chẵn lẻ góc dưới cùng bên trái góc tường
  11. trần đối diện E / W
  12. N / S trần
  13. đường chân trời đối diện E / W (giữa màn hình nếu không có tường)
  14. đường chân trời đối diện với N / S
  15. mặt đối diện E / W
  16. mặt đối diện N / S

Có 15 bức tường có thể cần được kết xuất bởi trò chơi, theo mô hình như thế này (với việc Vchỉ ra vị trí và vòng cung của người chơi):

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

Các gạch được sử dụng bởi 15 bức tường được xác định trong shapesdanh sách. Đó là một danh sách gồm 2 bộ. Giá trị đầu tiên của bộ dữ liệu biểu thị "tính chẵn lẻ" của bức tường, với 0chỉ ra rằng nó nên được vẽ với cùng các ký tự như một bức tường ngay trước mặt nhân vật và 1chỉ ra rằng nó phải là mô hình thay thế (ví dụ +vs X). Giá trị thứ hai là một danh sách các x,y,tbộ dữ liệu cho biết tọa độ màn hình và chỉ số ô của một pixel (các bức tường được hiển thị với tính chẵn lẻ sẽ 1được thêm vào từng chỉ số này). Các hình được sắp xếp theo khoảng cách, vì vậy ba hình đầu tiên đại diện cho các bức tường vuông góc hai gạch phía trước nhân vật, tiếp theo là hai bức tường song song hai gạch phía trước, v.v.

Các chức năng là:

  • rr: "kết xuất" màn hình (bằng cách in các ô trong bộ đệm màn hình).
  • dw: "vẽ tường" vào bộ đệm màn hình được cung cấp. Điều này sử dụng thuật toán họa sĩ, vì vậy những bức tường xa nhất được vẽ trước và có thể được che phủ bởi những người gần hơn.
  • ga: "get area" trả về một danh sách các giá trị boolean cho biết bức tường nào mờ đục đối với vị trí bản đồ nhất định và phải đối mặt.
  • rd: "đọc", một trình tạo đọc bản đồ, thu được các dòng. Điều này chỉ cần thiết bởi vì bảng điều khiển của IDLE thực hiện những thứ kỳ lạ khi bạn dán các đầu vào nhiều dòng thay vì nhập từng dòng một.
  • rm: "đọc bản đồ", phân tích bản đồ thành một danh sách các booleans lồng nhau, được lập chỉ mục bởi m[y][x][d](với d=0hướng Đông và d=1là Nam). Nó cũng thêm hai hàng và hai cột của hình vuông đệm, để tránh lỗi chỉ mục trong mã khác.
  • cl: "xóa" đầu ra (bằng cách viết đủ dòng mới để cuộn chế độ xem cũ khỏi đầu bảng điều khiển nhất).
  • gl: "vòng lặp trò chơi", trong đó đầu vào được thu thập và các nội dung trên được gọi.

Một vài "ảnh chụp màn hình":

Vị trí bắt đầu:

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

Nhìn dọc theo bức tường phía bắc:

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

Một vài bức ảnh phù hợp với ví dụ của bạn (lưu ý, các dòng đầu tiên trống đang bị cắt bởi Stack Overflow, chúng nằm trong đầu ra của chương trình):

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

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

Và:

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

Đây là một trong những chế độ xem lạ trong bản đồ được cung cấp, vì bức tường song song với chế độ xem của chúng tôi có cùng màu với bức tường vuông góc dán phía sau nó:

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

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

Đây là khu vực của lần bắn cuối cùng trông như thế nào từ phía trên:

_   _
 |
  V

Đẹp thêm phân tích và sơ đồ! Hừm, những bức tường đó cũng có màu giống như màu cuối cùng trong quá trình thực hiện của tôi. Điểm tốt về trường hợp cạnh. Tôi đã không nghĩ rằng điều đó sẽ xảy ra, nhưng nó phải như vậy. Đoán nó giống như tô màu bản đồ, và hai màu không thực sự đủ ...: - /
Tiến sĩ Rebmu 23/12/13
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.