Simulator DNA sederhana

18

Kode Anda akan menghasilkan representasi DNA ASCII-art yang sangat sederhana, selamanya. Dibutuhkan dua angka sebagai input dalam format apa pun yang Anda inginkan: sebagai daftar, sebagai argumen untuk fungsi, di stdin, dll.

  • Interval titik-mengambang Idalam detik antara 0,0 dan 1,0 (inklusif)
  • Level zoom Zsebagai bilangan bulat dari 1 hingga 64 (inklusif)

Kode Anda akan mencetak satu baris ke stdout atau yang setara setiap Idetik, menghasilkan output tanpa batas yang terlihat seperti ini (untuk level zoom 4):

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

Secara khusus, representasi kami DNA adalah sepasang gelombang sinus dihubungkan dengan tanda hubung, salah satu yang terdiri dari karakter a, c, g, dan t, yang lain dari karakter A, C, G, dan T. Jika xnomor 0-diindeks dari garis yang sedang kita cetak, posisi berbasis-0 karakter dalam gelombang huruf kecil diberikan oleh (sin(πx / Z) + 1) * Z, dan pada gelombang huruf besar diberikan oleh (-sin(πx / Z) + 1) * Z, keduanya bulat (tidak berlantai) ke terdekat bilangan bulat. Keterangan lebih lanjut:

  • Dalam kasus di mana kedua gelombang tumpang tindih, Anda harus mengganti gelombang yang ada di depan, dimulai dengan gelombang huruf besar. (Dimulai dengan gelombang huruf kecil akan memberi kita heliks ganda yang tidak ada !)
  • Mengabaikan kasus, A selalu berpasangan dengan T dan C selalu berpasangan dengan G, seperti pada DNA asli. Pasangan itu sendiri harus dipilih secara acak dengan distribusi yang seragam atas empat kemungkinan. Tidak masalah jika pilihan pasangan sama atau berbeda pada berturut-turut menjalankan kode Anda. Kualitas statistik dari pilihan acak Anda tidak menjadi masalah selama output tidak memiliki pola yang jelas dan periode setidaknya dalam miliaran (PRNG cacat seperti RANDU baik-baik saja.)
  • Anda tidak boleh memiliki spasi tambahan atau gabungkan setiap baris ke posisi maksimum gelombang pada level zoom tersebut (dalam contoh di atas, sembilan karakter.) Level zoom 1 mungkin memiliki satu ruang tambahan tambahan opsional untuk alasan matematis.

Karena DNA kecil, kode Anda harus sesingkat mungkin.

Lebih banyak contoh:

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

Tingkat pembesaran 2:

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

Tingkat zoom 1 (perhatikan ruang utama):

 G
 a
 C
 t
...
Luke
sumber
Terkait
Martin Ender
9
"Karena DNA kecil, kode Anda harus sesingkat mungkin." Betulkah?
TanMath
3
@TanMath Apakah Anda benar-benar membutuhkan alasan untuk Code-Golf? Backstories hampir selalu konyol seperti ini, ikuti saja.
Patrick Roberts
@ PatrickRoberts, saya tahu, tapi saya baru saja menunjukkan betapa konyolnya alasannya, banyak pegolf yang melakukannya. Jangan menganggapnya terlalu serius! ;)
TanMath
Apa yang dimaksud dengan "dipilih secara acak"? Apakah RANDU baik-baik saja? Bagaimana dengan urutan berulang yang lebih pendek?
KSFT

Jawaban:

4

Ruby, Rev B 171 161 byte

Memperbaiki output untuk z = 1 biaya 10 byte. Ini adalah kasus khusus: heliks benar-benar lebar 3 karakter jika Anda melihatnya pada 90 derajat, tetapi seperti yang kita lihat pada 0 derajat itu terlihat hanya lebar 1 karakter. nol spasi di depan pada z = 1 tidak lagi diperlukan

Beberapa penghematan dengan menghilangkan tanda kurung dan dengan mengalikan y.ab dengan 2 sebelum pemotongan saat menghitung jumlah - karakter yang diperlukan.

Akhirnya, saya menghindari include Math(diperlukan untuk sindan PI) dengan menggunakan bilangan aritmatika kompleks dengan kekuatan bilangan i. Bagian imajiner dari bilangan kompleks setara dengan sin x, kecuali bahwa ia mengulangi dengan periode 4 bukannya periode 2 * PI. Menyimpan untuk perubahan ini adalah 1 atau 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 byte

Ini jauh lebih lama dari yang diharapkan. Ada beberapa peluang golf potensial untuk dieksplorasi.

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

