Um simples simulador de DNA

18

Seu código irá gerar uma representação muito simples do DNA, em arte ASCII, para sempre. Serão necessários dois números como entrada em qualquer formato que você desejar: como uma lista, como argumentos para uma função, no stdin, etc.

  • Um intervalo de ponto flutuante Iem segundos entre 0,0 e 1,0 (inclusive)
  • Um nível de zoom Zcomo um número inteiro de 1 a 64 (inclusive)

Seu código imprimirá uma linha para stdout ou seu equivalente a cada Isegundo, produzindo uma saída infinita parecida com esta (para o nível de zoom 4):

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

Especificamente, a representação de ADN é um par de ondas sinusoidais ligados por hífens, uma que consiste nos caracteres a, c, g, e t, o outro dos caracteres A, C, G, e T. Se xé o número indexado 0 da linha que estamos imprimindo atualmente, a posição baseada em 0 do caractere na onda minúscula é dada por (sin(πx / Z) + 1) * Z, e na onda maiúscula é dada por (-sin(πx / Z) + 1) * Z, ambas arredondadas (sem piso) para a mais próxima inteiro. Detalhes adicionais:

  • Nos casos em que as duas ondas se sobrepõem, você precisa alternar qual onda está na frente, começando pela onda maiúscula. (Começar com a onda minúscula nos daria uma hélice dupla que não existe !)
  • Ignorando o caso, A sempre emparelha com T e C sempre emparelha com G, como no DNA real. Os pares devem ser escolhidos aleatoriamente com uma distribuição uniforme nas quatro possibilidades. Não importa se a escolha dos pares é igual ou diferente em execuções sucessivas do seu código. A qualidade estatística de suas escolhas aleatórias não é um problema, desde que a saída não tenha um padrão óbvio e um período de pelo menos bilhões (os PRNGs defeituosos como RANDU estão bons).
  • Você não deve ter espaços à direita ou colocar todas as linhas na posição máxima das ondas nesse nível de zoom (no exemplo acima, nove caracteres.) O nível de zoom 1 pode ter um espaço à direita adicional por razões matemáticas.

Como o DNA é pequeno, seu código precisará ser o mais curto possível.

Mais exemplos:

Nível 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
...

Nível de zoom 2:

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

Nível de zoom 1 (observe o espaço à esquerda):

 G
 a
 C
 t
...
Lucas
fonte
Relacionado.
Martin Ender
9
"Como o DNA é pequeno, seu código precisará ser o mais curto possível". Verdade?
TanMath
3
@TanMath Você realmente precisa de um motivo para Code-Golf? As histórias de fundo são quase sempre tolas assim, basta seguir em frente.
Patrick Roberts
@PatrickRoberts eu sei, mas eu estava apenas apontando o quão bobo é o motivo, muitos jogadores de código fazem. Não leve muito a sério! ;)
TanMath
O que significa "escolhido aleatoriamente"? RANDU está bem? Que tal uma sequência repetida mais curta?
KSFT 4/16/16

Respostas:

4

Ruby, Rev B 171 161 bytes

A correção da saída para z = 1 custa 10 bytes. É um caso especial: a hélice tem na verdade 3 caracteres de largura se você a observar a 90 graus, mas quando a vemos em 0 graus, ela tem apenas 1 caractere. zero espaços à esquerda em z = 1 não é mais necessário

Algumas economias eliminando colchetes e multiplicando y.abs por 2 antes do truncamento ao calcular o número de caracteres necessários.

Finalmente, evitei o include Math(necessário para sine PI) usando aritmética complexa de números com potências do número i. A parte imaginária do número complexo é equivalente ao pecado x, exceto que ele se repete no período 4 em vez do período 2 * PI. A gravação dessa alteração foi de 1 ou 0 bytes.

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

Isso é muito mais longo do que o esperado. Existem algumas oportunidades potenciais de golfe a serem exploradas.

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

