Простой симулятор ДНК


18

Ваш код будет генерировать очень простое представление ДНК в ASCII-искусстве, навсегда. В качестве входных данных он будет принимать два числа в любом формате: в виде списка, в качестве аргументов функции, в stdin и т. Д.

  • Интервал с плавающей запятой Iв секундах от 0,0 до 1,0 (включительно)
  • Уровень масштабирования Zв виде целого числа от 1 до 64 (включительно)

Ваш код будет печатать одну строку в стандартный вывод или его эквивалент каждую Iсекунду, создавая бесконечный вывод, который выглядит примерно так (для уровня масштабирования 4):

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

В частности, наше представление ДНК является парой синусоид , соединенных дефисом, один из символов a, c, gи t, с другой из символов A, C, Gи T. Если xэто индексированный номером 0 строки, которую мы сейчас печатаем, то позиция символа в волне нижнего регистра определяется как 0 (sin(πx / Z) + 1) * Z, а в волне верхнего регистра - (-sin(πx / Z) + 1) * Zкак округленная (без цвета) до ближайшей целое число. Дальнейшие подробности:

  • В случаях, когда две волны перекрываются, вам нужно чередовать, какая волна находится спереди, начиная с заглавной волны. (Начиная с строчной волны, мы получили бы двойную спираль, которой не существует !)
  • Игнорируя случай, A всегда соединяется с T и C всегда соединяется с G, как в реальной ДНК. Сами пары должны выбираться случайным образом с равномерным распределением по четырем возможностям. Неважно, если выбор пар одинаков или отличается при последовательных запусках вашего кода. Статистическое качество ваших случайных выборов не является проблемой, если выходные данные не имеют очевидной схемы и периода, по крайней мере, в миллиардах (ошибочные PRNG, такие как RANDU , хороши).
  • У вас не должно быть завершающих пробелов или добавьте каждую строку к максимальному положению волн на этом уровне масштабирования (в приведенном выше примере - девять символов). Уровень масштабирования 1 может иметь одно дополнительное дополнительное завершающее пробел по математическим причинам.

Поскольку ДНК маленькая, ваш код должен быть максимально коротким.

Больше примеров:

Уровень масштабирования 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
...

Уровень масштабирования 2:

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

Уровень масштабирования 1 (обратите внимание на начальный пробел):

 G
 a
 C
 t
...


9
«Поскольку ДНК маленькая, ваш код должен быть максимально коротким». В самом деле?
TanMath

3
@TanMath Вам действительно нужна причина для Code-Golf? Предыстории почти всегда такие глупые, просто продолжайте.
Патрик Робертс

@PatrickRoberts Я знаю, но я просто указывал, насколько глупа причина, которую делают многие игроки в гольф. Не принимай это слишком серьезно! ;)
TanMath

Что означает «случайно выбранный»? RANDU в порядке? Как насчет более короткой повторяющейся последовательности?
KSFT

Ответы:


4

Ruby, Rev B 171 161 байт

Фиксирование вывода для z = 1 стоит 10 байт. Это особый случай: на самом деле спираль имеет ширину 3 символа, если смотреть на нее под углом 90 градусов, а когда мы смотрим на нее под углом 0 градусов, ширина всего 1 символа. ноль пробелов на z = 1 больше не требуется

Некоторая экономия за счет исключения скобок и умножения y.abs на 2 перед усечением при расчете необходимого количества символов.

Наконец, я избежал include Math(требуется для sinи PI), используя арифметику комплексных чисел со степенями числа i. Мнимая часть комплексного числа эквивалентна sin x, за исключением того, что оно повторяется с периодом 4 вместо периода 2 * PI. Сохранение для этого изменения было 1 или 0 байтов.

->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 байт

Это намного дольше, чем ожидалось. Есть несколько потенциальных возможностей для игры в гольф.

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

Комментарий в тестовой программе

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]

Хорошо смотритесь! Одна небольшая проблема: есть пробел для уровня масштабирования 1. Кроме того, в вашей тестовой программе I=gets.to_iдолжно быть I=gets.to_f.
Лука

