Intérprete do RoboZZle

10

Sua tarefa é escrever um intérprete RoboZZle. Se você não conhece o jogo, assista ao vídeo em robozzle.com ou leia minha descrição abaixo.

Um robô vive em uma grade retangular de quadrados coloridos de vermelho, verde, azul ou preto. Quadrados pretos são inacessíveis. Os outros são acessíveis e alguns deles contêm uma estrela. O objetivo é coletar todas as estrelas sem pisar nos quadrados pretos ou cair no mapa. O robô ocupa um quadrado e enfrenta uma direção específica - esquerda, direita, cima ou baixo. Segue instruções de montagem agrupadas nas sub-rotinas F1, F2, ..., F5. Uma instrução é um par de predicado ("nenhum", "se estiver em vermelho", "se estiver em verde", "se estiver em azul") e uma ação ("vá em frente", "vire à esquerda", "vire à direita", "pinte o quadrado atual de vermelho", "pinte de verde", "pinte de azul", "não faça nada", "chame F1", ..., "chame F5"). As chamadas para sub-rotinas usam uma pilha e podem ser recursivas. Assim como na programação convencional, após a conclusão da última instrução de uma sub-rotina, a execução continua a partir do ponto em que a sub-rotina foi chamada. A execução começa na primeira instrução da F1 e continua até que o robô tenha visitado todos os quadrados com estrelas, ou quando o robô pisa em um quadrado preto ou fora do mapa, ou 1000 instruções foram executadas (falhas de predicados e ações "não fazer nada") não conte) ou não há mais instruções para executar (pilha insuficiente).

Entradas:

  • a- uma matriz de caracteres de 12x16 (como normalmente representada no seu idioma, por exemplo, conjunto de seqüências de caracteres) que codifica um mapa - '#'para quadrados inacessíveis (pretos), '*'para quadrados com uma estrela, '.'para o resto

  • c- uma matriz de caracteres de 12x16 que descreve as cores dos quadrados acessíveis - 'R'(vermelho), 'G'(verde) ou 'B'(azul). Quadrados inacessíveis serão representados por uma carta arbitrária dos três.

  • ye x- a linha e coluna com base em 0 do robô; a[y][x]é garantido para ser'.'

  • d- a direção que o robô está enfrentando: 0 1 2 3para a direita, para baixo, esquerda, para cima, ou seja, para (y,x+1), (y+1,x), (y,x-1),(y-1,x)

  • f- uma única sequência, as implementações concatenadas de F1 ... F5. Cada implementação é uma sequência (possivelmente vazia) de pares de ação de predicado (no máximo 10 pares por sub-rotina), finalizada com a '|'.

    • predicados: '_'nenhum, 'r'vermelho, 'g'verde, 'b'azul

    • ações: 'F'vá em frente, 'L'vire à esquerda, 'R'vire à direita, 'r'pinte de vermelho, 'g'pinte de verde, 'b'pinte de azul, '1'ligue para F1, ..., '5'ligue para F5, '_'não faça nada

Você não precisa nomear suas entradas como acima, mas seus valores devem ser os especificados.

Saída: 1(ou true) se o robô coletar todas as estrelas de acordo com as regras, 0( false) caso contrário.

Exemplo :

a=["################","################","##*....*...*#.##","##.####.#####.##","##.####.#####.##","##.####*...*#.##","##.########.####","##*........*#.##","################","################","################","################"]
c=["RRRRRRRRRRRRRRRR","RRRRRRRRRRRRRRRR","RRRBBBBRGGGGRRRR","RRBRRRRGRRRRRRRR","RRBRRRRGRRRRRRRR","RRBRRRRRGGGBRRRR","RRBRRRRRRRRGRRRR","RRRBBBBGGGGBRBRR","RRRRRRRRRRRRRRRR","RRRRRRRRRRRRRRRR","RRRRRRRRRRRRRRRR","RRRRRRRRRRRRRRRR"]
y=2; x=6; d=2

// and then depending on "f":
f="_FrLg2_1|_FbLrR_2||||" // result:1
f="_FrRg2_1|_FbLrR_2||||" // result:0 (stepped on a black square)
f="_FrLrL_1|_FbLrR_2||||" // result:0 (1000-step limit exceeded)
f="_FrLg2__|________||||" // result:0 (stack underflow)

Outro exemplo , envolvendo instruções de "pintura":

