Je vous présente les premiers 3% d'un auto-interprète Hexagony ...
|./...\..._..>}{<$}=<;>'<..../;<_'\{*46\..8._~;/;{{;<..|M..'{.>{{=.<.).|.."~....._.>(=</.\=\'$/}{<}.\../>../..._>../_....@/{$|....>...</..~\.>,<$/'";{}({/>-'(<\=&\><${~-"~<$)<....'.>=&'*){=&')&}\'\'2"'23}}_}&<_3.>.'*)'-<>{=/{\*={(&)'){\$<....={\>}}}\&32'-<=._.)}=)+'_+'&<
Essayez-le en ligne! Vous pouvez également l'exécuter sur lui-même, mais cela prendra environ 5 à 10 secondes.
En principe, cela pourrait correspondre à la longueur de côté 9 (pour un score de 217 ou moins), car elle utilise seulement 201 commandes, et la version non-golfée que j'ai écrite en premier (sur la longueur de côté 30) ne nécessitait que 178 commandes. Cependant, je suis à peu près sûr que cela prendrait une éternité pour que tout rentre dans l'ordre, donc je ne suis pas sûr de vouloir réellement le tenter.
Il devrait également être possible de jouer au golf un peu en taille 10 en évitant d’utiliser la dernière ou les deux dernières lignes, de sorte que les no-ops suivants puissent être omis, mais que cela nécessiterait une réécriture substantielle, comme l’un des premiers chemins. joint utilise le coin inférieur gauche.
Explication
Commençons par déplier le code et annoter les chemins de flux de contrôle:
C’est toujours assez compliqué, donc voici le même schéma pour le code "non-golfé" que j’ai écrit en premier (en fait, c’est le côté de longueur 20 et à l’origine j’ai écrit le code sur le côté de 30, mais c’était tellement n’améliore pas la lisibilité du tout, alors je l’ai compactée un peu pour rendre la taille un peu plus raisonnable):
Cliquez pour agrandir.
Les couleurs sont exactement les mêmes, à l'exception de quelques détails très mineurs, les commandes sans contrôle sont également identiques. Je vais donc expliquer comment cela fonctionne sur la version sans-golf, et si vous voulez vraiment savoir comment fonctionne le golfé, vous pouvez vérifier quelles sont les parties correspondantes dans le plus grand hexagone. (Le seul problème est que le code de golf commence par un miroir de sorte que le code commence dans le coin droit en partant de la gauche.)
L'algorithme de base est presque identique à ma réponse CJam . Il y a deux différences:
- Au lieu de résoudre l'équation du nombre hexagonal centré, je calcule simplement des nombres hexagonaux centrés consécutifs jusqu'à ce que l'un soit égal ou supérieur à la longueur de l'entrée. En effet, Hexagony ne dispose pas d’un moyen simple de calculer une racine carrée.
- Au lieu de rajouter tout de suite l'entrée avec no-ops, je vérifie plus tard si j'ai déjà épuisé les commandes de l'entrée et j'imprime un
.
si c'est le cas.
Cela signifie que l’idée de base se résume à:
- Lire et stocker la chaîne d’entrée tout en calculant sa longueur.
- Trouvez la plus petite longueur de côté
N
(et le nombre hexagonal centré correspondant hex(N)
) pouvant contenir l’entrée entière.
- Calculez le diamètre
2N-1
.
- Pour chaque ligne, calculez le retrait et le nombre de cellules (dont la somme est égale à
2N-1
). Imprimez le retrait, imprimez les cellules (en utilisant .
si l’entrée est déjà épuisée), imprimez un saut de ligne.
Notez qu'il n'y a que des no-ops et que le code actuel commence dans le coin gauche (le $
, qui saute par-dessus le >
, donc nous commençons vraiment,
par le chemin dans le gris foncé).
Voici la grille de mémoire initiale:
Ainsi, le pointeur de mémoire commence sur l’ entrée étiquetée par le bord , en direction du nord. ,
lit un octet de STDIN ou un -1
si nous avons atteint EOF dans ce bord. Par conséquent, la <
droite après est une condition pour savoir si nous avons lu toutes les entrées. Restons dans la boucle d'entrée pour l'instant. Le prochain code que nous exécutons est
{&32'-
Ceci écrit un 32 dans l' espace marqué par le bord , puis le soustrait de la valeur d'entrée dans le bord nommé diff . Notez que cela ne peut jamais être négatif car nous avons la garantie que l'entrée ne contient que de l'ASCII imprimable. Ce sera zéro quand l'entrée était un espace. (Comme Timwi le fait remarquer, cela fonctionnerait toujours si l'entrée pouvait contenir des sauts de ligne ou des tabulations, mais cela effacerait également tous les autres caractères non imprimables avec des codes de caractère inférieurs à 32.) Dans ce cas, le <
pointeur d'instruction (IP) dévie et le chemin gris clair est pris. Ce chemin réinitialise simplement la position du député avec {=
puis lit le caractère suivant - ainsi, les espaces sont ignorés. Sinon, si le personnage n'était pas un espace, nous exécutons
=}}})&'+'+)=}
Cette première se déplace autour de l'hexagone à travers le bord de la longueur jusqu'à son opposé au bord du diff , avec =}}}
. Ensuite , il copie la valeur de face de la longueur bord dans la longueur bord, et incrémente avec )&'+'+)
. Nous verrons dans une seconde pourquoi cela a du sens. Enfin, nous déplaçons le nouveau bord avec =}
:
(Les valeurs de bord particulières proviennent du dernier cas de test donné dans le défi.) À ce stade, la boucle se répète, mais avec tout ce qui a été déplacé d'un hexagone au nord-est. Donc après avoir lu un autre personnage, nous obtenons ceci:
Vous pouvez maintenant voir que nous écrivons progressivement l'entrée (espaces moins) le long de la diagonale nord-est, avec les caractères sur chaque bord et la longueur jusqu'à ce caractère étant stockée parallèlement à la longueur étiquetée .
Lorsque nous aurons terminé avec la boucle d'entrée, la mémoire ressemblera à ceci (où j'ai déjà étiqueté quelques nouveaux bords pour la partie suivante):
Le %
dernier caractère lu est le dernier, 29
le nombre de caractères non-espace lus. Maintenant, nous voulons trouver la longueur de côté de l'hexagone. Premièrement, il existe un code d’initialisation linéaire dans le chemin vert foncé / gris:
=&''3{
Ici, =&
copie la longueur (29 dans notre exemple) dans le bord étiqueté longueur . Puis ''3
passe à l’arête étiquetée 3 et définit sa valeur sur 3
(ce dont nous avons simplement besoin comme constante dans le calcul). Enfin {
se déplace vers le bord marqué N (N-1) .
Maintenant nous entrons dans la boucle bleue. Cette boucle incrémente N
(stockée dans la cellule N ) calcule ensuite son nombre hexagonal centré et le soustrait de la longueur entrée. Le code linéaire utilisé est:
{)')&({=*'*)'-
Ici, {)
se déplace et incrémente N . ')&(
se déplace vers le bord libellé N-1 , N
y copie et le décrémente. {=*
calcule leur produit en N (N-1) . '*)
multiplie cela par la constante 3
et incrémente le résultat dans l'arête nommée hex (N) . Comme prévu, il s'agit du nième nombre hexagonal centré. Enfin '-
calcule la différence entre cela et la longueur d’entrée. Si le résultat est positif, la longueur du côté n'est pas encore assez grande et la boucle se répète (où }}
ramène le MP vers le bord marqué N (N-1) ).
Une fois que le côté est assez grand, la différence sera nulle ou négative et nous obtenons ceci:
Tout d'abord, il y a maintenant le très long chemin vert linéaire qui fait l'initialisation nécessaire pour la boucle de sortie:
{=&}}}32'"2'=&'*){=&')&}}
Les {=&
départs en copiant le résultat dans la diff bord dans la longueur bord, parce que nous avons besoin de quelque chose là non positif plus tard. }}}32
écrit un 32 dans le bord étiqueté espace . '"2
écrit une constante 2 dans le bord non étiqueté au-dessus de diff . '=&
copie N-1
dans le deuxième bord avec la même étiquette. '*)
multiplie-le par 2 et incrémente-le de manière à obtenir la valeur correcte dans le bord appelé 2N-1 en haut. C'est le diamètre de l'hexagone. {=&')&
copie le diamètre dans l'autre bord étiqueté 2N-1 . Enfin, }}
retourne au bord marqué 2N-1 en haut.
Ré-étiquetons les bords:
Le bord sur lequel nous sommes actuellement (qui a toujours le diamètre de l'hexagone) sera utilisé pour parcourir les lignes de la sortie. Le bord intitulé indent calcule le nombre d'espaces nécessaires sur la ligne en cours. Les cellules étiquetées sur le bord seront utilisées pour parcourir le nombre de cellules dans la ligne en cours.
Nous sommes maintenant sur le chemin rose qui calcule le retrait . ('-
décrémente l' itérateur de lignes et le soustrait de N-1 (dans le bord indenté ). La courte branche bleu / gris du code calcule simplement le module du résultat ( ~
annule la valeur si elle est négative ou nulle et rien ne se produit si elle est positive). Le reste du chemin rose est celui "-~{
qui soustrait le retrait du diamètre dans le bord des cellules , puis revient au bord du retrait .
Le chemin jaune sale imprime maintenant l'indentation. Le contenu de la boucle est vraiment juste
'";{}(
Où '"
se déplace vers le bord de l' espace , l' ;
imprime, {}
revient en retrait et le (
décrémente.
Lorsque nous en avons terminé, le (deuxième) chemin gris foncé recherche le caractère suivant à imprimer. Le =}
déplace en position (ce qui signifie, sur le bord des cellules , en direction du sud). Nous avons ensuite une boucle très étroite {}
qui descend simplement de deux côtés dans la direction sud-ouest, jusqu'à atteindre la fin de la chaîne stockée:
Notez que j'ai rebaptisé un bord là-bas EOF? . Une fois que nous avons traité ce caractère, nous allons rendre ce bord négatif, afin que la {}
boucle se termine ici au lieu de la prochaine itération:
Dans le code, nous sommes à la fin du chemin gris foncé, où nous '
revenons d'un pas sur le caractère saisi. Si la situation est l’un des deux derniers diagrammes (c’est-à-dire qu’il reste encore un caractère de l’entrée que nous n’avons pas encore imprimée), nous prenons le chemin vert (le dernier, pour les personnes qui ne sont pas douées avec le vert et le vert). bleu). Celui-là est assez simple: ;
imprime le personnage lui-même. '
se déplace vers le bord d' espace correspondant qui contient toujours un 32 parmi les précédents et ;
imprime cet espace. Alors {~
fait notre EOF? négatif pour la prochaine itération, '
recule d'un pas afin que nous puissions revenir à l'extrémité nord-ouest de la chaîne avec une autre }{
boucle serrée . Qui se termine sur la longueurcellule (la cellule positive au-dessous de l’ hex (N) . Enfin, }
retourne au bord de la cellule .
Si nous avons déjà épuisé les entrées, alors la boucle qui recherche EOF? se terminera réellement ici:
Dans ce cas , '
se déplace sur la longueur cellule, et nous prenons le chemin bleu clair ( en haut) au lieu, qui imprime un no-op. Le code dans cette branche est linéaire:
{*46;{{;{{=
Le {*46;
écrit un 46 dans le bord étiqueté no-op et l’imprime (c’est-à-dire un point). Puis {{;
passe au bord de l’ espace et l’imprime. Le {{=
retourne au bord des cellules pour la prochaine itération.
À ce stade, les chemins se rejoignent et (
décrémentent le bord des cellules . Si l'itérateur n'est pas encore à zéro, nous prendrons le chemin gris clair, qui inverse simplement la direction du député, =
puis cherche le caractère suivant à imprimer.
Sinon, nous avons atteint la fin de la ligne en cours et l’IP prend le chemin violet. Voici à quoi ressemble la grille mémoire:
Le chemin violet contient ceci:
=M8;~'"=
Le =
renverse la direction du député à nouveau. M8
définit sa valeur sur 778
(car le code de caractère M
is 77
et digits s'ajoutent à la valeur actuelle). C’est ce qui se produit 10 (mod 256)
. Ainsi, lorsque nous l’imprimons avec ;
, nous obtenons un saut de ligne. Rend ensuite ~
le bord négatif, '"
revient au bord des lignes et =
inverse le MP une fois de plus.
Maintenant, si le bord des lignes est zéro, nous avons terminé. L’IP prendra le chemin rouge (très court), qui @
termine le programme. Sinon, nous continuons sur le chemin violet qui reboucle dans le rose pour imprimer une autre ligne.
Diagrammes de flux de contrôle créés avec HexagonyColorer de Timwi . Diagrammes de mémoire créés avec le débogueur visuel dans son IDE Esoteric .
abc`defg
, en fait, il deviendrait pastebin.com/ZrdJmHiR