Ensemble de construction de donjon

19

Quand j'étais enfant, je jouais au jeu Intellivision Advanced Dungeons and Dragons: Treasure of Tarmin . Les graphismes en 3D vous mettent dans une perspective à la première personne avec un réalisme choquant:

Graphiques 3D incroyablement réalistes

Mais ensuite, j'ai eu un C-64. Et j'ai pu dessiner sur la grille de 40x25 caractères en survolant l'écran, en définissant la couleur avec la touche Ctrl et un chiffre, et en plaçant des symboles où je voulais (pourquoi ne me bashlaisse-t-il pas faire cela?) . Le jeu de caractères avait des composants triangulaires et des composants en blocs solides. J'ai donc pu raisonner sur la façon dont on pouvait générer un rendu de sa perspective dans une grille à travers ce support.

J'ai trouvé la spécification vieille de près de trois décennies, dans du papier pour cahier à reliure spirale, sur "Dungeon Construction Set" cette semaine:

entrez la description de l'image ici

( MISE À JOUR : Les lecteurs attentifs remarqueront que cela ne tient pas tout à fait ensemble sur les pièces inclinées. Les nombres corrigés sont fournis ci-dessous.)

Bien que Treasure of Tarmin ait été joué sur une grille, les murs n'existaient que sur les bords des carrés de la grille. Ayant appris quels étaient les octets, j'ai réalisé que si je faisais la carte à partir d'octets ... alors chaque carré sur la carte pourrait avoir quatre états possibles pour chacun de ses bords:

  1. Dégagée
  2. mur
  3. Porte
  4. Autre chose?

