Jogo estável da vida

19

Desafio:

Dada uma matriz (ou matriz 2d) de 0s e 1s, produza o número de etapas necessárias para que o jogo da vida de Conway atinja um estado estável, ou -1, se nunca chegar a um. Um estado estável é um estado no qual nenhuma célula é ativada ou desativada a cada etapa. O jogo deve ser executado na matriz especificada, com o topo e o fundo conectados e os lados conectados. (ou seja, dada uma matriz 4x3, ela deve ser executada em um toro 4x3) A matriz de entrada não será maior que 15x15.

Nota: Se a matriz iniciar em um estado estável, a saída deverá ser 0.

Amostras:

Entrada:

[[0,0,0],  
 [0,1,1],  
 [0,1,0]]

Resultado:

2

Processo: (isso não precisa ser exibido)

[[0,0,0],
 [0,1,1],
 [0,1,0]]

[[1,1,1],
 [1,1,1],
 [1,1,1]]

[[0,0,0],
 [0,0,0],
 [0,0,0]]

Entrada:

[[0,0,1,1],
 [0,1,1,1],
 [0,1,0,0],
 [0,1,1,1]]

Resultado:

2

Processo:

[[0,0,1,1],
 [0,1,1,1],
 [0,1,0,0],
 [0,1,1,1]]

[[0,0,0,0],
 [0,1,0,1],
 [0,0,0,0],
 [0,1,0,1]]

[[0,0,0,0],
 [0,0,0,0],
 [0,0,0,0],
 [0,0,0,0]]

Entrada:

[[0,1,0,0],
 [0,1,0,0],
 [0,1,0,0],
 [0,0,0,0]]

Resultado:

-1

Processo:

[[0,1,0,0],
 [0,1,0,0],
 [0,1,0,0],
 [0,0,0,0]]

[[0,0,0,0],
 [1,1,1,0],
 [0,0,0,0],
 [0,0,0,0]]

[[0,1,0,0],
 [0,1,0,0],
 [0,1,0,0],
 [0,0,0,0]]

repetindo para sempre

Entrada:

[[0,0,0,0],
 [0,0,0,1],
 [0,1,1,1],
 [0,0,1,0]]

Resultado:

4

Processo:

[[0,0,0,0],
 [0,0,0,1],
 [0,1,1,1],
 [0,0,1,0]]

[[0,0,0,0],
 [1,0,0,1],
 [1,1,0,1],
 [0,1,1,1]]

[[0,1,0,0],
 [0,1,1,1],
 [0,0,0,0],
 [0,1,0,1]]

[[0,1,0,1],
 [1,1,1,0],
 [0,1,0,1],
 [1,0,1,0]]

[[0,0,0,0],
 [0,0,0,0],
 [0,0,0,0],
 [0,0,0,0]]

Entrada:

[[0,0,0,0],
 [0,1,1,0],
 [0,1,1,0],
 [0,0,0,0]]

Resultado:

0 0

Processo:

O estado inicial é estável.

Regras do Jogo da Vida

Se uma célula que está desativada (0) estiver ao lado de exatamente três (1), ela estará ativada. Caso contrário, é deixado de fora. Se uma célula que está ligada é próxima de 2 ou 3 em quadrados, ela diz que está. Caso contrário, está desligado.

poi830
fonte
Então, o que deve ser produzido se o padrão se repetir para sempre?
Fund Monica's Lawsuit
2
Formatos de entrada possíveis? Algum limite nos tamanhos da matriz? Caso contrário, e se tivermos uma matriz 100x100? Além disso, você provavelmente deve colocar um resumo das regras do Jogo da Vida na pergunta para que seja independente.
El'endia Starman
3
Ah eu vejo. Eu interpretei mal um dos exemplos. Outra questão, no entanto - em que ponto devemos assumir que não se torna estável? Porque tenho certeza de que existem muitos padrões que se tornam estáveis ​​após centenas ou milhares de iterações. Existe até uma categoria para isso: Methuselah
Fund Monica's Lawsuit
18
Tenho certeza de que esse desafio está essencialmente pedindo "resolver o problema da parada".
Mego
6
Como um contra-exemplo para mostrar 250 gerações nem sempre é suficiente: para uma matriz de 15 por 14, um planador em uma arena vazia levará 15 * 14 * 4 = 840 gerações para retornar ao seu estado original. Se o final desse longo caminho for bloqueado por um bloco 2 por 2, o planador se aniquilará, deixando uma configuração estável. Isso ocorrerá algumas fileiras antes do final do caminho, para evitar a destruição do planador logo no início, mas ainda bem mais de 600 gerações antes da estabilidade.
Trichoplax

