Mapper la chaîne à la courbe de Hilbert

27

Mappons quelques chaînes à un espace 2D, style fractal. Votre tâche consiste à calculer une courbe de Hilbert et à y déposer une chaîne.

La courbe de Hilbert, itérations 1 à 8

Tâche

La tâche consiste à prendre la chaîne d'entrée sur une seule ligne et à la disposer le long d'une courbe de Hilbert suffisamment grande pour la contenir, mais pas plus grande. Essayez de réduire le nombre d'octets aussi bas que possible; c'est un après tout!

Conditions

  • Tout espace à remplir avec des espaces, mais le remplissage n'est pas requis à la fin des lignes.
  • Le début de la ligne doit être dans le coin supérieur gauche et la fin dans le coin inférieur gauche.
  • Vous pouvez créer un programme ou une fonction.
  • De nouveaux cas de test peuvent apparaître, alors ne codez rien!

Bonus

Remarque: les bonus se cumulent comme ceci: -50% & -20% on 100B= -20% on 50Bou -50% on 80B= 40B.

  • -50% Si l'entrée est une chaîne multiligne, inversez le processus pour créer l'entrée d'origine. Cas de test pour le bonus: utilisez simplement ceux existants (y compris les cas de test de bonus!)
  • -20% Si vous supprimez tous les espaces inutiles de la sortie (par exemple, à la fin d'une ligne).
  • -5% Si vous ne polluez pas l'espace de noms global (vous savez ce que je veux dire!)

Cas de test

abcdefghijklmn

adef
bchg
 nij
 mlk


The quick brown fox jumps over the lazy dog.

Thn f ju
 ewooxpm
qckr rs 
ui btevo
    hlaz
    e  y
      do
      .g

Et pour le bonus de suppression des espaces:

No  hitespac  her 

Noher

hesc
itpa

Classement

Pour vous assurer que votre réponse apparaît, veuillez commencer votre réponse avec un titre, en utilisant le modèle Markdown suivant:

# Language Name, N bytes

Nest la taille de votre soumission. Si vous améliorez votre score, vous pouvez conserver les anciens scores dans le titre, en les barrant. Par exemple:

# Ruby, <s>104</s> <s>101</s> 96 bytes

Si vous souhaitez inclure plusieurs nombres dans votre en-tête (par exemple, parce que votre score est la somme de deux fichiers ou que vous souhaitez répertorier les pénalités de drapeau d'interprète séparément), assurez-vous que le score réel est le dernier numéro de l'en-tête:

# Perl, 43 + 2 (-p flag) = 45 bytes

Vous pouvez également faire du nom de la langue un lien qui apparaîtra ensuite dans l'extrait de classement:

# [><>](http://esolangs.org/wiki/Fish), 121 bytes
wizzwizz4
la source
Si quelqu'un peut faire d'autres tests, cela serait apprécié.
wizzwizz4
Les caractères devraient donc être représentés par des sommets de la courbe?
flawr
No..hitespac..her.où les points sont des espaces serait un meilleur test pour le bonus. (Et actuellement, le cas de test manque la fin .)
Martin Ender
Si vous adoptez l' approche L-system, vous pouvez également essayer http: // codegolf / questions / 48697 / ascii-l-system-renderer . Cela pourrait vous aider à jouer vos réponses.
wizzwizz4

Réponses:

7

CJAM, 119 117 113 112 109 * 0,5 * 0,8 = 43,6 octets

Merci à Dennis d'avoir économisé 1 octet.

Voici un début ...

{+e`W<e~}:F;q_N-,4mLm]0aa{4\#4e!1=f*\:G[zGGW%zW%G].ff+2/{~.+~}%}@:L/\_N&{N/]z:z:~$1f>sS}{4L#' e]f{f=SF}N*N}?F

Testez la transformation directe . Testez la transformation inverse.

Je suis sûr qu'il existe un moyen plus court de générer la courbe ...

Explication

Tout d'abord, je définis une fonction pour couper un élément de la fin d'un tableau, car j'en ai besoin à plusieurs endroits. Il attend le tableau et l'élément (à l'intérieur d'un tableau séparé) au-dessus de la pile.

{
  +  e# Append the element to the array.
  e` e# Run-length encode.
  W< e# Discard last run.
  e~ e# Run-length decode.
}:F; e# Store in F and discard.

Maintenant, la majorité du code détermine la taille de la courbe de Hilbert requise et la construit comme un tableau 2D où les éléments sont des indices le long de la courbe. Je construis cela sur la base de l'observation suivante:

Considérons la courbe de Hilbert 2x2:

01
32

La courbe de Hilbert 4x4 est:

0345
1276
ed89
fcba

Si nous soustrayons la valeur minimale de chaque quadrant (et les séparons un peu pour plus de clarté visuelle), nous obtenons:

03 01
12 32

21 01
30 32

Ce motif est valable pour toutes les tailles. Cela signifie que nous pouvons construire le niveau suivant à partir du niveau actuel, en utilisant comme quatre quadrants: a) la transposition du niveau actuel, b) le niveau actuel lui-même, c) la transposition le long de l'anti-diagonale, d) à nouveau le niveau actuel lui-même. Et puis nous les compensons respectivement 0, 1, 3, 2 fois la taille du niveau actuel.

