Dar direções

15

Desafio

Você deu um mapa a um amigo que se parece com isso:

      |
      /
     |
     /
    |
    \
     |
     \
      D

Um mapa simples que começa na parte superior e termina na parte inferior. Infelizmente, seu amigo não entende. Você pode decodificar o mapa para que ele possa lê-lo?

Entrada

A entrada é uma cadeia de caracteres que consistem em |, /, \, D, ^, Y, (espaço) , e novas linhas.

  • | diz para ficar na mesma coluna.
  • \ diz para mover para a coluna à direita e para baixo 1.
  • / diz para mover para a coluna à esquerda e para baixo 1.
  • D marca o destino.
    • ^ (se presente) fala de uma divisão no caminho.
    • Y(se presente) fala de uma nova junção de caminhos. Trate como um |.

A entrada será organizada de forma a criar um tipo de caminho:

   |
   |
   \
    |
    ^
   / \
  /   |
 D    |

Sempre haverá um espaço entre dois caminhos, e todos os caminhos se reunirão novamente ou alcançarão a última linha da entrada. Haverá apenas uma divisão por mapa. Não há limite para o comprimento do mapa de entrada. Nunca haverá mais de dois caminhos.

Resultado

A saída deve ser uma sequência de instruções.

  • " L " deve dizer ao seu amigo para mover L eft e dar um passo à frente.
  • " R " deve dizer ao seu amigo para mover R ight e tomar um passo em frente.
  • " F " deve dizer ao seu amigo para dar um passo à frente.

Para o mapa de exemplo de entrada, a saída seria a seguinte:

F F L F R R R

Observe que seu amigo está começando na parte superior do mapa e voltado para baixo no mapa. Dê as instruções da perspectiva dele. Para uma instância de "^", seu programa deve poder escolher o caminho que leva ao destino (D). Se os dois caminhos forem recombinados, seu programa deverá escolher o caminho mais reto (aquele com mais |s) a seguir. Instruções devem ser separados por espaços, e deve terminar em D .

Exemplos

Entrada

      |
      |
      \
       \
        ^
       / |
      |  |
      \  |
       \ \
        \ \
         \ /
          Y
          D

Resultado

F F L L L F F F L L R F

Como o caminho mais à esquerda contém apenas 1 |, usamos o caminho mais à direita que possui 3.


Entrada

\
 |
 /
|
\
 |
 /
D

Resultado

L F R F L F R

Entrada

    /
   \
    /
   \
    ^
   \ \
    D \

Resultado

R L R L R L

Outros detalhes

  • Isso é código de golfe, então a pessoa com o código mais curto até a próxima quarta-feira, 19 de agosto, vence.
  • Feedback construtivo bem-vindo e muito apreciado.
  • Parcialmente inspirado em A Map to Hidden Treasure
  • Sinta-se livre para alterar o título para algo mais criativo.
  • Se você encontrar erros que eu cometi, corrija-os.
  • E claro, divirta-se.

Obrigado!

Um pouco tarde, talvez, mas UndefinedFunction é o vencedor da codificação em JavaScript! Obrigado a todos que entraram. Nenhuma outra entrada será aceita.

The_Basset_Hound
fonte
Ainda parece fora. O primeiro exemplo termina L L, o que eu acho que deveria ser L L L. O exemplo com Yainda tem um 1no final e também parece ter outros erros. Li o mapa como F F R R R F F F R R L Fse entendesse as regras corretamente.
Martin Ender
@ MartinBüttner você deveria terminar em D, você precisaria apenas de 2 Ls. 3 Ls passariam D.
The_Basset_Hound 12/08
2
Um caminho pode chegar a um beco sem saída antes de chegar à última linha? Ou todos os caminhos alcançarão a última linha da entrada?
jrich
@ BassetHound não deveria haver um Lpara o ^e dois Lpara os dois /? E por que você adicionou mais duas Fao final do Yexemplo?
Martin Ender
@ETHproductions Sim.
The_Basset_Hound 14/08

