Підземелля будівництво набір


19

У дитинстві я грав у гру Intellivision Advanced Dungeons and Dragons: Treasure of Tarmin . 3-D графіка поставить вас в точку зору від першої особи з приголомшливим реалізмом:

Шокуюче реалістична тривимірна графіка

Але тоді я отримав C-64. І мені вдалося намалювати сітку символів розміром 40x25, курсивши по екрану, встановивши колір клавішею Ctrl і цифрою, і поставивши символи куди хотілося (чому bashмені це не дозволяють?) . Набір символів мав трикутні компоненти та компоненти твердого блоку. Тому я міг міркувати, як можна створити відображення своєї точки зору в сітці через цей носій.

Цього тижня я знайшов специфікацію майже трьох десятиліть у зошиті папером для зошитів про "Будівельний набір підземелля" цього тижня:

введіть тут опис зображення

( ОНОВЛЕННЯ . Уважні читачі помітять, що це не дуже сильно тримається на нахилених частинах. Виправлені номери наведені нижче.)

Хоча скарб Тарміна грав на сітці, стіни існували лише на краях сітчастих квадратів. Дізнавшись, що таке байти, я зрозумів, що якби я зробив карту з байтів ... то кожен квадрат на карті міг би мати чотири можливі стани для кожного її краю:

  1. Безперешкодно
  2. Стіна
  3. Двері
  4. Щось ще?

Я ніколи не збирався писати це (до останньої ночі). Я думав, що іншим може бути цікаво спробувати.

Отже, ваше завдання - реалізувати лабіринт на основі символів, який реалізує мої (виправлені !!) характеристики ... але використовуючи технології 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?

На кожному кроці виведіть дисплей 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)

Роз'яснення

  • Суміжні комірки можуть не погоджуватися щодо спільних стінок. Тож південний край на квадраті може бути стіною, тоді як північний край на площі до півдня від нього буде безперешкодним. В оригінальному дизайні я вважав цю особливість: вона допускає такі цікаві ідеї, як односторонні двері ... або невидимі стіни, які з’являються лише після того, як ви пройшли через них. Для цього спрощення дотримуйтесь того самого правила: для навігації та візуалізації зверніть увагу лише на стан краю на найближчій до вас клітинці у напрямку, до якого ви стикаєтесь .

  • Вид набагато краще з "затіненням". Отже, для повних блоків чергуйте або Unicode 2593 ▓ і 2591 ░, або використовуйте, Xі +якщо ваша реалізація ASCII.

  • Символи трикутника 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
Doorknob

1
@Doorknob Не дозволяйте це обдурити вас ... насправді це не все так важко. Існує досить гарна підказка зі списками трьох обмежуючих розмірів. А що таке гольф, а не проблема, яка вирішується і потім формується? :-) Але я дозволю людям вибрати, як вони хочуть її вирішити ... НП
доктор Ребму

Не могли б ви пояснити два стовпці Xs, на ваш погляд, на 3, 2обличчя півдня?
джазпі

Особливо той, що праворуч. Я бачу, чому ліва там. Але правильний, здається, порушує Роз'яснення №1.
джазпі

@jazzpi На жаль, ви праві, карта, яку я виклав, потребує підпорядкування 1! Молодці. Виправлено. (Я, мабуть, поставив бідну південну стіну в моїй власній версії в якийсь момент, мабуть ... але добре мати тест-зразок у зразку ... тому давайте залишимо південну стіну!)
Доктор Ребму,

Відповіді:


10

Commodore 64 Basic

Людина, це було весело. І важко. C64 Basic майже не піддається відладці, ви навіть не можете використовувати printналагодження, оскільки екран вже зроблений для надання підземелля. Ви знаєте, що вам цікаво, коли ви пишете такий код 55250 goto 55110. Дійкстра вб’є мене.

Програма використовує два кольори та діагональні символи.

Потрібно говорити, що я не займався гольфом. Адже він говорить про виклик коду зараз. Це 7183 байти, якщо вас цікавить.

