Quiero verte morir de sed


12

Eres un viajero que cruza el desierto entre dos pueblos. No puede llevar suficiente agua para cruzar sin detenerse. Esta es una variación de un rompecabezas clásico.

Las normas

Un desierto se ve así: una cuadrícula WxH de espacio mayormente vacío. El espacio marcado Ses donde comienzas, Ees donde quieres terminar, y un cuadrado marcado con el número N contiene N unidades de agua. Cuadrados marcados con una .bodega cero agua.

.....................................
........S............................
.....................................
.........7...........................
.....................................
.......................3.............
.....5...............................
................................2....
.....................................
.....................................
.....................................
...............................E.....
.....................................
....................7................
.....................................
.....................................

Comienzas en S con 5 unidades de agua.

Puede transportar como máximo 5 unidades de agua.

Cada turno tu

  1. mover un cuadrado hacia arriba, abajo, izquierda o derecha,
  2. consume 1 unidad de agua que llevas,
  3. recoger o dejar caer una cierta cantidad de unidades de agua.

A su vez, está anotada así: (direction)(+|-)(units of water), +indica que se está recogiendo el agua, -que está dejarlo caer.

Ejemplo de turnos:

D+0        Move Down
R+0        Move Right
D+2        Move Down, pick up two units of water.
U-1        Move Up, drop one unit of water.

Si realiza estos movimientos comenzando en S en el ejemplo anterior, el desierto se ve así después.

.....................................
........S............................
.........1...........................
.........5...........................
.....................................
.......................3.............
.....5...............................
................................2....
.....................................
.....................................
.....................................
...............................E.....
.....................................
....................7................
.....................................
.....................................

No puedes recoger más agua de la que ya está en tu plaza. Cuando recojas el agua, deduce esa cantidad de unidades del recuento de fichas.

Solo puede recoger agua para contener un máximo de 5 unidades.

Ningún mosaico puede contener más de 9 unidades, excepto S que contiene unidades de infinito.

Solo puedes soltar tanta agua como la que tienes actualmente.

El agua en el suelo permanece sin cambios hasta que la recojas nuevamente.

Si regresa a S, puede recoger cualquier cantidad de agua sin agotarla.

Si llegas a E, entonces ganas . Aún ganas si consumes tu última unidad de agua en E.

Si, después de tu turno, tienes cero agua y no estás en E, mueres .

Entrada y salida

Su programa recibirá un mapa inicial de tamaño arbitrario STDINcomo arte ASCII en el formato anterior. Puede suponer que es rectangular, es decir, todas las líneas tienen la misma longitud, que hay exactamente uno Sy un Ecuadrado, todas las líneas están terminadas \ny todo el STDIN se ajustará a esta expresión regular:/^[SE1-9\.\n]+$/

Su programa escribirá la siguiente salida en STDOUT:

  1. la lista de movimientos,
  2. El estado final del mapa.

Puede generar la lista de movimientos en cualquier formato conveniente.

El estado final del mapa se imprimirá en el mismo formato que la entrada, excepto que además mostrará la ruta que tomó por el desierto marcando todas las fichas visitadas #, si esa ficha no contiene agua y no es S o E (es decir, es .)

Ejemplo de entrada:

.....S.
.......
.......
E......
....8..

EJEMPLO resultado ganador:

D+0
D+0
D+0
D+0
L+5
L+0
L+0
L+0
L+0
U+0
.....S.
.....#.
.....#.
E....#.
####3#.

No trivialidad

Cuando publique su código, publique una entrada de mapa de muestra para la cual su código encuentre una solución que satisfaga las siguientes condiciones de no trivialidad:

  • S y E están separados por al menos 10 movimientos.
  • Cualquier cuadrado que inicialmente contenga N unidades de agua debe estar rodeado por un borde de ancho N en el que todos los cuadrados estén .(sin agua, ni S ni E)

EJEMPLO

........2.
..........
..........
S.1..2....
..........
..........
........1.
..3.......
.........E

Si aumenta la cantidad de agua en cualquier azulejo, lo anterior se vuelve trivial.

Requisitos

