Corredores espeluznantes

8

Este desafío está inspirado en un juego de mesa que jugué hace algún tiempo.
La historia de este desafío no necesariamente tiene que leerse, el objetivo de la sección desafío debe explicar todo lo necesario.

La historia

Las personas están encerradas dentro de una habitación grande con un monstruo devorador de humanos. Las paredes de la habitación están encantadas, teletransportando objetos a través de la habitación cuando se tocan. Dicho monstruo marcha a través de la habitación, buscando carne. El primer humano a la vista será consumido por sus afilados dientes.

El objetivo del desafío

Te dan el mapa de la habitación, que incluye la ubicación del pueblo y del monstruo.

%%%KLMNOPQRSTA%
%%J           B
%I       %    C
H             D
G   %%        E
F             F
E      %      G
D             H
C   %        I%
B           J%%
%ATSRQPONMLK%%%

Analicemos los componentes del mapa.

  • Cartas de Aa T: si el monstruo pisa uno de estos, se teletransportará a la segunda aparición de esta carta y no cambiará su dirección. Solo habrá cero o dos de cualquier letra en la pizarra.
  • %: Revestimientos. Solo por formato y aspecto agradable.
  • #: La ubicación inicial del monstruo.
  • *: Las ubicaciones de las personas.
  •  : Azulejos vacíos, el monstruo puede moverse libremente sobre ellos.

Algunas cosas adicionales a tener en cuenta sobre el mapa:

  • Las dimensiones del mapa y la ubicación del objeto no serán constantes, por lo que su código tendrá que adaptarse dinámicamente a eso.

El monstruo siempre irá en la dirección en la que se enfrenta actualmente (mirando hacia el oeste al comienzo) a menos que vea a un humano, en cuyo caso se girará hacia el humano más cercano .
El monstruo ve a un humano si no hay azulejos de pared o teletransportador en una línea recta horizontal o vertical entre él y el humano.

Otra cosa a tener en cuenta es que si el monstruo está frente a una pared sólida ( %) o tiene que decidir entre dos humanos, siempre dará prioridad a la derecha sobre la izquierda.

Si el monstruo no puede girar a la derecha y dar un paso adelante por alguna razón, en su lugar , girará a la izquierda .

Entonces, al final, el orden en que el monstruo prioriza las direcciones sería hacia adelante, derecha, izquierda, hacia atrás.

Entrada

  • El mapa, incluida la ubicación inicial del monstruo y las posiciones de las personas como sus respectivos personajes. No debe haber otra entrada que la cadena del mapa, o la matriz de cadenas o caracteres.

La entrada puede recibirse en cualquier formato razonable; una sola cadena o una matriz de cadenas para el mapa.

Salida

La coordenada de la persona que primero se come el monstruo.

Las coordenadas comienzan desde la esquina superior izquierda y están indexadas en 0, por lo que el primer mosaico tendrá las coordenadas (0 | 0). Si está utilizando la indexación 1, especifíquelo en su respuesta.

Reglas

  • Este es el , gana el código más corto en bytes en cualquier idioma.
  • Las lagunas estándar están prohibidas.
  • Puedes suponer que el monstruo siempre podrá alcanzar a un humano.

Casos de prueba

Entrada:

%%%KLMNOPQRSTA%
%%J           B
%I       %*   C
H   *         D
G   %%        E
F       #     F
E      %      G
D      *      H
C   %        I%
B           J%%
%ATSRQPONMLK%%%

Salida: (10,2)como el monstruo no puede ver a las otras dos personas cuando pasa corriendo, se teletransporta a la otra Fpared, donde verá a la última persona.

Entrada:

%%%KLMNOPQRSTA%
%%J           B
%I      *     C
H     %%%   * D
G      #%     E
F     %%%   % F
E             G
D       %     H
C       *    I%
B       *   J%%
%ATSRQPONMLK%%%

Salida: (12,3)

Entrada:

%%%KLMNOPQRSTA%
%%J           B
%I     %%%    C
H     *%#%    D
G             E
F             F
E       %     G
D             H
C            I%
B           J%%
%ATSRQPONMLK%%%

Salida: (6, 3)

Entrada:

