Maximum lifespan on torus

For general discussion about Conway's Game of Life.
User avatar
Vort
Posts: 42
Joined: May 14th, 2024, 6:35 am

Re: 249 generations, 10x10 torus

Post by Vort » June 3rd, 2024, 11:12 am

iddi01 wrote:
June 3rd, 2024, 8:27 am
Also, anyone else thinks this thread belongs better in Patterns forum?
It's fine to move it to more appropriate place.
iddi01 wrote:
June 3rd, 2024, 8:27 am
Using my methuselah searcher, i got 249 generations on 10x10 torus:
How fast your script simulate 10x10 torus soups?
Mine is getting approximately 8000 soups/sec on single core of i5-4690.
After ~1.5 million soups lifespan of 490 was achieved, after 4.4 million, lifespan of 491:

Code: Select all

x = 10, y = 10, rule = B3/S23:T10,10
7bo$2o4b2obo$o$2o3bo$bo$b3obo2bo$bobobo$4b2o3bo$2obobo$bo4bo2bo!
Code:

Code: Select all

using System;
using System.Collections.Generic;
using System.IO;
using System.Text;

namespace TorusLife
{
    class Pattern : IEquatable<Pattern>
    {
        public const int GridWidth = 10;
        public const int GridHeight = 10;
        int[,] cells;

        public Pattern()
        {
            cells = new int[GridWidth, GridHeight];
        }

        public Pattern(Pattern source)
        {
            cells = new int[GridWidth, GridHeight];
            for (int y = 0; y < GridHeight; y++)
                for (int x = 0; x < GridWidth; x++)
                    cells[x, y] = source.cells[x, y];
        }

        public Pattern Generate()
        {
            var result = new Pattern();
            for (int y = 0; y < GridHeight; y++)
            {
                int ym1 = y - 1;
                int yp1 = y + 1;
                if (ym1 < 0)
                    ym1 = GridHeight - 1;
                else if (ym1 >= GridHeight)
                    ym1 = 0;
                if (yp1 < 0)
                    yp1 = GridHeight - 1;
                else if (yp1 >= GridHeight)
                    yp1 = 0;
                for (int x = 0; x < GridWidth; x++)
                {
                    int xm1 = x - 1;
                    int xp1 = x + 1;
                    if (xm1 < 0)
                        xm1 = GridWidth - 1;
                    else if (xm1 >= GridWidth)
                        xm1 = 0;
                    if (xp1 < 0)
                        xp1 = GridWidth - 1;
                    else if (xp1 >= GridWidth)
                        xp1 = 0;
                    int neighbours =
                        cells[xm1, ym1] +
                        cells[x, ym1] +
                        cells[xp1, ym1] +
                        cells[xm1, y] +
                        cells[xp1, y] +
                        cells[xm1, yp1] +
                        cells[x, yp1] +
                        cells[xp1, yp1];

                    if (cells[x, y] == 1)
                        result.cells[x, y] = (neighbours == 2 || neighbours == 3) ? 1 : 0;
                    else
                        result.cells[x, y] = neighbours == 3 ? 1 : 0;
                }
            }
            return result;
        }

        public void Randomize(Random rnd)
        {
            for (int y = 0; y < GridHeight; y++)
                for (int x = 0; x < GridWidth; x++)
                    cells[x, y] = rnd.Next(100) < 35 ? 1 : 0;
        }

        public void Measure(out int lifespan, out int period)
        {
            Pattern pattern = this;
            var patterns = new Dictionary<Pattern, int>();
            patterns.Add(this, 0);

            for (int i = 0; ; i++)
            {
                pattern = pattern.Generate();
                int patternIndex;
                if (patterns.TryGetValue(pattern, out patternIndex))
                {
                    period = patterns.Count - patternIndex;
                    lifespan = i - period + 1;
                    return;
                }
                patterns.Add(pattern, i + 1);
            }
        }

        public void WriteRLE(string fileName, string comment = null)
        {
            var sb = new StringBuilder();
            if (comment != null)
                sb.AppendLine($"#C {comment}");
            sb.AppendLine($"x = {GridWidth}, y = {GridHeight}, rule = B3/S23:T{GridWidth},{GridHeight}");
            for (int y = 0; y < GridHeight; y++)
            {
                for (int x = 0; x < GridWidth; x++)
                    sb.Append(cells[x, y] == 1 ? 'o' : 'b');
                sb.AppendLine(y == GridHeight - 1 ? "!" : "$");
            }
            File.WriteAllText(fileName, sb.ToString());
        }

        public void ReadRLE(string[] lines)
        {
            int x = 0;
            int y = 0;

            string scount = "";

            foreach (var line in lines)
            {
                if (line.StartsWith("#"))
                    continue;
                if (line.StartsWith("x"))
                {
                    if (line.Split(':')[1] != $"T{GridWidth},{GridHeight}")
                        throw new Exception();
                    continue;
                }

                for (int i = 0; i < line.Length; i++)
                {
                    char c = line[i];
                    if (c >= '0' && c <= '9')
                    {
                        scount += c;
                    }
                    else
                    {
                        if (c == '$')
                        {
                            x = 0;
                            int count = 1;
                            if (scount != "")
                                count = int.Parse(scount);
                            y += count;
                            scount = "";
                        }
                        else if (c == '!')
                            break;
                        else if (c == 'o' || c == 'b')
                        {
                            int count = 1;
                            if (scount != "")
                                count = int.Parse(scount);
                            for (int k = 0; k < count; k++)
                            {
                                cells[x, y] = c == 'o' ? 1 : 0;
                                x++;
                                WrapCoordinates(ref x, ref y);
                            }
                            scount = "";
                        }
                    }
                }
            }
        }