Je n'ai jamais réussi à l'écrire (jusqu'à hier soir). J'ai pensé que ça pourrait être amusant pour les autres d'essayer.

Votre tâche consiste donc à implémenter un rendu de labyrinthe basé sur le mode caractère qui implémente mes spécifications (corrigées !!) ... mais en utilisant les technologies de 2013.

Contribution

Étant donné que la spécification ne définit pas le rendu des portes, nous supposerons simplement que les seules options sont mur et non mur. Pour plus de simplicité, votre entrée est une carte composée de lignes de chaînes qui ressemblent à ceci:

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

Ce serait une carte 5x5. Le coin supérieur gauche (1,1) a son ensemble de murs West et North. Le coin inférieur droit (5,5) a son ensemble de murs extérieur Set East.

C'est beaucoup moins amusant sans navigation sur la carte. Donc au minimum, placez votre joueur à (1,1) face au nord et offrez-lui:

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

À chaque étape, affichez un affichage 16x15 de la perspective à la première personne, comme défini par la spécification papier du carnet. Pour vous éviter d'avoir à compter, la taille des murs plats aux trois distances est:

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

Les dimensions limites des murs inclinés sont les suivantes:

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

Clarifications

  • Les cellules adjacentes peuvent ne pas être d'accord sur les murs partagés. Ainsi, le bord sud d'un carré pourrait être un mur, tandis que le bord nord du carré au sud serait dégagé. Dans la conception originale, je considérais cela comme une caractéristique: cela permet des idées intéressantes comme des portes à sens unique ... ou des murs invisibles qui n'apparaissent qu'après les avoir franchis. Pour cette simplification, suivez la même règle: pour la navigation et le rendu, faites attention uniquement à l'état du bord sur la cellule la plus proche de vous dans la direction vers laquelle vous faites face .

  • La vue est bien meilleure avec "l'ombrage". Donc, pour vos blocs complets, alternez soit Unicode 2593 ▓ et 2591 ░, soit utilisez Xet +si votre implémentation est ASCII.

  • Les caractères triangulaires Unicode (25E2 ◢, 25E3 ◣, 25E4 ◤, 25E5 ◥) sont un peu boiteux pour dessiner cela. En plus de ne pas avoir de variantes ombrées, elles étirent souvent uniquement la largeur du caractère et non la pleine hauteur ... même dans les polices à largeur fixe. Vous pouvez dessiner des blocs entiers ou des caractères slash ou quelque chose de votre choix dans les endroits où je voulais des diagonales. Des solutions créatives intéressantes qui incorporent la couleur et utilisent ces caractères au lieu de l'ombrage sont appréciées.

  • Vous pouvez supposer que les murs les plus extérieurs sont définis pour délimiter la zone de jeu, vous n'avez donc pas à vous soucier de rendre quoi que ce soit en dehors du labyrinthe. Tous les murs plus éloignés de vous que la spécification sont ignorés et ne laissent qu'un espace vide.

  • L'ombrage du mur que vous voyez directement devant vous si vous faites face au nord à (1,1) doit être FONCÉ. Ombrage alterné sur les murs adjacents de la carte, de sorte que si tous les murs étaient présents, un mur clair ne serait jamais en contact avec un mur sombre.

  • Une implémentation C-64 qui fait réellement ce que je voulais à l'origine ... avec les caractères diagonaux et tout ... l'emportera sur tout autre critère d'entrée. :-)

Exemples

Pour l'exemple de carte ci-dessus ...

Au (1,3) plein sud:

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

Au (3,2) plein sud:

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

À (3,2) face à l'est:

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

À (2,3) face au nord:

               /
 ++++++++++++++X
 ++++++++++++++X
 ++++++++++++++X
 ++++++++++++++X
X++++++++++++++X
X++++++++++++++X
X++++++++++++++X
X++++++++++++++X
X++++++++++++++X
 ++++++++++++++X
 ++++++++++++++X
 ++++++++++++++X
 ++++++++++++++X
               \
Dr. Rebmu
la source
1
Je suggère d'en faire un défi de code - un golf serait beaucoup trop illisible et difficile: P
Poignée de porte
1
@ Doorknob Ne vous laissez pas tromper ... ce n'est en fait pas si difficile. Il y a un assez bon indice avec les listes de 3 tailles de délimitation. Et qu'est-ce qu'un golf, sinon un défi qui est résolu puis réduit? :-) Mais je laisserai les gens choisir comment ils veulent le résoudre ... NP
Dr. Rebmu
Pourriez-vous s'il vous plaît expliquer les deux colonnes de Xs dans votre vue du 3, 2sud?
jazzpi
Surtout celui du côté droit. Je vois pourquoi celui de gauche est là. Mais la bonne semble violer la clarification n ° 1.
jazzpi
@jazzpi Oups, vous avez raison, la carte que j'ai mise en place doit obéir à la clarification 1! Bien joué. Fixé. (Je mettrais le mur sud manquant dans ma propre version à un moment donné apparemment ... mais c'est bien d'avoir un cas de test dans l'échantillon ... alors laissons le mur sud dehors!)
Dr. Rebmu

Réponses:

10

Commodore 64 Basic

C'était amusant. Et dur. C64 Basic est presque non débogable, vous ne pouvez même pas utiliser le printdébogage car l'écran est déjà utilisé pour le rendu du donjon. Vous savez que vous vous amusez lorsque vous écrivez du code comme 55250 goto 55110. Dijkstra va me tuer.

Le programme utilise deux couleurs et des caractères diagonaux.

Inutile de dire que je ne l'ai pas joué au golf. Il dit défi de code maintenant, après tout. C'est 7183 octets si vous êtes intéressé.

C'est lent - à la vitesse par défaut, il faut plusieurs secondes pour rendre la scène. La taille maximale de la carte est de 10 x 10, mais elle peut être modifiée en modifiant la ligne 120.

J'ai développé et testé cela en utilisant l' émulateur VICE . Le code ci-dessous est affiché en ASCII, ce qui signifie PETSCII décalé . Cependant, lors de la saisie de la carte, vous devez utiliser PETSCII non décalé .

Capture d'écran: Capture d'écran

Code:

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

Image de la bande: téléchargez-la ici .

Les exemples:

exemples

