Office Escape: planeje sua saída!


32

É o sprint final ... e metade do seu time está doente. Você está trabalhando até tarde, apenas fazendo sua última confirmação do dia, esperando ... por que as luzes se apagaram? Não me lembro do cara da segurança por aí ... oh não! Deixei minhas chaves em casa!

À medida que o horror da situação afunda, você decide que vai escapar .

Resumo da tarefa

Para efetuar sua fuga, você precisa de um plano! No entanto, você sabe que qualquer plano tem uma chance de falhar, e planos diferentes exigem quantidades diferentes de esforço.

Sendo faminto, cansado e engenheiro, você decide escrever um pequeno programa para determinar a melhor maneira de escapar do complexo, equilibrando as preocupações de probabilidade de sucesso e o esforço necessário.

Você faz um mapa do edifício:

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

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

Para escapar do escritório, você terá que sair do mapa. Aqui você vê que existem 2 janelas ( !), qualquer uma delas levaria você à liberdade, mas apenas uma delas acessível. Definimos 'fora do mapa' como tendo os pés fora dos limites do 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

As células originalmente consumidas pelo fugitivo são consideradas vazias.

Especificações de ação

Você tem várias ações possíveis à sua disposição. Eles são definidos por transições de estado simples com alguma probabilidade de sucesso inteiro. Por exemplo, para caminhar, você move uma célula do fugitivo, que representamos com esta transição:

Degrau

1 stp, 100%, espelho

 o.            o
 |.   -->      |
 #            #

Os pontos mostram células que devem estar vazias (ou escaláveis, mas não sólidas ou destrutíveis), porque entramos nela ou através dela. O 100% significa que você tem 100% de chance de não se machucar e terminar sua fuga ousada. Todas as probabilidades serão porcentagens inteiras entre 1% e 100%, inclusive. O primeiro diagrama mostra o estado inicial (parado em algo sólido, próximo a algum espaço vazio). O segundo diagrama mostra o estado do terminal (movido para o espaço vazio). Não há requisitos para que células não especificadas (espaços ) à esquerda (estado inicial) sejam algo em particular. Células não especificadas (espaço,) à direita (estado terminal) deve ser o mesmo de antes (por exemplo, o que estiver atrás do fugitivo ou o que eu estiver andando (seja um espaço vazio ou outro)). ) os diagramas mudam apenas as células de destrutíveis para vazias, nenhuma outra alteração pode ocorrer. "1 stp" significa que custa 1 stp: definimos o "stp" como a quantidade de energia necessária para dar um passo.

"espelho" significa que esta ação tem duas formas. A ação "direita" é mostrada, a ação "esquerda" é um espelho exato, por exemplo:

.o
.|
 # 

é a forma espelho (esquerda) de

o.
|.
# 

A ação direita é chamada "Direita" (por exemplo, "Passo à direita"). A ação esquerda é chamada "Esquerda" (por exemplo, "Etapa à esquerda")

Nestes diagramas, o fugitivo é mostrado por

o
|

em pé (2 unidades de altura) e

%

quando agachado (1 unidade de altura). As células que devem ser sólidas ou destrutíveis são indicadas por um hash #,. As células que não devem ser sólidas ou destrutíveis são indicadas por um ponto .. As células que devem ser destrutíveis são indicadas por um estrondo !. Um espaço vazio recém-criado é mostrado por um sublinhado _. xé um ponto de referência que não se move (não existe, nenhuma restrição sobre o que essa célula deve ser (como um espaço )).

Nota: ignoramos a questão da desaceleração rápida quando você bate no chão e, sim, neste jogo você pode fazer saltos épicos totalmente em escadas)

Degrau

1 stp, 100%, espelho

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

Descer

1 stp, 100%, espelho

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

Aleatório

3 stp, 100%, espelho

 %.         %
x#   -->  x# 

Subir

10 stp, 95%, espelho

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

Solta

0 stp, 100%

 o         
 |  -->   o
x.       x|

Drop (Suporte)

0 stp, 100%

 %        o
x.  -->  x|

Escalar

2 stp, 100%

 =        o
 o  -->   |
x|       x

Crouch

2 stp, 100%

 o
 |  -->   %
x#       x#

Ficar de pé

4 stp, 100%

 .        o
 %  -->   |
x#       x#

Salto curto

4 stp, 95%, espelho

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

Salto em comprimento

7 pts, 75%, espelho

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

Pulo alto

12 stp, 90%, espelho

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

Coloque as costas para ele!

20 stp, 80%, espelho

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

