Resolver um labirinto de seta reversa


14

Este é um "labirinto de flechas":

  v      < 
>     v    
      >  ^ 
>         v
^ <       *

As *marcas no local onde você terminará. Seu objetivo é descobrir onde o labirinto começa (daí o labirinto reverso). Nesse caso, é o primeiro >na segunda linha.

  v------< 
S-+---v  | 
  |   >--^ 
>-+-------v
^ <       *

Observe que todas as setas devem ser usadas. Observe também que você pode assumir que as linhas serão preenchidas com espaços com o mesmo comprimento.

Seu programa deve inserir o labirinto de qualquer maneira razoável (stdin, de um arquivo, caixa de mensagem etc.), no entanto, o labirinto deve estar completamente intacto. Por exemplo, você não pode inserir as linhas separadas por vírgulas; a entrada deve ser exatamente o labirinto.

Você deve produzir o início do labirinto de qualquer maneira razoável. Por exemplo, você poderia

  • produzir as coordenadas do início
  • produzir o labirinto inteiro com a seta inicial substituída por um S
  • imprima o labirinto inteiro com todas as setas, exceto a seta inicial removida (espaço em branco intacto!)
  • etc.

Desde que você possa dizer pela saída qual seta é a seta inicial, tudo bem. Por exemplo, uma saída de

"0"
"2"

está bem, independentemente das novas linhas e citações, porque você ainda pode saber onde foi o início.

Isso é , então o código mais curto em bytes vencerá.


É razoável supor que cada seta tem apenas uma outra seta apontando para ela? Não haverá uma instância em que possa haver vários pontos de partida, existe?
Danny

O "início do labirinto" é uma flecha a partir da qual a flecha é visitada uma vez? Em outras palavras, a pergunta de Danny + suposição de que não há loops.
Shiona

3
"você não pode inserir as linhas separadas por vírgulas; a entrada deve ser exatamente o labirinto." Isso parece uma restrição desnecessária, já que "exatamente o labirinto" já é de fato delimitado por novas linhas entre as linhas. Por que priorizar um delimitador em detrimento de outro?
Jonathan Van Matre

1
@ Doorknob Isso é razoável, já que se poderia, teoricamente, codificar uma solução compactada inteira nos "delimitadores". No entanto, suspeito que a restrição introduz um certo viés linguístico contra certos idiomas. E se a regra eram "linhas de entrada pode ser delimitado por qualquer um personagem de sua escolha. Todas as linhas devem ser delimitados pelo mesmo personagem."? Eu acho que o limite superior é útil porque estabelece um domínio dentro do qual seu programa deve funcionar.
Jonathan Van Matre

1
@ Pedro Isso só significa que, por exemplo, >v^na >está apontando para o v, não o ^. Vou editar mais coisas quando voltar para casa em um computador hoje.
Maçaneta

Respostas:


4

GolfScript, 55 bytes

:&,,{&=42>},.{.&="><^v"?[1-1&n?~.~)]=:d;{d+.&=32=}do}%-

Demonstração online

Assume que todas as linhas de entrada são preenchidas com espaços do mesmo comprimento e separadas por novas linhas. Emite o deslocamento de bytes da seta inicial desde o início da sequência de entrada (por exemplo, 12para o exemplo do labirinto no desafio).

Especificamente, este programa localiza os desvios de bytes de todas as setas que não possuem nenhuma outra seta apontando para elas (supondo que todas as setas apontem para uma seta ou uma meta; um comportamento estranho pode ocorrer se isso não for verdade). Por padrão, se houver várias dessas setas (que, por especificação, não devem ser possíveis em entradas válidas), suas compensações serão simplesmente concatenadas na saída. Se desejar, você pode anexar n*ao programa para separá-los por novas linhas.

Versão descolada com comentários:

:&                     # save a copy of the input string in the variable "&"
,, { &= 42> },         # make a list of the byte offsets of all arrows 
.                      # duplicate the list and apply the following loop to it:
{
  .                    # make a copy of the original offset...
  &=                   # ...and get the corresponding character in the input
  "><^v" ?             # map the arrow character to integers 0 to 3, and...
  [ 1 -1 &n?~ .~) ]=   # ...map these to +1, -1, -len-1 or +len+1, where len is the...
  :d;                  # ...length of the first input line, and save result as "d"
  { d+ . &= 32= } do   # add d to the byte offset until another non-space is found
} %
-                      # subtract the resulting list of target offsets from the
                       # original list of arrow offsets; this should leave exactly
                       # one offset, which is left on the stack for output

1
Você pode salvar três caracteres se fizer uma linha w.
Howard

