Office Escape: ¡planifica tu salida!

32

Es el sprint final ... y la mitad de tu equipo está enfermo. Estás trabajando hasta tarde, solo haces tu último compromiso para el día, esperando ... ¿por qué se han apagado las luces? No recuerdo al tipo de seguridad que viene ... ¡oh no! ¡Dejé mis llaves en casa!

A medida que se hunde el horror de la situación, decides que vas a escapar .

Resumen de tareas

Para lograr tu escape, ¡necesitas un plan! Sin embargo, usted sabe que cualquier plan tiene la posibilidad de fallar, y diferentes planes requieren diferentes cantidades de esfuerzo.

Al estar hambriento, cansado e ingeniero, decide escribir un programa corto para determinar la mejor manera de escapar del complejo, equilibrando las preocupaciones sobre la probabilidad de éxito y el esfuerzo que requerirá.

Haces un mapa del edificio:

#######################
#                =    #
!                =    !    <-- window
#          !     =    #        (freedom!)
#################=    #
#    #           =    #
#    #           =    #
#    #           =    #
# o  !   # #  !  =    #
##|  !  ## #  !  =    #
#######################

  ^  ^           ^
  me my door     ludicrously high shelves
     (locked)    (climbable)

Para escapar de la oficina, tendrás que alejarte del mapa. Aquí puede ver que hay 2 ventanas ( !), cualquiera de las dos lo llevaría a la libertad, pero solo una de ellas es accesible. Definimos 'fuera del mapa' como tener los pies fuera de los límites del mapa.

Tipos de células

  - empty, you can be here (i.e. the escapee can consume this cell)