Comentado no programa de teste

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
fonte
Parece bom! Um pequeno problema: existe um espaço principal para o nível de zoom 1. Além disso, o seu programa de teste I=gets.to_ideve estar I=gets.to_f.
Lucas
Ops! Você está certo de que Z = 1 é um caso especial. Isso não foi intencional e, na verdade, é uma contradição nas regras, dada a matemática que forneci. Vou adicionar o espaço inicial para Z = 1 para tornar a matemática consistente.
Luke
@Luke em regras gerais não deve ser alterado, mas na verdade havia uma contradição. Até onde eu sei, as outras respostas também não o consideraram. Mais tarde, atualizarei minha resposta, pois será mais curta dessa maneira.
Level River St
@Luke atualizado, mas significa que tenho um espaço à esquerda e um à direita em Z = 1. Entendo que isso está de acordo com o espírito do que você deseja e, portanto, está bem, embora não esteja estritamente de acordo com o fraseado de espaços à direita e o exemplo de Z = 1.
Level River St
Opa novamente, sim, tudo bem. Desculpe pela confusão.
Lucas
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]);}

Ou a versão mais longa que analisa a entrada da 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]);}

É uma implementação geral bastante burra, com alguns truques de impressão. Há algumas inclusões ausentes, usa a sintaxe K&R para a função e depende dos inicializadores de intervalo do GCC, portanto isso não é muito padrão. Além disso, a versão da função ainda usa globais, por isso só pode ser chamada uma vez!

A versão da função usa 2 parâmetros; aguarde (em segundos) e zoom. Aqui está um chamador para ele:

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

Correr como:

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

Demolir:

// 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
fonte
Você tem permissão para usar uma função em vez de main (), que salvará os bytes de strtode atof.
Lucas
@Luke Ah legal; Vou ver o quanto que salva ...
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);}}

Preparou isso muito rápido, então tenho certeza de que há outras coisas que eu poderia fazer para diminuir minha pontuação, mas estou feliz por ter esse programa para compilar e executar corretamente na primeira tentativa.

Versão de golfe:

#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: Ajustei o loop para imprimir tudo em uma instrução de impressão e usei o fato de que variáveis ​​são definidas como int por padrão para raspar alguns bytes. UPDATE2: Alguns renomeadores de var e alguns encurtamentos lógicos para economizar mais alguns bytes.

Danwakeem
fonte
Você precisa se apossar do GCC. É Linux, mas você também pode executá-lo no Windows com o Cygwin. Variáveis ​​(se declaradas no início do programa ou como argumentos de função) não precisam de um tipo, elas são assumidas como int. O mesmo com as funções. E tenho certeza de que você não precisará dessas inclusões.
Level River St
1
Além disso, você tem muitos printfs :-D. 1. use putchar para imprimir um caractere de cada vez ou 2. calcule o que deseja imprimir e imprima tudo com put. 3. elabore como usar um único printf com uma grande expressão complexa. Enfim, +1.
Level River St
Ok, obrigado pelas sugestões! Vou tentar fazer uma única declaração impressa. Essa é uma boa ideia e tenho certeza de que melhoraria minha pontuação. Vou regolfar isso quando tiver algum tempo hoje. Obrigado @steveverrill
Danwakeem
2

JavaScript (ES6) 241 244 227 222 231 bytes

Isso parecia interessante - eu amo arte ASCII!
Apenas começou, ainda no processo de jogar golfe ...

(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: Acontece que eu realmente não posso colocá-lo em eval () - caso contrário, ele não pode acessar os vars I e Z (adiciona 9 bytes)

- economizou 6 bytes graças a user81655
- economizou 5 bytes graças a Dave

Explicação

(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ᴄʜᴇʀᴏɴғᴀɪʟ
fonte
1
Você pode salvar outros 4 bytes usando em c^=!evez de c+=a==b(permite remover a %2verificação posteriormente). Também -m+2poderia ser 2-m!
Dave
@Dave - obrigado! Você se importaria de explicar o que c ^ =! E realmente faz? Eu nunca vi isso antes :)
Aᴄʜᴇʀᴏɴғᴀɪʟ
É o mesmo que c=c^(e==0); aplica um XOR da mesma maneira que você adicionou anteriormente. Se você não estiver familiarizado com XOR, é uma operação bit a bit: OU exclusivo (wikipedia pode explicar-lo corretamente)
Dave