Respostas:

5

Javascript (ES6), 261 248 252 248 212 bytes

Como apenas uma divisão deve ser suportada:

s=>(s=s.replace(/ /g,o="",d=0).split(`
`)).map((v,j)=>{if(v=="^")for(v="/\\";(w=s[++j])&&(x=w[1]);d=x=="D"?1:w[0]=="D"?0:x>"{"?d+1:w[0]>"{"?d-1:d);o+=(p=v[d>0?1:0]||v[0])<"0"?"R ":p<"E"?"":p=="\\"?"L ":"F "})?o:o


No entanto, 240 bytes e podemos lidar com várias divisões:

s=>(s=s.replace(/ /g,o="").split(`
`)).map((v,j)=>{if(!v[1])t=d=0
else if(!t){for(t=1;(w=s[++j])&&(x=w[1]);d=x=="D"?1:w[0]=="D"?0:x>"{"?d+1:w[0]>"{"?d-1:d);o+=d>0?"L ":"R "}o+=(p=v[d>0?1:0])<"0"?"R ":p<"E"||p=="^"?"":p=="\\"?"L ":"F "})?o:o


Ambos os programas definem funções anônimas.

Para usar, dê um nome às funções adicionando f=antes do código.

Depois, eles podem ser chamados com

alert(f(
`   |
   |
   \\
    |
    ^
   / \\
  /   |
 D    |`
))


Explicação

(desatualizado, mas ainda o mesmo conceito. Para a solução de divisão múltipla)

s=>
    //Remove all spaces from the input
    (s=s.replace(/ /g,o="",
                 //Define function c, to convert symbols to directions
                 c=p=>p<"0"?"R ":p<"E"||p=="^"?"":p=="\\"?"L ":"F "
    //Split input into an array by newlines
    ).split(`
`))
    //for each string v in the input array, at index j:
    .map((v,j)=>{
        //if v is only one character long:
        if(!v[1]){
            t=1     //assign t to 1 (true) - we need to test if multiple paths
            d=0     //assign d to 0 - reset the counter for determining shortest path
        }
        //if v is two characters long, and we have not tested which path is shorter yet:
        else if(t){
            for(    t=0;    //assign t to 0 - we will have tested which path is longer

                    //for each string left in the input, while the input still has two characters:
                    (w=s[++j]) && w[1];
                    //assign d to determine which direction to go. This will be conveyed by if d>0
                    d=
                        w[1]=="D"?1:    //if the dest. is only on one side, choose that side.
                        w[0]=="D"?0:
                        w[1]=="|"?d+1:  //otherwise add/subtract from d (like a tug of war) to determine which side has more "|"
                        w[0]=="|"?d-1:
                        d
               );
            o+=d>0?"L ":"R "    //append to the output which direction was taken
        }

        o+=c(v[d>0?1:0])    //append to the output the characters from the correct path in any case, determined by d calculated above 
                            //(or defaulting to 0, if path is not split, in which case the only character is appended)

    }) ? o : o  //coerce the result, which will evaluate to an array of the input, to the output (o) and implicitly return


Notas

  • Todas as barras invertidas ( \) na entrada são escapadas como \\, para que o javascript possa reconhecê-las.

  • Ambas as saídas contêm um espaço à direita.

jrich
fonte
Dang, pensei que eu tinha tudo resolvido.
The_Basset_Hound
9

PHP, 634 631 607 396 382 381 347 338 330 337 324 bytes

Meu primeiro golfe, então seja gentil. Quaisquer dicas são imensamente apreciadas.

<?php
foreach(str_split(strtr(fgets(STDIN),[' '=>'']))as $i){if($i!=D){$z=$i=='^';$x=$i==Y;$s=!$z&&!$x?($i=='/'?R:($i=='|'?F:($i=='\\'?L:$s))):$s;$a.=$z?R:($x?F:(($c==1||!$c)?$s:''));$b.=$z?L:($x?F:(($c==2||!$c)?$s:''));$c=$z?1:($x?0:($c==1?2:($c==2?1:$c)));}else{echo(substr_count($a,F)<substr_count($b,F)&&$c==0||$c==2)?$b:$a;}}

