Generar una ruta de no intersección ascii-art

18

Dadas 2 entradas enteras que representan el tamaño del campo, xy y, generan una ruta a través del campo.

Ejemplo de salida para 5, 4:

#    
#    
# ###
### #

Todo el campo es de 5 por 4, y hay un camino hecho de marcas que cruzan el campo.

La ruta siempre debe comenzar en la esquina superior izquierda e ir a la esquina inferior derecha. Toda la ruta debe ser aleatorizada cada vez que se ejecuta el programa. Cada ruta válida debe ser una salida posible.

Las reglas para los caminos son:

  • Hecho de hashmarks

  • Cada hash solo está conectado a otros 2 hashes (es decir, el camino no se cruza ni corre junto a sí mismo)

Los espacios no hash pueden llenarse con cualquier otro carácter, pero deben ser consistentes (es decir, todos los espacios, todos los puntos, etc.)

Ejemplos:

2, 2

##
 #

3, 4

##
 ##
  #
  #

5, 5

#####
    #
    #
    #
    #

6, 5

## ###
 # # #
## # #
# ## #
###  #

7, 9

#######
      #
####  #
#  #  #
#  #  #
#  #  #
#  ####
#
#######

Este tipo de camino es similar a una caminata aleatoria que se evita a sí mismo, sin embargo, no puede ser adyacente a sí mismo a diferencia de una verdadera SIERRA.

La continuidad del camino y el contacto del camino se definen sin diagonales.

Rɪᴋᴇʀ
fuente
¿Formato de salida RBX.Lua válido? ;)
devRicher
¿Es correcto que, siempre que cada ruta válida tenga una probabilidad positiva de ser elegida, la distribución de probabilidad es arbitraria?
falla
@devRicher, sí
Rɪᴋᴇʀ
@flawr sí, eso es correcto
Rɪᴋᴇʀ

Respuestas:

11

MATLAB, 316 305 300 293 bytes

function P=f(a,b);z(a,b)=0;P=z;c=@(X)conv2(+X,[0,1,0;1,1,1;0,1,0],'s');B=1;while B;P=z;P(1)=1;for K=1:a*b;S=z;S(a,b)=1;for L=2:a*b;S(~S&c(S)&~P)=L;end;[i,j]=find(S&c(P==K));if i;r=randi(nnz(i));else;break;end;P(i(r),j(r))=K+1;if P(a,b);break;end;end;B=any(any(c(P>0)>3));end;P(P>0)=35;P=[P,'']

Gracias @LuisMendo por varias sugerencias y un montón de bytes =)

Pruébalo en línea! (Sin garantía: tenga en cuenta que se necesitaron algunos ajustes para que se ejecute en Octave: en primer lugar, necesitaba eliminar la functionpalabra clave y codificar los valores, en segundo lugar: los espacios no se imprimen correctamente como en Matlab. Tampoco lo hice verifique los comandos de convolución de Octave, que podrían actuar de manera diferente).

Ejemplo de salida para entrada (7,10)(ya puede tomar bastante tiempo):

#         
#         
##        
 ##       
  #   ### 
  #   # ##
  #####  #

Explicación

Esto genera rutas secuencialmente desde la parte superior izquierda a la parte inferior derecha con la conectividad 4 deseada, y luego utiliza el muestreo de rechazo para rechazar rutas que violan el criterio de que no puede tener partes adyacentes.