Soco

8 pts, 90%, espelho

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

Pontapé

8 pts, 85%, espelho

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

Carimbo

8 stp, 90%

 o        o
 |  -->   |
x!       x_

Planos

Um plano é uma sequência das ações definidas acima. Por exemplo:

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

Observe a inclusão das gotas. As regras devem ser definidas para impedir que você faça qualquer coisa, menos cair, mas você ainda precisa planejar!

Qualquer plano tem um esforço necessário, que é a soma dos esforços para cada etapa. Há também uma probabilidade de sucesso, que é o produto das probabilidades de sucesso de cada ação. Exemplo simples:

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%

Um 'exemplo trabalhado' para o mapa na parte superior da página pode ser encontrado como uma essência .

Entrada

A entrada vem em duas partes, um número inteiro e uma matriz ou sequência de caracteres.

O número inteiro é a probabilidade mínima de sucesso aceitável (em porcentagem). Ele deve ser interpretado como uma porcentagem, portanto 80 significa que seu plano deve ter sucesso com probabilidade não inferior a 80%.

Um mapa válido é um retângulo que inclui o escapado em pé (tamanho mínimo de 1x2) e nenhum símbolo não especificado. Todas as linhas terão o mesmo comprimento. Você pode aceitar a entrada como uma sequência delimitada unidimensional (o delimitador deve ser um único caractere consistente ou um dos pares CRLF e LFCR), como uma matriz unidimensional semelhante ou uma matriz bidimensional. Se o formato de entrada escolhido não indicar a largura ou a altura do mapa de alguma forma, você poderá aceitar argumentos adicionais para eles (você deve declarar isso claramente em sua resposta). Você pode combinar argumentos de linha de comando e entrada padrão, se for adequado (por exemplo, mapa de stdin, probabilidade mínima de sucesso de argv). A seguir, exemplos de mapas válidos e inválidos.

Válido:

o
|

Válido:

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

Inválido (sem fugitivo):

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

Inválido (o fugitivo sempre começa em pé):

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

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

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

Inválido (não é um retângulo / linhas de comprimento diferente):

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

Você pode assumir que sua entrada é válida (não me importo com o que o seu programa faz se receber uma entrada inválida).

Saída

A saída é texto (ASCII). Pode ser retornado como uma sequência ou impresso na saída padrão. O plano deve ser delimitado por um LF, CRLF ou LFCR. Da mesma forma, deve haver outro LF, CRLF ou LFCR após o esforço necessário. Uma quebra de linha à direita é opcional.

Você deve produzir um plano ideal junto com o esforço necessário, ou "Não há esperança!" se esse plano não existir. Você não precisa gerar a probabilidade de sucesso. Este texto pode ou não ter uma quebra de linha à direita.

Um plano ideal é definido como um plano (veja acima) que exige o mínimo de esforço com pelo menos a probabilidade de sucesso especificada. Observe que os cálculos de probabilidade devem ser exatos; você não pode assumir que o ponto flutuante é bom o suficiente (é por isso que não espero que você os produza). Tentarei projetar casos de teste para testar isso razoavelmente (se você passar por eles e não fizer nenhuma suposição idiota, poderá considerar seu envio válido).

Formato:

Required Effort: <effort>
<plan>

Por exemplo, para a entrada

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

Uma saída apropriada seria:

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

A probabilidade de sucesso aqui é de 76,5%.

Para o mesmo mapa, mas com uma probabilidade mínima de sucesso de 80%, você teria que "Colocar as costas nele", o que exigiria mais esforço, mas atenderia aos critérios de probabilidade de sucesso. Se a probabilidade mínima de sucesso for maior que 80%, você precisará pensar um pouco mais (dar um soco ou chutar parte da porta e sair). Se a probabilidade mínima de sucesso fosse de 100%, você teria que imprimir "Não há esperança!"

Exemplos

É possível que haja mais de um plano válido para uma entrada, sua saída não precise ser exatamente isso, mas deve ter o esforço necessário correto e ser um plano válido. Você pode usar o verificador para verificar suas soluções (veja abaixo)

Entrada:

100
o
|

Saída:

Required Effort: 0
Drop

Entrada:

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

Saída:

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 #
! # ! | #
#########

Saída:

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 o mesmo mapa, mas 80%, a saída deve ser:

There is no hope!

Para o mesmo mapa, mas 50%, o esforço necessário se torna 56 com um plano diferente)

Entrada:

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

Saída:

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 ! !
#| ! !
### ##
######