%%%%%%%%%%%%%%%
%#%       %%% %
%A%ABCD   %*% %
%*F     G %%% %
% %BC% FD     %
% %   %    %%%%
% %  %       %%
%   % %%   G  %
%             %
%*%    %    % %
%%%%%%%%%%%%%%%

Salida: (1,9)


¡Buena suerte!

Ian H.
fuente
Debe indicar, explícitamente y al principio de la descripción, que los humanos no se mueven. Al principio no me di cuenta de que son estacionarias. Si fuera un humano en esa situación, ¡ciertamente me mudaría!
DLosc

Respuestas:

6

Python 2 , 565 .. 422 445 1 .. 444 463 2 .. 468 467 463 bytes

u,U='*%'
G=lambda s:u==s.strip('# ')[0]and len(N)-s.find(u)
b=input()
D=3;j=''.join;N,w,P,t,B=j(b),len(b[0]),[0,1,0,-1],{},map(j,zip(*b))
for c in N:
 l,r=N.find(c),N.rfind(c)
 if u<c:t[l]=r;t[r]=l
 if'#'==c:x,y=l%w,l/w
while u!=b[y][x]:
 O=[B[x][y-1::-1],b[y][x+1:],B[x][y+1:],b[y][x-1::-1]];L,R=O[D-1],O[~D]
 if u<b[y][x]:X=t[x+w*y];x,y=X%w,X/w
 elif(G(O[D])<1)*(G(R)+G(L)+(U==O[D][0])):D-=0<G(L)>G(R)or(U==R[0])*-~(U==L[0])or-1;D%=4
 x+=P[D];y+=P[~D]
print x,y

Pruébalo en línea!

Ahorré muchos bytes gracias a Halvard , Ian y Jonathan

1: Obtuve más tiempo para arreglar el caso de girar dos veces y encontrar la ubicación del monstruo.
2: Se hizo aún más largo ... Monster no debería cambiar de dirección cuando se teletransporta.

TFeld
fuente
Algo golfed a 482 bytes . Enlace acortado debido a que TIO es demasiado largo
Halvard Hummel
@Halvard Un pequeño golf, solo 1 byte: 481
1
l.replace('#',' ')-> l.replace(*"# ").
Jonathan Frech
Solucionado, pero ahora tiene la misma longitud que el tuyo, 434 bytes
2
@KevinCruijssen Sé que Ian dijo que mi respuesta cuenta como válida, pero la cambiaré de todos modos.
TFeld
5

Perl 6 ,343 334 333 328 322 308 bytes

{my \m=%((^@^a X ^@a[0]).map:{.[0]i+.[1]=>@a[.[0]][.[1]]});my \a=m<>:k.classify
({m{$_}});my$m=a<#>[0];my$d=-1;{$d=($d,i*$d,-i*$d,-$d).min({$^q;first ?*,map
{(((my \u=m{$m+$_*$q})~~"%")*(1+!($_-1))+(u~~"A".."Z"))*m+(u~~"*")*$_},^m});
$m+=$d;$m=$d+(a{m{$m}}∖~$m).pick while m{$m}~~"A".."Z"}...{m{$m}~~"*"};$m}

Pruébalo en línea!

(No hay nuevas líneas en el código real. Las inserté solo para ajustar las líneas para una lectura más fácil).

Esto no puede tratar con mapas donde es posible ver fuera de los límites del mapa. (I.e. Mapas con espacios vacíos en el borde). Si esto es importante, +5 bytes. Pero, dado que los teletransportadores bloquean LoS ahora, no debería.

Explicación : Es una función que toma el mapa como una lista de listas de caracteres. Vamos a dividirlo en declaraciones:

my \m=%((^@^a X ^@a[0]).map:{.[0]i+.[1]=>@a[.[0]][.[1]]});: Hagamos un hash ("diccionario" para Pythonists) llamado m(es una variable sin sigil, por lo que debemos ser explícitos sobre la asignación de un hash con él %(...)) que asocia claves complejas de forma x+iycon caracteres que están en la columna yth rown y xth del mapa

my \a=m<>:k.classify({m{$_}});: Esto hace un "diccionario inverso" de m, llamado a: hay una clave correspondiente a cada valor en m, y el valor es una lista de coordenadas complejas (claves en m) que contienen ese carácter. Entonces, por ejemplo a{"#"}, dará una lista de todas las coordenadas donde hay una #en el mapa. (Esta también es una variable sin sigilo, pero tenemos suerte ya que classifydevuelve un hash).