Berkomentar dalam program uji

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
sumber
Terlihat bagus! Satu masalah kecil: ada ruang terkemuka untuk level zoom 1. Selain itu, I=gets.to_iseharusnya dalam program pengujian Anda I=gets.to_f.
Luke
Aduh! Anda benar bahwa Z = 1 adalah kasus khusus. Itu tidak disengaja dan sebenarnya merupakan kontradiksi dalam aturan mengingat matematika yang saya berikan. Saya akan menambahkan spasi terdepan untuk Z = 1 untuk membuat matematika konsisten.
Luke
@ Lukas dalam aturan umum tidak boleh diubah, tapi memang ada kontradiksi. Sejauh yang saya tahu, jawaban lain belum mempertimbangkannya. Saya akan memperbarui jawaban saya nanti, karena akan lebih pendek seperti itu.
Level River St
@ Lukas diperbarui, tetapi itu berarti saya memiliki ruang terdepan dan ruang tambahan di Z = 1. Saya mengerti bahwa itu sesuai dengan semangat apa yang Anda inginkan dan karenanya OK, meskipun tidak sepenuhnya sesuai dengan ungkapan tentang spasi tambahan dan contoh untuk Z = 1.
Level River St
Ups lagi, ya tidak apa-apa. Maaf bila membingungkan.
Luke
3

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

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

Atau versi yang lebih panjang yang mem-parsing input dari main:

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

Ini adalah implementasi keseluruhan yang cukup bodoh, dengan beberapa trik printf dilemparkan. Ada beberapa yang hilang termasuk, menggunakan sintaks K&R untuk fungsi, dan bergantung pada range initializer GCC, jadi ini tidak terlalu standar. Juga versi fungsi masih menggunakan global, jadi hanya bisa dipanggil sekali!

Versi fungsi mengambil 2 parameter; tunggu (dalam detik) dan perbesar. Ini peneleponnya:

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

Jalankan sebagai:

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

Kerusakan:

// 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
sumber
Anda diizinkan menggunakan fungsi alih-alih main (), yang akan menghemat byte strtoddan atof.
Luke
@ Lukas Ah keren; Saya akan melihat berapa banyak yang dihemat ...
Dave
3

C, 569 402 361 byte

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

Mengocok ini dengan cukup cepat sehingga saya yakin ada beberapa hal lain yang bisa saya lakukan untuk mengurangi skor saya tetapi saya senang saya mendapatkan program ini untuk dikompilasi dan berjalan dengan baik pada upaya pertama.

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

UPDATE: Saya menyesuaikan loop untuk mencetak semuanya dalam satu pernyataan cetak dan menggunakan fakta bahwa variabel didefinisikan sebagai int secara default untuk mencukur beberapa byte. UPDATE2: Beberapa var renaming dan beberapa pemendekan logika untuk mencukur beberapa byte lagi.

Danwakeem
sumber
Anda harus mendapatkan GCC. Ini Linux tetapi Anda juga dapat menjalankannya di windows dengan Cygwin. Variabel (jika dideklarasikan di awal program atau sebagai argumen fungsi) tidak perlu tipe, mereka dianggap int. Sama dengan fungsinya. Dan saya cukup yakin Anda tidak akan membutuhkannya.
Level River St
1
Anda juga memiliki terlalu banyak printf: -D. Baik 1. menggunakan putchar untuk mencetak satu karakter pada satu waktu atau 2. mencari tahu apa yang ingin Anda cetak dan kemudian mencetak semuanya dengan put. 3. mencari cara menggunakan printf tunggal dengan ekspresi kompleks besar di dalamnya. Pokoknya, +1.
Level River St
Oke terima kasih atas sarannya! Saya akan mencoba dan membuat satu pernyataan cetak. Itu ide yang bagus dan saya yakin itu akan meningkatkan skor saya. Saya akan mengatur ulang ini ketika saya punya waktu hari ini. Terima kasih @steveverrill
Danwakeem
2

JavaScript (ES6) 241 244 227 222 231 byte

Ini terlihat menarik - Saya suka seni ASCII!
Baru mulai, masih dalam proses bermain 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: ternyata saya tidak bisa memasukkannya ke eval () - jika tidak, ia tidak bisa mengakses vars I dan Z (jadi tambah 9 byte)

- Disimpan 6 byte berkat pengguna81655
- disimpan 5 byte berkat Dave

Penjelasan

(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ᴄʜᴇʀᴏɴғᴀɪʟ
sumber
1
Anda dapat menyimpan 4 byte lainnya dengan menggunakan c^=!ealih-alih c+=a==b(memungkinkan Anda menghapus %2cek nanti). Juga -m+2bisa 2-m!
Dave
@Dave - terima kasih! Maukah Anda menjelaskan apa yang sebenarnya Anda lakukan? Saya belum pernah melihat itu sebelumnya :)
A
Itu sama dengan c=c^(e==0); itu berlaku XOR dengan cara yang sama seperti sebelumnya Anda memiliki tambahan. Jika Anda tidak terbiasa dengan XOR, ini adalah operasi bitwise: eXclusive OR (wikipedia dapat menjelaskannya dengan benar)
Dave