Presumiblemente, su programa encontrará varios intentos fallidos antes de encontrar una solución, si la hay.

  1. Su programa eventualmente debe resolver cualquier entrada solucionable.
  2. Quiero verte morir : tu programa generará los movimientos y el mapa final de la ruta a la muerte para cada intento fallido de encontrar una solución.
  3. Si encuentra una solución ganadora, imprima la salida completa para eso y finalice.
  4. Ejecute hasta que se encuentre una solución, pero no intente la misma solución dos veces : todas las muertes deben ser por rutas distintas.
  5. Use esto como entrada de prueba:

(requiere al menos un movimiento para dejar caer un caché de agua en algún punto medio).

 S........
 .........
 .........
 ........E

El código más corto que se publica con una entrada de demostración no trivial que resuelve gana.


Esto debe aclararse para especificar si el programa necesita poder resolver algún mapa solucionable, o si solo tiene que funcionar para un mapa. Definitivamente alentaría a los primeros; en el caso de un mapa, será más fácil codificar la solución que calcularla.

Editado para mayor claridad. Sí, usted gana si tiene más de cero agua, y sí, su programa debe resolver todas las entradas solucionables.
Spray

¿Qué le impide usar un algoritmo como A * y dejar caer una ruta de 5 unidades en cada mosaico al que va sucesivamente y vuelve al inicio si se sube a un mosaico sin 5 aguas?
Azul

Nada. Adelante.
Spray

La estrategia 'cargar toda el agua de S' debería funcionar, aunque será terriblemente tediosa. Considere S.,.,.,.,. E .... E donde, ye son realmente puntos. Las comas son donde guardas tus escondites en el camino, y 'e' es donde necesitas 5 agua para correr por E. 4 pasos para mover 1 agua a la primera coma (E + 0 E-1 W + 0 W + 4). 16 pasos para mover 1 agua a la segunda coma. 52 al tercero, 160 al cuarto, 484 para dejar caer 1 agua hacia e y volver a los pasos de S. 1926 y estás en e cargando 5 agua, 5 más para correr hacia E, 1931 pasos. Cada dos pasos de la ruta triplican efectivamente la longitud de su solución.
Sparr

Respuestas:


12

Perl, 299 + 1 = 300254 + 1 = 255 bytes

Esto seguramente será superado por uno de los idiomas de golf cuando la gente vea mi algoritmo :-)

Corre con -n(penalización de 1 byte).

La versión anterior no cumplía con la especificación porque dejaba agua extra en el mapa y no la mostraba en la versión final del mapa; Mientras cambiaba el algoritmo para lidiar con esa situación, logré jugar golf un poco más pequeño en el proceso.

