Ein einfacher DNA-Simulator

18

Ihr Code wird für immer eine sehr einfache ASCII-artige Darstellung der DNA erzeugen. Es werden zwei Zahlen in einem beliebigen Format als Eingabe verwendet: als Liste, als Argumente für eine Funktion, für stdin usw.

  • Ein Gleitkommaintervall Iin Sekunden zwischen 0,0 und 1,0 (einschließlich)
  • Eine Zoomstufe Zals Ganzzahl von 1 bis einschließlich 64

Ihr Code druckt jede ISekunde eine Zeile auf stdout oder das Äquivalent davon und erzeugt eine unendliche Ausgabe, die ungefähr so ​​aussieht (für Zoomstufe 4):

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

Genauer gesagt, unsere Darstellung von DNA ist ein Paar von Sinuswellen , die durch Bindestriche verbunden ist , eine aus den Zeichen a, c, g, und t, die andere der Zeichen A, C, G, und T. Wenn xdie 0-indizierten Nummer der Zeile sind wir gerade gedruckt wird , die 0-basierte Position des Zeichens in der Kleinwelle wird durch gegeben (sin(πx / Z) + 1) * Z, und in der Groß Welle ist gegeben durch (-sin(πx / Z) + 1) * Z, die beide abgerundet (nicht platt) zum nächstgelegenen ganze Zahl. Weitere Details:

  • In Fällen, in denen sich die beiden Wellen überlappen, müssen Sie abwechseln, welche Welle vorne liegt, beginnend mit der Welle in Großbuchstaben. (Wenn wir mit der Welle in Kleinbuchstaben beginnen, erhalten wir eine Doppelhelix, die es nicht gibt !)
  • Ignoriert man den Fall, so paart sich A immer mit T und C immer mit G, wie in der realen DNA. Die Paare selbst sollten zufällig mit einer gleichmäßigen Verteilung über die vier Möglichkeiten ausgewählt werden. Es spielt keine Rolle, ob die Auswahl der Paare bei aufeinanderfolgenden Codeausführungen gleich oder unterschiedlich ist. Die statistische Qualität Ihrer zufälligen Auswahl ist kein Problem, solange die Ausgabe kein offensichtliches Muster und einen Zeitraum von mindestens mehreren Milliarden aufweist (fehlerhafte PRNGs wie RANDU sind in Ordnung).
  • Sie dürfen entweder keine nachgestellten Leerzeichen haben oder jede Zeile bis zur maximalen Position der Wellen in dieser Zoomstufe auffüllen (im obigen Beispiel neun Zeichen). Zoomstufe 1 kann aus mathematischen Gründen ein optionales zusätzliches nachgestelltes Leerzeichen enthalten.

Da die DNA klein ist, muss Ihr Code so kurz wie möglich sein.

Mehr Beispiele:

Zoomstufe 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
...

Zoomstufe 2:

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

Zoomstufe 1 (beachten Sie das führende Leerzeichen):

 G
 a
 C
 t
...
Luke
quelle
Verbunden.
Martin Ender
9
"Da die DNA klein ist, muss Ihr Code so kurz wie möglich sein." "Ja wirklich?"
TanMath
3
@TanMath Brauchen Sie wirklich einen Grund zum Code-Golf? Die Hintergrundgeschichten sind fast immer so albern, machen Sie einfach mit.
Patrick Roberts
@PatrickRoberts Ich weiß, aber ich wies nur darauf hin, wie albern der Grund ist, das machen viele Code-Golfer. Nimm es nicht zu ernst! ;)
TanMath
Was bedeutet "zufällig ausgewählt"? Ist RANDU in Ordnung? Was ist mit einer kürzeren Wiederholungssequenz?
KSFT

Antworten:

4

Ruby, Rev. B 171 161 Bytes

Das Fixieren der Ausgabe für z = 1 kostet 10 Bytes. Es ist ein Sonderfall: Die Helix ist wirklich 3 Zeichen breit, wenn Sie sie bei 90 Grad betrachten, aber wenn wir sie bei 0 Grad betrachten, sieht sie nur 1 Zeichen breit aus. Null führende Leerzeichen auf z = 1 werden nicht mehr benötigt

Einige Einsparungen durch Eliminieren von Klammern und durch Multiplizieren von y.abs mit 2 vor dem Abschneiden bei der Berechnung der Anzahl der benötigten Zeichen.

Schließlich habe ich das include Math(für sinund erforderliche PI) vermieden, indem ich eine komplexe Zahlenarithmetik mit Potenzen der Zahl verwendet habe i. Der Imaginärteil der komplexen Zahl ist äquivalent zu sin x, außer dass er sich mit Periode 4 anstelle von Periode 2 * PI wiederholt. Das Speichern für diese Änderung betrug entweder 1 oder 0 Byte.

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

Das ist viel länger als erwartet. Es gibt einige mögliche Golfgelegenheiten, die erforscht werden müssen.

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

