NetHack é um jogo parecido com um roguel onde o jogador deve recuperar o Amuleto do Yendor do nível mais baixo da masmorra. Geralmente jogado via telnet, o jogo inteiro é representado com gráficos ASCII. O jogo é extremamente desafiador e requer conhecimento de muitas mecânicas do jogo para ter sucesso.
Para os propósitos deste desafio, suponha que o calabouço inteiro seja um nível único e apenas 5 × 16 caracteres. Além disso, suponha que este é um calabouço "seguro" ou que você está implementando apenas um protótipo - não haverá monstros, preocupações com a fome etc. Na verdade, você deve rastrear apenas a localização do personagem, do amuleto e do jogo terminará efetivamente quando o jogador chegar ao mesmo local que o amuleto.
Requisitos do desafio
- Haverá uma masmorra 5 × 16 (nível único).
- Dê ao jogador um local inicial (opcionalmente aleatório) e o amuleto um aleatório separado (diferente a cada vez que o programa for executado) começando dentro da masmorra. Ou seja, o amuleto não pode começar no mesmo quadrado que o jogador.
- Aceite quatro teclas de entrada que movem o leitor um quadrado de cada vez (quatro direções cardinais). A leitura / processamento de outras entradas é permitida (uma função readline () que requer pressionar 'enter', etc).
- Viajar para fora dos limites da masmorra não é permitido. Por exemplo, se o jogador estiver na borda direita da masmorra, pressionando a direita, não deverá fazer nada.
- Após a geração inicial e após cada movimento, imprima o estado do jogo. Como esse é o código golf e a impressão é bastante desinteressante, ignore a contagem de caracteres para a função de impressão e a chamada de função, assumindo que não há alterações de estado . As células vazias devem ser mostradas como ponto (
.
), amuleto como aspas duplas ("
) e caractere como símbolo (@
). - O jogo termina quando o jogador "descobre" o amuleto (chega ao mesmo quadrado)
Ganhando
Este é um desafio de código de golfe, o código mais curto para atender aos requisitos de uma semana a partir de hoje será declarado vencedor.
Exemplo
Aqui está um exemplo de solução em C # (ungolfed) para mostrar os requisitos básicos e a amostra de saída.
using System;
namespace nh
{
class Program
{
static Random random = new Random();
// player x/y, amulet x/y
static int px, py, ax, ay;
static void Main(string[] args)
{
px = random.Next(0, 16);
py = random.Next(0, 5);
// amulet starts on a position different from the player
do { ax = random.Next(0, 16); } while (px == ax);
do { ay = random.Next(0, 5); } while (py == ay);
print();
do
{
// reads a single keypress (no need to press enter)
// result is cast to int to compare with character literals
var m = (int)Console.ReadKey(true).Key;
// Move the player. Here standard WASD keys are used.
// Boundary checks for edge of dungeon as well.
if (m == 'W')
py = (py > 0) ? py - 1 : py;
if (m == 'S')
py = (py < 5) ? py + 1 : py;
if (m == 'A')
px = (px > 0) ? px - 1 : px;
if (m == 'D')
px = (px < 16) ? px + 1 : px;
// print state after each keypress. If the player doesn't
// move this is redundant but oh well.
print();
// game ends when player is on same square as amulet
} while (px != ax || py != ay);
}
static void print()
{
Console.Write('\n');
for (int y=0; y<5; y++)
{
for (int x = 0; x < 16; x++)
{
if (x == px && y == py)
Console.Write('@');
else if (x == ax && y == ay)
Console.Write('"');
else
Console.Write('.');
}
Console.Write('\n');
}
}
}
}
A contagem total de caracteres é 1474, mas, ignorando as chamadas para a função de impressão e sua definição, a contagem final de caracteres é 896
.
Saída quando o programa é executado:
................
...."...........
..........@.....
................
................
Saída (incluindo acima) depois que a tecla 'a' é pressionada duas vezes:
................
...."...........
..........@.....
................
................
................
...."...........
.........@......
................
................
................
...."...........
........@.......
................
................
Respostas:
TI-BASIC,
4241383635 bytesPara a sua calculadora gráfica da série TI-83 ou 84+.
A direção que o jogador seguirá é uma função do código da tecla pressionada, mas quatro teclas que definitivamente funcionam são as seguintes na linha superior:
O amuleto começa em um dos cinco quadrados da primeira coluna e o jogador começa no canto inferior direito. Por exemplo, um arranjo possível é:
Explicação
A posição do jogador é armazenada como um número complexo de
0+0i
para15+4i
, onde a parte real vai para a direita e a parte imaginária desce. Isso facilita a verificação fácil dos limites na parte superior e esquerda: simplesmente deslocamos o número levemente e arredondamos para zero. Por exemplo, se o deslocamento for0.5
e nossa posição for-1+3i
(fora da tela à esquerda), a posição será corrigida paraiPart(-0.5+3.5i)=0+3i
onde deveria estar. Verificar os limites inferior e direito é um pouco mais complicado; precisamos subtrair o número de uma constanteC
, que é sobre15.635 + 4.093i
(é o menor que eu poderia encontrar entre15+4i
e16+5i
), arredondar, subtrair deC
novo para virar o número de volta e arredondar novamente.Quando uma tecla é pressionada, a posição do jogador não ajustado se move em 1 unidade em alguma direção, mas a parte inteira muda apenas quando certas teclas são pressionadas. Felizmente, as teclas que funcionam estão todas na linha superior. Abaixo está um gráfico das compensações nos casos em que as teclas 11, 12, 13 e 15 são pressionadas e quando nenhuma tecla é pressionada (Nenhuma pressão é o ponto dentro do quadrado central, fazendo com que as partes inteiras permaneçam inalteradas; as quatro teclas pressionam 'offsets' têm partes inteiras diferentes).
C
é a cruz vermelha no centro do círculo.Código antigo (42 bytes):
Limitações
Não há como escapar de um
"
caractere, portanto, cadeias com a"
não podem ser geradas dentro de um programa. Portanto, isso usa a marca de trema em¨
vez de uma citação (se houvesse uma string já existente com uma marca de citação, eu poderia exibi-la). Para obter¨
e entrar@
em um programa, é necessária uma ferramenta externa; no entanto, é válido TI-BASIC.fonte
CHIP-8 , 48 bytes
Isso pode não ser considerado legal, mas por que diabos não. Eu escrevi meu programa no CHIP-8, uma linguagem de programação baseada em bytecode para um console de jogos virtual. Você pode tentar o programa completo (99 bytes) no seu navegador usando um emulador / depurador que escrevi chamado Octo:
http://johnearnest.github.io/Octo/index.html?gist=1318903acdc1dd266469
Um dump hexadecimal desse programa completo é o seguinte:
Você pode mover o player com as teclas ASWD ou 7589 no teclado CHIP-8 original. Se eu remover todo o código e dados para desenhar o plano de fundo e o player, recebo esse despejo de 48 bytes:
A forma completa e não-gasta do programa foi escrita em uma linguagem assembly de alto nível, da seguinte maneira:
Observe que os próprios bytes compilados são a linguagem de programação CHIP-8; o montador é simplesmente um meio mais conveniente de compor esses programas.
fonte
Python 3, 86 bytes
Contando apenas as duas linhas inferiores e caindo
d();
.fonte
a=id(9)%79
pora=id(9)%p
.raw_input
chamada para justinput
.C,
122121115104102101 bytesPrimeira postagem aqui! Espero que você goste :)
o
é a função de impressão, erm. Nosso corajoso herói pode ser movido com 2, 4, 6 e 8, mas cuidado para não enviar nenhuma outra entrada (sem novas linhas!).Atualização 1: trazido
a
ei
para dentromain
dos parâmetros.Atualização 2: OP tendo confirmado que uma única sequência de entrada está correta, me livrei
scanf
(da qual costumava pular a nova linha).Atualização 3: Usou uma matriz composta literal e modificou o layout de entrada. O programa agora fica confuso se você digitar uma direção inválida;)
Atualização 4: Observe que a chamada para a função de impressão não conta. Tomou nota para ler as regras com mais cuidado.
Atualização 5: um byte salvo, graças a Mikkel Alan Stokkebye Christia.
fonte
!!(p%16)
serp%16>0
? Não me lembro da minha ordem de operações.-
não pode deixar de se aterp
, então parênteses são necessários de qualquer maneira. O duplo-bang é apenas ofuscação :)CJam,
464544403937 bytesA primeira linha (define uma função que imprime o estado atual do jogo) e os Ps na segunda linha (chamam essa função) não contribuem para a contagem de bytes.
Tanto a posição inicial quanto a posição do amuleto são selecionadas pseudo-aleatoriamente. A distribuição é tão uniforme e o PRNG subjacente permite.
A entrada é E, 6, 9e Bpara cima , para baixo , esquerda e direita , com Caps Lockactivado, seguido por Enter.
Versão alternativa
Ao custo de mais quatro bytes, o formato de entrada é aprimorado significativamente:
Testando
Como a E / S é interativa, você deve tentar esse código com o interpretador Java .
Baixe a versão mais recente e execute o programa assim:
Para evitar pressionar Enterapós cada tecla e para atualizações no local da saída, você pode usar este wrapper:
Invoque assim:
Versão principal
Versão alternativa
Função P
fonte
Java, 231 bytes (196 se função)
Aqui está o código completo do programa em 342:
Sem a função de impressão, 231:
Se apenas uma função estiver correta (não estou claro nas especificações), então eu posso reduzir isso um pouco mais para 196:
E com algumas quebras de linha para um pouco de clareza ...
Observe que não estou contando a função de impressão em
p(p,y)
si, mas estou contando a chamada para ela, pois tenho coisas alterando dentro da instrução de chamada.Funciona com letras maiúsculas
ASDW
. Devido à maneira como verifica essas letras, algumas outras letras também podem funcionar, mas as especificações não dizem nada sobre o que deve acontecer se eu pressionar teclas diferentes.fonte
void m()
torna-se()->
p+=
?Java, 574 bytes
Basicamente, o mesmo que a versão C #, exceto ofuscada e minimizada.
fonte
Julia, 161 bytes
Usos w, a, s, e dpara mover para cima, esquerda, baixo, e direita, respectivamente.
Código completo, incluindo impressão (330 bytes):
Código pontuado, exclui impressão (161 bytes):
A diferença aqui é que não salvamos o estado do jogo como uma matriz; todas as informações relevantes estão contidas nas matrizes
c
ea
. E, claro, nada é impresso. O usuário não será mais solicitado a entrar quando o jogador atingir o amuleto.Ungolfed + explicação (código completo):
fonte
a=[rand(1:5),1] c=a+1
Lote, 329 bytes
fonte
Microsoft Windows [Version 6.1.7601]
Microsoft Windows [Version 6.2.9200]
)Perl,
228222 caracteres (sem contar as novas linhas que não fazem parte do funcionamento do código) - 207 se não contar as partes da instruçãoprint
eprint if
que são usadas para impressão, mas não aumentam a lógica do jogo; 144 se também considerar o código de geração de representação de campo como parte da impressão, conforme sugerido por Yakk nos comentários)Esse código usa wasd em minúsculas para controle; entrada deve ser confirmada com Enter. Testado com Perl 5.14.2.
Observe que, para esse código, é impossível separar cálculo e impressão, pois as operações são feitas diretamente na representação impressa usando expressões regulares.
Explicação:
Esta linha determina a posição do jogador e do amuleto. A posição do jogador é determinada
$==rand(80)
e é realmente fácil de entender: Em um tabuleiro 5 × 16, há 80 posições distintas onde o jogador pode estar. A posição é armazenada na$=
variável que força o valor armazenado em número inteiro; isso economiza alguns bytes por não precisar converter explicitamente o resultado em número inteiro (rand
fornece um valor de ponto flutuante).Como uma das posições já está ocupada pelo jogador, restam apenas 79 posições para o amuleto e, portanto, a posição do amuleto
$a=$==rand(79)
é usada. Mais uma vez, a atribuição de$=
forçar uma conversão para inteiro, no entanto, atribuo-a mais$a
para reutilizar$=
para a posição do jogador.Agora, para evitar que o amuleto ocupe a mesma posição que o jogador, ele é avançado em uma posição se sua posição for pelo menos tão grande quanto a do jogador, fornecendo uma distribuição uniforme nos locais não ocupados pelo jogador. Isto é conseguido por
$a = ($a >= $=)
onde$=
aqui ocupa a posição do jogador. Agora a primeira linha é gerada inserindo as duas atribuições iniciais em vez dos primeiros$a$ and the only
$ = `nesta expressão.Isso gera o campo inicial e, posteriormente, as impressões são.
("."x80)
apenas gera uma sequência de 80 pontos.=~s/(.{$=})./\1@/r
então substitui o$=
th caractere por@
, e=~s/(.{$=})./\1@/r
o$a
th caractere por"
. Devido aor
modificador, eles não tentam modificar no local, mas retornam a string modificada, por isso podem ser aplicados às expressões anteriores. Por fim,=~s/(.{16})/\1\n/gr
insere uma nova linha a cada 16 caracteres. Observe que o campo é armazenado na variável especial$_
que pode ser usada implicitamente em instruções posteriores.Isso cria um hash contendo as regras de substituição para as diferentes jogadas. Uma versão mais legível disso é
As chaves são os caracteres para os movimentos e os valores são cadeias que contêm a regra de substituição correspondente.
Este é o loop principal.
while(/"/)
verifica se ainda há um"
caractere$_
(ou seja, no campo). Se passarmos para o amuleto, seu personagem será substituído pelo personagem do jogador, desaparecendo do campo.eval $r{getc STDIN}
lê um caractere da entrada padrão, consulta a regra de substituição correspondente do has%r
e aplica-a$_
ao campo. Isso avalia como verdadeiro se uma substituição foi realmente feita (ou seja, a chave foi encontrada no hash e a movimentação foi possível; uma movimentação impossível não corresponderá à regra de substituição). Nesse caso,print
é executado. Como é chamado sem argumento, ele imprime$_
, ou seja, o campo modificado.fonte
("."x80)=~s/(.{$=})./\1@/r=~s/(.{$a})./\1"/r=~s/(.{16})/\1\n/gr
está bem próximo à primeira vista, mas meu perl-fu está alguns anos enferrujado. Eu poderia ter perdido uma mudança de estado lá.C #,
256 248 234 227 226225 bytesUsa as setas do NumPad com o NumLock ativado para se mover.
Recuado e comentado para maior clareza:
fonte
Main
método não precisa ser chamadoMain
, para que você possa cortar outros três caracteres.Html + JavaScript (ES6), pontuação talvez 217
Muito longo, mas jogável online nos trechos abaixo.
A linha 6 (T.value ...) é para saída e não é contada (mas, por simplicidade, contei a área de texto nas tags de abertura e fechamento, mesmo que também seja de saída)
Quanto à aleatoriedade: o amuleto está sempre na metade direita da grade e o jogador sempre começa na metade esquerda.
Clique na área de texto (após ampliá-la) para iniciar e reiniciar o jogo.
Snippet do EcmaScript 6 (somente Firefox)
Fragmento EcmaScript 5 (testado no Chrome)
fonte
ActionScript 3: 267 bytes
Um exemplo de trabalho está online
var a:int,p:int,t;function g(){var r=Math.random;while(p==a){a=r()*80;p=r()*80}addEventListener("keyDown",function(e){if(a==p)return;if(e.keyCode==87&&p>15)p-=16if(e.keyCode==83&&p<64)p+=16if(e.keyCode==65&&p%16>0)p--if(e.keyCode==68&&(p+1)%16>0)p++print()});print()}
Aqui está um programa completo (espaços em branco incluídos para facilitar a leitura) usando a função de jogo:
fonte
Javascript:
307216Você pode jogar no trecho abaixo! Os números à esquerda são apenas para que o console (pelo menos um no Chrome) não mescle as linhas.
Para executar o código:
Sem golfe:
Editar 1: leia as regras com mais cuidado e reescreva meu código de acordo
fonte
SpecBAS -
428402 (excluindo impressão,466425 quando contados)Usa Q / A / O / P para mover para cima / baixo / esquerda / direita, respectivamente.
A linha para imprimir a masmorra na linha 1 é a única linha que pode ser ignorada, mas também diminuiu um pouco.
A referência ao # 34 é apenas uma maneira abreviada de colocar CHR $ (34) no código.
Obrigado @ Thomas Kwa, eu não tinha notado que a posição inicial do jogador era aleatória era opcional. Também usei instruções SE separadas para eliminar alguns caracteres.
fonte
2 LET px=1: LET py=1: LET ax=2: LET ay=INT(RND*5)
e também usandoIF instead of ELSE IF
.Outro C #,
221171170Aqui está outra maneira em C # com ambas as posições aleatórias. Queria mostrar isso mesmo que essa parte tenha 7 bytes a mais que a solução da Hand-E-Food.
A resposta do Hand-E-Food será mais curta, é claro, assim que ele usar o Console.Read ().
A desvantagem do Consol.Read é que pressionar o Enter necessário faz com que o campo seja impresso mais duas vezes.
Mas acho que não é necessário imprimir apenas com entrada (real).
A navegação é feita pelo 8426 como na solução Hand-E-Foods.
Edit: (adicionada nova solução e movido o PrinterClass para o final)
Edit2: (alterado de 14 para 15 e salvo o byte iniciando no canto inferior direito)
Adaptando a técnica do Mauris, é possível derreter para 171 bytes em C # (é claro que agora sem as duas posições aleatórias):
A classe Printer é quase a mesma, apenas uma nova sobrecarga de impressão ...
fonte
Ruby, 185
Aqui está um exemplo de Ruby também.
Eu sou muito novo em Ruby, talvez alguém saiba como fazer isso melhor :)
Contei lineFeeds como 1, pois o Programa falhará caso contrário ...
A navegação é feita pelo 8462. Você precisa enviar entradas sempre que pressionar enter.
fonte
QBasic, 103 bytes
De acordo com as regras do desafio, o
Show
subprograma não é incluído na contagem de bytes nem é aShow p, q, a, b
chamada (com a nova linha a seguir).Para mover, digite um número e pressione Enter:
1
para ir para a esquerda,2
para subir,3
para a direita e4
para baixo.Este código não gera o estado do jogo no final, quando o jogador encontra o amuleto. Para fazer isso, adicione outro
Show p, q, a, b
após aIF
declaração.Explicação
Vamos
a
,b
representam as coordenadas do amuleto ep
,q
as coordenadas do jogador. O jogador começa em (0, 0) e o amuleto começa na linha 0, com uma coluna entre 1 e 9, inclusive, com base no dígito 1 do tempo atual.O resto é apenas um monte de matemática com condicionais. O importante é lembrar que condicionais no QBasic retornam
0
para falso,-1
para verdadeiro. Vejamos a instrução de atualização da linha do jogador:Se
m=2
, queremos subir subtraindo 1 dep
, desde quep>0
. Da mesma forma, sem=4
quisermos descer adicionando 1 ap
, desde quep<4
. Podemos obter o comportamento desejado multiplicando. Se os dois fatores forem-1
, o produto deles será1
, do qual podemos subtrair ou adicionarp
. Se um dos condicionais for0
, o produto será0
, sem efeito.Da mesma forma, a condição para determinar se o jogador encontrou o amuleto é:
Se qualquer um dos condicionais for verdadeiro, sua soma será diferente de zero (ou
-1
ou-2
) e, portanto, verdadeira, e o programa retornará à linha 1. Uma vez quep
iguaisa
eq
iguaisb
, ambos os condicionais serão0
, então sua soma será0
e o fluxo de controle poderá alcançar a final do programa.fonte