¡Haz un analizador de serpientes!

14

Las serpientes se ven así:

      >>>v
@     ^  v
^  >>>^  v
^        v
^<<<<<<<<<

La serpiente puede cruzarse sobre sí misma como en este caso:

 @
 ^
>^>v
 ^<<

Para que un crossover sea válido, los caracteres de cada lado deben moverse en la misma dirección. El caso de

 @
>^v
 ^<

puede considerarse poco claro e inválido.

La salida es una cadena de WASDrepresentación que va desde la cabeza hasta la cola ( @).

Dada una serpiente que no retrocede y no es ambigua, ¿puede escribir un programa que genere la secuencia de movimientos que realiza la serpiente?

Este es el código de golf, por lo que gana la respuesta más corta.

Casos de prueba:

(Nota: @se puede reemplazar con cualquier carácter que no esté en v^<>)

Entrada:

>>>>v
    v
  v<<  @
  v    ^
  >>>>>^

Salida: ddddssaassdddddww


Entrada:

@>>v
^  v
^  v
^<<<

Salida: dddsssaaawww


Entrada:

>>>v
   v       @
   v       ^
   >>>>v   ^
       >>>>^

Salida: dddsssddddsddddwww


Entrada:

@<< v
  ^ v
v<^<<
v ^
>>^

Salida: ssaaaassddwwwwaa


Entrada:

@v<v
^v^v
^v^<
^<

Salida: ssawwasssawww

CAD97
fuente
1
¿La entrada tiene que ser una sola cadena o podemos tomar una cadena []? ¿Cada línea de la entrada acolchada tiene la misma longitud o tenemos que lidiar con una longitud de línea irregular?
CAD97
2
Esto me está dando retrocesos horribles al camino de una hormiga en una pregunta de cubo de rubik.
Matt
¿El segmento inicial siempre estará en la línea 0, char 0, o tendremos que encontrarlo?
MayorMonty
1
Los casos de prueba de @SpeedyNinja 2 y 4 tienen ambos comienzos no en (0,0), y el caso de prueba 0 (las serpientes parecen) no comienza O termina en (0,0).
CAD97
1
@ CAD97 Oh, eso es divino;)
MayorMonty

Respuestas:

7

Java, 626 539 536 529 bytes

-87 bytes al guardar algunos en muchos lugares. Gracias al Sr. Public por señalar algo.

-3 bytes porque no puedo eliminar todos los espacios primero (gracias mbomb007)

+8 bytes para solucionar este caso:

@v<v
^v^v
^v^<
^<

-15 bytes por declaración de variable de carga frontal

s->{String o="",t;String[]p=s.split("\n");int h=p.length,w=p[0].length(),y=0,x,b=0,a,n,m;char[][]d=new char[h][w];for(;y<h;y++)for(x=0;x<w;x++){d[y][x]=p[y].charAt(x);if(d[y][x]=='@')d[y][x]=' ';}for(;b<h;b++)for(a=0;a<w;a++){t="";x=a;y=b;n=0;m=0;while(!(y<0|y>h|x<0|x>w||d[y][x]==' ')){if(y+m>=0&y+m<h&x+n>=0&x+n<w&&d[y+m][x+n]==d[y-m][x-n])d[y][x]=d[y-m][x-n];n=m=0;switch(d[y][x]){case'^':t+="W";m--;break;case'<':t+="A";n--;break;case'v':t+="S";m++;break;case'>':t+="D";n++;}x+=n;y+=m;}o=t.length()>o.length()?t:o;}return o;}

Versión legible:

static Function<String,String> parser = snake -> {
    // declare all variables in one place to minimize declaration overhead
    String output = "", path;
    String[] split = snake.split("\n");
    int h=split.length, w=split[0].length(), y=0, x, startY=0, startX, dx, dy;
    char[][] board = new char[h][w];
    // setup char[][] board
    for (; y<h; y++)
        for (x=0; x<w; x++) {
            board[y][x]=split[y].charAt(x);
            if(board[y][x]=='@')board[y][x]=' ';
        }
    // find the longest possible path
    for (; startY<h; startY++)
        for (startX=0; startX<w; startX++) {
            path = "";
            x=startX; y=startY; dx=0; dy=0;
            while (!(y<0 | y>h | x<0 | x>w || board[y][x] == ' ')) {
                if (y + dy >= 0 & y + dy < h & x + dx >= 0 & x + dx < w
                        && board[y + dy][x + dx] == board[y - dy][x - dx]) {
                    board[y][x] = board[y - dy][x - dx];
                } dx = dy = 0;
                switch(board[y][x]) {
                    case '^':path+="W";dy--;break;
                    case '<':path+="A";dx--;break;
                    case 'v':path+="S";dy++;break;
                    case '>':path+="D";dx++;break;
                }
                x+=dx; y+=dy;
            }
            output = path.length()>output.length()?path:output;
        }
    return output;
};