marinus
la source
1
OMG. Si d'autres veulent résoudre ce problème pour le mieux, c'est bien ... mais vous avez gagné la prime par définition de l'atout dans le défi. J'ai été tenté de retirer un émulateur et de le faire pour des raisons de nostalgie, mais j'ai pensé qu'il était plus productif de l'écrire en rouge pour voir dans quelle mesure le compilateur croisé du compilateur pouvait tenir le coup. source pour cela . Je vais le refaire et le poster à un moment donné ... mais la prime vous appartient! Gros applaudissements.
Dr.Rebmu
1
En outre, RE: Dijkstra, il a une citation amusante sur l'immortalité : "Je veux dire, si dans 10 ans, lorsque vous faites quelque chose de rapide et de sale, vous visualisez soudainement que je regarde par-dessus vos épaules et vous vous dites" Dijkstra pas aimé ça, eh bien, ce serait assez d'immortalité pour moi. " Donc je suppose qu'il a réalisé son souhait! :-)
Dr. Rebmu
@ Dr.Rebmu: merci pour la générosité! Cela m'a pris littéralement toute la journée pour écrire :)
marinus
10

(pourquoi ne me bashlaisse-t-il pas faire ça?)

Je devais juste le faire maintenant.

Bash, 12743 caractères

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

S'il vous plaît, gardez à l'esprit que c'est à peu près la première chose que j'ai faite avec bashcela, c'était plus que simplement passer quelques commandes ensemble. Il serait probablement beaucoup plus réductible si je ne codais pas en dur tous les murs, mais cela semblait plus facile. Il n'a aucune cohérence. Le format d'octet pour chaque carré est choisi d'une manière horrible. Mais ça marche.

J'ai même ajouté un support pour le mouvement à travers les touches fléchées :)

Voici quelques captures d'écran de l'exemple d'entrée (notez que ma carte commence à (0 | 0)):

0 | 0, face au nord 0 | 2, plein sud 2 | 1, face à l'est 2 | 1, plein sud 1 | 2, face au nord

Mis à part le quatrième, ils ressemblent tous à ceux de l'échantillon (voir mon commentaire sur l'OP).

Ces captures d'écran ont été prises sur urxvt v9.15 avec une prise en charge de 256 couleurs, cela aurait probablement l'air assez merdique sur un terminal 88 couleurs, et les terminaux sans prise en charge Unicode ne fonctionnent pas du tout. La police que j'ai utilisée était Source Code Pro d'Adobe.

jazzpi
la source
1
Haha, en bash, et en couleur aussi! Agréable. Vous aviez tout à fait raison à propos de ce mur, apparemment, à un moment donné, je l'avais "réparé" dans mon programme. Je l'ai donc corrigé. :-) Merci pour la capture!
Dr.Rebmu
3

Voici ma version, en Python 3. C'est quelque chose comme 3k caractères et pourrait devenir un peu plus petit avec un petit effort (il y a beaucoup d'espace blanc qui pourrait être supprimé, pour commencer).

Il utilise actuellement +X/\comme caractères de dessin, mais il est configuré pour dessiner avec des caractères Unicode si vous avez une police à largeur fixe qui les rendra correctement. Il prend en charge l'utilisation de tuiles distinctes pour les parties angulaires des murs à coleurs différents, bien que je n'utilise pas cette fonctionnalité. Il vous permet également de fournir des dalles de plafond, de sol et "distantes", et vous pouvez en utiliser différentes lorsque le joueur est face à l'est ou à l'ouest par rapport au nord ou au sud. Hélas, cela n'a jamais semblé très bon, donc probablement tous ces éléments devraient être vides (ou quelque chose de solide, comme ).