# - solid (your desk, your in-tray), you can't be here, but you can stand on it
! - destructible, (co-worker's desk, door, window), you can't be here until you smash it first (turning it into an empty cell)
= - climbable, (fire ladder, filing cabinet, etc.), you can be here

Las células originalmente consumidas por el fugitivo se consideran vacías.

Especificaciones de acción

Tiene varias acciones posibles a su disposición. Estos se definen por transiciones de estado simples con alguna probabilidad de éxito de enteros. Por ejemplo, para caminar, mueves una celda del fugitivo, que representamos con esta transición:

Paso

1 stp, 100%, espejo

 o.            o
 |.   -->      |
 #            #

Los puntos muestran celdas que deben estar vacías (o escalables, pero no sólidas o destructibles) ya sea porque nos movemos hacia él o a través de él. El 100% significa que tienes un 100% de posibilidades de no lastimarte y terminar tu atrevida fuga. Todas las probabilidades serán porcentajes enteros entre 1% y 100% inclusive. El primer diagrama muestra el estado inicial (parado sobre algo sólido, parado al lado de algún espacio vacío). El segundo diagrama muestra el estado terminal (movido al espacio vacío). No hay requisitos para que ninguna celda no especificada (espacios ) a la izquierda (estado inicial) sea algo en particular. Celdas no especificadas (espacio,) a la derecha (estado terminal) debe ser el mismo que antes (p. ej., lo que estaba detrás del fugitivo, o lo que sea que esté caminando (ya sea un espacio vacío o de otra manera). Tenga en cuenta que todo está a la derecha (estado terminal ) los diagramas solo cambiarán las celdas de destructibles a vacías, no pueden ocurrir otros cambios. "1 stp" significa que cuesta 1 stp: definimos "stp" como la cantidad de energía requerida para dar un paso.

"espejo" significa que esta acción tiene dos formas. Se muestra la acción "derecha", la acción "izquierda" es un espejo exacto, por ejemplo:

.o
.|
 # 

es la forma espejo (izquierda) de

o.
|.
# 

La acción derecha se llama "Derecha" (por ejemplo, "Paso a la derecha") La acción izquierda se llama "Izquierda" (por ejemplo, "Paso a la izquierda")

En estos diagramas, el fugitivo se muestra por

o
|

cuando está de pie (2 unidades de altura) y

%

al agacharse (1 unidad de altura). Las células que deben ser sólidos o destructible se indican mediante un hash, #. Las celdas que no deben ser sólidas o destructibles se indican con un punto .. Las celdas que deben ser destructibles se indican mediante una explosión !. Un espacio vacío recién creado se muestra con un guión bajo _. xes un punto de referencia que no se mueve (no existe, sin restricciones sobre lo que debe ser esa celda (como un espacio )).

Nota: ignoramos el problema de la desaceleración rápida cuando tocas el piso, y sí, en este juego puedes hacer saltos totalmente épicos en las escaleras)

Paso

1 stp, 100%, espejo

 o.         o
 |.  -->    |
x#        x#

Bajar

1 stp, 100%, espejo

 =         =
 o.  -->    o
 |.         |
x=        x= 

Barajar

3 stp, 100%, espejo

 %.         %
x#   -->  x# 

Subir

10 stp, 95%, espejo

 o.         %
 |#  -->    #
x#        x#

soltar

0 stp, 100%

 o         
 |  -->   o
x.       x|

Soltar

0 stp, 100%

 %        o
x.  -->  x|

Subir

2 stp, 100%

 =        o
 o  -->   |
x|       x

Agacharse

2 stp, 100%

 o
 |  -->   %
x#       x#

Estar

4 stp, 100%

 .        o
 %  -->   |
x#       x#

Salto corto

4 stp, 95%, espejo

 o..          o
 |..  -->     |
x#         x#

Salto largo

7 stp, 75%, espejo

 o...           o
 |...  -->      |
x#          x#

Salto alto

12 stp, 90%, espejo

 ..         o
 o.  -->    |
 |          
x#        x# 

¡Dale la espalda!

20 stp, 80%, espejo

 o!.         _o
 |!.  -->    _|
x#         x#

Puñetazo

8 stp, 90%, espejo

 o!        o_
 |   -->   |
x#        x#

Patada

8 stp, 85%, espejo

 o         o
 |!  -->   |_
x#        x#

Sello

8 stp, 90%

 o        o
 |  -->   |
x!       x_

Planes

Un plan es una secuencia de las acciones definidas anteriormente. Por ejemplo:

Step Left
High Jump Left
Crouch
Shuffle Left
Shuffle Left
Stand
Long Jump Left
Put your back into it! Left
Step Left

Tenga en cuenta la inclusión de las gotas. Las reglas deben establecerse para que dejes de hacer cualquier cosa que no sea caer, ¡pero aún tienes que planearlo!

Cualquier plan tiene un esfuerzo requerido, que es la suma de los esfuerzos para cada paso. También hay una probabilidad de éxito, que es el producto de las probabilidades de éxito de cada acción. Ejemplo simple:

Step Right:          1stp,  100%
Long Jump Right:     7stp,  75%
Step Right:          1stp,  100%
Stamp:               8stp,  90%
Drop:                0stp,  100%
Drop:                0stp,  100%
Drop:                0stp,  100%
Drop:                0stp,  100%
Step Left:           1stp,  100%
Step Left:           1stp,  100%
High Jump Left:      12stp, 90%

Effort = 1+7+1+8+1+1+12 = 31
Success Probability = 75%*90*90% = 60.75%

Un 'ejemplo trabajado' para el mapa en la parte superior de la página se puede encontrar como una idea general .

Entrada

La entrada viene en dos partes, un número entero y una matriz o cadena de caracteres.

El entero es su probabilidad mínima aceptable (porcentaje) de éxito. Debe interpretarse como un porcentaje, por lo que 80 significa que su plan debe tener éxito con una probabilidad no inferior al 80%.

Un mapa válido es un rectángulo que incluye el fugitivo de pie (tamaño mínimo de 1x2) y sin símbolos no especificados. Todas las filas serán de la misma longitud. Puede aceptar la entrada como una cadena delimitada unidimensional (el delimitador debe ser un solo carácter consistente, o uno de par CRLF y LFCR), como una matriz unidimensional similar o una matriz bidimensional. Si el formato de entrada elegido no indica el ancho o la altura del mapa de alguna manera, puede aceptar argumentos adicionales para estos (debe indicarlo claramente en su respuesta). Puede mezclar argumentos de línea de comando y entrada estándar si le conviene (por ejemplo, mapa de stdin, probabilidad mínima de éxito de argv). Los siguientes son ejemplos de mapas válidos e inválidos.

Válido:

o
|

Válido:

  #     #
  !   o #
  !   | #
#########

Inválido (sin fugitivo):

  #     #
  !     #
  !     #
#########

Inválido (el fugitivo siempre comienza a pararse):

  #     #
  !     #
  !   % #
#########

Inválido (símbolo inválido):

  #     #
  !  ~  #
  !     #
#########

Inválido (no un rectángulo / filas de diferente longitud):

  #     #
  !   o #
  !   | # 
#########

Puede suponer que su entrada es válida (no me importa lo que haga su programa si recibe una entrada no válida).

Salida

La salida es texto (ASCII). Puede devolverse como una cadena o imprimirse en salida estándar. El plan debe estar delimitado por un LF, CRLF o LFCR. Del mismo modo, debe haber otro LF, CRLF o LFCR después del esfuerzo requerido. Un salto de línea final es opcional.

Debe generar un plan óptimo junto con el esfuerzo que requiere, o "¡No hay esperanza!" si no existe tal plan. No necesita generar la probabilidad de éxito. Este texto puede tener o no un salto de línea final.

Un plan óptimo se define como un plan (ver arriba) que requiere el mínimo esfuerzo con al menos la probabilidad de éxito dada. Tenga en cuenta que sus cálculos de probabilidad deben ser exactos, no puede suponer que el punto flotante es lo suficientemente bueno (es por eso que no espero que los genere). Intentaré diseñar casos de prueba para probar esto de manera justa (si los aprueba y no hace suposiciones tontas, entonces puede considerar que su envío es válido).

Formato:

Required Effort: <effort>
<plan>

Por ejemplo, para la entrada

50
  #     #
  !   o #
  !   | #
#########

Un resultado apropiado sería:

Required Effort: 23
Step Left
Step Left
Step Left
Kick Left
Punch Left
Step Left
Step Left
Step Left
Step Left

La probabilidad de éxito aquí es 76.5%.

Para el mismo mapa, pero con una probabilidad mínima de éxito del 80%, debería "poner su espalda en él", lo que requeriría más esfuerzo pero cumpliría los criterios de probabilidad de éxito. Si la probabilidad mínima de éxito fuera mayor al 80%, necesitaría pensar un poco más (golpear o patear una parte de la puerta y arrastrarse). Si la probabilidad mínima de éxito fuera del 100%, tendría que imprimir "¡No hay esperanza!"

Ejemplos

Es posible que haya más de un plan válido para una entrada, su salida no necesita ser exactamente, pero debe tener el esfuerzo requerido correcto y ser un plan válido. Puede usar el verificador para verificar sus soluciones (ver más abajo)

Entrada:

100
o
|

Salida:

Required Effort: 0
Drop

Entrada:

5
# =      #
# =      !
# = !  ! !
# =#######
# =      #
# =   o  #
# = ! |  #
##########

Salida:

Required Effort: 57
Step Left
Kick Left
Step Left
Step Left
Step Left
Climb Up
Climb Up
Climb Up
Climb Up
Climb off Right
High Jump Right
Long Jump Right
Step Right
Drop
Kick Right
Crouch
Shuffle Right
Shuffle Right

Entrada:

60
#########
# ! #   #
! ! ! o #
! # ! | #
#########

Salida:

Required Effort: 58
Step Left
Kick Left
Crouch
Shuffle Left
Shuffle Left
Stand
Punch Left
Clamber Up Left
Shuffle Left
Drop (Stand)
Kick Left
Crouch
Shuffle Left
Shuffle Left

Para el mismo mapa, pero 80%, la salida debería ser:

There is no hope!

Para el mismo mapa, pero 50%, el esfuerzo requerido se convierte en 56 con un plan diferente)

