Cubificateur le plus efficace


19

Cubically est trop fastidieux pour écrire manuellement un code. Votre défi est de traduire le texte ASCII en code source Cubically.

Cubiquement

Ce n'est qu'un rapide aperçu de Cubically; le référentiel a un guide et des détails plus complets.

Cubically est un esolang que j'ai écrit il y a quelque temps, conçu pour être pénible à utiliser. Il contient deux morceaux de mémoire, un Rubik's Cube 3x3x3 et un registre appelé "bloc-notes".

Mémoire

Le Rubik's Cube interne est initialisé comme ceci:

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

Après avoir effectué un virage à 90 ° dans le sens horaire sur la face droite, le cube mémoire ressemblerait à ceci:

   002
   002
   002
111225333044
111225333044
111225333044
   554
   554
   554

Commandes

Un caractère non entier définit la commande par défaut. Pour chaque entier avant que la commande par défaut ne soit à nouveau définie, la commande est exécutée avec cet entier. Par exemple, x524y312exécuter la commande xavec 5, puis avec 2, puis avec 4, puis exécuter la commande yavec 3, puis avec 1, puis avec 2.

Les entiers utilisés par les commandes représentent des index de faces. Il en x0serait de même xpour la face UP (indexée 0). x1se produirait xsur la face GAUCHE (indexée à 1), et ainsi de suite.

L'exécution d'une commande avec 6exécutera cette commande sur la valeur du bloc-notes. L'exécution d'une commande avec un entier supérieur à 6 entraînera une erreur.

Voici quelques exemples de commandes:

  • R1 - tournez la face DROITE dans le sens des aiguilles d'une montre de 90 ° pour que le cube interne ressemble au deuxième exemple ci-dessus
  • R11 - tourner deux fois la face DROITE dans le sens des aiguilles d'une montre de 90 °, identique à R2
  • +0 - ajouter toutes les valeurs de la face UP au bloc-notes
  • +000 - ajouter trois fois toutes les valeurs de la face UP au bloc-notes
  • @6 - imprimer la face inexistante indexée en 6e (mémoire) en tant que caractère
  • %4 - imprimer la somme de toutes les valeurs sur la face arrière sous forme d'entier

Une liste complète des commandes et de la syntaxe est disponible dans le référentiel .

Défi

Vous prendrez du texte ASCII en entrée et imprimerez un programme cubique en sortie.

