Quando eu era criança, joguei o jogo Intellivision Advanced Dungeons and Dragons: Treasure of Tarmin . Os gráficos 3D colocam você em uma perspectiva em primeira pessoa com um realismo chocante:
Mas então eu tenho um C-64. E pude desenhar na grade de 40x25 caracteres, percorrendo a tela, definindo a cor com a tecla Ctrl e um dígito e colocando os símbolos em qualquer lugar que eu quisesse (por que não bash
me permite fazer isso?) . O conjunto de caracteres tinha componentes triangulares e componentes de bloco sólido. Então, eu pude argumentar sobre como alguém pode gerar uma renderização de sua perspectiva em uma grade através desse meio.
Encontrei a especificação de quase três décadas de idade, em papel de caderno encadernado em espiral, sobre "Dungeon Construction Set" esta semana:
( ATUALIZAÇÃO : Leitores cuidadosos perceberão que isso não se sustenta nas partes inclinadas. Os números corrigidos são fornecidos abaixo.)
Embora Treasure of Tarmin tenha sido jogado em uma grade, as paredes existiam apenas nas bordas dos quadrados da grade. Tendo aprendido o que eram bytes, percebi que se eu fizesse o mapa com bytes ... cada quadrado no mapa poderia ter quatro estados possíveis para cada uma de suas bordas:
- Desobstruído
- parede
- Porta
- Algo mais?
Eu nunca escrevi (até a noite passada). Eu pensei que poderia ser divertido para os outros tentarem.
Portanto, sua tarefa é implementar um renderizador de labirinto baseado no modo de personagem que implemente meu (corrigido !!) especificações ... mas usando as tecnologias de 2013.
Entrada
Como a especificação não define a renderização para portas, apenas assumiremos que as únicas opções são parede e não parede. Por uma questão de simplicidade, sua entrada é um mapa composto por linhas de seqüências de caracteres que se parecem com isso:
WN.. .N.. .N.. .N.. .N.E
W... .... .... ..S. ...E
W... .N.E W... .N.. ...E
W... .... .... .... ...E
W.S. ..S. ..S. ..S. ..SE
Isso seria um mapa 5x5. O canto superior esquerdo (1,1) tem sua parede W
est e N
ortogonal. O canto inferior direito (5,5) tem sua parede S
externa e E
externa.
Isso é consideravelmente menos divertido sem navegação no mapa. Então, no mínimo, coloque seu jogador em (1,1) voltado para o norte e ofereça a ele:
[F]orward, [B]ackward, turn [L]eft, turn [R]ight or [Q]uit?
Em cada etapa, produza uma exibição de 16x15 da perspectiva em primeira pessoa, conforme definido pelas especificações do papel para notebook. Para evitar que você precise contar, o tamanho das paredes planas nas três distâncias são:
14x13 (directly in front of you; e.g. wall is in same cell)
8x7 (one step away)
6x5 (two steps away)
Os tamanhos delimitadores das paredes inclinadas são:
1x15 (your direct left or right; e.g. wall is in same cell)
3x13 (one step away)
1x7 (two steps away)
Esclarecimentos
As células adjacentes podem discordar sobre paredes compartilhadas. Portanto, a borda sul de um quadrado pode ser uma parede, enquanto a borda norte da praça ao sul dela seria desobstruída. No design original, considerei isso um recurso: permite idéias interessantes como portas de mão única ... ou paredes invisíveis que só aparecem depois que você passa por elas. Para essa simplificação, siga a mesma regra: para navegação e renderização, preste atenção apenas ao status da borda na célula mais próxima de você na direção em que está voltada .
A vista é muito melhor com "sombreamento". Portanto, para seus blocos completos, alterne o Unicode 2593 ▓ e 2591 ░ ou use
X
e+
se sua implementação for ASCII.Caracteres de triângulo Unicode (25E2 ◢, 25E3 ◣, 25E4 ◤, 25E5 ◥) são um pouco ruins para desenhar isso. Além de não ter variantes sombreadas, elas geralmente aumentam apenas a largura do caractere e não a altura total ... mesmo em fontes de largura fixa. Você pode desenhar blocos completos ou caracteres de barra ou algo de sua escolha nos locais que eu queria diagonais. Soluções criativas interessantes que incorporam cores e usam esses caracteres em vez de sombreado são apreciadas.
Você pode assumir que as paredes mais externas estão definidas para limitar a área de jogo, para que você não precise se preocupar em renderizar nada fora do labirinto. Quaisquer paredes mais afastadas de você do que as especificações são ignoradas e apenas deixam espaço vazio.
O sombreamento da parede que você vê diretamente à sua frente se estiver voltado para o norte em (1,1) deve ser ESCURO. Sombreamento alternativo nas paredes adjacentes no mapa, de modo que, se todas as paredes estivessem presentes, uma parede clara nunca encostaria em uma parede escura.
Uma implementação C-64 que realmente faz o que eu originalmente pretendia ... com os caracteres diagonais e tudo ... superará qualquer outro critério de entrada. :-)
Exemplos
Para o mapa de amostra fornecido acima ...
Em (1,3), virado a sul:
/
/+
/X+
/XX+
/XXX+
+++++XXXXXX+XXX+
+++++XXXXXX+XXX+
+++++XXXXXX+XXX+
+++++XXXXXX+XXX+
+++++XXXXXX+XXX+
\XXX+
\XX+
\X+
\+
\
Na (3,2) virada a sul:
/* blank line */
X /
X /+
X /++
X +++
X +++
X +++
X +++
X +++
X +++
X +++
X \++
X \+
X \
/* blank line */
Em (3,2), de frente para o leste:
/* blank line */
/
/X
/XX
XXX
+++++XXXXXX+XXX+
+++++XXXXXX+XXX+
+++++XXXXXX+XXX+
+++++XXXXXX+XXX+
+++++XXXXXX+XXX+
XXX
\XX
\X
\
/* blank line */
Em (2,3), voltado para o norte:
/
++++++++++++++X
++++++++++++++X
++++++++++++++X
++++++++++++++X
X++++++++++++++X
X++++++++++++++X
X++++++++++++++X
X++++++++++++++X
X++++++++++++++X
++++++++++++++X
++++++++++++++X
++++++++++++++X
++++++++++++++X
\
fonte
X
s na sua opinião,3, 2
voltadas para o sul?Respostas:
Commodore 64 Basic
Cara, isso foi divertido. E difícil. O C64 Basic é quase indeformável, você nem pode usar a
print
depuração porque a tela já foi usada para renderizar a masmorra. Você sabe que está se divertindo quando escreve código como55250 goto 55110
. Dijkstra vai me matar.O programa usa duas cores e caracteres diagonais.
Escusado será dizer que não joguei golfe. Afirma agora o desafio do código , afinal. É 7183 bytes, se você estiver interessado.
É lento - na velocidade padrão, leva alguns segundos para renderizar a cena. O tamanho máximo do mapa é 10 por 10, mas pode ser alterado editando a linha 120.
Eu desenvolvi e testei isso usando o emulador VICE . O código abaixo é exibido em ASCII, o que significa PETSCII deslocado . No entanto, ao inserir o mapa, você deve usar PETSCII sem deslocamento .
Captura de tela:
Código:
Imagem da fita: faça o download aqui .
Os exemplos:
fonte
Eu só tinha que agora.
Bash, 12743 caracteres
Lembre-se de que essa é praticamente a primeira coisa que fiz com
bash
mais do que apenas juntar alguns comandos. Provavelmente seria bastante redutível se eu não codificasse todas as paredes, mas parecia mais fácil. Não tem consistência alguma. O formato de bytes para cada quadrado é escolhido de maneira horrível. Mas funciona.Eu até adicionei suporte ao movimento pelas teclas de seta :)
Estas são algumas capturas de tela para a entrada de amostra (observe que meu mapa começa em (0 | 0)):
Além do quarto, todos também se parecem com os exemplos (veja meu comentário sobre o OP).Essas capturas de tela foram tiradas no urxvt v9.15 com suporte a 256 cores, provavelmente pareceriam uma porcaria em um terminal de 88 cores e os terminais sem suporte a unicode não funcionam. A fonte que eu usei foi o Source Code Pro da Adobe.
fonte
Aqui está minha versão, no Python 3. É algo como caracteres 3k e pode ficar um pouco menor com um pouco de esforço (há muito espaço em branco que pode ser removido, para começar).
Atualmente, ele usa
+X/\
como caracteres de desenho, mas é configurado para desenhar com caracteres Unicode se você tiver uma fonte de largura fixa que os renderize corretamente. Ele suporta o uso de ladrilhos separados para as partes angulares das paredes de formas diferentes, embora eu não esteja usando esse recurso. Ele também permite que você forneça teto, piso e telhas "distantes", e você pode usar diferentes para quando o jogador estiver de frente para leste ou oeste versus norte ou sul. Infelizmente, isso nunca pareceu muito bom, então provavelmente todos esses itens devem estar em branco (ou algo sólido, como█
).Infelizmente, no meu sistema Windows 7, passei um tempo horrível tentando encontrar uma fonte monoespaçada com o conjunto completo de caracteres de bloco (por exemplo,
▜
e▟
). A maioria dos que encontrei não pôde ser disponibilizada nocmd
console por algum motivo (talvez porque não sejam perfeitamente espaçados?). Se você acha que seu console é mais funcional, tente usar o conjunto alternativo de caracteres que comentei na parte superior do arquivo, o que não parece muito ruim, mesmo com apenas duas cores. Ele preencheu tetos e pisos, e principalmente paredes transparentes.O código:
O conjunto de caracteres é especificado na parte superior do arquivo. A ordem dos caracteres é:
/
com uma parede abaixo dela)Existem 15 paredes que podem precisar ser renderizadas pelo jogo, em um padrão como este (com
V
indicação da posição e do arco de visão do jogador):Os ladrilhos usados pelas 15 paredes são definidos na
shapes
lista. É uma lista de duas tuplas. O primeiro valor da tupla indica a "paridade" da parede,0
indicando que ela deve ser desenhada com os mesmos caracteres de uma parede diretamente na frente do caractere e1
indicando que esse deve ser o padrão alternativo (por exemplo,+
vsX
). O segundo valor é uma lista dex,y,t
tuplas indicando as coordenadas da tela e o índice de blocos de um pixel (as paredes renderizadas com paridade ímpar serão1
adicionadas a cada um desses índices). As formas são ordenadas por distância, de modo que as três primeiras representam as paredes perpendiculares duas peças à frente do personagem, seguidas pelas duas paredes paralelas duas peças à frente e assim por diante.As funções são:
rr
: "renderiza" a tela (imprimindo os blocos no buffer da tela).dw
: "desenhar paredes" para um buffer de tela fornecido. Isso usa o algoritmo dos pintores, de modo que as paredes mais distantes são desenhadas primeiro e podem ser cobertas por outras mais próximas.ga
: "get area" retorna uma lista de valores booleanos indicando quais paredes são opacas para uma determinada posição e face do mapa.rd
: "read", um gerador que lê o mapa, produzindo as linhas. Isso é necessário apenas porque o console do IDLE faz coisas estranhas quando você cola entradas com várias linhas em vez de inserir uma linha por vez.rm
: "read map", analisa o mapa em uma lista aninhada de booleanos, indexados porm[y][x][d]
(comd=0
leste ed=1
sul). Ele também adiciona duas linhas e duas colunas de quadrados de preenchimento, para evitar erros de índice no outro código.cl
: "limpe" a saída (escrevendo novas linhas suficientes para rolar a exibição antiga da parte superior da maioria dos consoles).gl
: "game loop", onde a entrada é coletada e o material acima é chamado.Algumas "capturas de tela":
A posição inicial:
Olhando ao longo da parede norte:
Algumas fotos correspondentes aos seus exemplos (observe, as primeiras linhas em branco estão sendo cortadas pelo Stack Overflow, elas estão na saída do programa):
E:
Aqui está uma das vistas mais estranhas no mapa fornecido, pois a parede paralela à nossa vista é da mesma cor da parede perpendicular que se destaca atrás dela:
Aqui está como seria a área da última foto de cima:
fonte