Juego estable de la vida

19

Desafío:

Dada una matriz (o matriz 2d) de 0s y 1s, genera el número de pasos que toma para que el juego de la vida de Conway alcance un estado estable, o -1 si nunca llega a uno. Un estado estable es un estado en el que no se activan o desactivan celdas en cada paso. El juego debe ejecutarse en la matriz dada, con la parte superior e inferior conectadas y los lados conectados. (es decir, dada una matriz de 4x3, debería ejecutarse en un toro de 4x3) La matriz de entrada no será mayor que 15x15.

Nota: Si la matriz comienza en un estado estable, la salida debería ser 0.

Muestras:

Entrada:

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

Salida:

2

Proceso: (esto no necesita ser mostrado)

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

Salida:

2

Proceso:

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

Salida:

-1

Proceso:

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

repitiendo para siempre

Entrada:

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

Salida:

4 4

Proceso:

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

Salida:

0 0

Proceso:

El estado inicial es estable.

Reglas del juego de la vida

Si una celda que está apagada (0) está al lado de exactamente tres en (1) celdas, se enciende. De lo contrario, se deja fuera. Si una celda que está encendida está al lado de 2 o 3 en cuadrados, dice encendido. De lo contrario, está apagado.

poi830
fuente
Entonces, ¿qué debería salir si el patrón se repite para siempre?
Financia la demanda de Mónica el
2
Posibles formatos de entrada? ¿Algún límite en los tamaños de matriz? Si no, ¿qué pasa si tenemos una matriz de 100x100? Además, probablemente debería poner un resumen de las reglas del Juego de la Vida en la pregunta para que sea autónomo.
El'endia Starman
3
Oh ya veo. Leí mal uno de los ejemplos. Sin embargo, otra pregunta: ¿en qué punto debemos suponer que no se estabiliza? Porque estoy seguro de que hay muchos patrones que se vuelven estables después de cientos o miles de iteraciones. Incluso hay una categoría para ello: Matusalén
Fondo de la demanda de Mónica
18
Estoy bastante seguro de que este desafío es esencialmente preguntar "resolver el problema de detención".
Mego
66
Como un contraejemplo para mostrar 250 generaciones no siempre es suficiente: para una matriz de 15 por 14, un planeador único en una arena vacía de lo contrario tomará 15 * 14 * 4 = 840 generaciones para volver a su estado original. Si el final de ese largo camino está bloqueado por un bloqueo de 2 por 2, el planeador se aniquilará dejando una configuración estable. Esto será unas pocas filas antes del final del camino para evitar destruir el planeador justo al comienzo, pero aún más de 600 generaciones antes de la estabilidad.
trichoplax

Respuestas:

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&

No recomendaría probar más de 4x4, porque tomará una eternidad (y mucha memoria).

Explicación

Esto simplemente simula el juego de la vida para 2 N pasos, donde N es el número de células en la entrada. Esto garantiza que si el sistema se instala en un estado estable, lo hemos alcanzado. Luego, encontramos el primer par de estados idénticos consecutivos en la historia simulada.

Veamos el código:

2^Length[Join@@#]

Esto calcula 2 N , ya que Join@@se usa para aplanar una lista 2D.

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

Esto simula el Juego de la Vida durante 2 N generaciones. La matriz 3x3 especifica la vecindad de un autómata 2D totalista y 224es el número de regla del Juego de la vida estándar. He escrito sobre cómo calcular este número aquí en Mathematica.SE .

Partition[...,2,1]

Esto obtiene todos los pares consecutivos (superpuestos) de generaciones.

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

Esto encuentra el primer par de generaciones idénticas, por defecto 0si no se encuentra ninguno y limita la búsqueda a profundidad 1. Si por ejemplo un par se encuentra, el resultado es devuelto en una lista sin embargo. Entonces usamos:

#&@@...

Para extraer el primer elemento de esa lista (el valor predeterminado de 0, siendo atómico, no se ve afectado por esto).

...-1

Finalmente restamos uno porque el desafío espera 0índices basados -1en el fracaso y.

Martin Ender
fuente
8

Lua, 531 509 488 487 464 424 405 404 Bytes

¿Quién quiere una presentación masiva? \ o /

Editar: lo mejoré, pero ya no sé cómo jugarlo, así que ... las explicaciones son comentarios añadidos :)

Guardado ~ 60 bytes con la ayuda de @ KennyLau

pequeño corte golf uno más byte por el cambio de nombre aen Ypara evitar la conversión hexadecimal inline

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

Sin golf

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 prueba

Aquí hay algunos casos de prueba

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

Jalea, 26 25 bytes

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

Pruébalo en línea! o verificar todos los casos de prueba .

Casos de prueba más grandes (de la respuesta de @ Katenkyo ): 15 × 15 estable | Planeador 15 × 14

Cómo 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
fuente
5

Perl, 154 151 144 140 137 133 129 bytes

Incluye +3 para -ap0

Ejecutar con la entrada como una línea de grupos de dígitos separados por espacio

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

Esto solo es realmente necesario en caso de que la entrada sea inmediatamente estable. En todos los demás casos, también puede darle más convenientemente como líneas separadas de dígitos:

life.pl
0000
0001
0111
0010
^D

Sin embargo, dar entrada de esta manera daría 1 en lugar de 0 para una configuración inmediatamente estable.

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

Casi venciendo a Mathematica en este ...

Solo en versiones anteriores de Perl (donde podría usar una constante como variable) funciona esta solución de 126 bytes:

#!/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

En caso de que haya al menos 2 filas, esta solución de 123 bytes funciona en todas las versiones de 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
fuente
1

rubí, 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}

Mantengo un historial de cada tablero, así que si obtengo un tablero que he visto antes, sé que sucedió una de las dos cosas. primero podría ser que hemos encontrado una posición estable, en cuyo caso será la más resentida de nuestra historia. La otra posibilidad es que tengamos un bucle.

MegaTom
fuente
La matriz de 15x15 significa que tenemos 2 ^ 225 tableros posibles, dudo mucho que incluso puedas memorizar esas matrices utilizando la memoria de todas las computadoras del mundo (incluso si la mayoría de los juegos probablemente terminen con menos de 1000 tableros). Buena suerte al abordar eso en Máquinas de 64 bits.
GameDeveloper
1
@DarioOO Incluso un planeador en una placa de 15x14 necesitará "solo" la generación 840 antes de regresar a su primer estado, por lo que podemos esperar que casi todo sea inferior a 1000 gens. Además, 1000 gens en un 15x15 con números enteros de 32 bits resultan en un uso de memoria de 15*15*4*1000-> 900 KB, lo suficientemente bueno para los casos en que necesitaremos 10k + gens :).
Katenkyo
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)

Verificación

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
fuente