Jeu de vie stable

19

Défi:

Étant donné une matrice (ou un tableau 2D) de 0 et de 1, affichez le nombre d'étapes nécessaires pour que le jeu de la vie de Conway atteigne un état stable, ou -1 s'il n'en atteint jamais un. Un état stable est un état dans lequel aucune cellule n'est activée ou désactivée à chaque étape. Le jeu doit fonctionner dans la matrice donnée, avec le haut et le bas connectés et les côtés connectés. (c.-à-d. étant donné une matrice 4x3, elle devrait fonctionner sur un tore 4x3) La matrice d'entrée ne sera pas supérieure à 15x15.

Remarque: Si la matrice démarre dans un état stable, la sortie doit être 0.

Échantillons:

Contribution:

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

Production:

2

Processus: (cela n'a pas besoin d'être affiché)

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

Contribution:

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

Production:

2

Processus:

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

Contribution:

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

Production:

-1

Processus:

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

répéter pour toujours

Contribution:

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

Production:

4

Processus:

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

Contribution:

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

Production:

0

Processus:

L'état de départ est stable.

Règles du jeu de la vie

Si une cellule désactivée (0) se trouve à côté d'exactement trois cellules activées (1), elle est activée. Sinon, il est laissé de côté. Si une cellule qui est allumée est à côté de 2 ou 3 sur des carrés, elle est allumée. Sinon, il est désactivé.

poi830
la source
Alors, que devrait-on produire si le motif se répète pour toujours?
Fund Monica's Lawsuit
2
Formats d'entrée possibles? Des limites sur les tailles de matrice? Sinon, que se passe-t-il si nous avons une matrice 100x100? De plus, vous devriez probablement mettre un résumé des règles du jeu de la vie dans la question afin qu'il soit autonome.
El'endia Starman
3
Oh je vois. J'ai mal lu l'un des exemples. Une autre question, cependant - à quel moment devrions-nous supposer qu'il ne devient pas stable? Parce que je suis sûr qu'il existe de nombreux modèles qui deviennent stables après des centaines ou des milliers d'itérations. Il y a même une catégorie pour cela: Methuselah
Fund Monica's Lawsuit
18
Je suis à peu près sûr que ce défi consiste essentiellement à "résoudre le problème d'arrêt".
Mego
6
Comme contre-exemple, montrer 250 générations n'est pas toujours suffisant: pour une matrice de 15 par 14, un seul planeur dans une arène autrement vide prendra 15 * 14 * 4 = 840 générations pour revenir à son état d'origine. Si la fin de ce long chemin est bloquée par un bloc 2 par 2, le planeur s'anéantira en laissant une configuration stable. Ce sera à quelques rangées de la fin de la trajectoire pour éviter de détruire le planeur dès le départ, mais toujours bien plus de 600 générations avant la stabilité.
trichoplax

Réponses:

10

Mathematica, 130 129 octets

#&@@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&

Je ne recommanderais pas d'essayer plus de 4x4 entrées, car cela va prendre une éternité (et beaucoup de mémoire).

Explication

Cela simule simplement le jeu de la vie pour 2 N étapes où N est le nombre de cellules dans l'entrée. Cela garantit que si le système s'installe dans un état stable, nous l'avons atteint. Ensuite, nous trouvons la première paire d'états identiques consécutifs dans l'histoire simulée.

Passons en revue le code:

2^Length[Join@@#]

Ceci calcule 2 N , car Join@@est utilisé pour aplatir une liste 2D.

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

Cela simule le jeu de la vie pour 2générations N. La matrice 3x3 spécifie le voisinage d'un automate 2D totaliste et224est le numéro de règle de Game of Life standard. J'ai écrit sur la façon de calculer ce nombre ici sur Mathematica.SE .

Partition[...,2,1]

Cela obtient toutes les paires de générations consécutives (qui se chevauchent).

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

Cela trouve la première paire de générations identiques, par défaut à 0 si aucune n'est trouvée et limite la recherche à la profondeur 1. Si une telle paire est trouvée, le résultat est cependant renvoyé dans une liste. Nous utilisons donc:

#&@@...

Pour extraire le premier élément de cette liste (la valeur par défaut de 0, étant atomique, n'en est pas affectée).

...-1

Enfin, nous soustrayons un parce que le défi attend des 0indices basés sur et -1pour l'échec.

Martin Ender
la source
8

Lua, 531 509 488 487 464 424 405 404 octets

Qui veut une soumission massive? \ o /

Edit: Amélioré, mais je ne sais plus comment jouer au golf, alors ... des explications sont à venir des commentaires ajoutés :)

Enregistré ~ 60 octets avec l'aide de @ KennyLau

petit golf coupant un octet de plus en renommant aen Ypour empêcher la conversion hexadécimale en ligne

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

Non 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

Cas de test

Voici quelques cas de test

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
la source
5

Gelée, 26 25 octets

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

Essayez-le en ligne! ou vérifiez tous les cas de test .

Cas de test plus grands (d' après la réponse de @ Katenkyo ): 15 × 15 stable |Planeur 15 × 14

Comment ça fonctionne

ṙ-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
la source
5

Perl, 154 151 144 140 140 137 133 129 octets

Comprend +3 pour -ap0

Exécuter avec l'entrée comme une ligne de groupes de chiffres séparés par un espace

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

Cela n'est vraiment nécessaire que si l'entrée est immédiatement stable. Dans tous les autres cas, vous pouvez également le donner plus facilement sous forme de lignes de chiffres séparées:

life.pl
0000
0001
0111
0010
^D

Donner une entrée de cette manière donnerait cependant 1 au lieu de 0 pour une configuration immédiatement stable.

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

Presque battre Mathematica sur celui-ci ...

Ce n'est que sur les anciennes versions de Perl (où vous pouviez utiliser une constante comme variable) que cette solution de 126 octets fonctionne:

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

Dans le cas où il y a certainement au moins 2 lignes, cette solution de 123 octets fonctionne sur toutes les versions 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
la source
1

rubis, 207 octets

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

Je garde un historique de chaque planche, donc si j'obtiens une planche que j'ai vue avant de savoir qu'une des deux choses s'est produite. il se pourrait d'abord que nous ayons trouvé une position stable, auquel cas ce sera la plus rancunière de notre histoire. l'autre possibilité est que nous ayons une boucle.

MegaTom
la source
La matrice 15x15 signifie que nous avons 2 ^ 225 cartes possibles, je doute fortement que vous puissiez même mémoriser ces matrices en utilisant la mémoire de tous les ordinateurs dans le monde (même si la plupart des jeux se termineront probablement avec moins de 1000 cartes). Machines 64 bits.
GameDeveloper
1
@DarioOO Même un planeur sur une carte 15x14 n'aura besoin "que" de la génération 840 avant de revenir à son premier état, nous pouvons donc nous attendre à ce que presque tout soit inférieur à 1000 personnes. De plus, 1000 personnes sur un 15x15 utilisant des entiers 32 bits entraînent une utilisation de la mémoire de 15*15*4*1000-> 900 Ko, assez bien pour les cas où nous aurons besoin de 10k + gens :).
Katenkyo
1

Julia, 92 88 octets

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)

Vérification

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