Un simulateur d'ADN simple

18

Votre code va générer pour toujours une représentation très simple de l'ADN ASCII de l'ADN. Il prendra deux nombres en entrée dans n'importe quel format que vous souhaitez: comme une liste, comme arguments d'une fonction, sur stdin, etc.

  • Un intervalle Ià virgule flottante en secondes compris entre 0,0 et 1,0 (inclus)
  • Un niveau de zoom Zsous forme d'entier de 1 à 64 (inclus)

Votre code imprimera une ligne sur stdout ou son équivalent toutes les Isecondes, produisant une sortie infinie qui ressemble à ceci (pour le niveau de zoom 4):

    A
 T-----a
G-------c
 G-----c
    g
 t-----A
a-------T
 c-----G
    T
 A-----t
C-------g
...

En particulier, notre représentation de l' ADN est une paire d'ondes sinusoïdales reliés par des traits d' union, une constituée des caractères a, c, g, et t, à l'autre des caractères A, C, G, et T. Si xle numéro 0-indexé de la ligne que nous imprimons actuellement, la position de base 0 du caractère dans la vague minuscule est donnée par (sin(πx / Z) + 1) * Z, et dans la vague majuscule est donnée par (-sin(πx / Z) + 1) * Z, à la fois arrondie (non parqueté) au plus proche entier. Plus de détails:

  • Dans les cas où les deux ondes se chevauchent, vous devez alterner celle qui se trouve à l'avant, en commençant par l'onde en majuscule. (Commencer par l'onde minuscule nous donnerait une double hélice qui n'existe pas !)
  • En ignorant la casse, A s'associe toujours à T et C s'associe toujours à G, comme dans l'ADN réel. Les paires elles-mêmes doivent être choisies au hasard avec une distribution uniforme sur les quatre possibilités. Peu importe si le choix des paires est le même ou différent lors des exécutions successives de votre code. La qualité statistique de vos choix aléatoires n'est pas un problème tant que la sortie n'a pas de modèle évident et une période au moins dans les milliards (les PRNG défectueux comme RANDU sont très bien.)
  • Vous devez avoir aucun espace de fin ou remplir chaque ligne à la position maximale des vagues à ce niveau de zoom (dans l'exemple ci-dessus, neuf caractères.) Le niveau de zoom 1 peut avoir un espace de fin supplémentaire facultatif pour des raisons mathématiques.

Parce que l'ADN est petit, votre code devra être aussi court que possible.

Plus d'exemples:

Niveau de zoom 8:

        T
     C-----g
  A-----------t
 C-------------g
G---------------c
 T-------------a
  T-----------a
     T-----a
        c
     g-----C
  t-----------A
 g-------------C
a---------------T
...

Niveau de zoom 2:

  A
T---a
  c
g---C
  G
A---t
  c
a---T
...

Niveau de zoom 1 (notez l'espace de tête):

 G
 a
 C
 t
...
Luc
la source
En relation.
Martin Ender
9
"Parce que l'ADN est petit, votre code devra être aussi court que possible." Vraiment?
TanMath
3
@TanMath Avez-vous vraiment besoin d'une raison pour Code-Golf? Les histoires sont presque toujours idiotes comme ça, allez-y.
Patrick Roberts
@PatrickRoberts Je sais, mais je soulignais à quel point la raison est idiote, de nombreux golfeurs de code le font. Ne le prends pas trop au sérieux! ;)
TanMath
Que signifie «choisi au hasard»? Est-ce que RANDU va bien? Qu'en est-il d'une séquence répétée plus courte?
KSFT

Réponses:

4

Ruby, Rev B 171 161 octets

La correction de la sortie pour z = 1 coûte 10 octets. C'est un cas particulier: l'hélice fait vraiment 3 caractères de large si vous la regardez à 90 degrés, mais comme nous la regardons à 0 degré, elle ne fait que 1 caractère de large. aucun espace de tête sur z = 1 n'est plus nécessaire

Quelques économies en éliminant les crochets et en multipliant y.abs par 2 avant la troncature lors du calcul du nombre de caractères nécessaires.

Enfin, j'ai évité le include Math(requis pour sinet PI) en utilisant l'arithmétique des nombres complexes avec des puissances du nombre i. La partie imaginaire du nombre complexe est équivalente à sin x, sauf qu'elle se répète avec la période 4 au lieu de la période 2 * PI. L'enregistrement pour cette modification était de 1 ou 0 octet.

->z,i{x=0
loop{y=z*("i".to_c**x).imag
s=(?-*(y.abs*2)).center z*2+1
s[z-y+0.5]='TGAC'[r=rand(4)]
x!=0&&s[z+y+0.5]='actg'[r]
puts s
sleep i
x+=2.0/z
x>3.99&&x=0}}

Ruby, Rev A 165 octets

C'est bien plus long que prévu. Il y a quelques opportunités potentielles de golf à explorer.

include Math
->z,i{x=0
loop{y=z*sin(x)
s=('--'*(y.abs+h=0.5)).center(z*2+1)
s[z+h-y]='TGAC'[r=rand(4)]
x!=0&&s[z+h+y]='actg'[r]
puts s
sleep(i)
x+=PI/z
x>6.28&&x=0}}

Commenté dans le programme de test

include Math
f=->z,i{x=0
  loop{y=z*sin(x)
    s=('--'*(y.abs+h=0.5)).center(z*2+1)  #make a space-padded string of z*2+1 characters, containing enough - signs
    s[z+h-y]='TGAC'[r=rand(4)]            #insert random capital letter, saving index in r
    x!=0&&s[z+h+y]='actg'[r]              #insert small letter. This will normally go on top of the capital as it is done second, but supress for x=0 to make helix
    puts s
    sleep(i)
    x+=PI/z                               #increment x
    x>6.28&&x=0                           #reset x if equal to 2*PI (this proofs against loss of floating point precision, making correct output truly infinite.)
  }
}

Z=gets.to_i
I=gets.to_f
f[Z,I]
Level River St
la source
Vous cherchez bien! Un problème mineur: il y a un espace de premier plan pour le niveau de zoom 1. En outre, dans votre programme de test I=gets.to_idevrait être I=gets.to_f.
Luke
Oups! Vous avez raison de dire que Z = 1 est un cas spécial. Ce n'était pas intentionnel et c'est en fait une contradiction dans les règles étant donné les calculs que j'ai fournis. Je vais ajouter l'espace de tête pour Z = 1 pour rendre les mathématiques cohérentes.
Luke
@Luke dans les règles générales ne devrait pas être changé, mais il y avait effectivement une contradiction. Pour autant que je sache, les autres réponses ne l'ont pas considéré non plus. Je mettrai à jour ma réponse plus tard, car elle sera plus courte de cette façon.
Level River St
@Luke mis à jour, mais cela signifie que j'ai à la fois un espace de début et un espace de fin sur Z = 1. Je comprends que cela est conforme à l'esprit de ce que vous voulez et donc OK, bien que ce ne soit pas strictement conforme à la formulation des espaces de fuite et à l'exemple de Z = 1.
Level River St
Oups encore, oui ça va. Désolé pour la confusion.
Luke
3

C, 294 289 285 283 281 270 265 237 218 octets

#include<math.h>
o,i,p,r;char*c="acgtTGCA",d[256]={[0 ...254]='-'};P(w,z)float w;{for(;;poll(0,0,r=w*1e3))p=fabs(sinf(M_PI*i++/z))*z+.5,r=rand()&3,o^=4*!p,printf(p?"%*c%s%c\n":"%*c\n",z-p+1,c[r+o],d+256-p*2,c[r+4-o]);}

Ou la version plus longue qui analyse les entrées du principal:

#include<stdlib.h>
#include<math.h>
o,i,p,r;char*c="acgtTGCA",d[256]={[0 ...254]='-'};main(n,v)char**v;{for(;n=strtod(v[2],0);poll(0,0,n=atof(v[1])*1e3))p=fabs(sinf(M_PI*i++/n))*n+.5,r=rand()&3,o^=4*!p,printf(p?"%*c%s%c\n":"%*c\n",n-p+1,c[r+o],d+256-p*2,c[r+4-o]);}

C'est une implémentation globale assez stupide, avec quelques astuces printf. Il a des inclusions manquantes, utilise la syntaxe K&R pour la fonction et s'appuie sur les initialiseurs de plage de GCC, donc ce n'est pas très standard. De plus, la version de la fonction utilise toujours des globaux, elle ne peut donc être appelée qu'une seule fois!

La version de la fonction prend 2 paramètres; attendez (en secondes) et zoomez. Voici un appelant pour cela:

#include <stdlib.h>
int main( int argc, const char *const *argv ) {
    if( argc != 3 ) {
        printf( "Usage: %s <delay> <zoom>\n", argv[0] );
        return EXIT_FAILURE;
    }
    const float delay = atof( argv[1] );
    const int zoom = strtod( argv[2], 0 );
    if( delay < 0 || zoom <= 0 ) {
        printf( "Invalid input.\nUsage: %s <delay> <zoom>\n", argv[0] );
        return EXIT_FAILURE;
    }
    P( delay, zoom );
    return EXIT_SUCCESS;
}

Courir comme:

./dna <delay> <zoom>
./dna 0.5 8

Panne:

// Globals initialise to 0
o,                                 // Ordering (upper/lower first)
i,                                 // Current iteration
p,                                 // Current indent
r;                                 // Current random value
char*c="acgtTGCA",                 // The valid letters
    d[256]={[0 ...254]='-'};       // Line of dashes (for printing)
main(n,v)char**v;{                 // K&R-style main definition (saves 2 bytes)
    // n will be used for Zoom, random number & casting delay
    for(
        ;n=strtod(v[2],0);         // Store zoom
        poll(0,0,n=atof(v[1])*1e3) // After each loop, use poll to delay
                                   // (Use variable to cast delay to int)
    )
        p=fabs(sinf(M_PI*i++/n))*n+.5,   // Calculate separation / 2
        r=rand()&3,                      // Pick random number [0-4)
        o^=4*!p,                         // Reverse order if crossing
        printf(p                         // Print... if not crossing:
                ?"%*c%s%c\n"             //  indent+character+dashes+character
                :"%*c\n",                //  Else indent+character
                n-p+1,                   // Width of indent + 1 for char
                c[r+o],                  // First character
                d+256-p*2,               // Dashes
                c[r+4-o]                 // Second character
        );
}
Dave
la source
Vous êtes autorisé à utiliser une fonction au lieu de main (), ce qui vous fera économiser les octets de strtodet atof.
Luke
@Luke Ah cool; Je verrai combien cela économise ...
Dave
3

C, 569 402 361 octets

#include<stdlib.h>
u,l,r,m,n,Z,I,y=0,x=0;main(c,char**v){Z = atoi(v[1]);I=atof(v[2])*1000000;srand(time(0));char *a="ACGTtgca";while(1){r=rand()%4;usleep(I);double s=sin(3.14*x++/Z);u=floor(((-1*s+1)*Z)+0.5);l=floor(((s+1)*Z)+0.5);m=(u<l)?u:l;n=u<l?l:u;char z[n+1];memset(z,' ',n);z[l]=a[r+4];z[u]=a[r];for(y=m+1;y<n;y++)z[y]='-';z[n+1]='\0';printf("%s\n",z);}}

Cela a été assez rapide, donc je suis sûr qu'il y a d'autres choses que je pourrais faire pour diminuer mon score, mais je suis juste content d'avoir compilé et exécuté correctement ce programme dès la première tentative.

Version De-golf:

#include<stdio.h>
#include<math.h>
#include<unistd.h>
#include<time.h>
#include<stdlib.h>
u,l,r,m,n,Z,I,y=0,x=0;
main(c,char**v){
   Z = atoi(v[1]);
   I=atof(v[2])*1000000;
   srand(time(0));
   char *a="ACGTtgca";
   while(1){
      r=rand()%4;
      usleep(I);
      double s=sin(3.14*x++/Z);
      u=floor(((-1*s+1)*Z)+0.5);
      l=floor(((s+1)*Z)+0.5);
      m=(u<l)?u:l;
      n=u<l?l:u;
      char z[n+1];
      memset(z,' ',n);
      z[l]=a[r+4];
      z[u]=a[r];
      for(y=m+1;y<n;y++)z[y]='-';
      z[n+1]='\0';
      printf("%s\n",z);
   }
}

MISE À JOUR: J'ai ajusté la boucle pour tout imprimer dans une seule instruction d'impression et j'ai utilisé le fait que les variables sont définies comme int par défaut pour raser certains octets. UPDATE2: Certains var renommage et certains raccourcissement logique pour raser quelques octets de plus.

Danwakeem
la source
Vous devez vous procurer GCC. C'est Linux mais vous pouvez également l'exécuter sur Windows avec Cygwin. Les variables (si elles sont déclarées au début du programme ou comme arguments de fonction) n'ont pas besoin de type, elles sont supposées être int. La même chose avec les fonctions. Et je suis sûr que vous n'aurez pas besoin de ces inclusions.
Level River St
1
Vous avez également trop de printfs :-D. Soit 1. utilisez putchar pour imprimer un caractère à la fois, soit 2. déterminez ce que vous voulez imprimer, puis imprimez le tout avec des put. 3. déterminer comment utiliser un seul printf avec une grande expression complexe. Quoi qu'il en soit, +1.
Level River St
D'accord merci pour les suggestions! Je vais essayer de faire une seule déclaration d'impression. C'est une bonne idée et je suis sûr que cela améliorerait mon score. Je reverrai cela quand j'aurai du temps aujourd'hui. Merci @steveverrill
Danwakeem
2

JavaScript (ES6) 241 244 227 222 231 octets

Cela semblait intéressant - j'adore l'art ASCII!
Je viens de commencer, toujours en train de jouer au golf ...

(I,Z)=>{c=i=0,setInterval(_=>{with(Math)m=sin(PI*i++/Z),a=round(++m*Z),b=round((2-m)*Z),r=random()*4|0,D="TGAC"[r],d="actg"[r],e=a-b,c^=!e,p=" ".repeat(a>b?b:a)+(c?D:d)+"-".repeat(e?abs(e)-1:0)+(e?a>b?d:D:""),console.log(p)},I*1e3)

--- EDIT: il s'avère que je ne peux pas vraiment le mettre dans eval () - sinon il ne peut pas accéder aux vars I et Z (ajoute donc 9 octets)

- sauvé 6 octets grâce à user81655
- sauvé 5 octets grâce à Dave

Explication

(I,Z)=>{
  c=i=0,                                // clear vars
  setInterval(_=>{                      // repeat

    with(Math)                         
      m=sin(PI*i++ / Z),                // calculate waves
      a=round(++m * Z),
      b=round((2-m) * Z),
      r=random()*4|0,                   // get random amino-acids
      D="TGAC"[r],
      d="actg"[r],
      e=a-b,
      c^=!e,                            // alternate upper/lowercase
      p=                                // prepare output
        " ".repeat(
          a>b ? b : a
        )+(
          c ? D : d
        )+

        "-".repeat(
          e ? abs(e)-1 : 0
        )+(
          e ? a>b ? d : D : ""
        ),

      console.log(p)                    // return output
  },I*1e3)                              // repeat for every 'I' seconds
}
Aᴄʜᴇʀᴏɴғᴀɪʟ
la source
1
Vous pouvez enregistrer 4 octets supplémentaires en utilisant c^=!eau lieu de c+=a==b(vous permet de supprimer la %2coche plus tard). Pourrait aussi -m+2être 2-m!
Dave
@Dave - merci! Pourriez-vous expliquer ce que c ^ =! E fait réellement? Je n'ai jamais vu ça avant :)
Aᴄʜᴇʀᴏɴғᴀɪʟ
C'est la même chose que c=c^(e==0); il applique un XOR de la même manière que vous aviez précédemment un ajout. Si vous n'êtes pas familier avec XOR, c'est une opération au niveau du bit: eXclusive OR (wikipedia peut l'expliquer correctement)
Dave