Écrire un programme qui produit son niveau miroir

31

Il y a 95 caractères ASCII imprimables :

 !"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\]^_`abcdefghijklmnopqrstuvwxyz{|}~

Dans la police Consolas (le bloc de code Stack Exchange par défaut), certains des caractères ont des miroirs autour d'un axe vertical de symétrie:

  • Ces paires de personnages sont des miroirs l'une de l'autre: () [] {} <> /\
  • Ces personnages sont des miroirs d'eux-mêmes: ! "'*+-.8:=AHIMOTUVWXY^_ovwx|(Notez que l'espace est un.)
  • Ceux-ci n'ont pas de miroirs: #$%&,012345679;?@BCDEFGJKLNPQRSZ`abcdefghijklmnpqrstuyz~

( i, l, 0, #, Et probablement d' autres personnages sont leurs propres miroirs dans certaines polices , mais nous allons en tenir aux formes Consolas.)

Une chaîne est considérée comme un miroir d'elle-même si elle est composée uniquement des 39 caractères miroir , disposés de telle sorte que la chaîne présente une ligne de symétrie verticale centrale. ](A--A)[Est donc un miroir de lui-même mais ](A--A(]ne l'est pas.

Écrivez un programme de longueur paire d'une ligne qui est un miroir de lui-même. Lorsque N copies de sa moitié gauche lui ont été ajoutées et que N copies de sa moitié droite lui ont été ajoutées, il devrait produire N + 1. N est un entier non négatif.

Par exemple, si le programme était ](A--A)[(moitié gauche:, ](A-moitié droite:) -A)[, alors:

  • L'exécution ](A--A)[devrait sortir 1. (N = 0)
  • L'exécution ](A-](A--A)[-A)[devrait sortir 2. (N = 1)
  • L'exécution ](A-](A-](A--A)[-A)[-A)[devrait sortir 3. (N = 2)
  • L'exécution ](A-](A-](A-](A--A)[-A)[-A)[-A)[devrait sortir 4. (N = 3)
  • . . .
  • L'exécution ](A-](A-](A-](A-](A-](A-](A-](A-](A-](A--A)[-A)[-A)[-A)[-A)[-A)[-A)[-A)[-A)[-A)[devrait sortir 10. (N = 9)
  • etc.

Règles

  • Sortie vers stdout ou l'alternative la plus proche de votre langue. Il peut y avoir une nouvelle ligne de fin facultative. Aucune entrée ne doit être prise.
  • Le processus devrait théoriquement fonctionner pour N jusqu'à 2 15 -1 ou au-delà, avec suffisamment de mémoire et de puissance de calcul.
  • Un programme complet est requis, pas seulement une commande REPL .

Le programme initial le plus court (N = 0 cas) en octets gagne.

Les passe-temps de Calvin
la source
Dans certaines polices, #c'est aussi sa propre reflexion, mais, vous avez raison, pas dans les consoles.
SuperJedi224
1
L'utilisation de réponses est-elle autorisée? En d'autres termes: faut-il écrire un programme complet valide ou une expression suffit? Je pense à l'entrée Haskell qui fonctionnerait lors de l'exécution dans ghci mais n'est pas un programme complet valide.
Bakuriu
@Bakuriu Un programme complet est requis. La réponse Haskell n'est pas valide.
Calvin's Hobbies
4
Pourquoi les miroirs «b» et «d» ne sont-ils pas les uns des autres? Cela rend mon plan impossible: P
Thijs ter Haar
1
@ThijsterHaar En fait, je n'y ai pas pensé, mais il semble que leurs formes Consolas soient juste légèrement différentes, désolé: P
Calvin's Hobbies

Réponses:

20

Pip, 12 8 4 octets

Maintenant avec 66% d'octets en moins!

x++x
  • xest une variable, préinitialisée à "". Dans le contexte numérique, cela devient 0.
  • La première mi-temps, sans la finale +, exprime la forme x+x+...+x. Ceci est une déclaration valide qui ne fait rien.
  • La seconde mi-temps, y compris la finale +de la première mi-temps, exprime la forme ++x+x+...+x. ++xincréments xà 1, et le reste l'ajoute à lui-même N fois. Étant donné que les expressions sont évaluées de gauche à droite dans Pip, l'incrément est garanti de se produire en premier et le résultat est égal au nombre de niveaux de miroir.
  • À la fin, la valeur de cette expression est imprimée automatiquement.

Malheureusement, Pip ne traite pas bien les expressions énormes: cette solution provoque une maximum recursion depth exceedederreur pour N au-dessus de 500 environ. Voici une solution précédente qui ne fonctionne pas, pour 8 octets :

x++oo++x

Plus sur Pip

DLosc
la source
OK, à moins que quelqu'un ne poste une réponse de 2 octets, on dirait que vous avez celui-ci dans le sac. Soit dit en passant, je ne sais pas si vous l'avez exécuté avec N = 32767 , mais la sortie réelle est Fatal error: maximum recursion depth exceeded while calling a Python object.
Dennis
@ Dennis Ouais, j'ai rencontré ça en fait - ça commence assez tôt, environ 600 sinon avant. La raison en est que les expressions sont évaluées en évaluant récursivement toutes les sous-expressions en premier, ce qui x+x+...+xgénère une profondeur de récursion O (N). Peut-être que cela invalide cette réponse. Je vais ajouter une note.
DLosc
La limite de récursivité est facilement réglable en Python, n'est-ce pas?
Dennis
@Dennis Oui, bien qu'il y ait des avertissements désastreux sur ce que cela fera à votre système si vous le placez trop haut, donc je ne l'ai jamais essayé. ;) De plus, il n'est pas possible de le configurer depuis Pip , donc cela me semble un peu comme tricher. Si vous voulez l'essayer, je serais intéressé à entendre les résultats.
DLosc
J'ai essayé. Sur ma machine, l'augmentation de la limite de récursivité à 20000 segfaults déjà. Mais comme la réponse ne doit fonctionner qu'avec une mémoire et une puissance de calcul suffisantes , cela ne devrait pas poser de problème.
Dennis
34

GolfScript, 10 octets

!:{)::(}:!

Essayez-le en ligne avec Web Golfscript: N = 0 , N = 1 , N = 2 , N = 3 , N = 41

Web GolfScript a une limite de 1024 caractères, mais l'interpréteur Ruby gère parfaitement N = 32767 :

$ curl -Ss http://www.golfscript.com/golfscript/golfscript.rb > golfscript.rb
$ echo '"!:{):"32768*":(}:!"32768*' | ruby golfscript.rb > mirror-level-32767.gs
$ ruby golfscript.rb mirror-level-32767.gs
32768

Comment ça marche

Sans aucune entrée, GolfScript a initialement une chaîne vide sur la pile.

Dans la première moitié gauche, les événements suivants se produisent:

  • !applique NOT logique à la chaîne vide. Cela pousse 1.

  • :{enregistre l'entier sur la pile dans la variable {.

    Oui, c'est un identifiant valide, bien qu'il n'y ait aucun moyen de récupérer la valeur stockée.

  • ) incrémente l'entier sur la pile.

  • : est une instruction incomplète.

Les moitiés gauche suivantes, les événements suivants se produisent:

  • :!(où :est un reste d'avant) enregistre l'entier sur la pile dans la variable !.

    Oui, c'est aussi un identifiant valide. Cela rompt la !commande, mais nous ne l'utilisons plus.

  • :{, )Et le :travail comme avant.

Dans la première moitié droite, les événements suivants se produisent:

  • ::(où :est un reste d'avant) enregistre l'entier sur la pile dans la variable :.

    Oui, même cela est un identifiant valide. Comme avec {, il n'y a aucun moyen de récupérer la valeur stockée.

  • ( décrémente l'entier de la pile, donnant le nombre de moitiés gauches.

  • }, car il est inégalé et termine immédiatement l'exécution.

    Il s'agit d'une fonctionnalité non documentée. Je les appelle des super-commentaires .

Le code restant est simplement ignoré.

Dennis
la source
Il semble vraiment bizarre d'avoir un niveau inégalé }dans la seconde moitié de votre code dans une compétition miroir.
ballesta25
C'est une astuce courante dans les variantes de palindrome. Dans "\""/", la quatrième citation double serait également inégalée puisque la seconde a été échappée.
Dennis
27

Code machine Z80, 8 6 octets *

<8ww8> * Suppose certaines conditions en entrant depuis Amstrad BASIC

<   INC A         // A=A+1
8w  JR C, #77     ## C is unset unless A has overflowed, does nothing

w   LD (HL), A    // Saves A to memory location in HL (randomly initialised)
8>  JR C, #3E     ## C is still unset, does nothing

Aest initialement 0 lorsqu'il est entré depuis BASIC. Il incrémente A n fois, puis l'écrit n fois dans le même emplacement mémoire (qui est défini sur un emplacement légèrement aléatoire par BASIC)! L' JRopération Jump Relative ne fait jamais rien car le Cdrapeau est toujours non défini, il est donc utilisé pour "commenter" l'octet suivant! Cette version triche légèrement en supposant certaines conditions d'entrée, à savoir la saisie à partir des garanties BASIC qui Aest toujours 0. L'emplacement de(HL) n'est pas garanti pour être sûr, et en fait, est probablement un endroit dangereux. Le code ci-dessous est beaucoup plus robuste, c'est pourquoi il est tellement plus long.

Code machine Z80, 30 octets

En ASCII: o!.ww.!>A=o>{))((}<o=A<!.ww.!o

Fondamentalement, la première moitié garantit la création d'une valeur nulle et la seconde moitié l'incrémente et l'écrit en mémoire. Dans la version développée ci-dessous ##désigne un code qui ne sert à rien dans sa moitié du miroir.

o   LD L, A       ## 
!.w LD HL, #772E  // Load specific address to not corrupt random memory!
w   LD (HL), A    ## Save random contents of A to memory
.!  LD L, #21     ## 
>A  LD A, #41     // A=#41
=   DEC A         // A=#40
o   LD L, A       // L=#40
>{  LD A, #7B     ## 
)   ADD HL, HL    // HL=#EE80
)   ADD HL, HL    // HL=#DD00. L=#00 at this point

((  JR Z, #28     ## 
}   LD A, L       // A=L
<   INC A         // A=L+1
o   LD L, A       // L=L+1
=   DEC A         // A=L
A   LD B, C       ## 
<   INC A         // A=L+1
!.w LD HL, #772E  // Load address back into HL
w   LD (HL), A    // Save contents of A to memory
.!  LD L, #21     ## 
o   LD L, A       // L=A

Répartition des instructions autorisées:

n   op    description
--  ----  -----------
28  LD    LoaD 8-bit or 16-bit register
3   DEC   DECrement 8-bit or 16-bit register
1   INC   INCrement 8-bit or 16-bit register
1   ADD   ADD 8-bit or 16-bit register

Available but useless instructions:
3   JR    Jump Relative to signed 8-bit offset
1   DAA   Decimal Adjust Accumulator (treats the A register as two decimal digits
          instead of two hexadecimal digits and adjusts it if necessary)
1   CPL   1s ComPLement A
1   HALT  HALT the CPU until an interrupt is received

Sur les 39 instructions autorisées, 28 sont des opérations de chargement (le bloc de 0x40 à 0x7F sont toutes des LDinstructions à un octet ), dont la plupart ne sont d'aucune utilité ici! La seule instruction de chargement en mémoire encore autorisée est LD (HL), Ace qui signifie que je dois stocker la valeur dans A. Puisque Ac'est le seul registre qui reste avec une INCinstruction autorisée , c'est en fait assez pratique!

Je ne peux pas charger Aavec 0x00 pour commencer car ASCII 0x00 n'est pas un caractère autorisé! Toutes les valeurs disponibles sont loin de 0 et toutes les instructions mathématiques et logiques ont été interdites! Sauf que ... je peux encore le faire ADD HL, HL, ajouter du 16 bits HLà lui - même! Hormis le chargement direct de valeurs (inutile ici!), INCrementing Aet DECrementing A, Lou HLc'est la seule façon dont j'ai de changer la valeur d'un registre! Il y a en fait une instruction spécialisée qui pourrait être utile dans la première moitié mais une douleur à contourner dans la seconde moitié, et une instruction complémentaire qui est presque inutile ici et prendrait juste de la place.

J'ai donc trouvé la valeur la plus proche de 0 que je pouvais: 0x41. Comment est-ce proche de 0? En binaire, c'est 0x01000001. Je le décrémente donc, le charge Let le fais ADD HL, HLdeux fois! Lest maintenant nul, dans lequel je charge de nouveau A! Malheureusement, le code ASCII pour ADD HL, HLest )donc je dois maintenant utiliser (deux fois. Heureusement, (est JR Z, e, où eest l'octet suivant. Donc, il engloutit le deuxième octet et je dois juste m'assurer qu'il ne fait rien en faisant attention au Zdrapeau! La dernière instruction pour affecter l' Zindicateur a été DEC A(contre-intuitivement, ADD HL, HLne le change pas) et puisque je sais que Ac'était 0x40 à ce point, il est garanti que ce Zn'est pas défini.

La première instruction de la seconde moitié JR Z, #28ne fera rien les 255 premières fois car l'indicateur Z ne peut être défini que si A a dépassé de 255 à 0. Après cela, la sortie sera erronée, cependant, car elle ne sauvegarde de toute façon que des valeurs de 8 bits qui ne devrait pas avoir d'importance. Le code ne doit pas être développé plus de 255 fois.

Le code doit être exécuté comme un extrait de code, car tous les moyens disponibles pour retourner proprement ont été interdits. Toutes les instructions RETurn sont supérieures à 0x80 et les quelques opérations de saut autorisées ne peuvent que passer à un décalage positif, car toutes les valeurs négatives à 8 bits ont également été interdites!

CJ Dennis
la source
6
QUOI. QU'EST-CE QUE C'EST.
cloudfeet
4
Maintenant, j'ai vu cette réponse, en utilisant GolfScript / J / etc. semble juste tricher. : p
cloudfeet
Existe-t-il des processeurs compatibles Z80 avec un registre A 16 bits? Je demande car la question nécessite que le code fonctionne pour N = 32767 .
Dennis
1
@Dennis Pour qu'un programme fonctionne pour N = 32767, il doit comporter au moins 2 x 32767 ou 65534 octets. Le Z80 ne peut traiter que 65 536 octets de mémoire, ce qui en fait une tâche impossible car je ne pense pas que je puisse rendre le programme plus petit que 6 octets! Le Aregistre est toujours à 8 bits, sinon le processeur ne serait pas compatible avec le Z80. Je dirais que suffisamment de mémoire et de puissance de calcul ont été couverts ici!
CJ Dennis
1
@Dennis Vous comprenez pourquoi aucun processeur compatible Z80 n'aura de Aregistre autre que 8 bits? Le changer en 16 bits casserait le code en se basant sur 255 + 1 = 0 par exemple. Il faudrait inventer un processeur, appelons-le le Z160, qui utilise un registre 16 bits par défaut mais utilise toujours le même jeu d'instructions 8 bits que le Z80. Bizarre!
CJ Dennis
19

J, 16 14 octets

(_=_)]++[(_=_)

Coutumes:

   (_=_)]++[(_=_)
1
   (_=_)]+(_=_)]++[(_=_)+[(_=_)
2
   (_=_)]+(_=_)]+(_=_)]++[(_=_)+[(_=_)+[(_=_)
3

Explication:

  • J évalue de droite à gauche.

  • (_=_)est inf equals infce qui est vrai, a une valeur de 1, donc l'expression devient 1+]...[+1. ( (8=8)ça marcherait aussi mais ça a l'air plus cool. :))

  • [et ]retourner leurs arguments gauche et droit respectivement s'ils ont 2 arguments. S'ils n'en obtiennent qu'un, ils le retournent.

  • +ajoute les 2 arguments. S'il n'en obtient qu'un, il le renvoie.

Maintenant, évaluons une expression de niveau 3 (allant de droite à gauche):

(_=_)]+(_=_)]++[(_=_)+[(_=_)  NB. (_=_) is 1

1]+1]+1]++[1+[1+[1  NB. unary [, binary +

1]+1]+1]++[1+[2  NB. unary [, binary +

1]+1]+1]++[3  NB. unary [, unary +

1]+1]+1]+3  NB. unary +, binary ]

