Movimento eficiente do robô

24

Isenção de responsabilidade: a história contada nesta pergunta é inteiramente fictícia e inventada apenas com o objetivo de fornecer uma introdução.

Meu chefe comprou um novo robô de brinquedo e quer que eu ajude a programá-lo. Ele deseja inserir instruções simples de flecha para que ela se mova. Estas instruções são: ^ (para avançar) <(para virar à esquerda) e> (para virar à direita). No entanto, agora que programei o robô, ele deseja funcionalidades adicionais. Ele quer que eu transforme qualquer sequência de setas que ele insere, para que, em vez de fazer com que o robô siga o caminho indicado, ele se mova para o local desejado, indicado pelo local em que terminaria se seguisse o caminho inserido com a mesma eficiência. possível. Peço a vocês, membros do PP&CG, que me ajudem nessa tarefa.

Sua tarefa:

Escreva um programa ou função para converter uma sequência composta por setas em uma sequência que chegue ao local indicado pela entrada o mais rápido possível. Virar leva exatamente o tempo que se move para trás ou para frente.

Entrada:

Uma sequência de setas, como indicado acima. Se desejar, caracteres diferentes podem ser substituídos pelas setas, mas certifique-se de incluir o fato de fazê-lo em sua resposta. Todos os casos de teste usam as setas normalmente.

Saída:

Uma sequência de setas (ou caracteres equivalentes) que levam o robô ao destino desejado da maneira mais eficiente possível.

Casos de teste:

Observe que as soluções oferecidas são apenas possibilidades e que outras soluções podem ser válidas.

>^<<^^>^^    -> ^^<^
^^^^>^^^^    -> ^^^^>^^^^
>>>^^^^^^    -> <^^^^^^
>^>^>^>^     -> (empty string)
^<^^<^^<^^^^ -> >^^>^

Pontuação:

A memória do robô é limitada, portanto, seu programa deve ter a menor contagem de bytes possível.

Gryphon - Restabelecer Monica
fonte
Como na entrada o robô termina exatamente onde foi iniciado, não são necessários comandos para movê-lo para lá da maneira mais eficiente possível.
Gryphon - Restabelece Monica
Oh, interpretou mal a corda. Minha culpa.
JungHwan Min 02/08/19
Solicitação de caso de teste ^<^^<^^<^^^^-> >^^>^?
JungHwan Min 2/17/17
11
@ pizzakingme, desculpe, mas meu chefe é muito preguiçoso e só quer digitar um caractere por movimento.
Gryphon - Restabelece Monica
11
Eu programo robôs competitivos e posso confirmar que é exatamente assim que eles funcionam.
Joe

Respostas:

9

Retina , 103 74 71 bytes

<
>>>
+`(>(\^*>){3})\^
^$1
+`\^(>\^*>)\^
$1
>>(\^*)>(\^+)
<$2<$1
<?>*$

Experimente online! O link inclui casos de teste. Explicação:

<
>>>

Vire à esquerda e vire à direita em triplo

+`(>(\^*>){3})\^
^$1

Reduza todas as voltas no módulo 4.

+`\^(>\^*>)\^
$1

Cancele os movimentos em direções opostas.

>>(\^*)>(\^+)
<$2<$1

Vire uma volta tripla à direita novamente para a esquerda. Isso também lida com o caso do >>^>^qual precisa se tornar <^<^.

<?>*$

Exclua curvas desnecessárias à direita.

Neil
fonte
Impressionante. Eu sabia que tinha que haver uma maneira de obter esse sub 100 bytes em algum idioma, e sua resposta estava tão perto antes. +1
Gryphon - Restabelece Monica
6

Mathematica, 135 bytes