Kommentiert im Testprogramm

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
quelle
Gut aussehen! Ein kleines Problem: Es gibt ein führendes Leerzeichen für Zoomstufe 1. Auch in deinem Testprogramm I=gets.to_isollte das sein I=gets.to_f.
Luke
Hoppla! Sie haben Recht, dass Z = 1 ein Sonderfall ist. Das war nicht beabsichtigt und ist in der Tat ein Widerspruch zu den Regeln, wenn man die von mir bereitgestellte Mathematik berücksichtigt. Ich werde das führende Leerzeichen für Z = 1 hinzufügen, um die Mathematik konsistent zu machen.
Luke
@ Luke in allgemeinen Regeln sollte nicht geändert werden, aber in der Tat gab es einen Widerspruch. Soweit ich das beurteilen kann, haben die anderen Antworten dies ebenfalls nicht berücksichtigt. Ich werde meine Antwort dann später aktualisieren, da sie auf diese Weise kürzer wird.
Level River St
@ Luke aktualisiert, aber es bedeutet, ich habe sowohl ein führendes Leerzeichen als auch ein nachfolgendes Leerzeichen auf Z = 1. Ich verstehe, dass dies dem Geist von dem entspricht, was Sie wollen, und daher in Ordnung ist, obwohl es nicht strikt der Formulierung über nachgestellte Leerzeichen und dem Beispiel für Z = 1 entspricht.
Level River St
Hoppla, ja, das ist in Ordnung. Entschuldigung für die Verwirrung.
Luke
3

C 294 289 285 283 281 270 265 237 218 Bytes

#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]);}

Oder die längere Version, die Eingaben von main analysiert:

#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]);}

Es ist eine ziemlich blöde Gesamtimplementierung mit ein paar printf-Tricks. Es hat einige fehlende Includes, verwendet K & R-Syntax für die Funktion und stützt sich auf die GCC-Bereichsinitialisierer, daher ist dies nicht sehr Standard. Auch die Funktionsversion verwendet immer noch Globals, so dass sie nur einmal aufgerufen werden kann!

Die Funktionsversion akzeptiert 2 Parameter; Warten Sie (in Sekunden) und zoomen Sie. Hier ist ein Anrufer dafür:

#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;
}

Rennen wie:

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

Nervenzusammenbruch:

// 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
quelle
Sie dürfen anstelle von main () eine Funktion verwenden, die Ihnen die Bytes von strtodund erspart atof.
Luke
@ Luke Ah cool; Ich werde sehen, wie viel das spart ...
Dave
3

C 569 402 361 Bytes

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

Ich habe das ziemlich schnell durchgearbeitet und bin mir sicher, dass ich noch einige andere Dinge tun kann, um meine Punktzahl zu senken, aber ich bin einfach froh, dass ich dieses Programm beim ersten Versuch richtig kompilieren und ausführen konnte.

De-Golf-Version:

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

UPDATE: Ich habe die Schleife so angepasst, dass alles in einer print-Anweisung gedruckt wird, und habe die Tatsache verwendet, dass Variablen standardmäßig als int definiert sind, um einige Bytes zu sparen. UPDATE2: Einige Var-Umbenennungen und einige Logik-Kürzungen, um ein paar Bytes mehr zu sparen.

Danwakeem
quelle
Sie müssen GCC erreichen. Es ist Linux, aber Sie können es auch unter Windows mit Cygwin ausführen. Variablen (wenn zu Beginn des Programms oder als Funktionsargumente deklariert) benötigen keinen Typ, sie werden als int angenommen. Das gleiche gilt für Funktionen. Und ich bin mir ziemlich sicher, dass Sie diese Includes nicht brauchen werden.
Level River St
1
Außerdem hast du zu viele printfs :-D. Entweder 1. Verwenden Sie Putchar, um jeweils ein Zeichen zu drucken, oder 2. Ermitteln Sie, was Sie drucken möchten, und drucken Sie dann alles mit Puts. 3. Erarbeiten Sie, wie Sie ein einzelnes printf mit einem großen komplexen Ausdruck verwenden. Wie auch immer, +1.
Level River St
Okay danke für die Vorschläge! Ich werde versuchen, eine einzelne Druckerklärung abzugeben. Das ist eine gute Idee und ich bin mir sicher, dass es meine Punktzahl verbessern würde. Ich werde das regolf machen, wenn ich heute etwas Zeit habe. Thanks @steveverrill
Danwakeem
2

JavaScript (ES6) 241 244 227 222 231 Byte

Das sah interessant aus - ich liebe ASCII-Kunst!
Gerade erst angefangen, noch dabei, Golf zu spielen ...

(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: stellt sich heraus, dass ich es nicht in eval () schreiben kann - ansonsten kann es nicht auf die Vars I und Z zugreifen (fügt also 9 Bytes hinzu)

- 6 Bytes dank user81655
gespart - 5 Bytes dank Dave gespart

Erläuterung

(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ᴄʜᴇʀᴏɴғᴀɪʟ
quelle
1
Sie können c^=!estattdessen weitere 4 Bytes speichern c+=a==b(Sie können das %2Häkchen später entfernen ). Könnte -m+2auch sein 2-m!
Dave
@ Dave - danke! Würde es Ihnen etwas ausmachen zu erklären, was c ^ =! E tatsächlich tut? Ich habe das noch nie gesehen :)
6.
Es ist dasselbe wie c=c^(e==0); Es wendet ein XOR auf die gleiche Weise an, wie Sie es zuvor hinzugefügt hatten. Wenn Sie mit XOR nicht vertraut sind, handelt es sich um eine bitweise Operation: EXKLUSIV-ODER (Wikipedia kann dies richtig erklären)
Dave