Entrada:

50
#######################
#          #     =    #
!          #     =    !
#          #     =    #
######  #####!## =### #
#=   ##       #  =    #
#=#############  =    #
#=               =    #
#= o             =    #
#!!|             =    #
#######################

Salida:

Required Effort: 121
Step Right
Step Right
Step Right
Step Right
Step Right
Step Right
Step Right
Step Right
Step Right
Step Right
Step Right
Step Right
Step Right
Step Right
Climb Up
Climb Up
Climb Up
Climb Up
Climb Up
Climb Up
Climb off Right
Long Jump Left
Step Left
Step Left
Stamp
Drop
Drop
Crouch
Shuffle Left
Shuffle Left
Shuffle Left
Shuffle Left
Shuffle Left
Shuffle Left
Stand
Clamber Up Left
Stand
Clamber Up Left
Stand
Step Left
Step Left
Step Left
Step Left
Punch Left
Clamber Up Left
Shuffle Left

Entrada:

66
######
#  ###
#o ! !
#| ! !
### ##
######

Salida:

Required Effort: 37
Step Right
Put your back into it! Right
Kick Right
Crouch
Shuffle Right
Shuffle Right

Entrada:

Este está diseñado para verificar una serie de suposiciones falsas de las que uno puede ser víctima y, en consecuencia, puede parecer un poco extraño

30
###################
# ## # # #   #  = #
! ## #   #   #  = #
#      #   #    = #
##  ############= #
# ## #         #= #
# =  #         #= #
! =  #         #= #
# =  #         #= #
#o=  !          ==#
#|=  !           =#
#!= # ==########==#
#   #    !   !!  =#
#   #    !!  !  = #
#   # !!!!#########
#   # #           #
#   # #           #
###################

Salida con posibilidad de éxito restricción 30:

Required Effort: 199
Long Jump Right
Put your back into it! Right
<snip>

Salida con posibilidad de éxito restricción 32:

Required Effort: 200
Long Jump Right
Punch Right
<snip>

Usando el mapa en la parte superior como entrada, con una probabilidad de restricción de éxito del 1%, debe obtener un esfuerzo requerido de 116 (posibilidad de éxito ~ 32%, esto tardó unos 20 segundos en ejecutarse en mi programa de prueba).

Criterios de victoria

Este es el código de golf, que gane el código más corto.

Para ser elegible, su función o programa debe funcionar y poder resolver cada uno de los casos de prueba anteriores en menos de 30 minutos en una máquina razonable. Mi solucionador los hace cada uno en menos de 30 segundos. Si la secuencia de comandos de prueba (a continuación) se ejecuta en menos de 30 minutos, seguramente está listo para comenzar.

Ejemplo de Solver, Verifier, Test Script y TestCases (con soluciones)