{a="^"~Table~Ramp@#&;a@#,s=If[#2>0,">","<"],a@Abs@#2,If[#<0,s,""],a@-#}<>""&@@ReIm[j=0;i=1;Switch[#,">",i*=I,"<",i/=I,"^",j+=i]&/@#;j]&

Leva um List das strings como entrada.

Explicação

j=0;i=1

Defina jcomo 0 e defina icomo 1.

/@#

Para cada caractere na entrada ...

Switch[#,">",i*=I,"<",i/=I,"^",j+=i]

Se o personagem for >, multiplique ipela unidade imaginária. Se o personagem for >, divida ipela unidade imaginária. Se o personagem for ^, adicione ia j.

ReIm[ ... ;j]

Pegue as partes reais e imaginárias de j. Isso fornece a coordenada cartesiana do robô.

... &@@

Aplique o seguinte a este resultado:


a="^"~Table~Ramp@#&;

Conjunto a como uma função que gera uma sequência com (input)ou 0caractere^ s, o que for maior.

{ ... }

Um Listcomposto por ...

a@#

aaplicado à primeira entrada (parte real de j)

s=If[#2>0,">","<"]

Se a segunda entrada (parte imaginária j) é maior do que 0, >. De outra forma,< ,. Defina spara o caractere resultante.

a@Abs@#2

a aplicado ao valor absoluto da segunda entrada.

If[#<0,s,""]

Se a primeira entrada for menor que 0, s ,. Caso contrário, sequência vazia.

a@-#

Aplique a aos tempos de entrada negativos.

... <>""

Junte as cordas.

JungHwan Min
fonte
2

Mathematica 119 Bytes

A posição final de JungHwan para o código do caminho era mais curta que a minha, então, usando isso. Eu acho que provavelmente existe uma maneira ainda mais curta de fazer isso ...

Eu uso a AnglePathfunção interna para decidir a posição final. Também defino os símbolos L, F e R para "<", "^" e ">", para salvar alguns caracteres de aspas.

L={0,Pi/2};R=-L;F={1,0};{a="F"~Table~Ramp@#&;a@#,s=If[#2>0,"L","R"],a@Abs@#2,If[#<0,s,""],a@-#}<>""&@@Last@AnglePath@#&

Uso:

%@{R,F,L,L,F,F,R,F,F}

Saída:

FFLF
Kelly Lowder
fonte
2

Ruby , 130 bytes

->s{w,d=0,1;s.bytes{|b|b>93?w+=d:d*=1i*(b<=>61)};r,c=w.rect;[w=(d=">><"[c<=>0])+?^*c.abs,?^*r.abs+w,w+d+?^*r.abs][r<=>0].chomp ?>}

Como funciona

->s{
    # We start from (0,0i), direction is +1
    w,d=0,1

    s.bytes{|b|
        # If it's ASCII 94, go one step forward,
        # else multiply direction by +i or -i
        b>93?w+=d:d*=1i*(b<=>61)
    }

    # Get the rectangular representation of the result
    r,c=w.rect

    # Now, create 2 strings of "^" (call them x and y) for horizontal and vertical moves
    # And a single ">" or "<" (call it d) for the direction change
    # If x>0, output x+d+y
    # If x==0, output d+y
    # If x>0, output d+y+d+x
    [w=(d=">><"[c<=>0])+?^*c.abs,?^*r.abs+w,w+d+?^*r.abs][r<=>0]

    #If y==0 we get an extra ">" sometimes
    .chomp ?>
}

Experimente online!

GB
fonte
2

J, 90 bytes

solução

t=.{&' ><'@*
g=.'^'#~|
(t,g@{.,t@-@(*/),g@{:`g@{:,t@{.,g@|@{.@.(0<:{:))@+.@(+/)@(=&1*j.**/\)

explicação

há um truque interessante usando números complexos (multiplicar por i é uma rotação esquerda de 90 graus e -i fornece uma correta).

então tomamos nossa entrada como números complexos: um 1 representa "avançar" e i / -i representa curvas à esquerda e à direita.

a posição final é calculada sem esforço com essa representação. Observe que esta é a primeira parte (mais à direita) da minha expressão final acima:

(+/)@(=&1*j.**/\)

Essa pequena linha acima é o que resolve o problema. Tudo o resto é apenas descobrir como formatar a resposta e certamente poderia ser significativamente mais eficiente.

Para entender a linha curta acima, observe que */\(a varredura de produtos parciais) fornece uma lista das posições que você está enfrentando em cada índice da entrada: i é norte, 1 e -1 são leste e oeste e -i é sul . Mas desde que começamos a olhar para o norte, temos que multiplicar todos aqueles por i que, em J, é representado porj. (mastigue essa frase por um momento).

Nós só realmente "move" quando a entrada original é 1, para que em seguida, multiplicar esse resultado elemento a elemento pela matriz booleano que é 1, onde a entrada original é 1 e 0 caso contrário: =&1*. O resultado dessa multiplicação é uma matriz de "etapas direcionais". Nossa posição final é simplesmente a soma dessas etapas:+/

teste

Infelizmente, não consigo fazer isso funcionar no TIO por algum motivo, mas colar o seguinte no console J verificará se ele funciona:

t=.{&' ><'@*
g=.'^'#~|
f=.(t,g@{.,t@-@(*/),g@{:`g@{:,t@{.,g@|@{.@.(0<:{:))@+.@(+/)@(=&1*j.**/\)

NB. test cases
NB. format input as complex numbers
convert=. {&0j1 0j_1 1@:('<>^'&i.)

s=. '^<^^<^^<^^^^'  NB. >^^>^
echo f convert s
s=. '>^<<^^>^^'     NB. ^^<^
echo f convert s
s=. '^^^^>^^^^'     NB. ^^^^>^^^^
echo f convert s
s=. '>>>^^^^^^'     NB. <^^^^^^
echo f convert s
s=. '>^>^>^>^'      NB. empty string
echo f convert s
Jonah
fonte
1

C # (.NET Core) , 349 bytes

n=>{int a=0,b=0,x=0,y=1,t=0,j=0,k=0,w,e,r;var p="";foreach(var c in n){if(c==62){t=x;x=y;y=-t;}if(c<61){t=x;x=-y;y=t;}if(c>63){a+=x;b+=y;}}while(a!=j|b!=k){w=0;e=a-j;r=b-k;if(r>=e&r>=-e){w=b-k;k+=w;}else if(r<=e&r<=-e){p+=">>";w=k-b;k-=w;}else if(r>=e&r<=-e){p+="<";w=j-a;j-=w;}else if(r<=e&r>=-e){p+=">";w=a-j;j+=w;}p+=new string('^',w);}return p;}

Experimente online!

Pega uma string como entrada e gera o caminho mais curto que a entrada seguiria.


Ungolfed & Commented

n =>
{
    // First, calculate the route that the robot is going to take, represented by xy
    int a = 0, b = 0; // The current coordinates (a=x, b=y)
    int x = 0, y = 1; // The movement vector
    int t = 0; // A temp variable
    var p = ""; // The path we are going to return
    // Calculate the path the robot is going to take by input
    foreach (var c in n)
    {
        if (c == '>') { t = x; x = y; y = -t; } // Turn movement vector right
        if (c == '<') { t = x; x = -y; y = t; } //                      left
        if (c == '^') { a += x; b += y; }       // Move forward
    }
    int j = 0, k = 0; // The new movement coordinates (j=x,k=y)
    // While the target position is not reached, move the robot
    while (a != j | b != k)
    {
        int w = 0; // The forward variable, counting how many times we have to go forward
        int e = a - j, r = b - k; // The target position minus the current position (e=x,r=y)
        if (r >= e & r >= -e) { w = b - k; k += w; } // Up
        else if (r <= e & r <= -e) { p += ">>"; w = k - b; k -= w; } // Down
        else if (r >= e & r <= -e) { p += "<"; w = j - a; j -= w; } // Left
        else if (r <= e & r >= -e) { p += ">"; w = a - j; j += w; } // Right
        p += new string('^', w);
    }
    // Return the final path
    return p;
}
Ian H.
fonte
1

JavaScript (Node.js) , 187 bytes

s=>{x=y=t=0;r=x=>"^".repeat(x<0?-x:x);for(c of s){t-=b=c<">"||-(c<"^");if(!b)[z=>++y,z=>++x,z=>--y,z=>--x][t&3]()}t=x<0?"<":">";return (y>0?r(y):"")+(x?t+r(x):"")+(y<0?(x?t:t+t)+r(y):"")}

Experimente online!

Versão golfada com espaço em branco

-14 bytes por @Neil


Ungolfed:

s=>{
  // convert turns to up/down/left/right movements to final destination
  let directions = [
    z=>++y, // up
    z=>++x, // right
    z=>--y, // down
    z=>--x  // left
  ];
  let x = y = direction = 0;
  for(c of s){
    let relativeDirection = "<^>".indexOf(c)-1; // relative turn offset: -1 = left, 1 = right
    direction += relativeDirection;
    if(direction<0){direction+=4} // make sure direction%4 > 0
    if(c==="^"){directions[direction%4]()} // do the movement if going forwards
  }
  // convert destination back to turns
  // the most efficient output has up before left/right before down
  let absoluteRepeat = num => "^".repeat(Math.abs(num));
  let turn = x<0 ? "<" : ">";
  let outp = "";
  if (y>0) { outp += absoluteRepeat(y) } // handle up before left/right
  if (x) { outp+=turn+absoluteRepeat(x) } // handle left/right
  if (y<0) { outp += (outp?turn:turn+turn)+absoluteRepeat(y)) } // handle down (including w/o left/right)
  return outp;
}
Birjolaxew
fonte
Use t&3em vez de t%4por que funciona com o negativo tde modo que você pode remover os 4+e os ()s. (x?"":t)+tpode ser gravado (x?t:t+t)para economizar 1 byte. O código de mudança de direção parece muito longo. Também acho que você provavelmente deveria substituir indexOfe Math.abscom comparações.
Neil
@ Neil Obrigado! Você poderia elaborar um pouco sobre a substituição indexOfpor uma comparação?
precisa saber é o seguinte
O melhor que pude fazer foi t-=b=c<'>'||-(c<'^').
Neil
1

Python 2 , 174 169 165 bytes

Edite 1: -5 bytes, permitindo que a direção esteja fora do intervalo de 0 a 3 e removendo o espaço em branco.

Edite 2: -4 bytes alterando a entrada para (1, 2, 3) em vez de (<, ^,>) desde que o OP o permitia, bem como alterando meu sistema de coordenadas para reduzir meu cálculo de distância.

n,d=[0,0],0
for c in input():exec'd-=1 n[d%2]+=(-1)**(d/2%2) d+=1'.split()[ord(c)-49]
print'2'*n[0]+'13'[n[1]>0]*any(n)+'2'*abs(n[1])+'13'[n[1]>0]*(n[0]<0)+'2'*-n[0]

Experimente online!

Determina as coordenadas finais através dos valores do dicionário que estão sendo executados e, em seguida, apenas imprime o caminho direto para a meta final.

Arnold Palmer
fonte
0

Perl 5 , 185 + 1 (-p) = 186 bytes

/</?$d--:/>/?$d++:/\^/?$m[$d%4]++:0for/./g;$y=$m[0]-$m[2];$x=$m[1]-$m[3];$q='^'x abs$x;$_=A.$q.B.('^'x-$y);$x<0?y/AB/</:$x?y/AB/>/:0;$_=('^'x$y).($x<0?'<':$x>0?'>':'').$q if$y>0;y/AB//d

Experimente online!

Xcali
fonte
0

JavaScript (tipo document.getElementById ()), 343 caracteres

function b(s){s=s.split('');c=[0,0];r=0;p='';w='<';e='>';n='^';for(i in s){r+=s[i]==e?.5:s[i]==w?-.5:0;r=r>1?-.5:r<-.5?1:r;c[1-Math.ceil(Math.abs(r%1))]+=s[i]==n?r>0?1:-1:0;}x=c[0];y=c[1];j=x<0?-x:x;k=y<0?-y:y;f=function(a){p+=a==j?x<0?w:x>0?e:'':j>k?y<0?w:y>0?e:'':y>0?e+e:'';for(i=0;i<a;i++){p+=n}};if(j>k){f(j);f(k)}else{f(k);f(j)}alert(p)}

expandido:

function b(s){

s = s.split('');
c = [0, 0];
r = 0;
p = '';
w = '<';
e = '>';
n = '^';

for(i in s){

    r += s[i] == e ? .5 : s[i] == w ? -.5 : 0;
    r = r > 1 ? -.5 : r < -.5 ? 1 : r;

    c[1 - Math.ceil( Math.abs( r%1 ) )] += s[i] == n ? r > 0 ? 1 : -1 : 0;

}

x = c[0];
y = c[1];
j = x < 0 ? -x : x;
k = y < 0 ? -y : y;

f = function(a){

    p += a == j ? x < 0 ? w : x > 0 ? e : '' : j > k ? y < 0 ? w : y > 0 ? e : '' : y > 0 ? e+e : '';

    for( i = 0; i < a; i++){
        p += n
    }

};

if( j>k ){

    f(j);
    f(k)

} else {

    f(k);
    f(j)

}

alert(p)

}

Uso:

b('^<^^<^^<^^^^')

alertas: >^^>^

Um robô com reverso teria sido útil.

logic8
fonte