a=["#***************","#*###*###*###*##","#*###*###*###*##","***#***#***#***#","***#***#***#***#","*###*###*###*###","***#***#***#***#","***#***#***#***#","***#***#***#***#","*###*###*###*###","*.*#***#***#***#","***#***#***#***#"]
c=["RGGGGGGGGGGGGGGG","RBRRRGRRRGRRRGRR","RBRRRGRRRGRRRGRR","RBRRGGGRGGGRGGGR","BRRRGGGRGGGRGGGR","BRRRGRRRGRRRGRRR","BRRRGGGRGGGRGGGR","RBRRGGGRGGGRGGGR","BRRRGGGRGGGRGGGR","BRRRGRRRGRRRGRRR","BGRRGGGRGGGRGGGR","RBRRGGGRGGGRGGGR"]
y=10; x=1; d=0
f="_2_R_R_1|_FgRgFgFg3rRr4b2_Fgb|_F_F_R|_2_L_r||"
// result:1

Para gerar seu próprio teste, vá para um quebra-cabeça da lista em robozzle.com , tente resolvê-lo (ou não), pressione F12 no seu navegador, digite o console JS:

r=robozzle;s=JSON.stringify;with(r.level)console.log('a='+s(Items)+'\nc='+s(Colors)+'\ny='+RobotRow+'\nx='+RobotCol+'\nd='+RobotDir+'\nf='+s(r.encodeSolution()))

e reformate o resultado para o seu idioma.

Vitórias mais curtas. Sem brechas.

ngn
fonte
11
Podemos usar caracteres distintos para representar os dados em vez dos fornecidos?
HyperNeutrino
11
Para sua resposta do APL ao desafio "Loop it", você pode classificar o último valor do ângulo diminuindo a magnitude complexa.
usar o seguinte comando
11
@ user202729 Ah, eu não esperava um comentário sobre esse desafio aqui :) Sua ideia funciona, obrigado! Vou tentar implementá-lo sem tornar o personagem muito vergonhoso.
NGN
11
Podemos considerar as matrizes de caracteres como uma lista de pares de locais e caracteres?
0
11
@ 0 'O princípio que tenho seguido aqui (veja também o comentário do HyperNeutrino) é permanecer o mais próximo possível do formato de entrada realmente usado no robozzle.com, por isso, receio que não deva ser uma lista de pares.
ngn 27/01

Respostas:

5

Prolog (SWI) , 574 bytes