Це повільно - при швидкості за замовчуванням знадобиться кілька секунд, щоб відтворити сцену. Максимальний розмір карти - 10 на 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 і опублікую його в якийсь момент ... але щедрота за вами! Великі оплески.
Доктор Ребму

1
Крім того, RE: Dijkstra, у нього є кумедна цитата про безсмертя : "Я маю на увазі, якщо через 10 років, коли ви робите щось швидке і брудне, ви раптом уявляєте, що я дивлюся через ваші плечі, і кажу собі" Dijkstra не сподобалось це "ну, це було б для мене достатньо безсмертя". Тому я здогадуюсь, він отримав своє бажання! :-)
Доктор Ребму

@ Dr.Rebmu: дякую за щедроту! Це писало мені буквально цілий день :)
marinus

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, зверненою на північ

Крім четвертої, всі вони також схожі на зразки (див. Мій коментар до ОП).

Ці скріншоти були зроблені на urxvt v9.15 з 256 кольоровою підтримкою, напевно, це виглядало б досить лайно на 88 кольоровому терміналі, а термінали без підтримки Unicode взагалі не працюють. Шрифт, який я використав, був вихідним кодом Pro від Adobe.


1
Ха-ха, в баш, і в кольорі теж! Приємно. Ви були абсолютно праві щодо тієї стіни, мабуть, я в якийсь момент "виправив її" у своїй програмі. Тому я його виправив. :-) Дякую за улов!
Доктор Ребму

3

Ось моя версія в Python 3. Це щось на кшталт 3k символів і може отримати трохи менше, доклавши невеликих зусиль (для початку можна буде видалити багато білого простору).

В даний час він використовує +X/\як свої символи малювання, але він налаштований для малювання символами Unicode, якщо у вас є шрифт фіксованої ширини, який відображатиме їх належним чином. Він підтримує використання окремих плиток для кутових частин стін, що відрізняються кольором, хоча я не використовую цю функцію. Це також дозволяє надати стелі, підлогу та "далекі" плитки, і ви можете використовувати різні для тих випадків, коли гравець виходить на схід чи захід проти півночі чи півдня. На жаль, це ніколи не виглядало дуже добре, тому, ймовірно, все це повинно бути порожнім (або чимось твердим, як ).

На жаль, у моїй системі 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додані до кожного з цих індексів). Форми впорядковані по відстані, тому перші три представляють перпендикулярні стіни на дві плитки попереду символу, за ними дві паралельні стіни, дві плитки попереду тощо.

Функції:

  • rr: "візуалізація" екрана (надрукувавши плитки в екрані буфера).
  • dw: "намалюйте стіни" до передбаченого буфера екрана. Для цього використовується алгоритм художників, тому найдальші стіни малюються спочатку і можуть закриватися ближчими.
  • ga: "get area" повертає список булевих значень із зазначенням, які стіни непрозорі для заданого положення карти та облицювання.
  • rd: "read", генератор, який читає карту, поступаючись рядкам. Це потрібно лише тому, що консоль IDLE робить дивні речі, коли ви вставляєте багаторядкові введення, а не вводите по одному рядку за раз.
  • rm: "читати карту", аналізує карту на вкладений список булевих знаків, індексований m[y][x][d](із d=0східним та d=1південним). Він також додає два рядки та два стовпчики прокладених квадратів, щоб уникнути помилок індексу в іншому коді.
  • 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]? 

Кілька знімків, що відповідають вашим прикладам (зверніть увагу, пусті перші рядки відсікаються за допомогою переповнення стека, вони є у вихідному програмі):

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

Приємний доданий аналіз та діаграми! Гм, ці стіни завиваються тим самим кольором, що і останній у моєму виконанні. Хороший момент про край корпусу. Я не думав, що це станеться, але це має бути. Здогадайтесь, це як фарбування карти, а двох кольорів насправді недостатньо ...: - /
Доктор Ребму
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.