Saída:

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

Entrada:

Esta foi projetada para verificar uma série de suposições falsas às quais podemos ser vítimas e, consequentemente, parecer um pouco estranhas

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

Saída com restrição de chance de sucesso 30:

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

Saída com restrição de chance de sucesso 32:

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

Usando o mapa na parte superior como entrada, com probabilidade de restrição de sucesso de 1%, você deve obter um Esforço necessário de 116 (chance de sucesso de ~ 32%, isso levou cerca de 20 segundos para ser executado no meu programa de teste).

Critérios de vitória

Este é o código-golfe, que ganhe o menor código.

Para ser elegível, sua função ou programa deve funcionar e ser capaz de resolver cada um dos casos de teste acima em menos de 30 minutos em uma máquina razoável. Meu solucionador faz cada um em menos de 30 segundos. Se o script de teste (abaixo) for executado em menos de 30 minutos, você certamente estará pronto.

Exemplo Solver, Verificador, Script de Teste e TestCases (com soluções)

O código C # para um solucionador e verificador de solução está disponível aqui como uma essência . A essência também contém file.txt, que é uma forma legível por máquina (suficiente) das ações descritas acima e é necessária para a execução do programa. Qualquer discrepância entre esse arquivo e as especificações não é intencional.

A essência também contém vários casos de teste (incluindo todos os exemplos acima) e um script do PowerShell para executar automaticamente um envio contra eles. Se o script indicar que um teste específico falhou, você poderá executar OfficeEscapeSolver.exe testcase<n>.txt outputFromYourProgram.txtpara ver mais detalhes. Soluções de exemplo para esses casos de teste estão em outra Gist .

Todo o código é uma bagunça completa (embora não seja destruída), mas você não precisa se afastar muito static void Main(...)para alterar a quantidade de saída ( fique à vontade para usar essas informações, forneci-as para seu benefício!).

Passar em um caso de teste não significa necessariamente que sua saída esteja bem formada, pois o script e o verificador são muito generosos. Sua saída deve corresponder à especificação acima para que seu envio seja válido.

Se você acha que encontrou um bug com o solucionador ou o script de teste, um erro file.txtou uma caixa de teste desonesta, por favor, comente esta postagem ou faça um ping no SE Chat; Provavelmente não notarei nenhuma outra tentativa de comunicação.

Não fornecerei o script de teste no Bash ou no Lote, porque não os conheço, mas fico feliz em incluir uma tradução e escreverei uma versão em C # se as pessoas quiserem.

Post Amble

Tem perguntas? Não demora, pergunte a eles hoje!

Esta tarefa deve exigir esforço , dar aos jogadores sérios algo para afundar os dentes.

Meus agradecimentos a ais523 por seu feedback sobre entrada / saída.

Eu posso fornecer mais casos de teste no arquivo gist se as pessoas quiserem mais (não querem que esta postagem se torne mais) ou se quiserem fornecer algumas próprias.


2
Grande desafio! Definitivamente vou dar uma chance a isso quando tiver tempo. :)
R. Kap

Você sabe, os perigos da queda (ou melhor, a rápida desaceleração na parte inferior) poderiam ter sido modelados, dando à transição de queda uma probabilidade de aproximadamente 95%. ;) Bom desafio!
Martin Ender

você pode escapar através de uma luz do céu ou túneis? ou seja, superior e inferior do campo? ou apenas esquerda ou direita?
Moogie 27/03

@ Moogie você pode realmente! Contanto que seus pés estejam livres, você estará livre (por exemplo, veja a caixa de teste 1x2, onde a solução é descartada apenas uma vez). Vou adicionar uma pequena caixa de teste à essência para testar a saída do teto.
VisualMelon

3
Adicionado recompensa à pergunta. Esta é uma ótima pergunta que merece respostas.
Programmer5000 #

Respostas:


3

Perl 5, 1425 1464 1481 1469 1485 1438 bytes

Esse foi um desafio divertido! E, surpreendentemente, parece que eu tenho o código mais curto até agora, um pouco abaixo de 1,5kB! :)

Tenho certeza de que finalmente consegui fazer isso funcionar. O delimitador usado é :e existe um preenchimento extra de duas linhas na parte superior e uma coluna de cada lado . Portanto, para sua contribuição de:

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

meu programa levaria: (NB: deve haver dois pontos no final, mas não no começo!)

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