Respostas:

10

Mathematica, 130 129 bytes

#&@@FirstPosition[Partition[CellularAutomaton[{224,{2,{{2,2,2},{2,1,2},{2,2,2}}},{1,1}},#,2^Length[Join@@#]],2,1],{x_,x_},0,1]-1&

Eu não recomendaria tentar mais do que entradas 4x4, porque isso levará uma eternidade (e muita memória).

Explicação

Este simplesmente simula o jogo da vida para 2 N etapas, onde N é o número de células na entrada. Isso garante que, se o sistema se estabilizar, chegamos a ele. Posteriormente, encontramos o primeiro par de estados idênticos consecutivos na história simulada.

Vamos analisar o código:

2^Length[Join@@#]

Isso calcula 2 N , pois Join@@é usado para achatar uma lista 2D.

CellularAutomaton[{224,{2,{{2,2,2},{2,1,2},{2,2,2}}},{1,1}},#,...]

Isso simula o Jogo da Vida para 2 N gerações. A matriz 3x3 especifica a vizinhança de um autômato 2D totalístico e 224é o número da regra do Game of Life padrão. Eu escrevi sobre como calcular esse número aqui no Mathematica.SE .

Partition[...,2,1]

Isso obtém todos os pares consecutivos (sobrepostos) de gerações.

FirstPosition[...,{x_,x_},0,1]

Ele localiza o primeiro par de gerações idênticas, padronizando 0se nenhum for encontrado e limitando a pesquisa a profundidade 1. Se esse par for encontrado, o resultado será retornado em uma lista. Então usamos:

#&@@...

Para extrair o primeiro elemento dessa lista (o valor padrão de 0, sendo atômico, não é afetado por isso).

...-1

Por fim, subtraímos um porque o desafio espera 0índices baseados -1em falhas e falhas.

Martin Ender
fonte
8

Lua, 531 509 488 487 464 424 405 404 bytes

Quem quer uma submissão maciça? \ o /

Edit: Melhorado, mas não sei mais jogar golfe, então ... explicações estão chegando comentários adicionados :)

Salvo ~ 60 bytes com a ajuda de @ KennyLau

pequeno corte golfe um byte mais renomeando aem Ypara prevenir a conversão hexadecimal sequenciados

function f(m)c={}t=table.concat::z::c[#c+1]={}p=c[#c]Y={}for i=1,#m do k=m[i]p[#p+1]=t(k)Y[i]={}for j=1,#k
do v=m[i%#m+1]l=j%#k+1w=m[(i-2)%#m+1]h=(j-2)%#k+1Y[i][j]=v[l]+k[l]+w[l]+v[h]+v[j]+w[h]+w[j]+k[h]end
end s=''for i=1,#m do k=m[i]for j=1,k do
x=Y[i][j]k[j]=k[j]>0 and((x<2or x>3)and 0or 1)or (x==3 and 1or 0)end
s=s..t(k)end for i=1,#c do
if(s==t(c[i]))then return#c>i and-1or i-1
end end goto z end

Ungolfed

function f(m)                -- takes a 2D array of 0 and 1s as input
  c={}                       -- intialise c -> contains a copy of each generation
  t=table.concat             -- shorthand for the concatenating function 
  ::z::                      -- label z, used to do an infinite loop
    c[#c+1]={}               -- initialise the first copy 
    p=c[#c]                  -- initialise a pointer to this copy
    a={}                     -- initialise the 2D array of adjacency
    for i=1,#m               -- iterate over the lines of m
    do
      k=m[i]                 -- shorthand for the current line
      p[#p+1]=t(k])          -- saves the current line of m as a string
      a[i]={}                -- initialise the array of adjacency for the current line
      for j=1,#k             -- iterate over each row of m
      do
                             -- the following statements are used to wraps at borders
        v=m[i%#m+1]          -- wrap bottom to top
        l=j%#k+1             -- wrap right to left
        w=m[(i-2)%#m+1]      -- wrap top to bottom
        h=(j-2)%#k+1         -- wrap left to right

        a[i][j]= v[l]        -- living cells are 1 and deads are 0
                +k[l]        -- just add the values of adjacent cells
                +w[l]        -- to know the number of alive adjacent cells
                +v[h]
                +v[j]
                +w[h]
                +w[j]
                +k[h]
      end
    end

    s=''                     -- s will be the representation of the current generation
    for i=1,#m               -- iterate over each line
    do
      k=m[i]                 -- shorthand for the current line
      for j=1,#k             -- iterate over each row
      do
        x=a[i][j]            -- shorthand for the number of adjacent to the current cell
                             -- the next line change the state of the current cell
        k[j]=k[j]>0          -- if it is alive
                and((x<2     --   and it has less than 2 adjacent
                    or x>3)  --   or more than 3 adjacent
                  and 0      --     kill it
                  or 1)      --     else let it alive
                or           -- if it is dead
                  (x==3      --   and it has 3 adjacent
                  and 1      --     give life to it
                  or 0)      --     else let it dead
      end
      s=s..t(k)              -- save the representation of the current line
    end
    for i=1,#c               -- iterate over all the generation done until now
    do                       
      if(s==t(c[i]))         -- if the representation of the current generation
      then                   -- is equal to one we saved
        return#c>i           -- check if it is the latest generation
              and-1          -- if it isn't, it means we are in a loop -> return -1
              or i-1         -- if it is, we did 2 generations without changing
                             --  -> return the number of generation
      end
    end
  goto z                     -- if we reach that point, loop back to the label z
end

Casos de teste

Aqui estão alguns casos de teste

function f(m)c={}t=table.concat::z::c[#c+1]={}p=c[#c]a={}for i=1,#m do k=m[i]p[#p+1]=t(k)a[i]={}for j=1,#k
do v=m[i%#m+1]l=j%#k+1w=m[(i-2)%#m+1]h=(j-2)%#k+1
a[i][j]=v[l]+k[l]+w[l]+v[h]+v[j]+w[h]+w[j]+k[h]end
end s=''for i=1,#m do k=m[i]for j=1,k do
x=a[i][j]k[j]=k[j]>0 and((x<2or x>3)and 0or 1)or (x==3 and 1or 0)end
s=s..t(k)end for i=1,#c do
if(s==t(c[i]))then return#c>i and-1or i-1
end end goto z end




print(f({{0,0,0},{0,1,1},{0,1,0}}))
print(f({{0,1,0,0},{0,1,0,0},{0,1,0,0},{0,0,0,0}}))
-- 53 generation, 15x15, takes 50-100 ms on a bad laptop
print(f({{0,0,0,0,1,1,0,1,0,0,0,0,1,0,0},
       {0,1,1,0,1,1,1,1,1,1,1,0,0,0,0},
       {0,1,1,1,0,1,0,1,0,0,0,0,1,0,0},
       {0,0,1,0,1,1,1,0,0,1,1,1,0,1,1},
       {1,1,0,0,1,1,1,0,1,1,0,0,0,1,0},
       {0,0,0,0,1,1,0,1,0,0,0,0,1,0,0},
       {0,1,1,0,1,1,1,1,1,1,1,0,0,0,0},
       {0,1,1,1,0,1,0,1,0,0,0,0,1,0,0},
       {0,0,1,0,1,1,1,0,0,1,1,1,0,1,1},
       {1,1,0,0,1,1,1,0,1,1,0,0,0,1,0},
       {0,0,1,0,1,1,1,0,0,1,1,1,0,1,1},
       {1,1,0,0,1,1,1,0,1,1,0,0,0,1,0},
       {0,0,0,0,1,1,0,1,0,0,0,0,1,0,0},
       {0,0,0,0,1,1,0,1,0,0,0,0,1,0,0},
       {0,1,1,0,1,1,1,1,1,1,1,0,0,0,0}}))
-- Glider on a 15x14 board
-- 840 distinct generation
-- loop afterward -> return -1
-- takes ~4-5 seconds on the same bad laptop
print(f({{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
       {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
       {0,0,0,0,1,0,0,0,0,0,0,0,0,0,0},
       {0,0,0,0,0,1,0,0,0,0,0,0,0,0,0},
       {0,0,0,1,1,1,0,0,0,0,0,0,0,0,0},
       {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
       {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
       {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
       {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
       {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
       {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
       {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
       {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
       {0,0,0,0,0,0,0,0,0,0,0,0,0,0,0}}))
Katenkyo
fonte
5

Geléia, 26 25 bytes

ṙ-r1¤SZµ⁺_|=3
ÇÐĿ-LiṪÇ$$?

Experimente online! ou verifique todos os casos de teste .

Casos de teste maiores ( da resposta de @ Katenkyo ): 15 × 15 estáveis | Planador 15 × 14

Como funciona

ṙ-r1¤SZµ⁺_|=3  Helper link. Argument: G (grid)
               This link computes the next state of G.

    ¤          Evaluate the three links to the left as a niladic chain.
 -               Yield -1.
   1             Yield 1.
  r              Range; yield [-1, 0, 1].
ṛ              Rotate the rows of G -1, 0 and 1 units up.
     S         Compute the sum of the three resulting grids.
               Essentially, this adds the rows directly above and below each given
               row to that row.
      Z        Zip; transpose rows and columns.
       µ       Convert the preceding chain into a link and begin a new chain.
        ⁺      Apply the preceding chain once more.
               This zips back and adds the surrounding columns to each column.
         _     Subtract G from the result.
               Each cell now contains the number of lit cells that surround it.
          |    That the bitwise OR of the result and G.
               Notably, 3|0 = 3|1 = 2|1 = 3.
           =3  Compare each resulting number with 3.


ÇÐĿ-LiṪÇ$$?    Main link. Argument: G (grid)

ÇÐL            Repeatedly apply the helper link until the results are no longer
               unique. Collect all unique results in a list.
         $     Evaluate the two links to the left as a monadic chain:
        $        Evaluate the two links to the left as a monadic chain:
      Ṫ            Pop the last element of the list of grids.
       Ç           Apply the helper link to get the next iteration.
     i           Get the index of the grid to the right (the first non-unique one)
                 in the popped list of grids. This yields 0 iff the popped list
                 doesn't contain that grid, i.e., the grid reached a stable state.
          ?    If the index is non-zero:
   -             Return -1.
    L            Else, return the length of the popped list of grids.
Dennis
fonte
5

Perl, 154 151 144 140 137 133 129 bytes

Inclui +3 para -ap0

Execute com a entrada como uma linha de grupos de dígitos separados por espaço

life.pl <<< "0000 0001 0111 0010"

Isso só é realmente necessário caso a entrada seja imediatamente estável. Em todos os outros casos, você também pode fornecer de forma mais conveniente linhas de dígitos separadas:

life.pl
0000
0001
0111
0010
^D

Dar entrada dessa maneira, no entanto, forneceria 1 em vez de 0 para uma configuração imediatamente estável.

life.pl:

#!/usr/bin/perl -ap0
map{@f=map$F[$_%@F]x3,$i-1..++$i;s%.%"0+($&+33)=~grep\$_,map{(//g)[@--1..@+]}\@f"%eeg}@Q=@F;$_=/@Q/?keys%n:$n{$_="@Q"}--||redo

Quase batendo o Mathematica neste ...

Somente nas versões perl mais antigas (nas quais você pode usar uma constante como variável) esta solução de 126 bytes funciona:

#!/usr/bin/perl -p0a
map{@f=map$F[$_++%@F]x2,-1..1;s%.%"0+($&+33)=~grep\$_,map{(//g)[@--1..@+]}\@f"%eeg}@Q=@F;$_=/@Q/?keys%n:$n{$_="@Q"}--||redo

Caso haja pelo menos 2 linhas, esta solução de 123 bytes funciona em todas as versões perl:

#!/usr/bin/perl -p0a
@F=@F[-$#F..!s%.%"0+($&+33)=~grep\$_,map{(//g,//g)[@--1..@+]}\@F[-1..1]"%eeg]for@Q=@F;$_=/@Q/?keys%n:$n{$_="@Q"}--||redo
Ton Hospel
fonte
1

rubi, 207 bytes

->a{r=[];(r<<a;a=(0...a.size).map{|i|(0...a[i].size).map{|j|n=0;(-1..1).map{|u|(-1..1).map{|v|n+=a[(i+u)%a.size][(j+v)%a[i].size]}};[n==3,n>2&&n<5][a[i][j]]?1:0}})while(!r.index(a));(a==r[-1])?r.index(a):-1}

Eu mantenho um histórico de cada placa, por isso, se eu tiver uma placa que já vi antes de saber que uma das duas coisas aconteceu. primeiro, pode ser que tenhamos encontrado uma posição estável, caso em que será a mais ressentida em nossa história. a outra possibilidade é que tenhamos um loop.

MegaTom
fonte
Matriz 15x15 significa que temos 2 ^ 225 placas possíveis, duvido que você possa até memorizar essas matrizes usando a memória de todos os computadores do mundo (mesmo que a maioria dos jogos termine com menos de 1000 placas). Boa sorte abordando isso em Máquinas de 64 bits.
GameDeveloper
11
@DarioOO Mesmo um planador em uma placa 15x14 precisará "apenas" da geração 840 antes de retornar ao seu primeiro estado, para que possamos esperar que quase tudo esteja abaixo de 1000 gens. Além disso, 1000 gens em um 15x15 usando números inteiros de 32 bits resultam em um uso de memória de 15*15*4*1000-> 900 KB, bom o suficiente para casos em que precisaremos de 10k + gens :).
precisa
1

Julia, 92 88 bytes

f(x,r...)=x∈r?(r[k=end]==x)k-1:f(sum(i->circshift(x,[i÷3,i%3]-1),0:8)-x|x.==3,r...,x)

Verificação

julia> f(x,r...)=x∈r?(r[k=end]==x)k-1:f(sum(i->circshift(x,[i÷3,i%3]-1),0:8)-x|x.==3,r...,x)
f (generic function with 1 method)

julia> f([0 0 0;0 1 1;0 1 0])
2

julia> f([0 0 1 1;0 1 1 1;0 1 0 0;0 1 1 1])
2

julia> f([0 1 0 0;0 1 0 0;0 1 0 0;0 0 0 0])
-1

julia> f([0 0 0 0;0 0 0 1;0 1 1 1;0 0 1 0])
4

julia> f([0 0 0 0;0 1 1 0;0 1 1 0;0 0 0 0])
0

julia> f([0 0 0 0 1 1 0 1 0 0 0 0 1 0 0;0 1 1 0 1 1 1 1 1 1 1 0 0 0 0;0 1 1 1 0 1 0 1 0 0 0 0 1 0 0;0 0 1 0 1 1 1 0 0 1 1 1 0 1 1;1 1 0 0 1 1 1 0 1 1 0 0 0 1 0;0 0 0 0 1 1 0 1 0 0 0 0 1 0 0;0 1 1 0 1 1 1 1 1 1 1 0 0 0 0;0 1 1 1 0 1 0 1 0 0 0 0 1 0 0;0 0 1 0 1 1 1 0 0 1 1 1 0 1 1;1 1 0 0 1 1 1 0 1 1 0 0 0 1 0;0 0 1 0 1 1 1 0 0 1 1 1 0 1 1;1 1 0 0 1 1 1 0 1 1 0 0 0 1 0;0 0 0 0 1 1 0 1 0 0 0 0 1 0 0;0 0 0 0 1 1 0 1 0 0 0 0 1 0 0;0 1 1 0 1 1 1 1 1 1 1 0 0 0 0])
53

julia> f([0 0 0 0 0 0 0 0 0 0 0 0 0 0 0;0 0 0 0 0 0 0 0 0 0 0 0 0 0 0;0 0 0 0 1 0 0 0 0 0 0 0 0 0 0;0 0 0 0 0 1 0 0 0 0 0 0 0 0 0;0 0 0 1 1 1 0 0 0 0 0 0 0 0 0;0 0 0 0 0 0 0 0 0 0 0 0 0 0 0;0 0 0 0 0 0 0 0 0 0 0 0 0 0 0;0 0 0 0 0 0 0 0 0 0 0 0 0 0 0;0 0 0 0 0 0 0 0 0 0 0 0 0 0 0;0 0 0 0 0 0 0 0 0 0 0 0 0 0 0;0 0 0 0 0 0 0 0 0 0 0 0 0 0 0;0 0 0 0 0 0 0 0 0 0 0 0 0 0 0;0 0 0 0 0 0 0 0 0 0 0 0 0 0 0;0 0 0 0 0 0 0 0 0 0 0 0 0 0 0])
-1
Dennis
fonte