Exemples (volés d' ici et ici ):

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

Règles

  • Votre programme peut ne pas contenir de dictionnaire contenant les traductions des 100 tests.
  • Votre programme doit se terminer en moins de 180 secondes (aucun programme de force brute qui prend des semaines).
  • Votre programme doit générer un code cubique valide qui se termine en moins de 180 secondes.
  • Votre programme prendra la saisie via la saisie standard, sauf si vous voulez jouer avec le pilote de test.
  • Votre programme doit produire du code cubique qui ne produit rien d'autre que l'entrée de votre programme lors de son exécution. ಠ_ಠ

Notation

Vous testerez votre programme avec 100 chaînes pseudo-aléatoires de longueur pseudo-aléatoire. (Un script bash est fourni qui fera cela pour vous.) Voici comment vous allez marquer:

  • Soit la longueur du programme de sortie égale à o .
  • Soit la longueur de la chaîne d'entrée l .
  • Soit une variable r le résultat de o / l .
  • Trouvez la moyenne de tous les r : (r 1 + r 2 + r ... + r 100 ) / 100 .

Testez avec ce script. Vous devrez le modifier comme indiqué. Notez que le programme ne vérifie pas si la sortie est un code cubique valide. Si vous ne pouvez pas faire fonctionner le script, je peux vous aider. Ping moi dans la salle de chat Cubically .



Est-ce que " @6- imprimer la somme de la face inexistante indexée 6 (bloc-notes) en tant que caractère" serait plus précis? Est-ce %4aussi une somme? Les +commandes font-elles face à la somme puis ajoutent cela à toutes les valeurs ou ...?
Jonathan Allan

@JonathanAllan @6/ %6imprime directement la valeur du bloc-notes sous forme de caractère / entier. @x/ %x(où x est une face existante) ajoute toutes les valeurs sur la xface -indexée et imprime la somme sous forme de caractère / entier. +ajoute toutes les valeurs de la face spécifiée au registre.
MD XF

Ah, pour une raison quelconque, je pensais que le bloc-notes avait aussi 9 valeurs.
Jonathan Allan

Réponses:


4

C ++ 11, score : 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
*/

Essayez-le en ligne! (générer du code Cubically à partir de l'ASCII) et (exécuter du code Cubically)

Explication:

  • Le programme imprime d'abord "RU", qui fait la somme des faces de {0,9,18,27,36,45}à {6, 15, 27, 26, 19, 42}. Ce qui rend cet ensemble de somme de faces utile, c'est que le pgcd est 1, donc par l'identité de Bézout, il existe un moyen de construire n'importe quel nombre à dpartir d'une somme (ou différence) de ces nombres.
  • Par conséquent, si le caractère suivant est chet que la valeur actuelle du bloc-notes est n, alors laissez d = ch - n, nous pouvons exécuter des commandes cubiquement sous la forme +{digits from 0 to 5}-{digits from 0 to 5}telle que la valeur du bloc-notes devienne ch. Ensuite, exécutez simplement %6pour imprimer la valeur du bloc-notes.
  • Pour trouver le moyen le plus efficace d'exprimer dsous forme de somme / différence de nombres dans l'ensemble de somme de faces, j'utilise l'algorithme Knapsack pour tous les nombres de 0 à 128. Par exemple, pour d=1, le programme obtient 27 - 26 = 1, donc il s'imprime +2-3, ce qui est 27 - 26 = 1. Qui peut être vu lors de l'exécution du programme avec entrée abc, la sortie du programme

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


Woah, excellent travail! L'algorithme Knapsack est ce que nous recherchions, je suppose.
TehPers

Notez qu'en raison des mises à jour linguistiques, vous pouvez obtenir un meilleur score en appelant @implicitement - @6peut être raccourci @dans tous les cas.
MD XF

17

Lua, Score : 85,91 13,50 13,20 12,70 9,41 9,32 9,83 9,66 9,12 9,06 8,03 (moyenne)

-- 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)

Essayez-le en ligne!

D'accord, je ne pense plus pouvoir optimiser cela.

Cette version parcourt chaque caractère, en ajoutant c% 9 (où c est la valeur décimale du caractère) en faisant :5+2/1, puis ajoute les parties divisibles par 9 en ajoutant la valeur faciale. Par exemple: :2/1+551@pour imprimer "e", où :2/1ajoute 2, +551ajoute 99 (9 * (5 + 5 + 1) ou 9 * 11) et @imprime la sortie. L'entrée est lue avec io.read().

Les optimisations incluent l'ajout / la soustraction directe après l'impression si la différence entre les caractères est un multiple de 9, la division de la valeur actuelle si possible plutôt que la définition de c% 9 à partir de zéro, et la répétition des caractères en imprimant à nouveau la valeur actuelle plutôt que de la recalculer. De plus, j'ai implémenté la méthode de Kamil pour imprimer instantanément n'importe quel visage qui contient déjà la valeur cible, et la suggestion de MD XF de ne pas utiliser :au début, mais plutôt de commencer par un +.


1
Vous pouvez toujours commenter vos propres questions et réponses, mais vous n'êtes pas encore tout à fait aux privilèges généraux de commentaire. Ne devrait pas être long avec des réponses comme celle-ci (ou, plus probablement, juste cette réponse une fois que quelques personnes de plus la verront)
Kamil Drakari

2
@MDXF Je ne suis pas si gentil que ça: P
Kamil Drakari

1
Vous pouvez changer local inp = io.read()pour local inp = io.read("*all"). Cela résout le problème.
MD XF

1
Une autre optimisation possible - puisque le bloc-notes commence à 0, vous n'avez pas réellement besoin de sortir, par exemple :5+124, vous pouvez simplement écrire +5124, ce qui réduira probablement un peu le score si vous le modifiez correctement.
MD XF

1
Vous obtiendrez probablement un meilleur score si vous modifiez votre réponse pour prendre en charge certaines des mises à jour cubiques récentes, telles que les changements de visage implicites.
MD XF

16

Cubiquement , Score : 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 

Essayez-le en ligne!

Il s'avère que vous n'avez besoin que de boucles conditionnelles, d'une face égale à 1 et d'un comportement de fin d'entrée cohérent.

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

L'ajout / la soustraction de la face GAUCHE consiste à mettre fin à la boucle lorsque EOF est lu.


2
Vous avez obtenu plaisantez. C'est incroyable.
MD XF

Oh hé, il a même un meilleur score que ma réponse C # d'origine!
Kamil Drakari

Notez qu'en raison des mises à jour linguistiques, vous pouvez obtenir un meilleur score en appelant @implicitement - @6peut être raccourci @dans tous les cas.
MD XF

9

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

-1,2 point de la suggestion de MD XF d'utiliser @6666...au lieu de @6@6@6@6...pour répéter le caractère et séquence d'initialisation supérieure

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");
            }
}