El código C # para un solucionador y un verificador de solución está disponible aquí como una idea general . La esencia también contiene file.txt, que es una forma legible por máquina (suficiente) de las acciones descritas anteriormente, y es necesaria para que el programa se ejecute. Cualquier discrepancia entre ese archivo y la especificación no es intencional.

La esencia también contiene una serie de casos de prueba (incluidos todos los ejemplos anteriores) y un script de PowerShell para ejecutar automáticamente un envío contra ellos. Si el script le dice que una prueba en particular ha fallado, puede ejecutarla OfficeEscapeSolver.exe testcase<n>.txt outputFromYourProgram.txtpara ver más detalles. Ejemplos de soluciones para estos casos de prueba están en otro Gist .

Todo el código es un completo desastre (aunque no está protegido), pero no debería tener que alejarse mucho static void Main(...)para cambiar la cantidad de salida (no dude en usar esta información, ¡la he proporcionado para su beneficio!).

Pasar un caso de prueba no significa necesariamente que su salida esté bien formada, ya que el script y el verificador son muy generosos. Su salida debe coincidir con la especificación anterior para que su envío sea válido.

Si crees que has encontrado un error con el solucionador o el script de prueba, un error file.txto un caso de prueba dudoso, por favor comenta en esta publicación o envíame un mensaje en el Chat SE; Probablemente no notaré ningún otro intento de comunicarme.

No proporcionaré el script de prueba en Bash o Batch, porque no los conozco, pero estoy feliz de incluir una traducción y escribiré una versión de C # si la gente quiere una.

Post Amble

¿Tienes preguntas? ¡No se demore, pregúnteles hoy!

Esta tarea está destinada a requerir esfuerzo , para dar a los golfistas serios algo en lo que hundir sus dientes.

Mi agradecimiento a ais523 por sus comentarios sobre Entrada / Salida.

Puedo proporcionar más casos de prueba en el archivo GIS si la gente quiere más (no quiere que esta publicación se vuelva más larga), o si desea proporcionar algunos de los suyos.

VisualMelon
fuente
2
¡Gran reto! Definitivamente voy a darle una oportunidad cuando tenga tiempo. :)
R. Kap
Ya sabes, los peligros de caer (o más bien la desaceleración rápida en la parte inferior) podrían haberse modelado al darle a la transición de caída una probabilidad de aproximadamente 95%. ;) ¡Buen desafío!
Martin Ender
¿Puedes escapar a través de una luz del cielo o túneles? es decir, arriba o abajo del campo? o solo izquierda o derecha?
Moogie
@Moogie, de hecho puedes! Siempre y cuando sus pies estén libres, usted es libre (por ejemplo, vea el caso de prueba 1x2, donde la solución simplemente se cae una vez). Agregaré un pequeño caso de prueba a la esencia para probar que sale del techo.
VisualMelon
3
Recompensa añadida a la pregunta. Esta es una gran pregunta que merece respuestas.
programador

Respuestas:

3

Perl 5, 1425 1464 1481 1469 1485 1438 bytes

¡Ese fue un desafío divertido! Y, sorprendentemente, ¡parece que tengo el código más corto hasta ahora en un poco menos de 1.5kB! :)

Estoy bastante seguro de que finalmente funcionó. El delimitador utilizado es :, y hay relleno adicional de dos filas en la parte superior y una columna a cada lado . Entonces, para su aporte de:

60
#########
# ! #   #
! ! ! o #
! # ! | #
#########

mi programa tomaría: (Nota: debe haber dos puntos al final, ¡pero no al principio!)

60
#########:# ! #   #:! ! ! o #:! # ! | #:#########:

Utilizo expresiones regulares para traducir de un mapa a otro y resolver por fuerza bruta. Sin embargo, no agrego mapas a la lista si ese mapa ya se ha alcanzado con menos esfuerzo y mayor o igual probabilidad. Los mapas se eliminan de la lista una vez que todos los movimientos posibles desde ese punto se han agotado. El programa finaliza con éxito cuando coincide con una expresión regular que muestra que he llegado al costado o al final y termina con "¡No hay esperanza!" cuando la lista de mapas está agotada.

Desafortunadamente, hubo una gran cantidad de eficiencia intercambiada para quitar algunos bytes, por lo que la versión de golf es bastante lenta;) Perl parece encontrar que las evaluaciones en particular son bastante dolorosas.

Sin más preámbulos,