1]+1]+3  NB. unary +, binary ]

1]+3  NB. unary +, binary ]

3

Comme nous le voyons, la moitié droite du 1's est ajoutée et le côté gauche du 1' s est omis, ce qui donne le nombre entier souhaité N, le niveau miroir.

Essayez-le en ligne ici.

randomra
la source
12

Haskell, 42 octets

(8-8)-(-(8-8)^(8-8))--((8-8)^(8-8)-)-(8-8)

Heureusement, un commentaire de ligne dans Haskell (-> --) peut être mis en miroir et la moitié (-> -) est une fonction valide. Le reste est un peu de mathématiques pour obtenir les chiffres 0et 1. Fondamentalement, nous avons (0)-(-1)un commentaire N=0et un préfixe (0)-(-1)-à chaque étape.

Si des nombres à virgule flottante sont autorisés pour la sortie, nous pouvons construire à 1partir 8/8de 26 octets et nous en tirer:

Haskell, 26 octets

(8-8)-(-8/8)--(8\8-)-(8-8)

Sorties 1.0, 2.0etc.

nimi
la source
2
Ceci est techniquement invalide car il doit s'agir d'un programme complet.
Calvin's Hobbies
@ Calvin'sHobbies: Je vois. Avons-nous un consensus sur les exigences minimales pour un programme complet? J'ai cherché des méta, mais je n'ai trouvé qu'une discussion sur les fonctions, pas sur les programmes. Selon la définition d'un programme complet, je pourrai peut-être corriger ma solution.
nimi
Je l'appellerais un programme complet si vous pouvez l'enregistrer dans un fichier program.hs, puis exécuter à $ runhaskell program.hspartir de la ligne de commande et voir la sortie. Je ne connais pas Haskell, donc je ne peux pas dire exactement ce qui doit changer.
Calvin's Hobbies
2
@ Calvin'sHobbies: runhaskellest un script shell qui configure un environnement et appelle finalement ghcle compilateur Haskell. Vous pouvez exécuter mon code directement avec ghc: ghc -e "(8-8)-(-8/8)--(8\8-)-(8-8)". Cela démarre ghcqui évalue le code fourni en argument, affiche le résultat et quitte. Pas de REPL, pas d'interaction. Bien sûr, cela ajouterait +1 au nombre d'octets pour -e.
nimi
@nimi: -ene contribue pas au score dans ce cas. Nous ne comptons pas les octets pour perl -Eou gcc -std=c99non.
Dennis
11