push@a,[split//];($s,$t)=(length$`,$.)if/S/;($e,$f)=(length$`,$.)if/E/}{$_.=$s<$e?($s++,R):$s>$e?($s--,L):$t<$f?($t++,D):($t--,U);$\=reverse$";$".=y/LRUD/RLDU/r.Y.reverse.($"=~y/Y/X/r);$a[$t-1][$s]=~y/./#/;$\.=$s-$e||$t-$f?redo:$_;print map{join'',@$_}@a

Ejemplo (he agregado saltos de línea a la salida para evitar la necesidad de desplazarse y mostrar la estructura):

MI.....
# .....
# .....
# .....
##### S
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLXRRRR
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLLXRRRRR
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLXRRRR
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLLUXDRRRRR
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLXRRRR
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLLXRRRRR
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLXRRRR
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLLUUXDDRRRRR
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLXRRRR
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLLXRRRRR
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLXRRRR
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLLUXDRRRRR
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLXRRRR
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLLXRRRRR
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLXRRRR
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLLUUUYDDDRRRRR
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLXRRRR
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLLXRRRRR
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLXRRRR
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLLUXDRRRRR
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLXRRRR
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLLXRRRRR
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLXRRRR
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLLUUYDDRRRRR
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLXRRRR
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLLXRRRRR
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLXRRRR
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLLUYDRRRRR
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLXRRRR
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLLYRRRRR
 LXR LLXRR LXR LLLXRRR LXR LLXRR LXR LLLLYRRRR
 LXR LLXRR LXR LLLYRRR LXR LLYRR LYR LLLLLUUUU

En la notación utilizada por el programa, el movimiento se representa por medio de L, R, U, y Dpara la izquierda, arriba, derecha, abajo, respectivamente. Por defecto, recoges 1 unidad de agua después de cada movimiento, pero esto se puede modificar agregando un personaje:

  • X: suelta 2 unidades de agua, en lugar de recoger 1
  • Y: suelta 1 unidad de agua, en lugar de recoger 1
  • (es decir, espacio): rellene completamente con agua (solo sale después de un movimiento S; el programa también sale con un espacio inicial, lo que tiene sentido porque comienza lleno en el agua)

Como puede ver, es posible cruzar este mapa (más bien estéril) sin usar grupos adicionales. De hecho, es posible obtener cualquier distancia bajo estas reglas, en cualquier mapa, sin la ayuda de agua previamente reemplazada. Como tal, este algoritmo simplemente ignora cualquier agua previamente reemplazada, lo que significa que no tengo que desperdiciar bytes tratando de manejarlo. Esto también significa que no vas a ver morir al bot, lo siento. Nunca se mueve más allá del rango en el que sabe que está garantizado para sobrevivir.

La razón por la que necesitamos ambos Xy Y(y un poco de código adicional para asegurarnos de tener la Xmayor parte de la estrategia, pero de vez Yen cuando al final) es que la especificación requiere que se publique la versión final del mapa. La forma más fácil de implementar esto es dejar el mapa completamente intacto (aparte de nuestro camino a través de cuadrados inicialmente vacíos), especialmente porque un cuadrado que comenzó con 9agua terminaría con 10(rompiendo el arte ASCII) si estuviera en el camino y solo usamosXy, por lo tanto, necesitamos encontrar alguna solución que evite dejar caer agua adicional en el mapa. El algoritmo aquí "naturalmente" arrojaría 1 unidad adicional de agua en cada cuadrado de la ruta; Como tal, el penúltimo tiempo que visitamos cada cuadrado, reducimos la cantidad de agua que cae en 1 mediante el uso de Y en lugar de X, por lo que en nuestra visita final, drenamos el cuadrado a su cantidad original de agua, en lugar de dejándolo un poco más húmedo que cuando empezamos.

Recomiendo no ejecutar esto en un mapa grande, porque tiene un rendimiento O (2 ^ n) (aunque el bot nunca muere de sed, es posible pensar que moriría de hambre usando una estrategia como esta).

Versión legible:

# implicit with -n: read a line of input into $_
push @a, [split //]; #/ split $_ into characters, store at the end of @a
($s,$t) = (length$`,$.) if /S/; # if we see S, store its coordinates
($e,$f) = (length$`,$.) if /E/  # if we see E, store its coordinates
}{ # Due to -n, loop back to start if there are more lines.

# From here onwards, $" stores the partial solution this iteration;
#                    $\ stores the partial solution last iteration;
#                    $_ stores the path from ($s,$t) to S.
# At the start of the program, $" is a space, $\ and $_ are empty.

$_ .=  # Work out the next step on the path:
  $s < $e ? ($s++,R) # if too far left, move right, record that in $_;
: $s > $e ? ($s--,L) # if too far right, move left, record that in $_;
: $t < $f ? ($t++,D) # if too far up,    move down, record that in $_;
: ($t--,U);          # in other cases we must be too far down.
$\ = reverse $";     # Store last iteration; $" is constructed backwards.
$" .=                # Extend $" by appending
  y/LRUD/RLDU/r .    # the path from ($s, $t) back to S;
  Y .                # a literal 'Y';
  reverse .          # $/ backwards (i.e. the path from S to ($s, $t);
  ($"=~y/Y/X/r);     # a copy of $" with all 'Y' changed to 'X'.
$a[$t-1][$s] =~      # At the current path coordinate,
  y/./#/;            # replace any '.' with '#'.
$\ .=                # Start appending to $\;
  $s-$e||$t-$f?redo  # if we're not at E, abort that and jump back to {{,
: $_;                # otherwise append $_ (the path from S to E).
print map            # For each element of some array
  {join'',@$_}       # output its concatenated elements
  @a                 # specifying that array as @a.
# Implicitly: print $\ (which specifies the sort of newline print uses).

¿La inundación que llena el mundo en espiral sería menos código que su bloque de condicionales para la dirección para buscar la salida?
Sparr

1
No creo que lo sea (aunque es posible que haya alguna forma que simplemente no estoy viendo; sin duda es una idea que vale la pena considerar). Todavía tiene que lidiar con cuatro direcciones, y ahora también tiene que lidiar con el borde del mapa, algo que no es un problema en esta versión del algoritmo.

Buen punto, re bordes. Creo que podría hacerse en un mapa infinito.
Sparr
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.