Cubificador mais eficiente


19

O Cubically é muito tedioso para escrever manualmente qualquer código. Seu desafio é traduzir o texto ASCII no código-fonte do Cubically.

Cubicamente

Este é apenas um rápido resumo do Cubically; o repositório tem um guia e detalhes mais completos.

Cubicamente é um esolang que escrevi há algum tempo, projetado para ser doloroso de usar. Ele contém dois pedaços de memória, um cubo de Rubik 3x3x3 e um registro chamado "bloco de notas".

Memória

O Cubo de Rubik interno é inicializado assim:

   000
   000          top face
   000
111222333444    left, front, right, and back faces, respectively
111222333444
111222333444
   555
   555          down face
   555

Depois de executar uma rotação de 90 ° no sentido horário na face direita, o cubo de memória ficaria assim:

   002
   002
   002
111225333044
111225333044
111225333044
   554
   554
   554

Comandos

Um caractere não inteiro define o comando padrão. Para cada número inteiro antes que o comando padrão seja definido novamente, o comando é executado com esse número inteiro. Por exemplo, x524y312executaria o comando xcom 5, depois com 2, depois com 4, depois executaria o comando ycom 3, depois com 1 e depois com 2.

Os números inteiros que os comandos usam representam índices de face. Então, x0teria desempenho xna face UP (indexada 0). x1executaria xna face ESQUERDA (indexada 1) e assim por diante.

Executar qualquer comando com 6executará esse comando no valor do bloco de notas. Executar qualquer comando com um número inteiro acima de 6 resultará em um erro.

Aqui estão alguns comandos de exemplo:

  • R1 - gire a face DIREITA no sentido horário 90 ° para que o cubo interno pareça com o segundo exemplo acima
  • R11 - gire a face DIREITA no sentido horário 90 ° duas vezes, idêntico ao R2
  • +0 - adicione todos os valores da face UP ao bloco de notas
  • +000 - adicione todos os valores da face UP ao bloco de notas três vezes
  • @6 - imprime o rosto inexistente do sexto índice (memória) como um caractere
  • %4 - imprime a soma de todos os valores na face BACK como um número inteiro

Uma lista completa de comandos e sintaxe está disponível no repositório .

Desafio

Você pegará o texto ASCII como entrada e imprimirá um programa Cubically como saída.

Exemplos (roubados daqui e aqui ):

Input -> Output
Hello, World! -> +53@6+1F2L2+0@6L2F2U3R3F1L1+2@66L3F3R1U1B3+0@6:4U1R1+00@6-000@6*0-4+000@6-00@6+2-000000@6-5+4000@6-00@6/0+00@6:0+0/0+00@6
1$2$3$4$5$6$7$8$9$10$ -> B1+2/2%6@4+00/0%6@4+00/1%6@4+21/1%6@4+30/0%6@4+22/1%6@4+22/1%6@4+40/1%6@4+52/1%6@4+42/1%6@4

Regras

  • Seu programa não pode conter um dicionário contendo as traduções para as 100 caixas de teste.
  • Seu programa deve terminar em menos de 180 segundos (nenhum programa de força bruta que leva semanas).
  • Seu programa deve gerar um código Cubically válido que termine em menos de 180 segundos.
  • Seu programa receberá entrada por entrada padrão, a menos que você queira mexer com o driver de teste.
  • Seu programa deve gerar um código cubicamente que não produza nada além da entrada do seu programa quando executado. ಠ_ಠ

Pontuação

Você testará seu programa com 100 seqüências pseudo-aleatórias de comprimento pseudo-aleatório. (É fornecido um script bash que fará isso por você.) Veja como você pontuará:

  • Deixe o comprimento do programa de saída ser o .
  • Seja o comprimento da string de entrada l .
  • Seja uma variável r o resultado de o / l .
  • Encontre a média de todos os r : (r 1 + r 2 + r ... + r 100 ) / 100 .