Breve explicação:
Eu tenho uma contagem que é 0 se a entrada tiver apenas um caminho. Quando o caminho se divide, a contagem é 1 para o caminho esquerdo e 2 para o caminho direito. Depois de definir os dois caminhos (ou apenas um), verifico qual caminho tem mais "F".

Versão Ungolfed:

<?php
$input = fgets(STDIN);
$inputArray = str_split($input);
foreach ($inputArray as $currentChar) {
    if ($currentChar != 'D') {
        if ($i == '^') {
            $firstPath .= 'R';
            $secondPath .= 'L';
            $count = 1;
        } elseif ($i == 'Y') {
            $secondPath .= 'F';
            $firstPath .= 'F';
            $count = 0;
        } else {
            if ($i == ' ') {
                continue;
            }
            if ($i == '/') {
                $direction = 'R';
            } else {
                if ($i == '|') {
                    $direction = 'F';
                } else {
                    if ($i == '\\') {
                        $direction = 'L';
                    } else {
                        $direction = '';
                    }
                }
            }
            if ($count == 1) {
                $firstPath .= $direction;
                $count = 2;
            } elseif ($count == 2) {
                $secondPath .= $direction;
                $count = 1;
            }
            if (!$count) {
                $firstPath .= $direction;
                $secondPath .= $direction;
            }
        }
    } else {
        echo (substr_count($firstPath, 'F') < substr_count($secondPath, 'F')) || $count == 2 ? $secondPath : $firstPath;
    }
};


Log:
economizou 36 bytes graças a Kamehameha.
Economizou muitos bytes alterando um pouco a lógica.
Economizou 42 bytes graças a axiac.
Substituído todos os if declaração por operadores ternários.

empurrão
fonte
3
Bem vindo ao site!
Isaacg
2
Você pode tentar em $a=$b='';vez de - $a='';$b='';Salva em torno de 3 bytes.
Kamehameha
1
Além disso, concatenação como $a=$a.'L ';pode ser reduzida para $a.='L '. Você parece ter feito isso em alguns lugares. Isso vai economizar cerca de 6 bytes :)
Kamehameha
1
Não conheço muito bem o PHP, mas acredito que você pode deixar o espaço depois de "como" na sua foreach ( foreach($e as$i)); Eu testei isso e parece funcionar bem.
ProgramFOX
1
Mais algumas dicas para salvar alguns bytes, como o @ProgramFox mencionou asno foreach, os espaços entre echoe o nome da variável podem ser removidos para que você tenha echo$b. Além disso, um par de testes de igualdade pode ser mais curto também, $c==0poderia ser !$ce se esse for o caso, você pode inicializar $ca ''com $ae $b!
Dom Hastings
3

PHP, 281 bytes

$b=0;$p=['|'=>'$a[$b].="F ";$f[$b]++;','/'=>'$a[$b].="R ";','\\'=>'$a[$b].="L ";','^'=>'$d.=$a[0];$n=2;$a=["R ","L "];$f=[];$b=1;','Y'=>'$d.=$a[$f[0]<$f[$n=1]]."F ";$a=[];','D'=>'$d.=$a[$b];exit(rtrim($d));'];foreach(str_split($argv[$n=1])as$c){if($x=$p[$c]){eval($x);$b=++$b%$n;}}

É o resultado de duas iterações de golfe. A versão ungolfed é:

$a=$f=[];       // these assignments are not required (they were suppresed in v2)
$n=1;           // this assignment can be squeezed into $argv[$n=1]
$b=0;           // if this assignment is suppressed $b becomes '' and breaks the logic
$code = [
    '|' => '$a[$b].="F ";$f[$b]++;',
    '/' => '$a[$b].="R ";',
    '\\'=> '$a[$b].="L ";',
    '^' => '$d.=$a[0];$n=2;$a=["R ","L "];$f=[];$b=1;',
    'Y' => '$d.=$a[$f[0]<$f[$n=1]]."F ";$a=[];',
    'D' => '$d.=$a[$b];echo(rtrim($d));',
];
foreach (str_split($argv[1]) as $char) {
    // ignore input characters not in the keys of $code
    if ($x = $code[$char]) {
        eval($x);
        $b = ++ $b % $n;   // cycles between 0 and 1 ($n == 2) or stays 0 ($n == 1)
    }
}

É bastante golfado em si e surgiu como uma melhoria do seguinte programa de golfe (312 bytes):

$b=0;foreach(str_split($argv[$n=1])as$c){if($c=='|'){$a[$b].='F ';$f[$b]++;}elseif($c=='/'){$a[$b].='R ';}elseif($c=='\\'){$a[$b].='L ';}elseif($c=='^'){$d.=$a[0];$n=2;$a=['R ','L '];$f=[];$b=1;}elseif($c==Y){$d.=$a[$f[0]<$f[$n=1]].'F ';$a=[];}elseif($c==D){$d.=$a[$b];exit(rtrim($d));}else continue;$b=++$b%$n;}

É a versão golfada do original:

$map = $argv[1];

$dir = '';              // the already computed directions
$nb = 1;                // the number of branches
$branches = [ '' ];     // the branches (2 while between '^' and 'Y', 1 otherwise)
$nbF = [ 0, 0 ];        // the number of 'F's on the branches (used to select the branch)
$curr = 0;              // the current branch
foreach (str_split($map) as $char) {
    if ($char == '|') {             // go 'F'orward
        $branches[$curr] .= 'F ';       // put it to the current branch
        $nbF[$curr] ++;                 // count it for the current branch
    } elseif ($char == '/') {       // go 'R'ight
        $branches[$curr] .= 'R ';
    } elseif ($char == '\\') {      // go 'L'eft
        $branches[$curr] .= 'L ';
    } elseif ($char == '^') {       // fork; choose the path ('L' or 'R') that contains the most 'F'orward segments
        $dir .= $branches[0];           // flush the current path (it was stored as the first branch)
        $nb = 2;                        // start two branches
        $branches = [ 'R ', 'L ' ];     // put the correct directions on each branch
        $nbF = [ 0, 0 ];                // no 'F's on any branch yet
        $curr = 1;                      // need this to let it be 0 on the next loop
    } elseif ($char == 'Y') {       // join
        $dir .= $branches[$nbF[0] < $nbF[1]];   // flush; choose the branch having the most 'F's
        $dir .= 'F ';                           // treat it like a "|"
        $branches = [ '' ];                     // back to a single, empty branch
        $nb = 1;
    } elseif ($char == 'D') {       // finish
        $dir .= $branches[$curr];       // flush
        break;                          // and exit; could use 'return' but it's one byte longer; use exit() in the final program and save 5 bytes
    } else {
        continue;
    }
    $curr = ++ $curr % $nb;
}
echo rtrim($dir);

Exemplo de execução:

$ php -d error_reporting=0 directions.php '
      |
      |
      \
       \
        ^
       / |
      |  |
      \  |
       \ \
        \ \
         \ /
          Y
          D
'; echo
F F L L L F F F L L R F
$

Ele também lida com vários garfos corretamente (é necessário ingressar antes da próxima bifurcação para ter no máximo duas ramificações a qualquer momento). Perguntei sobre vários garfos em um comentário, mas o código já estava pronto quando a resposta ("não necessária") chegou.

O código completo com o conjunto de testes e mais comentários pode ser encontrado no github .

axiac
fonte
Uau bom trabalho! Eu ainda preciso aprender algumas coisas!
jrenk