Roguelike pathfinding

21

Roguelike pathfinding

Sua tarefa será, dada uma matriz bidimensional dos elementos descritos abaixo, que representa uma masmorra, produzir ou retornar um único número representando a quantidade de peças de ouro que o ladino pode coletar sem acordar nenhum monstro.

Os elementos da matriz são os seguintes:

  1. Os espaços vazios são representados por um .ou um espaço, sua chamada;
  2. A posição inicial de Rogue é representada por, é claro @,;
  3. Uma peça de ouro é representada por $;
  4. Paredes são representadas por #;
  5. Monstros são representados por personagens da seguinte expressão regular: [a-zA-Z*&].

A matriz não deve conter caracteres não listados acima, portanto, você pode assumir que qualquer coisa que não seja uma parede, um espaço vazio, um trapaceiro ou uma peça de ouro é um monstro.

As regras para a busca de caminhos são:

  1. O ladino só pode andar através de células vazias ou células contendo ouro;
  2. Leva uma volta para mover para uma célula adjacente ou diagonalmente adjacente;
  3. Pegar o ouro é instantâneo;
  4. O ladino não pode permanecer adjacente ou diagonalmente a um monstro por mais de um turno sem acordá-lo, o que é proibido;
  5. O ladino pode entrar na área de consciência de um monstro várias vezes, o monstro só acordará se o ladino passar dois turnos consecutivos perto dele.

Regras de entrada e saída

Você pode obter a entrada em qualquer formato razoável, incluindo uma matriz bidimensional, uma matriz plana, uma string ou qualquer outra coisa. Se facilitar a sua vida, você também pode obter as dimensões da matriz.

É garantido que o ladino não estará perto de um monstro no começo.

Um programa completo ou uma função está bem.

Pontuação

Isso é , a pontuação é a contagem de bytes do seu envio, com menos sendo melhores.

Casos de teste

Eu uso pontos para espaços vazios aqui para fins de legibilidade, se você desejar, poderá usar espaços (veja acima). Observe também que é uma pura coincidência que o invasor esteja sempre no canto superior esquerdo; seu código também deve lidar com qualquer outra posição válida.

1)
@..
.$.
...  -> 1

Apenas um teste de sanidade.

2)
@....
...g$
.....  -> 0

Mais uma vez, um teste de sanidade.

3)
@....
...$g
.....  -> 1

O ladino pode pegar o ouro movendo-se da esquerda.

4)
@....g..
.......$
........
.....h..  -> 1

O ladino pode zigue-zague entre os monstros, nunca ficando por mais de um turno perto de cada um.

5)
@....z..
.......$
.....b..  -> 0

As táticas do caso de teste anterior não funcionam aqui - as áreas de sensibilidade dos monstros se sobrepõem.

6)
@$#.
###$
....  -> 1

Teste de sanidade.

7)
@..#..
$.$g.$
...#..  -> 2

Idem.

8)
@#.d#$
$...##
e.....
..$...
##..$b
.#..g$  -> 3

De todo o ouro aqui, apenas três podem ser alcançados com segurança: o ouro perto da posição inicial pode ser obtido descendo um e depois voltando à posição inicial. Para escapar do canto superior esquerdo, o ladino deve se mover diagonalmente para baixo e para a direita duas vezes. O ouro no meio não apresenta nenhum desafio. O ouro externo é guardado ge bpode ser obtido movendo-se na diagonal do local à direita do meio do ouro e depois retornando. O resto não pode ser obtido: o ouro no canto superior direito é bloqueado pelas paredes e o ouro no canto inferior direito requer duas voltas nas áreas de sensibilidade dos monstros.

Os seguintes casos de teste foram generosamente doados pelo mbomb007.

9)
  12345678
a @....g.D
b .......$
c ......#.
d .....h..  -> 1

Este é complicado. Um caminho é b4-b5-c6-b7-c8-b8(grab).

10)
  12345678
a @....g.D
b .......$
c .......#
d .....h..  -> 1

Um caminho é [bc]4-c5-b6-c7-b8(grab).