Teste com este script. Você precisará modificá-lo conforme as instruções. Observe que o programa não verifica se a saída é um código cubicamente válido. Se você não conseguir que o script funcione, eu posso ajudar. Ping me na sala de bate-papo cubicamente .



" @6- imprimiria a soma da face inexistente do sexto índice (bloco de notas) como caractere" seria mais precisa? %4Também é uma soma? Os +comandos sum face são adicionados a todos os valores ou ...?
27617 Jonathan Allan

@ JonAllAllan @6/ %6apenas imprime diretamente o valor do bloco de notas como um caractere / número inteiro. @x/ %x(onde x é qualquer face existente) adiciona todos os valores na xface indexada e imprime a soma como um caractere / número inteiro. +adiciona todos os valores na face especificada ao registro.
MD XF

Ah, por alguma razão, eu estava pensando no bloco de notas como tendo 9 valores também.
22617 Jonathan Allan

Respostas:


4

C ++ 11, Pontuação : 6.37

#include <iostream>
#include <vector>
#include <array>
#include <limits>
#include <algorithm>

const int inf = std::numeric_limits<int>::max(),
maxDiff = 128, nFace = 6;
std::array<int, maxDiff+1> plusvalue, totalvalue, plustrace, totaltrace;
std::array<int, nFace> input;

void prtrace(int value) {
    while (value) {
        std::cout << plustrace[value];
        value -= input[plustrace[value]];
    }
}

void prexpr(int i) {
    char xorwt = 0;
    if (i < 0) {
        xorwt = '+' ^ '-';
        i = -i;
    }
    if (totalvalue[i] != 0 && totalvalue[i] != inf) {
        std::cout << (char)('+' xor xorwt);
        prtrace(totaltrace[i]);
        if (totaltrace[i] != i) {
            std::cout << (char)('-' xor xorwt);
            prtrace(totaltrace[i] - i);
        }
    }
}

int main() {
    std::cout << "RU";
    input = {6, 15, 27, 26, 19, 42};
    std::cin >> std::noskipws;

    std::fill(plusvalue.begin(), plusvalue.end(), inf);
    plusvalue[0] = 1; // '+'
    for (int i = 0; i < nFace; ++i) { // knapsack, each value repeated inf times
        int val = input[i];
        if (val == 0) continue;
        for (int p = 0; p <= maxDiff - val; ++p) {
            if (plusvalue[p] != inf && plusvalue[p + val] > plusvalue[p] + 1) {
                plusvalue[p + val] = plusvalue[p] + 1;
                plustrace[p + val] = i;
            }
        }
    }
    for (int p = 0; p <= maxDiff; ++p) totalvalue[p] = plusvalue[p], totaltrace[p] = p;
    totalvalue[0] = 0;
    for (int sub = 1; sub <= maxDiff; ++sub) {
        if (plusvalue[sub] == inf) continue;
        for (int p = 0; p <= maxDiff - sub; ++p) {
            if (plusvalue[p+sub] != inf && totalvalue[p] > plusvalue[p+sub] + plusvalue[sub]) { // include '+'s
                totalvalue[p] = plusvalue[p+sub] + plusvalue[sub];
                totaltrace[p] = p+sub;
            }
        }
    }

//    // Note: plustrace[x] = i<=nFace : plustrace[x-input[i]] + 1 = plustrace[x]
//    long long sum = 0;
//    for (int i = 0; i <= maxDiff; ++i) {
//        sum += totalvalue[i];
//        std::cout << i << '\t' << totalvalue[i] << '\t';
//        prexpr(i);
//        std::cout << '\n';
//    }
//
//    std::cout << "_______________________________\n\nAverage = " << sum / (maxDiff + 1.) << '\n';

// RU 3.98131

    char ch;
    int cur = 0;
    while (std::cin >> ch) {
        int diff = ch - cur;
        prexpr(diff);
        std::cout << "@6";
        cur += diff; // cur = ch
    }
}