Eu uso expressões regulares para traduzir de um mapa para outro e resolver com força bruta. No entanto, não adiciono mapas à lista se esse mapa já tiver sido alcançado com menos esforço e maior ou igual probabilidade. Os mapas são removidos da lista quando todos os movimentos possíveis a partir desse ponto estiverem esgotados. O programa termina com êxito quando corresponde a um regex que mostra que eu cheguei ao lado ou ao fundo e termina com "Não há esperança!" quando a lista de mapas estiver esgotada.

Infelizmente, havia uma grande quantidade de eficiência trocada para decolar alguns bytes, portanto a versão para golfe é bastante lenta;) Perl parece achar as avaliações em particular bastante dolorosas.

Sem mais delongas,

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;}}}}

Por uma questão de sanidade, aqui está a mesma coisa com novas linhas e alguns comentários:

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)

Já vejo alguns lugares para aparar mais alguns bytes, mas ainda não quero passar por todos os testes. Mais tarde! :)

Edit: Adicionado alguns preenchimentos abaixo, bem como para ajustar sua saída mais exatamente ao escapar pelo chão. Foram removidas algumas das avaliações, para que o código também possa correr mais rápido agora!

Edit: Não estava manipulando escadas e cai muito bem. Ainda não lida bem com escadas ... Tentando consertar isso agora.


Ainda bem que alguém se diverte! Receio não poder aceitar isso como é atualmente, pois você não pode supor que a entrada tenha essas linhas ou preenchimentos extras na parte superior (mas a ~ 1,5kB, basta inserir imediatamente não deve doer demais!). Eu não tenho Perl nesta máquina, mas vou tentar encontrar uma maneira de testar isso hoje, para que eu possa verificar se funciona e executado em um prazo razoável e relatar de volta!
VisualMelon

11
@VisualMelon Não há problema, mudei o método de entrada e adicionei o preenchimento manualmente. Ele tende a explodir nos quebra-cabeças maiores, mas é executado em um período de tempo razoável para a maioria dos casos de teste.
7777 Chris

Ainda não testei isso, mas notei que você disse que isso usa um regex para detectar quando você sai do lado ou do fundo, mas também pode escapar do topo (consulte testcase10, que foi adicionado para esse fim), caso você tenha perdido this #
VisualMelon

11
@VisualMelon Ah, eu assumi que, para escapar do telhado, o fugitivo teria que subir no topo da sala e depois andar para o lado, saindo do telhado. Eu vejo a sentença relevante agora. Vou corrigi-lo :)
Chris

2
Você pode adicionar um link TIO?
Programmer5000 #

3

C #, 1814 1481 1395 bytes

Que trabalho árduo !! Estou realmente bastante satisfeito com isso agora!

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);}}

Experimente Online

Programa completo, leva entrada para STDIN, saída para STDOUT. Essencialmente, uma reescrita do meu solucionador original, usando um BFS simples e ineficientemente implementado para encontrar o caminho ideal. É razoavelmente rápido, não pode ser muito mais lento que minha outra implementação (ainda não o cronometrei), certamente funciona dentro do prazo. Grande parte do custo é a tabela Ação, que é codificada como valores separados por vírgula, que registram o nome, o mapa 'match / smash' e outras propriedades de cada ação disponível.

Começa lendo na probabilidade mínima de sucesso e no mapa. Em seguida, localiza o fugitivo, remove-o do mapa e cria um 'estado de pesquisa' que contém essas informações. Em seguida, ele executa o BFS, que examina repetidamente o próximo estado devido com o mínimo de esforço (garantindo que ele encontre uma solução ideal). Antes de expandir o nó, ele compara seu esforço e probabilidade de sucesso aos nós anteriores com o mesmo mapa e localização do fugitivo e se rejeita se uma rota melhor já tiver sido encontrada para esse estado. Se sobreviver a isso, ele se adiciona à tabela 'vista' para poder rejeitar o estado posteriormente. Tudo isso é para desempenho, e sem ele o fator de ramificação seria enorme. Em seguida, verifica se o fugitivo está fora do mapa. Se for, então ele vence! Ele rastreia o estado (cada estado anterior é registrado com cada estado) e cria o plano (ao contrário) antes de sair do loop BFS. Caso contrário, ele tenta aplicar todas as ações e adiciona qualquer uma que possa ser aplicada aodue antes de classificá-la para obter o estado de menor esforço na próxima iteração.

Código formatado e 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);
    }
}

Bom trabalho! Diverte-me incessantemente que sua pontuação antiga e nova pontuação são anagramas um do outro ... lol
HyperNeutrino

C # venceu Perl?
Esolanging Fruit
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.