        public void ReadRLE(string fileName)
        {
            ReadRLE(File.ReadAllLines(fileName));
        }

        public override int GetHashCode()
        {
            int hash = 0;
            int shift = 0;
            for (int y = 0; y < GridHeight; y++)
                for (int x = 0; x < GridWidth; x++)
                {
                    hash = hash ^ cells[x, y] << shift;
                    shift = (shift + 1) & 31;
                }
            return hash;
        }

        public bool Equals(Pattern other)
        {
            for (int y = 0; y < GridHeight; y++)
                for (int x = 0; x < GridWidth; x++)
                    if (cells[x, y] != other.cells[x, y])
                        return false;
            return true;
        }

        void WrapCoordinates(ref int x, ref int y)
        {
            if (x < 0)
                x += GridWidth;
            else if (x >= GridWidth)
                x -= GridWidth;
            if (y < 0)
                y += GridHeight;
            else if (y >= GridHeight)
                y -= GridWidth;
        }
    }

    class Program
    {
        Program()
        {
            Random rnd = new Random();

            int period;
            int lifespan;
            int totalMax = 0;
            int countMax = 0;
            int bestLifespan = 0;
            int bestLifespanLocal = 0;
            for (int i = 0; ; i++)
            {
                Pattern pattern = new Pattern();
                pattern.Randomize(rnd);
                pattern.Measure(out lifespan, out period);
                if (i % 100000 == 0 && i != 0)
                {
                    totalMax += bestLifespanLocal;
                    countMax++;
                    int avgMax = totalMax / countMax;
                    Console.WriteLine($"Iteration: {i,6}, lifespan: {bestLifespan,4} | {bestLifespanLocal,4} | {avgMax,4}");
                    bestLifespanLocal = 0;
                }
                if (lifespan > bestLifespan)
                {
                    bestLifespan = lifespan;
                    pattern.WriteRLE(
                        $"{Pattern.GridWidth}x{Pattern.GridHeight}_{lifespan}.rle",
                        $"lifespan: {lifespan}, period: {period}");
                }
                if (lifespan > bestLifespanLocal)
                    bestLifespanLocal = lifespan;
            }
        }

        static void Main(string[] args)
        {
            new Program();
        }
    }
}

User avatar
Vort
Posts: 42
Joined: May 14th, 2024, 6:35 am

Re: Maximum lifespan on torus

Post by Vort » June 3rd, 2024, 11:31 am

vilc wrote:
May 26th, 2024, 4:02 pm
And finally, statistics over 100,000,000 random soups on 6x6, 7x7 and 8x8 tori :
Your statistics show oscillations at highest lifespans.
I wonder if this is how various "inventions" manifest themselves.
(soup is able to jump lifespan gap if it "invents" something)

iddi01
Posts: 172
Joined: January 24th, 2024, 5:14 am
Location: B3-n/S1e2-a3-e4e

Re: 249 generations, 10x10 torus

Post by iddi01 » June 3rd, 2024, 11:33 am

Vort wrote:
June 3rd, 2024, 11:12 am
How fast your script simulate 10x10 torus soups?
Mine is getting approximately 8000 soups/sec on single core of i5-4690.
After ~1.5 million soups lifespan of 490 was achieved, after 4.4 million, lifespan of 491:
Unfortunately only 86 soups/second, nearly inactive compared to yours. (i use i3, probably still only around 250/sec on your computer)

That's an excellent demonstration of just how slow Python is compared to other programming languages, but it's the only one i'm reasonably good with.
Wiki: User:iddi01

I'm making a poll. please contribute.

First gun i constructed:

Code: Select all

x = 69, y = 69, rule = B3-n/S1e2-a3-e4e
2$32b3o$32bobo$32bobo$32b3o27$63b4o$b4o58bo2bo$bo2bo23bo4b2o28b4o$b4o
21bobo$28bo21$35bo$34b3o6$33b3o$33bobo$33bobo$33b3o!

User avatar
Vort
Posts: 42
Joined: May 14th, 2024, 6:35 am

Re: 249 generations, 10x10 torus

Post by Vort » June 3rd, 2024, 11:39 am

iddi01 wrote:
June 3rd, 2024, 11:33 am
That's an excellent demonstration of just how slow Python is compared to other programming languages...
Also large amount of abstraction layers may cause a slowdown.

I was worried that C# is not the best choice, C++ code will be probably 5-10 times faster.
However, getting higher and higher lifespans requires exponentially more resources, so I decided to stay with C# for now.

User avatar
confocaloid
Posts: 3275
Joined: February 8th, 2022, 3:15 pm

Re: Maximum lifespan on torus

Post by confocaloid » June 11th, 2024, 11:32 am

Related forum thread: viewtopic.php?f=7&t=3383 "Small Tori in B3/S23"
Vort wrote:
June 3rd, 2024, 11:12 am
iddi01 wrote:
June 3rd, 2024, 8:27 am
Also, anyone else thinks this thread belongs better in Patterns forum?
It's fine to move it to more appropriate place.
[...]
I think this discussion fits equally in either of two subforums. Note that the other thread is also in "General discussion".

Patterns and reactions on tori (and/or other universes with topology other than the infinite square tiling on the Euclidean plane) feel like an interesting topic for general discussion. (While the "Patterns" subforum is almost completely focused on the usual topology, without any wraparound or boundaries.)

Post Reply