/*
RU 3.98131
*/

Experimente online! (gerar código Cubicamente a partir de ASCII) e (executar código Cubicamente)

Explicação:

  • Primeiro, o programa imprime "RU", que soma a face de {0,9,18,27,36,45}a {6, 15, 27, 26, 19, 42}. O que torna útil esse conjunto de soma de faces é que o MDC é 1; portanto, pela identidade de Bézout, existe uma maneira de construir qualquer número a dpartir de uma soma (ou diferença) desses números.
  • Portanto, se o próximo caractere é che o valor atual do bloco de notas é n, então d = ch - n, podemos executar comandos Cubicamente no formato +{digits from 0 to 5}-{digits from 0 to 5}tal que o valor do bloco de notas se torne ch. Em seguida, basta executar %6para imprimir o valor do bloco de notas.
  • Para encontrar a maneira mais eficiente de expressar dcomo uma soma / diferença de números no conjunto de soma de faces, eu uso o algoritmo Knapsack para todos os números de 0 a 128. Por exemplo, para d=1, o programa recebe 27 - 26 = 1, então imprime +2-3, o que é 27 - 26 = 1. O que pode ser visto ao executar o programa com entrada abc, saída do programa

    RU + 4333 @ 6 + 2-3 @ 6 + 2-3 @ 6


Woah, ótimo trabalho! Acho que o algoritmo de mochila é o que estávamos procurando.
TehPers 08/09

Observe que, devido a atualizações de idioma, você pode obter uma pontuação melhor por meio de chamadas @implicitamente - @6pode ser reduzido para @todos os casos.
MD XF

17

Lua, Pontuação : 85,91 13,50 13,20 12,70 9,41 9,32 9,83 9,66 9,12 9,06 8,03 (Médio)

-- Get input
local inp = io.read("*a")

local out = ""
local faces = { [5] = 45, [4] = 36, [3] = 27, [2] = 18, [1] = 9 }
local lastChar = nil

-- Mode the program is in
-- 2 = not set (needs :), 1 = just set (needs +), 0 = normal
local mode = 1;
for i = 1, inp:len() do
  -- Character value at current position
  local c = string.byte(inp, i)

  if c == lastChar then
    -- Repeat character
    out = out .. "6"
  elseif c % 9 == 0 and c <= 45 then
    if #out == 0 then
      out = out .. "@"
    end
    out = out .. (c / 9)
  else
    local c2 = c

    -- Handle if difference from lastChar is divisible by 9
    if lastChar and (c - lastChar) % 9 == 0 then
      local difference = c - lastChar
      if difference > 0 then
        out = out .. "+"
      else
        out = out .. "-"
        difference = difference * -1
      end

      for index = 5, 1, -1 do
        local face = faces[index]
        while difference >= face do
          difference = difference - face
          out = out .. index
        end
      end
      c = 0
    end

    -- Handle anything not divisible by 9
    local extra = c % 9
    if extra > 0 then
      -- Try to optimize by dividing by 9, if possible
      if lastChar and math.floor(lastChar / 9) == extra then
        out = out .. "/1"
        mode = 1
        extra = 0
      else
        while extra > 0 do
          local n = extra > 5 and 5 or extra

          if mode == 2 then
            out = out .. ":"
            mode = 1
          elseif mode == 1 then
            out = out .. "+"
            mode = 0
          end
          out = out .. n
          extra = extra - n
        end
        out = out .. "/1"
        mode = 1
      end

      c = c - (c % 9)
    end

    -- Handle anything divisible by 9
    for index = 5, 1, -1 do
      local face = faces[index]
      while c >= face do
        if mode == 2 then
          out = out .. ":"
          mode = 1
        elseif mode == 1 then
          out = out .. "+"
          mode = 0
        end
        c = c - face
        out = out .. index
      end
    end

    out = out .. "@6"
    lastChar = c2
  end

  mode = 2
