Prosty symulator DNA

18

Twój kod na zawsze wygeneruje bardzo prostą reprezentację DNA w stylu ASCII. Przyjmie dwie liczby jako dane wejściowe w dowolnym formacie: jako listę, jako argumenty funkcji, na standardowe wejście itp.

  • Przedział zmiennoprzecinkowy Iw sekundach od 0,0 do 1,0 (włącznie)
  • Poziom powiększenia Zjako liczba całkowita od 1 do 64 (włącznie)

Kod wydrukuje co Isekundę jeden wiersz na standardowe wyjście lub jego ekwiwalent , tworząc nieskończone wyjście, które wygląda mniej więcej tak (dla poziomu powiększenia 4):

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

W szczególności, reprezentacja naszym DNA jest para sinusoidalnych połączonych łącznikami, składający się ze znaków a, c, gi t, z drugiej strony z postaci A, C, Gi T. Jeśli xjest to indeksowany przez 0 numer linii, którą obecnie drukujemy, pozycja znaku w fali małych liter jest określana przez 0 (sin(πx / Z) + 1) * Z, a w fali wielkich liter przez (-sin(πx / Z) + 1) * Z, obie zaokrąglone (nie zmienione) do najbliższej liczba całkowita. Dalsze szczegóły:

  • W przypadku, gdy dwie fale zachodzą na siebie, musisz na przemian, która fala znajduje się z przodu, zaczynając od fali wielkiej litery. (Zaczynając od małej litery, dałoby nam podwójną helisę, która nie istnieje !)
  • Ignorując wielkość liter, A zawsze łączy się z T i C zawsze łączy się z G, jak w prawdziwym DNA. Same pary powinny być wybierane losowo z równomiernym rozkładem na cztery możliwości. Nie ma znaczenia, czy wybór par jest taki sam, czy różny przy kolejnych uruchomieniach kodu. Jakość statystyczna twoich losowych wyborów nie stanowi problemu, o ile dane wyjściowe nie mają oczywistego wzorca i okresu co najmniej w miliardach (wadliwe PRNG, takie jak RANDU, są w porządku).
  • Musisz albo nie mieć spacji końcowych, albo wstawiać każdą linię do maksymalnego położenia fal na tym poziomie powiększenia (w powyższym przykładzie dziewięć znaków). Poziom powiększenia 1 może mieć jedno opcjonalne dodatkowe miejsce na końcu z powodów matematycznych.

Ponieważ DNA jest małe, kod będzie musiał być jak najkrótszy.

Więcej przykładów:

Poziom powiększenia 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
...

Poziom powiększenia 2:

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

Poziom powiększenia 1 (zwróć uwagę na wiodące miejsce):

 G
 a
 C
 t
...
Łukasz
źródło
Związane z.
Martin Ender
9
„Ponieważ DNA jest małe, Twój kod musi być możliwie jak najkrótszy”. Naprawdę?
TanMath,
3
@TanMath Czy naprawdę potrzebujesz powodu do Code-Golf? Historie są prawie zawsze takie głupie, po prostu idź z tym.
Patrick Roberts,
@PatrickRoberts Wiem, ale właśnie wskazywałem, jak głupi jest powód, dla wielu golfistów. Nie bierz tego zbyt poważnie! ;)
TanMath
Co oznacza „losowo wybrany”? Czy RANDU jest w porządku? A co z krótszą powtarzającą się sekwencją?
KSFT

Odpowiedzi:

4

Ruby, Rev B 171 161 bajtów

Naprawienie wyjścia dla z = 1 kosztuje 10 bajtów. Jest to szczególny przypadek: helisa ma naprawdę 3 znaki szerokości, jeśli spojrzysz na nią pod kątem 90 stopni, ale gdy spojrzymy na nią pod kątem 0 stopni, wygląda ona tylko na 1 znak szerokości. zero wiodących spacji na z = 1 nie jest już wymagane

Niektóre oszczędności poprzez eliminację nawiasów i mnożenie y.ab przez 2 przed obcięciem przy obliczaniu potrzebnej liczby znaków.

Wreszcie uniknąłem include Math(wymaganego sini PI) używania arytmetyki liczb zespolonych z potęgami liczby i. Urojona część liczby zespolonej jest równoważna sin x, z tym wyjątkiem, że powtarza się z okresem 4 zamiast okresu 2 * PI. Zapisywanie tej zmiany wynosiło 1 lub 0 bajtów.

->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 bajtów

Jest to znacznie dłużej niż oczekiwano. Istnieje kilka potencjalnych możliwości gry w golfa.

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