q           e# Read input.
_N-         e# Make a copy and remove all linefeeds.
,4mLm]      e# Take that string's length's logarithm with base 4, rounded up.
            e# This is the Hilbert curve level we need.
0aa         e# Push [[0]] as the level-0 Hilbert curve.
{           e# Store the Hilbert curve level in L. Then for each i from 0 to L-1...
  4\#       e#   Compute 4^i. This is the offset of the four quadrants.
  4e!1=     e#   Get [0 1 3 2] as the second permutation returned by 4e!.
  f*        e#   Multiply each of them by the offset.
  \:G       e#   Swap with the Hilbert curve so far and call it G.
  [         e#   Create an array with...
    z       e#     The transpose of G.
    G       e#     G itself.
    GW%zW%  e#     The anti-diagonal transpose of G.
    G       e#     G itself.
  ]
  .ff+      e#   Add the appropriate offsets to the indices in each of the four quadrants.
  2/        e# Split into a 2x2 grid.
  {         e# Map this onto each pair of quadrants...
    ~       e#   Dump both quadrants on the stack.
    .+      e#   Concatenate them line by line.
    ~       e#   Dump the lines on the stack.
  }%        e# Since this is a map, the lines will automatically be collected in an array.
}@:L/

Enfin, nous utilisons cette courbe d'indices de Hilbert pour appliquer la transformation appropriée à l'entrée:

\_        e# Swap the curve with the input and make another copy.
N&{       e# If the input contains linefeeds, execute the first block, else the second...
  N/      e#   Split the input into lines. The stack now has a grid of indices and a grid
          e#   of characters.
  ]z:z:~  e#   This is some weird transposition magic which zips up the indices with the
          e#   corresponding characters from both grids, and finally flattens the grid
          e#   into a linear list of index/character pairs. Those cells that don't have
          e#   characters due to trimmed whitespace in the input will be turned into
          e#   arrays containing only an index.
  $       e#   Sort the pairs (which sorts them by indices).
  1f>     e#   Discard the indices.
  s       e#   Flatten the result into a single string.
  S       e#   Leave a space on the stack to be trim trailing spaces later.
}{        e# or...
  4L#     e#   Compute the size of the Hilbert curve.
  ' e]    e#   Pad the input to that size with spaces.
  f{      e#   Map this block over lines of the curve, passing the padding input as an
          e#   additional parameter...
    f=    e#     For each index in the current line, select the appropriate character
          e#     from the padded input.
    SF    e#     Trim spaces from the end of the line.
  }
  N*      e#   Join the lines with linefeed characters.
  N       e#   Leave a linefeed on the stack to be trim trailing linefeeds later.
}?
F         e# We left either a space or a linefeed on stack... trim that character from
          e# the end of the string.
Martin Ender
la source
3

Python 3, 467 434 423 457 451 426 386 374 342 291 304 * 80% * 95% = 231,04 octets

La façon dont cela fonctionne est que je fais la courbe de Hilbert en utilisant un système Lindenmayer et que je suis les instructions gauche, droite et avant le long d'un tableau de chaînes. Il existe probablement de nombreuses façons de mieux jouer au golf; en particulier dans les conditions et dans la fabrication du tableau de chaînes. (J'ai essayé, [" "*p for i in range(p)]mais les chaînes ne prennent pas en charge l'attribution des éléments (apparemment). Si je pouvais faire en sorte que cela fonctionne, je pourrais également me débarrasser de la jointure)