@ Howard: Huh, então eu posso. Obrigado! Tinha que renomear zpara &evitar a necessidade de um espaço extra, no entanto. OTOH, ?~.~)faz um belo sorriso. :-)
Ilmari Karonen

5

GolfScript ( 101 100 bytes)

n/:g,,{:&g=:r,,{&1$r='^v<>*'?70429 3base 2/=++}/}%{,4=},.{2<}%:&\{2/~\{[~2$~@+(@@+(\]&1$?)!}do\;}%^`

A saída está no formato em [[x y]]que as coordenadas são baseadas em 0.

Demonstração online

O processamento ocorre em duas fases: a primeira fase transforma o labirinto em uma série de [x y dx dy]tuplas; a segunda fase mapeia cada seta / asterisco para a seta / asterisco para a qual aponta. (Asteriscos são apontados para si mesmos). Pela definição do problema, há exatamente uma seta que não está no resultado deste mapa e essa é a solução.


Eu estava colando minha resposta enquanto você postava isso. Tínhamos a mesma idéia, mas você conseguiu jogar com sucesso. Agradável!
Vereos

@ PeterTaylor Não vejo como ele lida corretamente com o caso, mencionado nos comentários.
209 Howard

@ Howard, não tenho idéia do que é esse caso. Pedirá esclarecimentos.
Peter Taylor

Você poderia gentilmente postar um exemplo de sua entrada e saída?
DavidC

@DavidCarraher, veja a demonstração online. ;'STUFF'simula o fornecimento STUFFvia stdin.
22414 Peter

2

Mathematica 491 323

Ungolfed com comentários

O procedimento começa no final ("*"), encontra a seta que aponta para ele e assim por diante até chegar ao início.

A função, f [labirinto].

(* positions of the arrowheads *)
aHeads[a_]:=Position[m,#]&/@{"^","v",">","<"}

(* position of the final labyrinth exit*)
final[a_]:=Position[a,"*"][[1]];


(* find arrowheads that point to the current location at {r,c} *)
aboveMe[{r_,c_},a_]:=Cases[aHeads[a][[2]],{r1_,c}/;r1<r]
belowMe[{r_,c_},a_]:=Cases[aHeads[a][[1]],{r1_,c}/;r1>r]
rightOfMe[{r_,c_},a_]:=Cases[aHeads[a][[4]],{r,c1_}/;c1>c]
leftOfMe[{r_,c_},a_]:=Cases[aHeads[a][[3]],{r,c1_}/;c1<c]

(*find the precursor arrowhead*)
precursor[{loc_,a_,list_:{}}]:=

(* end the search when the precursor has already been traversed or when there is no precursor *)
Which[MemberQ[list,loc],list,
loc=={},list,True,


(* otherwise find the next precursor *)

precursor [{Achatar [{aboveMe [loc, a], belowMe [loc, a], rightOfMe [loc, a], leftOfMe [loc, a]}, 2], a, Preceder [lista, loc]}]]

(* return the path through the maze from start to finish *)
f[maze_]:= precursor[{final[maze[[1]]],maze[[1]]}]

Golfe

f@z_:=Module[{p,h,m=z[[1]]},h@a_:=Position[a,#]&/@{"^","v",">","<","*"};
  p[{v_,a_,j_:{}}]:=Module[{r,c,x=Cases},{r,c}=v;
  Which[MemberQ[j,v],j,v=={},j,True,
  p[{Flatten[{x[h[a][[2]],{r1_,c}/;r1<r],x[h[a][[1]],{r1_,c}/;r1>r],
  x[h[a][[4]],{r,c1_}/;c1>c],x[h[a][[3]],{r,c1_}/;c1<c]},2],a,Prepend[j,v]}]]];
  p[{Position[m,"*"][[1]],m}]]

Exemplo

O labirinto. Cada par ordenado contém a linha e a coluna de uma célula. Por exemplo, {2, 3} denota a célula na linha 2, coluna 3.

maze=Grid[Normal@ SparseArray[{{5, 5} -> "*",{1, 2} -> "v", {1, 5} -> "<",{2, 1} -> ">",
   {2, 3} -> "v",{3, 3} -> ">", {3, 5} -> "^",{4, 1} -> ">", {4, 5} -> "v",{5, 1} -> "^", 
   {5, 2} -> "<",{_, _} -> " "}]]

Labirinto


Entrada

f[maze]

Saída : o caminho do início ao fim.

{{2, 1}, {2, 3}, {3, 3}, {3, 5}, {1, 5}, {1, 2}, {5, 2}, {5, 1}, { 4, 1}, {4, 5}, {5, 5}}


Seu formato de entrada está incorreto - "a entrada deve ser exatamente o labirinto".
Maçaneta

A entrada agora é o próprio labirinto.
DavidC

Eu não segui o código, mas a maneira como você resolveu a coisa "a entrada é agora o labirinto" é hilária! +1 ... o número de crentes em "STDIN é universal" é espantoso.
Dr. belisarius

Fico feliz que você tenha gostado da solução para o problema de entrada.
DavidC

1

Acho que encontrei uma boa maneira de resolver isso, mas por acaso sou péssima em jogar golfe. Eu acho que isso pode ser MUITO mais curto, então vou explicar minha ideia para que outros possam usá-la se acharem boa.

Se todas as flechas tiverem que ser usadas, todas as flechas serão apontadas por outra flecha, exceto uma, que é a nossa solução.

Isso significa que na verdade não precisamos jogar o labirinto para trás, mas, começando pelo canto superior esquerdo, basta verificar a seta apontável mais próxima de cada um. Este é um verdadeiro analgésico para labirintos maiores (já que você não precisa verificar todas as quatro direções, mas apenas uma).

Aqui está a minha solução:

PHP, 622 bytes

$h=fopen("a.txt","r");$b=0;while(($z=fgets($h))!==false){$l[$b]=str_split($z,1);$b++;}$v=array("^","<","v",">");$s=array();for($i=0;$i<count($l);$i++){for($j=0;$j<count($l[0]);$j++){if(in_array($l[$i][$j],$v)){switch($l[$i][$j]){case"^":$c=$i-1;while($l[$c][$j]==" ")$c--;$s[]=$c.",".$j;break;case"v":$c=$i+1;while($l[$c][$j]==" ")$c++;$s[]=$c.",".$j;break;case"<":$c=$j-1;while($l[$i][$c]==" ")$c--;$s[]=$i.",".$c;break;case">":$c=$j+1;while($l[$i][$c]==" ")$c++;$s[]=$i.",".$c;break;}}}}for($i=0;$i<count($l);$i++){for($j=0;$j<count($l[0]);$j++){if(in_array($l[$i][$j],$v)){if(!in_array($i.",".$j,$s)){echo$i.",".$j;}}}}

Ungolfed:

$h=fopen("a.txt","r");
$b=0;
while(($z=fgets($h))!==false) {
    $l[$b]=str_split($z,1);
    $b++;
}
//Input ends here
$v = array("^","<","v",">");
$s = array();
//Here i check every arrow, and save every pointed one in $s
for($i=0;$i<count($l);$i++){
    for($j=0;$j<count($l[0]);$j++){
        if(in_array($l[$i][$j],$v)){
            switch($l[$i][$j]) {
                case "^":
                    $c=$i-1;
                    while ($l[$c][$j]==" ")
                        $c--;
                    $s[]=$c.",".$j;
                    break;
                case "v":
                    $c=$i+1;
                    while ($l[$c][$j]==" ")
                        $c++;
                    $s[]=$c.",".$j;
                    break;
                case "<":
                    $c=$j-1;
                    while ($l[$i][$c]==" ")
                        $c--;
                    $s[]=$i.",".$c;
                    break;
                case">":
                    $c=$j+1;
                    while ($l[$i][$c]==" ")
                        $c++;
                    $s[]=$i.",".$c;
                    break;
            }
        }
    }
}
//I check if the arrow is in $s. If not, we have a solution.
for($i=0;$i<count($l);$i++){
    for($j=0;$j<count($l[0]);$j++){
        if (in_array($l[$i][$j],$v)){
            if (!in_array($i.",".$j,$s)){
                echo$i.",".$j;
            }
        }
    }
}

1

PHP - 492 bytes

$r=split("\n",$m);$h=count($r);foreach($r as &$k)$k=str_split($k);$l=count($r[0]);$e=strpos($m,"*")+1-$h;$a=$x=$e%$l;$b=$y=floor(($e-$x)/$l);do{$c=0;for(;--$a>=0;){if($r[$b][$a]==">"){$x=$a;$c++;}if($r[$b][$a]!=" ")break;}$a=$x;for(;--$b>=0;){if($r[$b][$a]=="v"){$y=$b;$c++;}if($r[$b][$a]!=" ")break;}$b=$y;for(;++$a<$l;){if($r[$b][$a]=="<"){$x=$a;$c++;}if($r[$b][$a]!=" ")break;}$a=$x;for(;++$b<$h;){if($r[$b][$a]=="^"){$y=$b;$c++;}if($r[$b][$a]!=" ")break;}$b=$y;}while($c>0);echo "$x-$y";

Esta solução supõe que o mapa possa ser encontrado na variável local $m. O método mais curto que tenho para passar é via $_GET: $m=$_GET['m'];at 14 bytes. Uma versão não-gasta com o mapa na variável é fornecida abaixo para maior clareza da leitura.

$m=<<<EOT
  v      < 
>     v    
      >  ^ 
>         v
^ <       * 
EOT;

$r=split("\n",$m);
$h=count($r);
foreach($r as &$k)
    $k=str_split($k);
$l=count($r[0]);

$e=strpos($m,"*")+1-$h;

$a=$x=$e%$l;
$b=$y=floor(($e-$x)/$l);
do{
$c=0;
for(;--$a>=0;)
    {
        if($r[$b][$a]==">"){$x=$a;$c++;}
        if($r[$b][$a]!=" ")break;
    }
$a=$x;
for(;--$b>=0;)
    {
        if($r[$b][$a]=="v")
            {
                $y=$b;
                $c++;
            }
        if($r[$b][$a]!=" ")break;

    }
$b=$y;
for(;++$a<$l;)
    {
        if($r[$b][$a]=="<")
            {
                $x=$a;
                $c++;
            }
        if($r[$b][$a]!=" ")break;
    }
$a=$x;
for(;++$b<$h;)
    {
        if($r[$b][$a]=="^")
            {
                $y=$b;
                $c++;
            }
        if($r[$b][$a]!=" ")break;
    }
$b=$y;
}while($c>0);
echo "$x-$y";

1

K, 281 277 258

{{$[&/x in*:'{{~"*"=x 1}{(s;k . s;k;s:*1_w@&(k ./:w:{{x@&x in y}[(*:'{(x[1]@x 0;x 1)}\[x;(z;y)]);i]}[(h!b,b,a,a:#k:x 2)m;(h!(0 1+;0 -1+;1 0+;-1 0+))m:x 1;x 0])in"*",h:"><v^")}\(x;y;z;x)}[*x;y .*x;y];*x;.z.s[1_x]y]}[i@&~(x ./:i::,/(!#x),/:\:!b::#*x)in" *";x]}

Aqui está uma versão anterior, não destruída

solve:{[x]
    //j - indices of all possible starting points
    //i - every index
    j::i@&~(x ./:i::,/(!a:#x),/:\:!b:#*x) in " *";

    h::">v<^";

    //d - dictionary mapping each directional character to
    //    increment/decerement it needs to apply to the x/y axis
    d::h!((::;1+);(1+;::);(::;-1+);(-1+;::));

    //e - dictionary mapping each directional character to
    //    the maximum number of moves it should make in a 
    //    given direction
    e::h!b,a,b,a;

    //f - function to produce the indices of each point that is 
    //    passed once we move in a certain direction from a 
    //    certain index
    f::{{x@&x in y}[(last'{(x[0];x[0]@'x 1)}\[x;(y;z)]);i]};

    //g - function that, given a starting and a direction,
    //    moves in that direction until hitting another directional
    //    character. It then moves in the new direction etc. until
    //    it reaches the end point -- *
    g::{[x;y;z]
        {[x]
            q:x 0;m:x 1; k:x 2;
            w:f[e m;d m;q];
            s:*1_w@&(k ./:w)in h,"*";
            m:k . s;
            (s;m;k;s)}\[{~"*"=x 1};(x;y;z;x)]};

    // recursive function that starts at the first possible starting point
    // and plots its way to the end. If all other potential starting points
    // have been touched in this path, then this is the correct starting point.
    // else, recursively call the function with the next possible starting point
    :{$[min "b"$j in last'g[*x;y . *x;y];*x;.z.s[1_x;y]]}[j;x]
  }

Retorna o ponto de partida como x ynos índices baseados em 0.

k)maze
"  v      < "
">     v    "
"      >  ^ "
">         v"
"^ <       *"
k)solve maze
1 0

1

Python 422

with open("m.txt","r") as f:m=f.read().split("\n")
p=[(v.find("*"),p) for p,v in enumerate(m) if "*" in v][0]
g=[]
def f(a):
    x,y=b,c=a
    for p,i in enumerate((lambda x:[l[x] for l in m])(x)):
        if i in "^>v<" and((p<y and i=="v")or(p>y and i=="^")):return b,p
    for p,i in enumerate(m[y]):
        if i in "^>v<" and((p<x and i==">")or(p>x and i=="<")):return p,c
while True:
    z=f(p)
    if z in g:break
    else:g.append(z);p=z
print p

A entrada está em um arquivo chamado m.txt. A saída é, (x, y)mas se você alterar a última declaração de impressão para print g, a saída será uma lista, [(x, y), (x, y), ...]com todas as etapas para ir do fim ao início.

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.