Z*A:-findall(X^Y-D,(nth0(Y,Z,O),nth0(X,O,C),plus(32,C,D)),L),list_to_assoc(L,A).
N^C^G^[P,I|R]^F^X^Y^D:-map_assoc(\=(74),G);N<1e3,get_assoc(X^Y,G,J),J>67,put_assoc(X^Y,G,78,H),T=N+1,((I\=95,(P=95;get_assoc(X^Y,C,P)))->(between(49,53,I),plus(48,M,I),nth1(M,F,Q),append(Q,R,S),T^C^H^S^F^X^Y^D;member(I,`RL`),E is(D-I//3)mod 4,T^C^H^R^F^X^Y^E;I=70,(D=0,U is X+1;D=1,V is Y+1;D=2,U is X-1;D=3,V is Y-1),(U=X;V=Y),T^C^H^R^F^U^V^D;I>97,put_assoc(X^Y,C,I,W),T^W^H^R^F^X^Y^D);N^C^H^R^F^X^Y^D).
A+C+F+L:-A*G,C*B,split_string(F,"|","",P),maplist(string_codes,P,[M|N]),0^B^G^M^[M|N]^L.

Experimente online!

Isso define um predicado que, quando chamado, terá êxito se todas as estrelas forem coletadas com êxito e falharem de outra forma. O predicado leva os argumentos como tal: a+c+f+x^y^d.. ae cdeve ser uma lista de strings entre aspas de backtick, enquanto fdeve ser uma string de aspas duplas.

Explicação

Este programa contém três predicados, */2, ^/2, e +/2. Os */2predicados definidos na primeira linha são responsáveis ​​por parte do processamento de entrada. O ^/2predicado calcula recursivamente como o robô se move passo a passo e obtém êxito se o robô coletar legalmente todas as estrelas e falhar de outra forma. O +/2predicado é o principal predicado do programa e prepara a entrada para o ^/2predicado com alguma ajuda do */2predicado. Observe que tecnicamente cada um desses predicados leva apenas dois argumentos, mas usando operadores e correspondência de padrões, eles podem se comportar como se tivessem mais argumentos (discuto esse fenômeno mais detalhadamente aqui ).

*/2

Z*A:-findall(X^Y-D,(nth0(Y,Z,O),nth0(X,O,C),plus(32,C,D)),L),list_to_assoc(L,A).

Esse predicado recebe dois argumentos. A primeira é uma lista de listas de códigos de caracteres (é assim que o Prolog analisa as strings citadas pelo backtick). O segundo é um mapa associativo de pontos no mapa 12x16 (representado como X^Y) a 32, mais o código de caractere armazenado naquele ponto na lista de listas de códigos de caractere. O 32 é adicionado a cada um dos códigos de caracteres para que, para a matriz de cores, ele transforme os caracteres maiúsculos em caracteres minúsculos.

A maneira como isso é feito gera uma lista de pares de pontos e os códigos de caracteres nesse ponto usando findall/3. Em seguida, ele é usado list_to_assoc/2para criar o mapa associativo correspondente de pontos para o código de caractere naquele ponto.

O findall/3predicado é que um interno usa um "modelo" como seu primeiro argumento, um objetivo como seu segundo argumento e uma lista como seu terceiro argumento. O predicado preenche a lista com todos os valores possíveis do modelo que fazem com que o objetivo seja bem-sucedido. Devido à precedência do operador, o modelo que é passado para findall/3dentro */2é analisado como (X^Y)-D. O -operador representa um par de dois valores no Prolog, de modo que o modelo representa a localização do ponto ( X^Y) emparelhado com 32 mais o código de caractere do ponto ( D). Observe que o ^usado para representar o ponto não está de forma alguma conectado ao ^/2predicado.

Vamos considerar a meta que é passada para o findall/3predicado.

nth0(Y,Z,O),nth0(X,O,C),plus(32,C,D) % Note that the O (oh) is not a 0 (zero)

O objetivo contém três predicados, cada um dos quais precisa ter sucesso para que o objetivo seja bem-sucedido. O nth0/3predicado usado duas vezes é usado para obter o valor em um índice específico da lista (o 0nome indica que é zero indexado). A primeira chamada para ele armazena a Yth linha da matriz de caracteres Oenquanto a segunda chamada armazena o Xth caractere nessa linha C. O predicado final será plus/3bem-sucedido se seus dois primeiros argumentos somarem seu terceiro argumento. Isso é usado para fazer com que o código de caractere no par seja 32 maior que o código de caractere na matriz de caracteres que, como mencionado acima, transformará todas as letras maiúsculas em minúsculas.

Finalmente, findall/3armazena todas as X^Y-Dcombinações que fazem com que seu objetivo seja bem-sucedido na lista da Lqual o mapa associativo é construído.

Mais em breve...

0 '
fonte
4

JavaScript (ES6), 298 276 264 bytes

Guardado 8 bytes graças a @ngn

Recebe entrada como (a,c,x,y,d,f), onde ae csão matrizes de matrizes de caracteres. Retorna 0ou 1.

(a,c,x,y,d,f,k=1e3)=>(g=(F,p=0,s=f.split`|`[F],r=a[y])=>!k|!r|x&16||r[x]<'$'?2:/\*/.test(a)?(r[x]=o=0,(I=s[p+1],P=s[p])&&(P<'b'|P==c[y][x].toLowerCase()&&I!='_'&&k--?+I?o=g(I-1):I=='L'?d--:I=='R'?d++:I<'b'?y+=(d&=3,x-=~-d%2,2-d)%2:c[y][x]=I:0,o||g(F,p+2))):1)(0)&1

Casos de teste

Comentado

(                                           // main function taking:
  a, c, x, y, d, f,                         //   - input variables
  k = 1e3                                   //   - k = instruction counter
) => (                                      //
  g = (                                     // g = recursive execution function, taking:
    F,                                      //   - F = subroutine id
    p = 0,                                  //   - p = instruction pointer
    s = f.split`|`[F],                      //   - s = instruction string
    r = a[y]                                //   - r = current row in a[]
  ) =>                                      //
    !k |                                    // if we've executed 1000 instructions
    !r | x & 16 ||                          // or we've felt out of the map
    r[x] < '$' ?                            // or we've reached a black square:
      2                                     //   exit with error code 2
    :                                       // else:
      /\*/.test(a) ? (                      //   if there are still some stars:
        r[x] = o = 0,                       //     mark the current cell as visited
        (I = s[p + 1], P = s[p]) &&         //     I = instruction, P = predicate
        (                                   //     if P is defined:
          P < 'b' |                         //       if the predicate is '_'
          P == c[y][x].toLowerCase()        //       or it matches the color of the cell
          && I != '_'                       //       and the instruction is not '_',
          && k-- ?                          //       then decrement k and:
            +I ?                            //         if I is '1' ... '5':
              o = g(I - 1)                  //           process call to subroutine
            :                               //         else:
              I == 'L' ?                    //           if I is 'L':
                d--                         //             turn left
              :                             //           else:
                I == 'R' ?                  //             if I is 'R':
                  d++                       //               turn right
                :                           //             else:
                  I < 'b' ? (               //               if I is not a color:
                    y += (                  //                 I must be 'F',
                      d &= 3,               //                 so make the bot advance
                      x -= ~-d % 2,         //                 by updating x
                      2 - d                 //                 and y
                    ) % 2                   //
                  ) :                       //               else:
                    c[y][x] = I             //                 paint the current cell
          :                                 //       else:
            0,                              //         do nothing
          o ||                              //       provided that o is equal to 0,
          g(F, p + 2)                       //       go on with the next instruction
        )                                   //     end of instruction execution
      ) :                                   //   else:
        1                                   //     success: return 1
  )(0) & 1                                  // initial call to the subroutine F1
Arnauld
fonte
x+='2101'[d&3]-1,y+='1210'[d&3]-1->d&=3,x+=(1-d)%2,y+=(2-d)%2
NGN
11
xmuda por no máximo 1, então eu acho que você pode substituir x&~15porx&16
ngn
1

APL (Dyalog Classic) , 236 233 bytes

-3 graças a Erik, o Outgolfer

Agora que dei o bônus, estou postando uma solução de amostra para meu próprio desafio. Há espaço para melhorias aqui - fique à vontade para copiar e jogar mais.

a c r d f←⎕⋄c819cF0,('|'1f)/⍳≢ftn0
{~(⊂r)∊⍳⍴a:0'#'=ra:0p q2f↓⍨⊃⌽t⋄(_p'|')∧×≢t:0_:∇t↓←¯1⋄(⊃⌽t)+←2⋄~p'_',rc:∇0n+←1n>999:0⋄(ra)←'.'⋄~'*'a:1r+←(q'F'11 90j1*dd+←4|'.R.L'qq'rgb':∇(rc)←qq∊⎕d:∇t,←F[⍎q]⋄∇0}0

Experimente online!

Igual ao anterior, expandido com comentários:

io0                    0-based indices (not counted in the score)
a c r d f←⎕              decompose eval'ed input (⎕) into variables
c←819⌶c                 ⍝ make c lowercase
F←0,('|'=¯1⌽f)/⍳≢f      ⍝ split f at the '|'-s
t←n←0                   ⍝ t:stack, n:step counter
{                       ⍝ lambda
  ~(⊂r)∊⍳⍴a:0           ⍝ if the robot is off the map, return 0
  '#'=r⌷a:0             ⍝ if the robot is on a wall, return 0
  p q2f↓⍨⊃⌽t           current instruction - p:predicate, q:action
  (_p'|')∧1≥≢t:0       if at end of func and stack is empty, return 0
  _:∇t↓←¯1               if at end of func, pop from stack and recurse
  (⊃⌽t)+←2               increment program counter (top of stack)
  ~p'_',rc:∇0          if predicate doesn't match cell colour, recurse
  n+←1⋄n>999:0          ⍝ if too meany steps, return 0
  (r⌷a)←'.'             ⍝ consume star
  ~'*'∊a:1              ⍝ if no more stars left, return 1
  r+←(q≡'F')×11 9○0j1*d ⍝ if action is F, move forward
  d+←4|'.R.L'⍳q         ⍝ if action is L or R, turn left or right
  q∊'rgb':∇(r⌷c)←q      ⍝ if action is paint (r,g,b), do it
  q∊⎕d:∇t,←F[⍎q]        ⍝ if action is F1...F5, push on stack and recurse
  ∇0                    ⍝ action is nop (_), recurse
}0                      ⍝ call the lambda (argument will be ignored)
ngn
fonte
233 bytes
Erik the Outgolfer
@EriktheOutgolfer alteração aplicada, obrigado
ngn