chop($P=<>,$_=<>);s/:/ : /g;$w++until/:.{$w}:/;$z=$"x$w;$_="(.{".$w--."})"for($c,$h,$i,$j);$_="$z:$z: $_$z";$n="[ =]";$d="([!#])";@s=split/,/,'Drop,Drop (Stand),Stepx,Climb offx,Shufflex,Clamber Upx,Put your back into it!x,Punchx,Kickx,Short Jumpx,Long Jumpx,High Jumpx,Crouch,Stand,Climb Up,Stamp';@c=split/,/,"o$c\\|$c$n/ \$1o\$2|,%$c$n/o\$1|,o$n$h\\|$n$h${d}/ o\$1 |\$2\$3,=${c}o$n$h\\|$n$h=/=\$1=o\$2=|\$3=,%$n$h$d/ %\$1\$2,o$n$h\\|${d}$h${d}/ %".'$1 $2$3$4,'."o!$n$i\\|!$n$i${d}/  o\$1  |\$2\$3,o!$h\\|$c$d/o \$1|\$2\$3,\\|!$h$d/| \$1\$2,o$n$n$i\\|$n$n$i${d}/  o\$1  |\$2\$3,o$n$n$n$j\\|$n$n$n$j${d}/   o\$1   |\$2\$3,$n$n${h}o$n$h\\|$c$d/ o".'$1 |$2 $3$4,'."o$c\\|$c${d}/ \$1%\$2\$3,$n$c%$c${d}/o\$1|\$2\$3,=${c}o$c\\|/o\$1|\$2=,\\|$c!/|\$1 ";eval"*$_=sub{\$Q=\"$s[$_]\";s/$c[$_]/}"for(0..15);sub M{$_=join":",map{join'',reverse split//}split/:/};push@M,[$_,0,100,$P];$B{$_}=100;@E=(0,0,1,1,3,10,20,8,8,4,7,12,2,4,2,8);@P=map{100-$_}(0,0,0,0,0,5,20,10,15,5,25,10,0,0,0,10);$z='$N=[$_,$G+0,$t,$t[3]*100,"$t[4]$Q\n"];$N->[4]=~s/x/';do{sub A{@N=@$N;if($N[2]/$N[3]>$B{$N[0]}){push@M,$N;$B{$N[0]}=$N[2]/$N[3]}};die"There is no hope!\n"if(!(@M=grep$G-$_->[1]<21,@M));$e=-1;while(++$e<@M){@t=@{$M[$e]};$m=$_=$t[0];die"Required Effort: $t[1]\n$t[4]"if(/([|%]:|:[|%])/||/[|%][^:]*$/||/^$c:[^:]*[%|]/);for$x(0..15){$_=$m;$t=$t[2]*$P[$x];if($G==$E[$x]+$t[1]and$t>=$t[3]*100){&$x;eval"$z Right/";A;$_=$m;M;&$x;M;eval"$z Left/";A;}}}}

En aras de la cordura, esto es lo mismo con las nuevas líneas y algunos comentarios:

chop($P=<>,$_=<>); #Take input
s/:/ : /g; #Pad columns on either side so escapee can leave that way
$w++until/:.{$w}:/; #Find width
$z=$"x$w;#Setup a blank line for use in padding later
$_="(.{".$w--."})"for($c,$h,$i,$j); #Set up some generic capturing regexes for reuse
$_="$z:$z: $_$z"; #Pad the top and bottom so the escapee can leave those ways
$n="[ =]"; #Regex for nonsolid block
$d="([!#])"; #Regex for solid block
@s=split/,/,'Drop,Drop (Stand),Stepx,Climb offx,Shufflex,Clamber Upx,Put your back into it!x,Punchx,Kickx,Short Jumpx,Long Jumpx,High Jumpx,Crouch,Stand,Climb Up,Stamp'; #Movement names
@c=split/,/,"o$c\\|$c$n/ \$1o\$2|,%$c$n/o\$1|,o$n$h\\|$n$h${d}/ o\$1 |\$2\$3,=${c}o$n$h\\|$n$h=/=\$1=o\$2=|\$3=,%$n$h$d/ %\$1\$2,o$n$h\\|${d}$h${d}/ %".'$1 $2$3$4,'."o!$n$i\\|!$n$i${d}/  o\$1  |\$2\$3,o!$h\\|$c$d/o \$1|\$2\$3,\\|!$h$d/| \$1\$2,o$n$n$i\\|$n$n$i${d}/  o\$1  |\$2\$3,o$n$n$n$j\\|$n$n$n$j${d}/   o\$1   |\$2\$3,$n$n${h}o$n$h\\|$c$d/ o".'$1 |$2 $3$4,'."o$c\\|$c${d}/ \$1%\$2\$3,$n$c%$c${d}/o\$1|\$2\$3,=${c}o$c\\|/o\$1|\$2=,\\|$c!/|\$1 ";#Movement regexes
eval"*$_=sub{\$Q=\"$s[$_]\";s/$c[$_]/}"for(0..15); #Setup methods to apply regexes. Name of these methods are 0,1,2,3, etc, so we can easily call them in a loop
sub M{$_=join":",map{join'',reverse split//}split/:/}; #Method to mirror the map. All the regexes are right-moving, so the mirror effects are achieved by M;&$x;M
push@M,[$_,0,100,$P]; #Array for initial map position. Encodes: [Map,Effort value,Probability value 1,Probability value 2,Movements(initially undef)]. Two integers are used for the probability to avoid floating point (although after enough steps they overflow and are automatically converted to f.p.)
$B{$_}=100; #Hash map to hold best probability of reaching a map. A new map is never added if it requires at least as much effort and doesn't give a better probability.
@E=(0,0,1,1,3,10,20,8,8,4,7,12,2,4,2,8); #Effort values
@P=map{100-$_}(0,0,0,0,0,5,20,10,15,5,25,10,0,0,0,10); #Probability values
$z='$N=[$_,$G+0,$t,$t[3]*100,"$t[4]$Q\n"];$N->[4]=~s/x/'; #Setup map values
do{ #Loop over all efforts. Do-while loop starts at undef, which is converted to zero automatically when used in a numeric context
    sub A{@N=@$N;if($N[2]/$N[3]>$B{$N[0]}){push@M,$N;$B{$N[0]}=$N[2]/$N[3]}}; #Method to add a map to list.
    die"There is no hope!\n"if(!(@M=grep$G-$_->[1]<21,@M)); #Pares away maps that no longer can produce new maps, and prints "There is no hope!" to stderr if there are no maps left.
    $e=-1;
    while(++$e<@M){ #Loop over all maps. Note that this loops over even maps that are created during the loop
        @t=@{$M[$e]}; #Dereference info about each map
        $m=$_=$t[0]; $Setup map variables
        die"Required Effort: $t[1]\n$t[4]"if(/([|%]:|:[|%])/||/[|%][^:]*$/||/^$c:[^:]*[%|]/); #Checks if escaped, and gives message if so.
        for$x(0..15){
            $_=$m; #Put map into $_
            $t=$t[2]*$P[$x]; #Probability calculation
            if($G==$E[$x]+$t[1]and$t>=$t[3]*100){ #If effort values are right and probability is over threshold
                &$x; #Run regex
                eval"$z Right/"; #Set up map info
                A; #Add map to list @M (only if probability works out right)
                $_=$m;
                M;&$x;M; #Same thing, but mirrored now (generates movement left)
                eval"$z Left/";
                A;
            }
        }
    }
}while(++$G)

Ya puedo ver un par de lugares para eliminar algunos bytes más, pero todavía no quiero volver a hacer todas las pruebas. ¡Más tarde! :)

Editar: se agregó un poco de relleno a continuación para que se ajuste más exactamente a su salida al escapar por el piso. Se eliminaron algunas de las evaluaciones, por lo que el código también podría ejecutarse más rápido ahora.

Editar: No estaba manejando escaleras y caídas del todo bien. Todavía no maneja las escaleras del todo bien ... Tratando de arreglar eso ahora.

Chris
fuente
Me alegra que alguien más se divierta con eso. Me temo que no puedo aceptar esto como está actualmente, ya que no puede suponer que la entrada tiene esas filas o relleno adicionales en la parte superior (pero a ~ 1.5kB, simplemente insertar de inmediato no debería doler ¡demasiado!). No tengo Perl en esta máquina, pero intentaré encontrar una forma de probar esto hoy, para poder comprobar que funciona y se ejecuta en un marco de tiempo razonable, ¡e informar de nuevo!
VisualMelon
1
@VisualMelon No hay problema, cambié el método de entrada y agregué el relleno manualmente. Tiende a explotar en los rompecabezas más grandes, pero se ejecuta en un marco de tiempo razonable para la mayoría de los casos de prueba.
Chris
Todavía no probé esto, pero noté que dijiste que esto usa una expresión regular para detectar cuándo te alejas del costado o de la parte inferior, pero también puedes escapar de la parte superior (ver caso de prueba10 que se agregó para este propósito), en caso de que te hayas perdido esto
VisualMelon
1
@VisualMelon Ah, supuse que para escapar del techo el fugitivo tendría que subir a la habitación y luego caminar hacia un lado, fuera del techo. Ahora veo la oración relevante. Lo arreglaré :)
Chris
2
¿Podría agregar un enlace TIO?
programador
3

C #, 1814 1481 1395 bytes

¡Qué tarea! ¡Estoy realmente bastante satisfecho con esto ahora!

using C=System.Console;using System.Linq;class S{string M,N="";int x,y,E,c;decimal P=1;static void Main(){int W=0,H=0,x,i,j,k,X,Y,f,m,P=int.Parse(C.ReadLine());string l,M="",B,R="There is no hope!";for(;(l=C.ReadLine())!=null;H++,M+=l)W=l.Length;x=M.IndexOf("|");var D=new[]{new S{M=M,x=x%W,y=x/W}}.ToList();for(var N=D.ToDictionary(s=>R,s=>D);D.Count>0;D.Sort((z,w)=>z.E-w.E)){S s=D[f=0];D.Remove(s);var n=N[l=s.x+s.M+s.y+s.c]=N.ContainsKey(l)?N[l]:new S[0].ToList();if(n.All(o=>o.P<s.P|o.E>s.E)){n.Add(s);X=s.x;Y=s.y;if((X|Y|-X/W-Y/H)<0){R="Required Effort: "+s.E+s.N;break;}for(var A="0110d,Step,*** * #*,0110d,Climb off,=** * =*,3310d,Shuffle,***** #*,2:1/_,Clamber Up,*** *##*,0420_,Short Jump,****  *  #**,0730K,Long Jump,*****   *   #***,0<1/Z,High Jump,  * **#*,0D20P,Put your back into it!,****! *! #**,0800Z,Punch,***!**#*,0800U,Kick,*****!#*,0001d,Drop,*** ,1001d,Drop (Stand),*** ,2200d,Crouch,***#,1400d,Stand,* *#,020/d,Climb Up,=***,0800Z,Stamp,***!".Split(',');f<26;){l=A[k=f*3%48];B=A[++k]+(f>15?" Right":f>9?"":" Left");M=A[++k];m=f++/16*2-1;var Q=s.M.ToArray();var K=s.P*l[4]>=P&s.c==l[k=0]%2;for(j=Y-3;j++<=Y;)for(i=X;i!=X+m*M.Length/4;i+=m)if((K&="==  *!!##*!*=*|*| o*o ".Contains(""+((i|j)>=0&j<H&i<W?Q[x=j*W+i]:' ')+M[k]))&M[k++]==33)Q[x]=' ';if(K)D.Add(new S{M=new string(Q),N=s.N+"\n"+B,x=X+l[2]%48*m,y=Y+l[3]-48,c=l[0]/50,E=s.E+l[1]-48,P=s.P*l[4]/100});}}}C.Write(R);}}

Pruébalo en línea

Programa completo, toma entrada a STDIN, salida a STDOUT. Esencialmente, una reescritura de mi solucionador original, utilizando un BFS simple e ineficientemente implementado para encontrar la ruta óptima. Es razonablemente rápido, no puede ser mucho más lento que mi otra implementación (aunque realmente no lo he cronometrado), ciertamente se ejecuta dentro del límite de tiempo. Gran parte del costo es la tabla de acciones, que está codificada como valores separados por comas que registran el nombre, el mapa 'match / smash' y otras propiedades de cada acción disponible.

Comienza leyendo en la probabilidad mínima de éxito y el mapa. Luego localiza al fugitivo, lo elimina del mapa y crea un "estado de búsqueda" que contiene esta información. Luego, realiza el BFS, que observa repetidamente el siguiente estado debido con el menor esfuerzo (asegurando que encuentre una solución óptima). Antes de expandir el nodo, compara su esfuerzo y probabilidad de éxito con los nodos anteriores con el mismo mapa y ubicación de escape, y se rechaza si ya se ha encontrado una mejor ruta a este estado. Si sobrevive a esto, se agrega a la tabla 'visto' para que pueda rechazar el estado más adelante. Todo esto es por rendimiento, y sin él el factor de ramificación sería enorme. Luego verifica si el fugitivo está fuera del mapa. Si es así, ¡entonces gana! Realiza un seguimiento del estado (cada estado anterior se registra con cada estado) y construye el plan (a la inversa) antes de salir del bucle BFS. De lo contrario, intenta aplicar todas las acciones y agrega las que se pueden aplicar adue cola, antes de ordenar esta cola para que obtengamos el estado de menor esfuerzo en la próxima iteración.

Código formateado y comentado:

using C=System.Console;
using System.Linq;

// ascii
//   32
// ! 33
// = 61
// . 46
// * 42
// # 35
// | 124
// 0 48

class S // SearchState
{
    string M, // map
        N=""; // plan
    int x, // pos x
        y, // pos y
        E, // effort
        c; // crouching?
    decimal P=1; // chance (Probability)

    static void Main()
    {
        int W=0, // map width
            H=0, // map height
            x, // various temps
            i, // local x
            j, // local y
            k, // position in match/smash map
            X, // s.x
            Y, // s.y

            // action loop
            f, // T idx
            m, // A idx, action mirror (direction)

            P=int.Parse(C.ReadLine()); // probability of success constraint

        string l, // line, Act 'block' params, map hash, match pairs; all sorts!
            M="", // initial map
            B, // name of action
            R="There is no hope!"; // result string

        // read map
        for(;(l=C.ReadLine())!=null; // while there is input to read
            H++,M+=l) // increment height, and append to M
            W=l.Length; // record width

        // detect the escapee
        x=M.IndexOf("|");

        // create first state, and add it to Due list
        var D=new[]{new S{M=M,x=x%W,y=x/W}}.ToList();

        // 'seen' table, stores all the states we've been in which looked similar
        for(var N=D.ToDictionary(s=>R,s=>D); // these values are meaningless (and necessarily can't interfere), we just use it to save having to spec the type
            D.Count>0; // keep going until we run out of states to expand (-> no escape)
            D.Sort((z,w)=>z.E-w.E)) // enforce Breadth First Search
        {
            // get next State to expand, and remove it from Due
            S s=D[f=0];
            D.Remove(s);

            // retrieve or create seen list
            var n=N[l=s.x+s.M+s.y+s.c]= // store it, and cache it, l is 'hash', containing map and escapee state
                N.ContainsKey(l)? // already got a seen list for ths map?
                N[l]: // then use it!
                new S[0].ToList(); // otherwise create a new (empty) one

            // perf: only proceed if we havn't seen this map with better Effort and Chance yet
            if(n.All(o=>o.P<s.P|o.E>s.E))
            {
                // record that we have been seen
                n.Add(s);
                X=s.x;
                Y=s.y;

                if((X|Y|-X/W-Y/H)<0) // check if we our outside the bounds - this took 2.5hour to write. 1 byte.
                { // quit if both are positive or both are negative
                    // freedom
                    R="Required Effort: "+s.E+s.N;

                    // finished
                    break;
                }

                // [Crouch,Effort,dx,dy,Probability],Name,MatchSmash (first 10 are mirrors)
                // 0110d,Step,*** * #*,
                // 0110d,Climb off,=** * =*,
                // 3310d,Shuffle,***** #*,
                // 2:1/_,Clamber Up,*** *##*,
                // 0420_,Short Jump,****  *  #**,
                // 0730K,Long Jump,*****   *   #***,
                // 0<1/Z,High Jump,  * **#*,
                // 0D20P,Put your back into it!,****! *! #**,
                // 0800Z,Punch,***!**#*,
                // 0800U,Kick,*****!#*,
                // 0001d,Drop,*** ,
                // 1001d,Drop (Stand),*** ,
                // 2200d,Crouch,***#,
                // 1400d,Stand,* *#,
                // 020/d,Climb Up,=***,
                // 0800Z,Stamp,***!

                // attempt to expand this node with every action
                for(var A="0110d,Step,*** * #*,0110d,Climb off,=** * =*,3310d,Shuffle,***** #*,2:1/_,Clamber Up,*** *##*,0420_,Short Jump,****  *  #**,0730K,Long Jump,*****   *   #***,0<1/Z,High Jump,  * **#*,0D20P,Put your back into it!,****! *! #**,0800Z,Punch,***!**#*,0800U,Kick,*****!#*,0001d,Drop,*** ,1001d,Drop (Stand),*** ,2200d,Crouch,***#,1400d,Stand,* *#,020/d,Climb Up,=***,0800Z,Stamp,***!".Split(','); // Act string
                    f<26;) // read A into T // (cycle over first 10 twice, making them mirrors)  (very convieniently, 3*16==48=='0'==x!)
                {
                    // 'parse' next action
                    l=A[k=f*3%48]; // [Effort,Crouch,dx,dy,Probability]
                    B=A[++k]+(f>15?" Right":f>9?"":" Left"); // action name
                    M=A[++k]; // Match and Smash table (4x?, escapee at 0,2, #: solid, !: smashable (and is smashed), .: empty,  : don't care, =: climable)
                    //c=l[1]-48; // crouching transition (0: standing -> standing, 1: crouching -> standing, 2: standing -> crouching, 3: crouching -> crouching)
                    m=f++/16*2-1;

                    // this will be the new map
                    var Q=s.M.ToArray();

                    // K is whether we are allowed to apply this action
                    var K=s.P*l[4]>=P& // within chance limit
                        s.c==l[k=0]%2; // crouching matches

                    // compare the map to the Match/Smash map, to make sure we can apply this transform (and smash any ! we meet)
                    for(j=Y-3;j++<=Y;) // for the 4 rows
                        for(i=X;i!=X+m*M.Length/4;i+=m) // for each column (a.M has height 4, but variable width)
                            if((K&= // K still true?
                                "==  *!!##*!*=*|*| o*o ".Contains(""+ // check for an allowed pairing (cache pairing in M) (this includes the escapee, much cheaper to do this than remove him)
                                    ((i|j)>=0&j<H&i<W?Q[x=j*W+i]:' ') // we are within bounds and match, we are out of bounds (empty)
                                    +M[k])) // char from MatchSmash map
                                &M[k++]==33) // if it is destructible ('!' == 33)
                                Q[x]=' '; // then blank it out (x is necessarily set by the cell check)

                    if(K) // if K holds
                        D.Add(new S{M=new string(Q),N=s.N+"\n"+B, // assemble plan as we go (this will chew up memory, but none of the problems are big enough for it to matter)
                            x=X+l[2]%48*m,y=Y+l[3]-48,c=l[0]/50,E=s.E+l[1]-48,P=s.P*l[4]/100}); // add the resulting state to Due
                }
            }
        }

        C.Write(R);
    }
}
VisualMelon
fuente
¡Buen trabajo! Me divierte infinitamente que su puntaje anterior y el nuevo puntaje sean anagramas el uno del otro ... jajaja
HyperNeutrino
¿C # ha vencido a Perl?
Esolanging Fruit