Edit: Golfé certaines des conditions avec merci à Dennis . Et j'ai joué au golf sur la gamme de cordes. Et un changement sans octet car les résultats sortaient transposés par rapport aux exemples ci-dessus.

Edit: implémentation du bonus de suppression des espaces.

Edit: correction d'un bug dans mon code de suppression des espaces pour six octets supplémentaires

Edit: Étant donné que cette réponse ne pollue pas l'espace de noms global, j'obtiens le bonus de 5%, selon wizzwizz4 ici .

Edit: changé la façon dont gest incrémentée et décrémentée. Maintenant en utilisant eval()et str.translate.

Edit: Cette réponse est maintenant un programme au lieu d'une fonction.

Edit: correction de quelques bugs du golf précédent.

s=input();m=(len(bin(len(s)-1))-1)//2;t=eval("[' ']*2**m,"*2**m);t[0][0],*s=s;x=y=g=0;b="A";exec("b=b.translate({65:'-BF+AFA+FB-',66:'+AF-BFB-FA+'});"*m)
while s:
 c,*b=b;g+=(c<"-")-(c=="-")
 if"B"<c:x,y=[[x+1-g%4,y],[x,y+g%4-2]][g%2];t[x][y],*s=s
print("\n".join(''.join(i).rstrip()for i in t).rstrip())

Non golfé:

# hilbert(it) is now implemented in the code with exec("b=b.translate")

def hilbert(it):
    s="A"
    n=""
    for i in range(it):
        for c in s:
            if c == "A":
                n += "-BF+AFA+FB-"
            elif c == "B":
                n += "+AF-BFB-FA+"
            else:
                n += c
        s=n;n=""
    return s

def string_to_hilbert(string):
    length = len(string)
    it = (len(bin(length-1))-1)//2
    hil = hilbert(it)
    pow_2 = 2**it
    # we use eval("[' ']*pow_2,"*pow_2) in the code, but the following is equivalent
    output = [[" "for j in range(pow_2)] for i in range(pow_2)]
    output[0][0] = string[0]
    x = 0
    y = 0
    heading = 0
    while string: # while there are still characters in string
        char, *hil = hil
        if char == "-": heading = heading - 1
        elif char == "+": heading = heading + 1
        elif char == "F":
            if heading % 4 == 3: y += 1
            elif heading % 4 == 2: x -= 1
            elif heading % 4 == 1: y -= 1
            else: x += 1
            output[x][y], *string = string
    array = [''.join(i).rstrip()for i in output]
    array = "\n".join(array).rstrip()
    print(array)
    return
Sherlock9
la source
Curieux de connaître le bonus de 5%. Les variables sont-elles automatiquement locales en Python?
edc65
@ edc65 J'ai demandé à l'auteur du défi une chose similaire ici: chat.stackexchange.com/transcript/240?m=28529277#28529277 . J'espère que ça aide un peu. Sinon, nous pouvons continuer la discussion dans le chat.
Sherlock9
2

Rubis, 358 356 344 322 319 * 80% * 95% = 242,44 octets

Ceci est mon code Python transposé en Ruby. Je devrais écrire plus de réponses en Ruby. C'est une langue décente pour jouer au golf.

Edit: j'ai oublié que les fonctions n'ont pas besoin d'être nommées dans cette question.

Edit: Étant donné que cette réponse ne pollue pas l'espace de noms global, j'obtiens le bonus de 5%, selon wizzwizz4 ici .

->s{l=s.size;m=((l-1).bit_length+1)/2;x=2**m;t=(1..x).map{[" "]*x};t[0][0]=s[0];x=y=g=z=0;d=1;b=?A;m.times{b=b.split("").map{|c|c==?A?"-BF+AFA+FB-":c==?B?"+AF-BFB-FA+":c}.join("")};(c=b[z];z+=1;g+=c<?-?1:c==?-?-1:0;(g%2>0?y+=g%4-2:x+=1-g%4;t[x][y]=s[d];d+=1)if c>?B)while d<l;puts (t.map{|i|(i*'').rstrip}*"\n").rstrip}

Non golfé:

def map_string(string)
  len = string.size
  m = ((len-1).bit_length+1)/2
  pow = 2**m
  output = (1..pow).map{[" "]*pow}
  output[0][0] = s[0]
  x = y = heading = char_index = 0
  chars_in_output = 1
  b = ?A
  m.times do |j|
    a = b.split("").map do |char|
      if char == "A"
        "-BF+AFA+FB-"
      else if char == "B"
        "+AF-BFB-FA+"
      else
        char
      end
    end
    b = a.join("")
  end
  while chars_in_output < len
    char = b[char_index]
    char_index += 1
    if char == "-"
      heading += -1
    else if char == "+"
      heading += 1
    else if char == "F"
      if heading % 2 == 0
        y += heading % 4 - 2
      else
        x += 1 - heading % 4
      end
    end
    output[x][y] = string[char_index]
    char_index += 1
  end
  return (output.map{|i|(i*'').rstrip}*"\n").rstrip
Sherlock9
la source
Ce code est-il sous double licence sous une licence de code? Je voudrais produire un travail dérivé qui est publié sous la GPL (bien que toute licence compatible GPL fonctionnera avec cela). Il est actuellement publié sous CC BY-SA 3.0.
wizzwizz4
@ wizzwizz4 Chat ici: chat.stackexchange.com/rooms/56405/…
Sherlock9
1

JavaScript (ES6), 227 - 20%: 181,6 octets

m=>{for(n=1<<((33-Math.clz32(m.length-1))/2),t='',y=0;y<n;y++,t+=`
`)for(x=0;x<n;x++,t+=m[h]||' ')for(u=y,v=x,h=0,s=n;s>>=1;q||(p&&(u=s+~u,v=s+~v),[u,v]=[v,u]))h+=s*s*(3*!!(p=u&s)^!!(q=v&s));return t.replace(/ +$/mg,'').trim()}

Essayer d'obtenir le bonus de 5%

m=>{for(var n=1<<((33-Math.clz32(m.length-1))/2),t='',x,y=0;y<n;y++,t+=`
`)for(x=0;x<n;x++,t+=m[h]||' ')for(var p,q,u=y,v=x,h=0,s=n;s>>=1;q||(p&&(u=s+~u,v=s+~v),[u,v]=[v,u]))h+=s*s*(3*!!(p=u&s)^!!(q=v&s));return t.replace(/ +$/mg,'').trim()}

241 * 0.8 * 0.95: 183.16 plus grand

Moins golfé

m=>
{
  // calc the size of the bounding square, clz32 is a bit shorter than ceil(log2()
  n = 1<<( (33-Math.clz32(m.length-1)) / 2); 
  t = '';
  for(y = 0; y < n; y++) 
  {
    for(x = 0 ; x < n; x++)
    {
      // for each position x,y inside the square
      // get the index postion in the hilbert curve
      // see https://en.wikipedia.org/wiki/Hilbert_curve (convert x,y to d)
      for(u=y, v=x, h=0, s=n; s >>= 1; )
      {
        h += s*s*(3 * !!(p = u & s) ^ !!(q = v & s));
        q || (p && (u = s+~u, v = s+~v),[u,v]=[v,u])
      }
      // add char at given index to output  
      t += m[h]||' '; // blank if beyond the length of m
    }
    t += '\n'; // add newline add end line
  }
  return t.replace(/ +$/mg,'').trim() // to get the 20% bonus
}  

Tester

F=m=>{for(n=1<<((33-Math.clz32(m.length-1))/2),t='',y=0;y<n;y++,t+=`
`)for(x=0;x<n;x++,t+=m[h]||' ')for(u=y,v=x,h=0,s=n;s>>=1;q||(p&&(u=s+~u,v=s+~v),[u,v]=[v,u]))h+=s*s*(3*!!(p=u&s)^!!(q=v&s));return t.replace(/ +$/mg,'').trim()}

function Test() { O.textContent = F(I.value) }

Test()
#I { width: 90% }
#O { border: 1px solid #ccc}
<input id=I oninput='Test()' value='The quick brown fox jumps over the lazy dog.'>
<pre id=O></pre>

edc65
la source
Vaut-il la peine d'ajouter vars pour obtenir le bonus de 5%?
wizzwizz4
var s,x,y,u,v,t,p,q,n,hnon ça ne vaut pas @ wizzwizz4
edc65
Vous pouvez juste mettre varavant la première utilisation de chacun ... Oh, c'est encore pire.
wizzwizz4
@ wizzwizz4 dans l'ensemble, peut-être que vous avez un point ... j'essaie ... non. Dommage
edc65