function P=f(a,b);
z(a,b)=0;                                 % a matrix of zeros of the size of th efield
P=z;                                    
c=@(X)conv2(+X,[0,1,0;1,1,1;0,1,0],'s');  % our convolution function, we always convolute with the same 4-neighbourhood kernel
B=1;
while B;                                  % while we reject, we generate paths:
    P=z;
    P(1)=1;                               % P is our path, we place the first seed
    for K=1:a*b;                          % in this loop we generate the all shortest paths (flood fill) from the bottom right, withot crossing the path to see what fiels are reachable from the bottom left
        S=z;
        S(a,b)=1;                         % seed on the bottom left
        for L=2:a*b;
            S(~S&c(S)&~P)=L;              % update flood front
        end;
        [i,j]=find(S&c(P==K));            % find a neighbour of the current end of the path, that is also reachable from the bottom left
        if i;                             % if we found some choose a random one
            r=randi(nnz(i));
        else;
            break;                        % otherwise restart (might not be necessary, but I'm too tired to think about it properly=)
        end;
        P(i(r),j(r))=K+1;                 % update the end of the current path
        if P(a,b);                        % if we finished, stop continuing this path
            break;
        end;
    end;
    B=any(any(c(P>0)>3));                 % check if we actually have a valid path
end;
P(P>0)=35;                                % format the path nicely
P=[P,''];

Ah y como siempre:

La convolución es la clave del éxito.

falla
fuente
19

Befunge, 344 bytes

&v>>>#p_:63p:43g`\!+v>/*53g+\01g:2%2*1-\2/!*63g+\0\:v
 40$ v++!\`g14:p35:\<^2\-1*2%2p10::%4+g00:\g36\g35-1_v
#11^$_83p73v >1+:41g`!#v_$,1+:43g`!#v_@>->2>+00p+141^_
<p1^     vp< ^,g+7g36:<<<<1+55p36:<<<< ^1?0^#7g36g35*
8&p|!++!%9#2g+7g10\*!-g38g10!-g37:g00!!*<>3^
443>:!#v_>>1-::3%1-:53g+00p\3/1-:63g+01p^
^>^>>$#<"#"53g63g7+p41g53g-43g63g-+!#^_

Pruébalo en línea!

Como @flawr mencionó en su respuesta de MATLAB, esto puede llevar algún tiempo si el tamaño del campo no es trivial. De hecho, es bastante fácil entrar en una situación en la que realmente no vale la pena intentar esperar a que termine, porque es muy probable que espere hasta el final de los tiempos.

Para entender por qué sucede esto, es útil ver el programa mientras se ejecuta en uno de los muchos "depuradores visuales" de Befunge. Dado que los datos y el código son lo mismo en Befunge, podrá ver la ruta a medida que cambia con el tiempo. Por ejemplo, aquí hay una breve animación que muestra cómo se vería una parte de una carrera en una ruta lenta.

Animación que muestra la construcción del camino atascado en una esquina

Una vez que el algoritmo decide hacer ese giro fatídico a la izquierda en la parte inferior del límite del campo, esencialmente se ha condenado a una vida de vagar sin rumbo. A partir de ese momento, debe seguir todos los caminos posibles en esa área cercada antes de que pueda retroceder e intentar girar a la derecha. Y la cantidad de caminos potenciales en estos casos puede convertirse fácilmente en astronómica.

En pocas palabras: si parece que lleva mucho tiempo, probablemente sea una buena idea abortar la ejecución y comenzar de nuevo.

Explicación

Esto es básicamente un algoritmo recursivo, que prueba todas las rutas posibles a través del campo y luego desenrolla los pasos que ya se han seguido cada vez que se atasca. Como Befunge no tiene el concepto de funciones, una función recursiva está fuera de discusión, pero podemos emular el proceso rastreando el estado en la pila.

Esto funciona al llenar la pila con coordenadas potenciales que podemos querer seguir. Luego sacamos un conjunto de la pila y verificamos si es adecuado (es decir, dentro del rango y no superpuesto con una ruta existente). Una vez que tenemos un buen lugar, escribimos un# en el campo de juego en esa ubicación y agregamos esos detalles a la pila en caso de que necesitemos retroceder más tarde.

Luego empujamos cuatro conjuntos de coordenadas adicionales en la pila (en orden aleatorio) que indican los posibles caminos que podemos tomar desde esta nueva ubicación, y saltamos de regreso al inicio del ciclo. Si ninguna de las rutas potenciales es factible, llegaremos al punto en la pila donde guardamos la ubicación de# que escribimos, por lo que desharemos ese paso y continuaremos probando las coordenadas potenciales de un paso anterior.

Así es como se ve el código con las diversas partes componentes resaltadas:

Código fuente con rutas de ejecución resaltadas

* *Lea el ancho y la altura del campo, y presione las coordenadas de inicio junto con un 0marcador de tipo para indicar una ruta potencial en lugar de una ubicación de retroceso.
* *Verifique las ubicaciones de retroceso (indicadas por un 1marcador de tipo) que se revierten con un pcomando simple , ya que se almacenan en el formato exacto necesario para volver a escribir un espacio en el campo de juego.
* *Comprueba si las coordenadas todavía están dentro del campo de juego. Si están fuera de alcance, suéltelos de la pila y vuelva a intentarlo para probar las siguientes coordenadas potenciales.
* *Si están dentro del rango, obtenga los siguientes dos valores de la pila, que es la ubicación del paso anterior (requerido en la prueba que sigue a esto).
* *Compruebe si las coordenadas van a entrar en contacto con un segmento existente de la ruta. La ubicación del paso anterior obviamente se ignora de esta verificación.
* *Si todas las pruebas son exitosas, escriba unEmpuje cuatro destinos potenciales a los que se puede llegar desde la ubicación actual. El número aleatorio determina el orden en que se empujan y, por lo tanto, el orden en que se seguirán.#en el campo de juego y verifica si hemos llegado a la ubicación de destino.
* *Si es así, escriba la ruta final * *y salga.
* *De lo contrario, guarde las coordenadas en la pila con un 1marcador de tipo para retroceder más tarde.
* *Esto se interrumpe con un cálculo de número aleatorio que vamos a necesitar pronto. Regrese al inicio del bucle principal y procese los siguientes valores en la pila.
* *
* *

James Holderness
fuente
2
Santa vaca ¿Explicación?
Rɪᴋᴇʀ
@EasterlyIrk Gracias por la recompensa. Es muy apreciado.
James Holderness
Me alegro de que haya sido útil!
Rɪᴋᴇʀ
2

QBasic, 259 bytes

Seguro que amo GOTOs.

RANDOMIZE TIMER
INPUT w,h
DO
CLS
x=1
y=1
REDIM a(w+3,h+3)
2a(x+1,y+1)=1
LOCATE y,x
?"#"
d=INT(RND*4)
m=1AND d
x=x+m*(d-2)
y=y-d*m+m+d-1
c=a(x,y+1)+a(x+2,y+1)+a(x+1,y)+a(x+1,y+2)=1
IF(x=w)*c*(y=h)GOTO 9
IF(x*y-1)*x*y*c*(x<=w)*(y<=h)GOTO 2
LOOP
9LOCATE y,x
?"#"

Estrategia básica: en cada paso, imprima #a en la ubicación actual y muévase en una dirección aleatoria. Un conjunto ade 0 y 1 realiza un seguimiento de dónde hemos estado. Si el movimiento es legal y nos lleva al punto final, GOTO 9para salir del bucle e imprimir el final #. De lo contrario, si el movimiento es legal, dé otro paso. De lo contrario, borre la pantalla y comience de nuevo (¡mucho más golf que codificar un algoritmo de retroceso!).

Probado en mi computadora portátil en QB64, esto generalmente produce un resultado 9, 9en cinco segundos o menos. Las carreras de 10, 10han tomado entre tres y 45 segundos. Teóricamente, todas las rutas legales tienen una probabilidad distinta de cero, pero la probabilidad de una ruta con curvas grandes es muy pequeña. Sin embargo, ocasionalmente he visto caminos con una o dos curvas pequeñas:

Ruta de muestra

Versión sin golf y / o explicación en profundidad disponible bajo petición.

DLosc
fuente
2

R, 225 bytes

function(x,y){library(igraph);a=matrix(rep(" ",x*y),c(y,x));g=make_lattice(c(y,x));w=runif(ecount(g));for (i in 1:2){v=shortest_paths(g,1,x*y,weights=w)$vpath[[1]];w[]=1;w[E(g)[v%--%v]]=0;};a[v]="#";cat(rbind(a,"\n"),sep="")}

Explicación:

Generamos un gráfico regular (reticular) [x * y] no dirigido con pesos aleatorios en los bordes y luego encontramos el camino más corto desde el principio hasta el final. Sin embargo, en la ruta generada puede haber celdas que tengan más de dos vecinos, por ejemplo:

#
#
####
  ##
  #
  ###

Entonces deberíamos aplicar el algoritmo de ruta más corta dos veces. En la segunda vez, establecemos todos los pesos en 1, excepto los que están en la ruta actual que se establece en 0;

resultado

#
#
### 
  # 
  #
  ###

Sin golf:

function(x,y){
    library(igraph);#igraph library should be installed
    a=matrix(rep(" ",x*y),c(y,x));#ASCII representation of the graph
    g=make_lattice(c(y,x));# regular graph
    w=runif(ecount(g));#weights
    for (i in 1:2){
        #find vertices that are in the path
        v=shortest_paths(g,1,x*y,weights=w)$vpath[[1]];
        #set all weights to 1 except those that are in the current found path that set to 0
        w[]=1;
        w[E(g)[v%--%v]]=0;
    }
    a[v]="#";
    cat(rbind(a,"\n"),sep="")
}
rahnema1
fuente
1

JavaScript (ES7), 333 331 330 329 324 318 312 bytes

f=
(h,w,c=[...Array(h)].map(_=>Array(w).fill` `),g=a=>{for(z of b=[[[h-1,w-1]]])a.map(([x,y])=>b.every(([[i,j]])=>i-x|j-y)&(z[0][0]-x)**2+(z[0][1]-y)**2<2&&b.push([[x,y],...z]));return b.find(([[x,y]])=>!x&!y)||g([...a,[h,w].map(n=>Math.random()*n|0)])})=>g([]).map(([x,y])=>c[x][y]=`#`)&&c.map(a=>a.join``).join`
`
Height: <input type=number min=1 id=h>Width: <input type=number min=1 id=w><input type=button value="Generate!" onclick=o.textContent=f(+h.value,+w.value)><pre id=o>

Expansión: los #s se colocan aleatoriamente en la matriz hasta que se encuentra una ruta a través del campo mediante una búsqueda de amplitud; el primero, y por lo tanto el más corto, ese camino se emite; Esto garantiza que el camino no se cruce. Tenga en cuenta que, en particular para campos más grandes, es posible superar la pila del motor JS antes de encontrar una ruta. Sin golf:

function r(n) {
    return Math.floor(Math.random() * n);
}
function f(h, w) {
    var a = []; // array of placed #s
    var b; // breadth-first search results
    var c;
    do {
        a.push([r(h), r(w)]); // place a # randomly
        b = [[[h - 1, w - 1]]]; // start from bottom right corner
        for (z of b) // breadth-first search
            for ([x, y] of a) // find possible next steps
                if (!b.some(([[i, j]]) => i == x && j == y))
                    if ((z[0][0] - x) ** 2 + (z[0][1] - y) ** 2 < 2)
                        if (x || y)
                            b.push([[x, y], ...z]); // add to search
                        else if (!c)
                            c = [[x, y], ...z]; // found path
    } while (!c);
    a = [...Array(h)].map(() => Array(w).fill(' '));
    for ([x, y] of c) // convert path to output
        a[x][y] = '#';
    return a.map(b => b.join('')).join('\n');
}
Neil
fuente