my$m=a<#>[0];my$d=-1: Establece la posición inicial del monstruo. Buscamos #en el hash inverso a. Debemos usar [0]ya a<#>que todavía es una lista, incluso cuando contiene solo 1 elemento. El $dcontiene la dirección del monstruo; lo ponemos al oeste. (La dirección también es un número complejo, también lo -1es el oeste).

OK, la siguiente declaración es bastante desagradable. Primero, echemos un vistazo a esto: {$^q;first ?*,map {(((my$u=m{my$t=$m+$_*$q})~~"%")*(1+!($_-1))+($u~~"A".."Z"))*m+($u~~"*")*abs($t-$m)},^m}esta es una rutina que evalúa LoS en la dirección dada. Si hay un humano en esta dirección, devuelve la distancia al humano. Si hay un muro o un teletransporte en esta dirección, devuelve el número total de cuadrados del mapa. (El punto es dar un número tan alto que sea mayor que cualquier distancia legítima a un humano.) Finalmente, si el muro está en esta dirección y en la distancia 1, devolvemos 2 × el número total de cuadrados del mapa. (Queremos nunca atravesar paredes, por lo que estos necesitan incluso un puntaje mayor. En breve elegiremos el mínimo).

Usamos esto en la construcción $d=($d,i*$d,-i*$d,-$d).min( LoS deciding block );. Como utilizamos números complejos para todo, podemos obtener fácilmente direcciones que son relativas hacia adelante, derecha, izquierda y hacia atrás desde la dirección original simplemente multiplicándola por 1, i, -i (recuerde que el yeje va en la dirección opuesta se utilizará de matemáticas) y -1, respectivamente. Por lo tanto, formamos la lista de direcciones en este orden, y encontramos la dirección que tiene la "distancia a un humano" mínima (de acuerdo con el bloque anterior), lo que asegura que cualquier humano derrote cualquier pared y que cualquier cosa venza a una pared que sea correcta debajo de la nariz del monstruo Usamos el hecho de que la minfunción da la primeravalor mínimo Si hay un empate en la distancia entre varias direcciones, el monstruo preferirá avanzar de derecha a izquierda o hacia atrás, que es exactamente lo que queremos. Entonces obtenemos una nueva dirección que luego se asigna $d.

$m+=$d; solo hace que el monstruo avance en la nueva dirección.

$m=$d+(a{m{$m}}∖~$m).pick while m{$m}~~"A".."Z"se encarga de los teletransportes. Digamos que el monstruo está en un teletransporte A. Primero buscamos Aen el hash inverso %ay eso sube las dos posiciones del teletransporte A, luego descartamos la posición actual usando el operador de diferencia de conjunto (que parece una barra invertida). Dado que hay exactamente 2 ocurrencias de cada teletransporte en el mapa y el monstruo seguramente está parado en uno, la diferencia será un conjunto con 1 elemento. Luego usamos .pickpara elegir un elemento aleatorio (lo creas o no, pero esta es la forma más corta de obtener el elemento del conjunto de 1 elemento :—)). Este tipo de cosas suceden hasta que el monstruo termina en algún lugar no en un portal.

Todo en los últimos 4 párrafos describe una construcción masiva de forma {change direction; step; handle teleports}...{m{$m}~~"*"}. Esta es una secuencia que llama al bloque a la izquierda de ...hasta que la condición a la derecha de la ...sea ​​verdadera. Y eso es cierto cuando el monstruo está en una plaza con un humano. La lista en sí contiene valores de retorno del bloque enorme a la izquierda, que es basura (muy probablemente (Any)) porque devuelve el valor del ciclo while al final. Esencialmente, lo usamos como un ciclo while más barato. El valor de la lista se descarta (y el compilador se queja de un "uso inútil ...en un concurso de sumideros", pero a quién le importa). Cuando todo esto está hecho, regresamos $m: la posición del monstruo después de encontrar un humano, aún como un número complejo (así, para la primera prueba, damos 10+2iy así sucesivamente).

Ramillies
fuente
Buena explicación! :)
Ian H.
3

JavaScript (ES6), 258 245