11)
  12345678
a @....g.D
b ......#$
c .......#
d .....h..  -> 1

A parede extra não muda nada, [bc]4-c5-b6-c7-b8(grab)ainda é uma solução.

Michail
fonte
Você deve adicionar um exemplo maior e mais complexo. Além disso, quais são as dimensões mínima e máxima da masmorra? A masmorra 1x1 é @uma entrada válida?
Mbomb007
@ mbomb007 Adicionei um novo exemplo. Quanto ao tamanho da grade, acho que limitá-la a pelo menos 3x3 é razoável.
22418 Michail
@ mbomb007 Se importa se eu editar seus exemplos? Eles, uma vez entendidos, mostram muito bem a lógica.
22818 Michail
Fique à vontade. Foi para isso que eu os fiz. Também é possível observar que a rotação de um caso de teste não deve ter impacto sobre o resultado.
mbomb007

Respostas:

5

soluções anteriores (parte delas) podem ser encontradas no contêiner de rodapé no link tio (essas provavelmente são mais legíveis)

JavaScript (Node.js) , 883 436 411 360.345 311 bytes

g=>g.map((r,y)=>[...r].map((c,x)=>A+=c=="$"&&P(g,x,y)),A=0)|A
P=(g,x,y,m={},p={},I=i=9,M={})=>{if(/[.$]/.test(c=(g[y]||0)[x])){for(;i--;)if(/^[^.@$#]$/.test(C=(g[y+~-(i/3)]||0)[x+i%3-1])){if(m[C])return
M[C]=1}for(;I--;)if(!p[(X=x+~-(I/3))+","+(Y=y+I%3-1)]&&P(g,X,Y,M,{...p,[x+","+y]:1}))return 1}return c=="@"}

Experimente online!

Explicação -

em vez de passar do jogador para o dinheiro, fui do estojo para o @. acho que deveria ser mais rápido, porque sei quando parar de procurar (alcançar o @) e, quando você procura dinheiro, precisa sempre se mexer até cobrir todos os pontos (e as maneiras de alcançá-los). então o algo é bem simples assim - a principal função g.map((r,y)=>[...r].map((c,x)=>A+=c=="$"&&P(g,x,y)),A=0)|A: encontre dinheiro -> se você o encontrou -> comece a procurar o jogador -> se você o encontrou -> incremente A agora vamos ao localizador de caminho conhecido como P, if(/[.$]/.test(c=(g[y]||[])[x]))apenas verifique se a célula atual é "especial" -> nesse caso, queremos retornar se for um jogador. casos especiais: @ # (monstro)

for(;i--;) if(/^[a-zA-Z*&]$/.test(C=(g[y+~-(i/3)]||0)[x+i%3-1])) -> if my neighbor is a monster {if(m[C])return false -> and it already was in the previous turn - this path is not value M[C]=1} -> if not just add it to the neighbors monsters for(;I--;) if(!p[(X=x+~-(I / 3))+","+(Y=y+I%3-1)]&&P(g,X,Y,M,{...p,[x+","+y]:1}))return true repita os vizinhos novamente - se eu ainda não estava lá (p é o caminho escolhido) continue path (call P)

DanielIndie
fonte
Bom golfe! Algumas coisas que notei: (1) seu código possui espaços em branco desnecessários nas 2ª e 7ª linhas, e a maioria das quebras de linha é desnecessária (apenas as linhas 1 e 6 precisam de uma pausa) e I / 3possui espaço desnecessário. Elimine esse espaço em branco e sua pontuação é realmente 324! (2) return true pode ser return 1(um valor verdadeiro) e return falsepode ser simplesmente return(implícito undefined, um valor de falsey). (3) O regex ^[^.@$#]$para verificar se não há monstros é mais curto do que ^[a-zA-Z*&]$para verificar se há monstros. Bom trabalho!
Apsillers
sim eu sei que eu posso torná-lo muito mais curto :)
DanielIndie
@apsillers atualizados para a solução sem espaços :)
DanielIndie