Essayez-le en ligne!

Ma dernière version fait en fait une certaine manipulation du cube! Yay!

Ce premier Console.Writepoint, il y a une manipulation fixe MD XF qui a créé ce cube:

   242
   202
   242
000131555313
010121535343
000131555313
   424
   454
   424

La signification de ce cube est que l'un de ses côtés a une somme de 1, permettant des manipulations du bloc-notes à une échelle plus petite que des multiples de neuf, et en particulier il simplifie le mouvement relatif plutôt que d'avoir à partir de zéro pour chaque caractère; dans cet algorithme, l'addition et la soustraction sont utilisées pour emprunter le chemin le plus court entre les caractères.

La version de l'initialisation de MD XF fait que le côté 2 a une somme de 14, ce qui économise de nombreux octets de sortie pour des distances ASCII entre 14 et 20.

Peut désormais gérer les entrées avec des sauts de ligne internes, Console.Read () obtient des caractères individuels jusqu'à la fin du fichier; voir le lien TIO qui devrait avoir l'entrée

Hello, 
World!

Rasé quelques fractions de point en sortant immédiatement un caractère si sa valeur ASCII se trouve déjà exister d'un côté.

Script de test gracieuseté de MDXF


Soumission précédente ici et explication:

C'est un peu ennuyeux, mais pour autant que je sache, cela fonctionne. Certes, j'ai seulement essayé, Hello, World!mais j'ai exécuté la sortie dans l'interpréteur TIO Cubically et il a sorti "Hello, World!" alors j'ai supposé que ça marche.

Plutôt que de manipuler réellement le cube, le bloc-notes est simplement incrémenté par la somme de la 1 face (9) à plusieurs reprises jusqu'à ce qu'il ait la bonne valeur pour chaque caractère, puis l'imprime.


Les commentaires ne sont pas pour une discussion approfondie; cette conversation a été déplacée vers le chat .
Martin Ender

@MartinEnder Pourriez-vous les déplacer vers le salon de discussion existant à la place?
MD XF

@MDXF Je pourrais mais je ne peux pas dire s'ils finiraient par être complètement hors de propos et de contexte dans ce salon de discussion.
Martin Ender

@MartinEnder Les commentaires sont plus anciens que le salon de discussion, ils apparaîtront donc très loin dans la transcription, n'est-ce pas?
MD XF

Ils voudraient. Merci, je vais déplacer les messages.
Martin Ender
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.