Hélas, sur mon système Windows 7, j'ai eu un horrible temps à essayer de trouver une police à espacement fixe avec l'ensemble complet des caractères de bloc (par exemple et ). La plupart de celles que j'ai trouvées n'ont pas pu être disponibles dans la cmdconsole pour une raison quelconque (peut-être parce qu'elles ne sont pas parfaitement espacées?). Si vous pensez que votre console est plus fonctionnelle, essayez d'utiliser le jeu de caractères alternatif que j'ai commenté près du haut du fichier, qui ne semble pas trop mal même avec seulement deux couleurs. Il a rempli les plafonds et les planchers et surtout les murs transparents.

Le code:

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

Le jeu de caractères est spécifié près du haut du fichier. L'ordre des caractères est le suivant:

  1. même mur de parité
  2. mur de parité impair
  3. angle de paroi supérieur droit à parité égale (par exemple /avec un mur en dessous)
  4. angle de paroi supérieur droit à parité impaire
  5. angle de paroi supérieur gauche à parité égale
  6. parité impaire en haut à gauche de l'angle du mur
  7. angle de paroi inférieur droit égal parité
  8. angle de paroi en bas à droite de parité impaire
  9. parité égale en bas à gauche de l'angle du mur
  10. parité impaire en bas à gauche de l'angle du mur
  11. face au plafond E / W
  12. face au plafond N / S
  13. horizon orienté E / W (au milieu de l'écran s'il n'y a pas de murs)
  14. horizon face N / S
  15. face au sol E / W
  16. face au sol N / S

Il y a 15 murs qui pourraient devoir être rendus par le jeu, dans un modèle comme celui-ci (avec Vindication de la position et de l'arc de vue du joueur):

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

Les tuiles utilisées par les 15 murs sont définies dans la shapesliste. C'est une liste de 2 tuples. La première valeur du tuple indique la "parité" du mur, en 0indiquant qu'il doit être dessiné avec les mêmes caractères qu'un mur directement devant le personnage et en 1indiquant qu'il doit être le motif alternatif (par exemple +vs X). La deuxième valeur est une liste de x,y,ttuples indiquant les coordonnées d'écran et l'indice de tuile d'un pixel (les murs rendus avec une parité impaire auront été 1ajoutés à chacun de ces index). Les formes sont ordonnées par distance, de sorte que les trois premiers représentent les murs perpendiculaires deux carreaux devant le personnage, suivis par les deux murs parallèles deux carreaux devant, et ainsi de suite.

Les fonctions sont:

  • rr: "rendre" l'écran (en imprimant les tuiles dans le tampon d'écran).
  • dw: "dessiner des murs" vers un tampon d'écran fourni. Cela utilise l'algorithme des peintres, de sorte que les murs les plus éloignés sont dessinés en premier et peuvent être recouverts par des murs plus proches.
  • ga: "get area" renvoie une liste de valeurs booléennes indiquant quels murs sont opaques pour une position de carte et un revêtement donnés.
  • rd: "read", un générateur qui lit la carte, donnant les lignes. Cela n'est nécessaire que parce que la console d'IDLE fait des trucs bizarres lorsque vous collez des entrées multi-lignes plutôt que d'entrer une ligne à la fois.
  • rm: "read map", analyse la carte en une liste imbriquée de booléens, indexée par m[y][x][d](avec d=0étant Est et d=1étant Sud). Il ajoute également deux lignes et deux colonnes de carrés de remplissage, pour éviter les erreurs d'index dans l'autre code.
  • cl: "effacer" la sortie (en écrivant suffisamment de nouvelles lignes pour faire défiler l'ancienne vue du haut de la plupart des consoles).
  • gl: "boucle de jeu", où l'entrée est collectée et les éléments ci-dessus sont appelés.

Quelques "captures d'écran":

La position de départ:

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

En regardant le long du mur nord:

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

Quelques plans correspondant à vos exemples (notez que les premières lignes vides sont coupées par Stack Overflow, elles sont dans la sortie du programme):

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

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

Et:

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

Voici l'une des vues les plus étranges de la carte fournie, car le mur parallèle à notre vue est de la même couleur que le mur perpendiculaire qui sort derrière:

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

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

Voici à quoi ressemblerait la zone du dernier plan:

_   _
 |
  V
Blckknght
la source
Belle analyse et diagrammes ajoutés! Hm, ces murs finissent par être de la même couleur que le dernier dans ma mise en œuvre aussi. Bon point sur le cas de bord. Je ne pensais pas que cela arriverait, mais c'est en quelque sorte le cas. Je suppose que c'est comme la coloration de la carte, et deux couleurs ne suffisent pas en fait ...: - /
Dr. Rebmu