end
print(out)

Experimente online!

Ok, acho que não posso mais otimizar isso.

Esta versão interage com cada caractere, adicionando c% 9 (onde c é o valor decimal do caractere) :5+2/1, e adiciona as partes divisíveis por 9 adicionando o valor dessa face. Por exemplo: :2/1+551@para imprimir "e", onde :2/1adiciona 2, +551adiciona 99 (9 * (5 + 5 + 1) ou 9 * 11) e @imprime a saída. A entrada é lida com io.read().

As otimizações incluem adicionar / subtrair diretamente após a impressão, se a diferença entre os caracteres for um múltiplo de 9, dividir o valor atual, se possível, em vez de definir c% 9 do zero, e repetir os caracteres imprimindo o valor atual novamente, em vez de recalculá-lo. Além disso, implementei o método de Kamil de imprimir instantaneamente qualquer rosto que já contenha o valor-alvo e a sugestão do MD XF de não usar :no início, mas apenas começar com a +.


11
Você sempre pode comentar suas próprias perguntas e respostas, mas ainda não possui privilégios gerais para comentar. Não deve ser longo com respostas como este (ou, mais provavelmente, só mais esta resposta, uma vez por poucas pessoas vê-lo)
Kamil Drakari

2
@MDXF Eu não sou tão legal assim: P #
Kamil Drakari

11
Você pode mudar local inp = io.read()para local inp = io.read("*all"). Isso resolve o problema.
MD XF

11
Outra otimização possível - como o bloco de notas começa em 0, você não precisa produzir, por exemplo :5+124, basta escrever +5124, o que provavelmente diminuirá um pouco a pontuação se você o ajustar corretamente.
MD XF

11
Você provavelmente obterá uma pontuação melhor se alterar sua resposta para oferecer suporte a algumas das atualizações recentes do Cubically, como mudanças de rosto implícitas.
MD XF

16

Cubicamente , pontuação : 86.98

U3D1R3L1F3B1U1D3~:7+1(-1@3(-1%1)6:1+3111@6%1-31111+004@6:1+11111%6:1+45@6:1-1%6~:7+1)6 

Experimente online!

Acontece que tudo que você precisa é de loops condicionais, uma face igual a 1 e um comportamento consistente de final de entrada.

U3D1R3L1F3B1U1D3     set LEFT face sum to 1
~:7                  read input, set notepad to input
+1                   add 1 to notepad

(                    open loop that can always be jumped to
 -1                   subtract 1 from notepad
 @3                   print RIGHT face (ASCII 43 '+')

            ##   the following mechanism gets the output program's   ##
            ##   notepad to the current inputted ASCII value:        ##

 (                    open loop that can always be jumped to
  -1                   subtract 1 from notepad
  %1                   print '1'
 )6                   jump back to loop while notepad is nonzero

            ##   the following mechanism prints "/1@6:1"             ##

 :1+3111@6            set notepad to 47,    print (ASCII 47 '/')
 %1                   print LEFT face       print (integer '1')
 -31111+004@6         set notepad to 64,    print (ASCII 64 '@')
 :1+11111%6           set notepad to 6,     print (integer 6)
 :1+45@6              set notepad to 58,    print (ASCII 58 ':')
 :1-1%6               set notepad to 0,     print (integer 1)

 ~                    read input
 :7+1                 set notepad to input plus 1, so EOF changes to zero
)6                    loop if notepad is truthy

A adição / subtração da face ESQUERDA é fazer com que o loop termine quando o EOF for lido.


2
Você tem que estar brincando. Isto é incrível.
MD XF

Oh, ei, ele ainda tem uma pontuação melhor do que minha resposta original em C #!
Kamil Drakari

Observe que, devido a atualizações de idioma, você pode obter uma pontuação melhor por meio de chamadas @implicitamente - @6pode ser reduzido para @todos os casos.
MD XF