Упс! Вы правы, что Z = 1 - это особый случай. Это не было преднамеренным и на самом деле противоречие в правилах, учитывая математику, которую я предоставил. Я собираюсь добавить начальное пространство для Z = 1, чтобы сделать математику последовательной.
Лука

@ Люк в целом правила не должны быть изменены, но на самом деле было противоречие. Насколько я могу судить, другие ответы тоже не рассматривали это. Я обновлю свой ответ позже, так как он будет короче таким образом.
Уровень Река St

@Luke обновлен, но это означает, что у меня есть и начальный, и конечный пробел на Z = 1. Я понимаю, что это соответствует духу того, что вы хотите, и, следовательно, все в порядке, хотя это не совсем соответствует формулировке о конечных пробелах и примере для Z = 1.
Уровень Река St

Опять же, да, это нормально. Извините за путаницу.
Лука

3

C 294 289 285 283 281 270 265 237 218 байт

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

Или более длинная версия, которая анализирует входные данные из 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]);}

Это довольно глупая общая реализация, с некоторыми добавленными трюками printf. Она содержит некоторые недостающие включения, использует синтаксис K & R для функции и использует инициализаторы диапазона GCC, так что это не очень стандартно. Кроме того, версия функции по-прежнему использует глобальные переменные, поэтому ее можно вызывать только один раз!

Версия функции принимает 2 параметра; подождите (в секундах) и увеличьте масштаб. Вот вызывающая сторона для этого:

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

Беги как:

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

Сломать:

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

Вам разрешено использовать функцию вместо main (), которая сэкономит вам байты strtodи atof.
Люк

@ Люк А, круто; Я посмотрю, сколько это спасет ...
Дейв

3

C 569 402 361 байт

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

Довольно быстро это удалось, так что я уверен, что есть и другие вещи, которые я мог бы сделать, чтобы уменьшить свой счет, но я просто счастлив, что мне удалось скомпилировать и запустить эту программу с первой попытки.

Де-гольф версия:

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

ОБНОВЛЕНИЕ: Я настроил цикл для печати всего в одном операторе печати и использовал тот факт, что переменные по умолчанию определены как int, чтобы сбрить некоторые байты. ОБНОВЛЕНИЕ 2: Некоторые переименовывают переменные и некоторые сокращают логику, чтобы сбрить еще несколько байтов.


Вы должны получить GCC. Это Linux, но вы также можете запустить его на Windows с Cygwin. Переменные (если они объявлены в начале программы или в качестве аргументов функции) не нуждаются в типе, они предполагаются как int. То же самое с функциями. И я уверен, что вам не понадобятся эти включения.
Уровень Река St

1
Также у вас слишком много printfs :-D. 1. Используйте putchar для печати по одному символу за раз или 2. определите, что вы хотите напечатать, а затем напечатайте все с помощью путов. 3. понять, как использовать один printf с большим сложным выражением. В любом случае +1.
Уровень Река Св

Хорошо, спасибо за предложения! Я постараюсь сделать одно печатное заявление. Это хорошая идея, и я уверен, что это улучшит мой счет. Я повторю это, когда у меня будет время сегодня. Спасибо @steveverrill
Danwakeem

2

JavaScript (ES6) 241 244 227 222 231 байт

Это выглядело интересно - я люблю искусство ASCII!
Только началось, все еще в процессе игры в гольф ...

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

--- РЕДАКТИРОВАТЬ: оказывается, я не могу на самом деле положить его в eval () - в противном случае он не может получить доступ к VAR I и Z (поэтому добавляет 9 байтов)

- сэкономил 6 байтов благодаря user81655
- сэкономил 5 байтов благодаря Dave

объяснение

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

1
Вы можете сохранить еще 4 байта, используя c^=!eвместо c+=a==b(позволяет убрать %2проверку позже). Также -m+2может быть 2-m!
Дэйв

@ Дэйв - спасибо! Не могли бы вы объяснить, что на самом деле делает c ^ =! E? Я никогда не видел этого раньше :)
Aᴄʜᴇʀᴏɴғᴀɪʟ

Это так же, как c=c^(e==0); он применяет XOR так же, как вы ранее добавили. Если вы не знакомы с XOR, это побитовая операция: eXclusive OR (Википедия может объяснить это правильно)
Дэйв
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.