Skomentowane w programie testowym

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
źródło
Wygląda dobrze! Jeden drobny problem: istnieje wiodąca przestrzeń dla poziomu powiększenia 1. Również w twoim programie testowym I=gets.to_ipowinno być I=gets.to_f.
Łukasza
Ups! Masz rację, że Z = 1 jest szczególnym przypadkiem. To nie było zamierzone i jest w rzeczywistości sprzecznością w przepisach, biorąc pod uwagę matematykę, którą przedstawiłem. Dodam wiodącą przestrzeń dla Z = 1, aby matematyka była spójna.
Luke
@Luke w ogólnych zasadach nie należy zmieniać, ale rzeczywiście istniała sprzeczność. O ile wiem, inne odpowiedzi też tego nie rozważały. Później zaktualizuję swoją odpowiedź, ponieważ w ten sposób będzie ona krótsza.
Level River St
@Luke zaktualizowane, ale oznacza to, że mam zarówno spację wiodącą, jak i spację końcową na Z = 1. Rozumiem, że jest to zgodne z duchem tego, czego chcesz i dlatego jest OK, choć nie jest to ściśle zgodne z frazowaniem o końcowych spacjach i przykładem dla Z = 1.
Level River St
Ups, znowu, tak, w porządku. Przepraszam za zamieszanie.
Łukasza
3

C 294 289 285 283 281 270 265 237 218 bajtów

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

Lub dłuższa wersja, która analizuje dane wejściowe z głównego:

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

Jest to dość głupia ogólna implementacja, z pewnymi sztuczkami printf. Brakuje w niej niektórych elementów, używa dla tej funkcji składni K&R i polega na inicjatorach zakresu GCC, więc nie jest to zbyt standardowe. Również wersja funkcji nadal używa globałów, więc można ją wywołać tylko raz!

Wersja funkcji przyjmuje 2 parametry; poczekaj (w sekundach) i powiększ. Oto osoba dzwoniąca:

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

Uruchom jako:

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

Awaria:

// 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
źródło
Możesz użyć funkcji zamiast main (), która pozwoli ci zaoszczędzić bajty strtodi atof.
Łukasz
@Luke Ah cool; Zobaczę, ile to oszczędza ...
Dave
3

C, 569 402 361 bajtów

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

Szybko to poprawiłem, więc jestem pewien, że mogę zrobić kilka innych rzeczy, aby obniżyć swój wynik, ale cieszę się, że udało mi się skompilować i uruchomić ten program przy pierwszej próbie.

Wersja do gry w golfa:

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

AKTUALIZACJA: Dostosowałem pętlę, aby wydrukować wszystko w jednej instrukcji print i wykorzystałem fakt, że zmienne są domyślnie zdefiniowane jako int, aby golić niektóre bajty. AKTUALIZACJA 2: Zmiana nazwy var i skrócenie logiki w celu golenia jeszcze kilku bajtów.

Danwakeem
źródło
Musisz zdobyć GCC. Jest to Linux, ale można go również uruchomić w systemie Windows za pomocą Cygwin. Zmienne (jeśli są zadeklarowane na początku programu lub jako argumenty funkcji) nie potrzebują typu, przyjmuje się, że są int. To samo z funkcjami. I jestem prawie pewien, że nie będziesz potrzebować tych załączników.
Level River St
1
Masz też zbyt wiele printfs :-D. Albo 1. użyj putchar, aby wydrukować jeden znak na raz, lub 2. ustal, co chcesz wydrukować, a następnie wydrukuj wszystko za pomocą puttów. 3. dowiedz się, jak używać pojedynczego printf z dużym złożonym wyrażeniem. W każdym razie +1.
Level River St
OK dzięki za sugestie! Spróbuję sporządzić jedno oświadczenie drukowania. To dobry pomysł i jestem pewien, że poprawiłoby to mój wynik. Zrobię to ponownie, gdy będę miał dzisiaj trochę czasu. Dzięki @steveverrill
Danwakeem
2

JavaScript (ES6) 241 244 227 222 231 bajtów

Wyglądało to interesująco - uwielbiam sztukę ASCII!
Właśnie zaczęłam grać w golfa ...

(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)

--- EDYCJA: okazuje się, że właściwie nie mogę umieścić go w eval () - w przeciwnym razie nie może uzyskać dostępu do zmiennych I i Z (więc dodaje 9 bajtów)

- zapisane 6 bajtów dzięki użytkownikowi 81655
- zapisane 5 bajtów dzięki Dave'owi

Wyjaśnienie

(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ᴄʜᴇʀᴏɴғᴀɪʟ
źródło
1
Możesz zapisać kolejne 4 bajty, używając c^=!ezamiast c+=a==b(pozwala to usunąć %2czek później). Również -m+2może być 2-m!
Dave
@Dave - dzięki! Czy mógłbyś wyjaśnić, co właściwie robi c ^ =! E? Nigdy wcześniej tego nie widziałem :)
2016
To to samo co c=c^(e==0); stosuje XOR w taki sam sposób, jak wcześniej dodałeś. Jeśli nie znasz XOR, jest to operacja bitowa: eXclusive OR (wikipedia może to poprawnie wyjaśnić)
Dave