9

C # (.NET Core) , Pontuação: 129,98 11,73 10,82 9,62 10,33 10,32 10,20

-1,2 ponto da sugestão do MD XF para usar em @6666...vez de @6@6@6@6...para repetir caracteres e sequência de inicialização superior

static void Main()
{
	List<byte> input = new List<byte>();
            int inChar = Console.Read();
            while (inChar != -1)
            {
                input.Add((byte)inChar);
                inChar = Console.Read();
            }


            Console.Write("U3D1R3L1F3B1U1D3");
            byte[] sides = new byte[] { 20, 1, 14, 43, 24, 33 };

            byte currentChar = 0;

   	    if(currentChar == input[0] || sides.Contains(input[0])) Console.Write("@");

            foreach (byte character in input)
            {
		if (currentChar == character)
		{
			Console.Write("6");
			continue;
		}
		
		if (sides.Contains(character))
		{
			Console.Write(Array.IndexOf(sides, character));
			continue;
		}
                if (currentChar < character)
                {
                    Console.Write("+");
                    while (currentChar < character)
                    {
                        byte nextAdd = sides.Where(s => s + currentChar <= character).Max();
                        currentChar = (byte)(currentChar + nextAdd);
                        Console.Write(Array.IndexOf(sides, nextAdd));
                    }
                }

                if (currentChar > character)
                {
                    Console.Write("-");
                    while (currentChar > character)
                    {
                        byte nextSub = sides.Where(v => currentChar - v >= character).Max();
                        currentChar = (byte)(currentChar - nextSub);
                        Console.Write(Array.IndexOf(sides, nextSub));
                    }
                }

                Console.Write("@6");
            }
}

Experimente online!

Minha versão mais recente realmente faz alguma manipulação do cubo! Yay!

Na primeira Console.Write, há uma manipulação fixa MD XF elaborada que cria este cubo:

   242
   202
   242
000131555313
010121535343
000131555313
   424
   454
   424

O significado desse cubo é que um de seus lados tem uma soma de 1, permitindo manipulações do bloco de notas em uma escala menor que os múltiplos de nove e, em particular, simplifica o movimento relativo, em vez de precisar começar do zero cada caractere; Nesse algoritmo, adição e subtração são usadas para seguir o caminho mais curto entre os caracteres.

A versão da inicialização do MD XF faz com que o lado 2 tenha uma soma de 14, o que economiza muitos bytes de saída para distâncias ASCII entre 14 e 20.

Agora pode lidar com entradas com novas linhas internas, Console.Read () obtém caracteres individuais até o final do arquivo; veja o link do TIO que deve ter a entrada

Hello, 
World!

Raspou algumas frações de um ponto, emitindo imediatamente um caractere, se o valor ASCII já existir de um lado.

Script de teste cortesia do MDXF


Submissão anterior aqui e explicação:

Isso é meio chato, mas até onde eu sei, funciona. É certo que só tentei, Hello, World!mas executei a saída no intérprete TIO Cubically e ela produziu "Olá, mundo!" então eu assumi que funciona.

Em vez de realmente manipular o cubo, o bloco de notas é simplesmente incrementado pela soma da 1 face (9) repetidamente até obter o valor correto para cada caractere e depois imprimi-lo.


Comentários não são para discussão prolongada; esta conversa foi movida para o bate-papo .
Martin Ender

@MartinEnder Você poderia movê-los para a sala de bate-papo existente ?
MD XF

@MDXF Eu podia, mas não sei dizer se eles acabariam completamente fora do lugar e do contexto naquela sala de bate-papo.
Martin Ender

@MartinEnder Os comentários são mais antigos que a sala de bate-papo, então eles apareceriam na transcrição, certo?
MD XF

Eles iriam. Obrigado, vou passar as mensagens.
Martin Ender
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.