Toma una cuerda como v @\n>>>^. Crea una ruta que comienza en cada coordenada, luego devuelve la más larga. La anticipación requerida para los caminos superpuestos fue la parte más difícil.

CAD97
fuente
2
Estoy muy impresionado. No esperaba que nadie intentara esto. Y tú eres el primero. +1. (2016 bytes está bien, e incluso mejor para 2016: D)
Despoje todos los espacios, nombres, etc. Luego haré +1. No votaré hasta que se juegue correctamente.
mbomb007
2
O bien, tenga dos fragmentos de código, uno de la versión totalmente desarrollada, uno de los ejemplos legibles.
Mr Public
@ mbomb007 ¡Terminé el juego lógico de golf, así que aquí está la versión de golf adecuada!
CAD97
2
@ CAD97 Para este desafío, diría que este es un excelente golf en Java.
Mr Public
1

Rubí, 217

->a{r=''
z=a.index ?@
a.tr!('<^>v',b='awds').scan(/\w/){c=0
e,n=[a[z,c+=1][?\n]?p: c,d=c*a[/.*
/].size,a[z-c,c][?\n]?p: -c,-d].zip(b.chars).reject{|i,k|!i||a[v=i+z]!=k||0>v}.max_by{|q|q&[a[z]]}until n
z+=e
r=n*c+r}
r}

Esto comienza en el @ y camina hacia atrás, buscando vecinos que apunten a la posición actual ( z). Para elegir el camino correcto en las intersecciones de 4 vías, favorece a los vecinos que apuntan en la misma dirección ( max_by{...}). Si no se encuentran vecinos inmediatos, se supone que debe haber habido un cruce y alcanza un nivel a la vez hasta que encuentre uno ( until ny c+=1). Este proceso se repite para la cantidad de segmentos del cuerpo (sin incluir la cabeza) ( .scan(/\w/){...}).

El caso de prueba que agregué al acertijo me hizo tropezar, así que pasé de 182 caracteres a 218. Esos personajes adicionales se aseguraban de que mis movimientos horizontales no entraran en las líneas siguiente / anterior. Me pregunto si puedo lidiar con eso de una mejor manera.

Sin golf:

f=->a{
  result=''
  position=a.index ?@ # start at the @
  a.tr!('<^>v',b='awds') # translate arrows to letters
  a.scan(/\w/){           # for each letter found...
    search_distance=0
    until distance
      search_distance+=1
      neighbors = [
        a[position,search_distance][?\n]?p: search_distance,  # look right by search_distance unless there's a newline
        width=search_distance*a[/.*\n/].size,   # look down (+width)
        a[position-search_distance,search_distance][?\n]?p: -search_distance, # look left unless there's a newline
        -width                  # look up (-width)
      ]
      distance,letter = neighbors.zip(b.chars).reject{ |distance, letter_to_find|
        !distance || # eliminate nulls
         a[new_position=distance+position]!=letter_to_find || # only look for the letter that "points" at me
         0>new_position # and make sure we're not going into negative indices
       }.max_by{ |q| 
        # if there are two valid neighbors, we're at a 4-way intersection
        # this will make sure we prefer the neighbor who points in the same 
        # direction we're pointing in.  E.g., when position is in the middle of 
        # the below, the non-rejected array includes both the top and left.
        #   v
        #  >>>
        #   v
        # We want to prefer left.
        q & [a[position]] 
        # ['>',x] & ['>'] == ['>']
        # ['v',x] & ['>'] == []
        # ['>'] > [], so we select '>'.
       }
    end
    position+=distance
    result=(letter*search_distance)+result # prepend result
  }
  result # if anyone has a better way of returning result, I'm all ears
}
No es que Charles
fuente
Debería poder simplificar su lógica de alguna manera ya que su caso agregado se ha considerado fuera del alcance previsto.
CAD97