g=>eval("m=g[I='indexOf']`#`,D=[w=g[I]`\n`+1,-1,-w,k=t=1],g=[...g];for(n=1/0;n;(o=g[m+=d=D[k]])>'@'?g[g[u=m]=t=1,m=g[I](o),u]=o:o<'%'?t=1:o<'*'&&(m-=d,k=k+t++&3))D.map((d,j)=>{for(q=0,u=m;g[u+=d]<'%';++q);g[u]=='*'&n>q&&(n=q,k=j)});[m%w,m/w|0]")

Menos golf

g=>{
    var u, // temp current position 
        q, // distance so far scanning for a human
        o, // value of grid at current position
        d, // offset to move given current direction
        n = 1/0 // current distance from human
        m = g.indexOf(`#`), // monster position
        w = g.indexOf(`\n`)+1, // grid width + 1 (offset to next row)
        D = [w,-1,-w,1], // offset for moves, clockwise
        k = 1, // current direction, start going west
        t = 1; // displacement for turn
    g=[...g]; // string to array
    while (n) // repeat while not found (distance > 0)
    {
      // look around to find humans
      D.forEach( (d,j) => { // for each direction
        // scan grid to find a human
        for(q=0, u=m; g[u+=d]<'$'; ++q); // loop while blank space
        if ( g[u] == '*' // human found at position u
             & n>q ) // and at a shorter distance than current
        {
           n = q; // remember min distance
           k = j; // set current direction
        }
      })
      // if human found, the current direction is changed accordingly
      d = D[k] // get delta
      m += d // move
      o = g[m] // current cell in o
      if (o > '@') // check if letter (teleporter)
      {
        t = 1 // keep current direction, next turn will be right
        u = m // save current position in u
        g[u] = 1 // clear current cell, so I can find only the other teleporter
        m = g.indexOf(o) // set current position to other teleporter
        g[u] = o // reset previous cell content
      }
      else if (o > '%') // check if '*'
      {
        // set result, so we can exit the loop
        r = [ m%w, m/w|0 ] // x and y position 
      }
      else if (o == '%') // check if wall
      {
        // turn
        m -= d // current position invalid, go back
        k = (k+t) % 4 // turn right 90° * t
        t = t+1 // next turn will be wider
      }
      else
      {
        t = 1 // keep current direction, next turn will be right
      }
    }
    return r
}

Prueba

var F=
g=>eval("m=g[I='indexOf']`#`,D=[w=g[I]`\n`+1,-1,-w,k=t=1],g=[...g];for(n=1/0;n;(o=g[m+=d=D[k]])>'@'?g[g[u=m]=t=1,m=g[I](o),u]=o:o<'%'?t=1:o<'*'&&(m-=d,k=k+t++&3))D.map((d,j)=>{for(q=0,u=m;g[u+=d]<'%';++q);g[u]=='*'&n>q&&(n=q,k=j)});[m%w,m/w|0]")

var grid=[`%%%KLMNOPQRSTA%\n%%J           B\n%I       %*   C\nH   *         D\nG   %%        E\nF       #     F\nE      %      G\nD      *      H\nC   %        I%\nB           J%%\n%ATSRQPONMLK%%%`
,'%%%KLMNOPQRSTA%\n%%J           B\n%I      *     C\nH     %%%   * D\nG      #%     E\nF     %%%   % F\nE             G\nD       %     H\nC       *    I%\nB       *   J%%\n%ATSRQPONMLK%%%'
,'%%%KLMNOPQRSTA%\n%%J           B\n%I     %%%    C\nH     *%#%    D\nG             E\nF             F\nE       %     G\nD             H\nC            I%\nB           J%%\n%ATSRQPONMLK%%%'
,'%%%%%%%%%%%%%%%\n%#%       %%% %\n%A%ABCD   %*% %\n%*F     G %%% %\n% %BC% FD     %\n% %   %    %%%%\n% %  %       %%\n%   % %%   G  %\n%             %\n%*%    %    % %\n%%%%%%%%%%%%%%%'
]
$(function(){
  var $tr = $('tr')
  grid.forEach(x=>{
    $tr.eq(0).append("<td>"+x+"</td>")
    $tr.eq(1).append("<td>"+F(x)+"</td>")
  })
})
td {padding: 4px; border: 1px solid #000; white-space:pre; font-family:monospace }
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table ><tr/><tr/></table>

edc65
fuente