CJam, 14 octets

]X+:+OooO+:+X[

Essayez-le en ligne dans l'interpréteur CJam: N = 0 , N = 1 , N = 2 , N = 3 , N = 41

Notez que ce code se termine par un message d'erreur. À l'aide de l'interpréteur Java, ce message d'erreur peut être supprimé en fermant ou en redirigeant STDERR. 1

Comment ça marche

Dans les moitiés gauches, les événements suivants se produisent:

  • ] enveloppe la pile entière dans un tableau.

  • Xajoute 1à ce tableau.

  • :+ calcule la somme de tous les éléments du tableau.

  • Oo affiche le contenu d'un tableau vide (c.-à-d. rien).

Dans la première moitié droite, les événements suivants se produisent:

  • o imprime l'entier sur la pile, qui est la sortie souhaitée.

  • O+ tente d'ajouter un tableau vide à l'élément le plus haut de la pile.

    Cependant, la pile était vide avant de pousser O. Cela échoue et met fin à l'exécution du programme.

Le code restant est simplement ignoré.

1 Selon le méta-sondage Les soumissions devraient-elles être autorisées à se terminer avec une erreur? , cela est autorisé.

Dennis
la source
Je serais sceptique sur l'acceptation en raison de l'erreur, mais comme ce n'est pas gagnant, je ne suis pas trop inquiet.
Calvin's Hobbies
Des tâches comme celle-ci sont étonnamment difficiles dans CJam, étant donné que c'est une langue de golf. Même une erreur de syntaxe (par exemple, un opérateur non défini) dans un bloc non exécuté empêchera l'exécution complète du programme. J'essaie toujours de me débarrasser de l'erreur.
Dennis