Startseite > .NET, C# > Game of Life mit C# und WinForms

Game of Life mit C# und WinForms


Diesmal habe ich in C# mit Klassen so gearbeitet wie es im OOP angedacht ist. Die Zellen als Klasse, eine Gruppe der Zellen als Klasse, die Welt als Klasse, der Renderer als Klasse u.s.w…

Es ist lang geworden, da ich noch ein paar Gimmicks eingebaut habe; Zum Beispiel Mutanten und Zombies!

Das Hauptprogramm ist eigentlich schön kurz

C#
1
23
45
67
89
1011
1213
1415
1617
1819
2021
2223
2425
2627
2829
3031
3233
3435
3637
3839
4041
4243
4445
4647
4849
5051
5253
5455
    static class Program
    {        /// <summary>
        /// Der Haupteinstiegspunkt für die Anwendung.        /// </summary>
        [STAThread]        static void Main()
        {            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);              // Create world            World SimWorld = new World();
            SimWorld.GridSize = 18;            SimWorld.Height = 30;
            SimWorld.Width = 40;            SimWorld.LifeAbundancy = 30;
            SimWorld.Zombies = true;            SimWorld.Mutants = true;
            SimWorld.Initialize();              FormWorld mainWindow = new FormWorld(SimWorld);              // Initialize form            mainWindow.FormBorderStyle = FormBorderStyle.FixedSingle;
            mainWindow.MaximizeBox = false;            mainWindow.Height = 600;
            mainWindow.Width = 800;            mainWindow.UseHexagon = false;
            mainWindow.CellBornedColor = Color.GreenYellow;            mainWindow.CellDyingColor = Color.Gray;
            mainWindow.CellLiveColor = Color.LimeGreen;            mainWindow.CellZombieColor = Color.DarkSlateBlue;
            mainWindow.CellMutantColor = Color.Red;            mainWindow.BackgroundColor = Color.PaleGreen;
            mainWindow.RefreshTime = 50;              // Create and init Button            Button btnGo = new Button();
            mainWindow.Controls.Add(btnGo);            btnGo.AutoSize = true;
            btnGo.Text = "Simulation starten";            btnGo.Top = (mainWindow.Height >> 1) - (btnGo.Height >> 1);
            btnGo.Left = (mainWindow.Width >> 1) - (btnGo.Width >> 1);            // Assign Click event
            btnGo.Click += delegate(object sender, EventArgs e)            // I know this is bad... 
            {                btnGo.Visible = false;
                mainWindow.StartSimulation();            };
                        Application.Run(mainWindow);
        }    }
GeSHi 1.0.8.8


Aber die Klassen sind arg lang…

Und so sieht es aus…

Normal

image

Normal in Hexagon Anordnung

image

Mit Zombies

image

Mit Zombies und Mutanten

image

Und der Source Code

C#
1
23
45
67
89
1011
1213
1415
1617
1819
2021
2223
2425
2627
2829
3031
3233
3435
3637
3839
4041
4243
4445
4647
4849
5051
5253
5455
5657
5859
6061
6263
6465
6667
6869
7071
7273
7475
7677
7879
8081
8283
8485
8687
8889
9091
9293
9495
9697
9899
100101
102103
104105
106107
108109
110111
112113
114115
116117
118119
120121
122123
124125
126127
128129
130131
132133
134135
136137
138139
140141
142143
144145
146147
148149
150151
152153
154155
156157
158159
160161
162163
164165
166167
168169
170171
172173
174175
176177
178179
180181
182183
184185
186187
188189
190191
192193
194195
196197
198199
200201
202203
204205
206207
208209
210211
212213
214215
216217
218219
220221
222223
224225
226227
228229
230231
232233
234235
236237
238239
240241
242243
244245
246247
248249
250251
252253
254255
256257
258259
260261
262263
264265
266267
268269
270271
272273
274275
276277
278279
280281
282283
284285
286287
288289
290291
292293
294295
296297
298299
300301
302303
304305
306307
308309
310311
312313
314315
316317
318319
320321
322323
324325
326327
328329
330331
332333
334335
336337
338339
340341
342343
344345
346347
348349
350351
352353
354355
356357
358359
360361
362363
364365
366367
368369
370371
372373
374375
376377
378379
380381
382383
384385
386387
388389
390391
392393
394395
396397
398399
400401
402403
404405
406407
408409
410411
412413
414415
416417
418419
420421
422423
424425
426427
428429
430431
432433
434435
436437
438439
440441
442443
444445
446447
448449
450451
452453
454455
456457
458459
460461
462463
464465
466467
468469
470471
472473
474475
476477
478479
480481
482483
484485
486487
488489
490491
492493
494495
496497
498499
500501
502503
504505
506507
508509
510511
512513
514515
516517
518519
520521
522523
524525
526527
528529
530531
532533
534535
536537
538539
540541
542543
544545
546547
548549
550551
552553
554555
556557
558559
560561
562563
564565
566567
568569
570571
572573
574575
576577
578579
580581
582583
584585
586587
588589
590591
592593
594595
596597
598599
600601
602603
604605
606607
608609
610611
612613
614615
616617
618619
620621
622623
624625
626627
628629
630631
632633
634635
636637
638639
640641
642643
644645
646647
648649
650651
652653
654655
656657
658659
660661
662663
664665
666667
668669
670671
672673
674675
676677
678679
680681
682683
684685
686687
688689
690691
692693
694695
696697
698699
700701
702703
704705
706707
708709
710711
712713
714715
using System;
using System.Drawing;using System.Threading;
using System.Windows.Forms;  namespace GameOfLifeCSharpWinForms{
    static class Program    {
        /// <summary>        /// Der Haupteinstiegspunkt für die Anwendung.
        /// </summary>        [STAThread]
        static void Main()        {
            Application.EnableVisualStyles();            Application.SetCompatibleTextRenderingDefault(false);
             // Create world
            World SimWorld = new World();            SimWorld.GridSize = 16;
            SimWorld.Height = 30;            SimWorld.Width = 60;
            SimWorld.LifeAbundancy = 30;            SimWorld.Zombies = true;
            SimWorld.Mutants = true;            SimWorld.Initialize();
             FormWorld mainWindow = new FormWorld(SimWorld);
             // Initialize form
            mainWindow.FormBorderStyle = FormBorderStyle.FixedSingle;            mainWindow.MaximizeBox = false;
            mainWindow.Height = 600;            mainWindow.Width = 800;
            mainWindow.UseHexagon = true;            mainWindow.CellBornedColor = Color.GreenYellow;
            mainWindow.CellDyingColor = Color.Gray;            mainWindow.CellLiveColor = Color.LimeGreen;
            mainWindow.CellZombieColor = Color.DarkSlateBlue;            mainWindow.CellMutantColor = Color.Red;
            mainWindow.BackgroundColor = Color.PaleGreen;            mainWindow.RefreshTime = 400;
             // Create and init Button
            Button btnGo = new Button();            mainWindow.Controls.Add(btnGo);
            btnGo.AutoSize = true;            btnGo.Text = "Simulation starten";
            btnGo.Top = (mainWindow.Height >> 1) - (btnGo.Height >> 1);            btnGo.Left = (mainWindow.Width >> 1) - (btnGo.Width >> 1);
            // Assign Click event            btnGo.Click += delegate(object sender, EventArgs e)
            // I know this is bad...             {
                btnGo.Visible = false;                mainWindow.StartSimulation();
            };                        Application.Run(mainWindow);        }
    }      public class FormWorld : Form     {
        private static World World;        public bool Simulate { get; set; }
        public Color BackgroundColor { set; get; }        public Color CellDyingColor { get; set; }
        public Color CellBornedColor { get; set; }        public Color CellLiveColor { get; set; }
        public Color CellZombieColor { get; set; }        public Color CellMutantColor { get; set; }
        public int RefreshTime { get; set; }          public bool UseHexagon         { 
            get{return World.Cells.HexagonGrid;}            set { World.Cells.HexagonGrid = value; } 
        }          public FormWorld(World world): base()        {
            World = world;        }
         public void StartSimulation()
        {            Simulate = true;
             Graphics g = Graphics.FromHwnd(this.Handle);
            int Generation = 0;              while (Simulate)            {
                this.Invalidate();                Application.DoEvents();
                DrawBuffer(g);                  this.Text = "Generation: " + Generation +                    " Population: " + World.Cells.Population +
                    " Stable Generation: " +                     World.StableGenerationCounter +
                    " Is Stable: " + World.IsStable.ToString();                  Generation++;                if (World.IsStable) Simulate = false;
                 Thread.Sleep(RefreshTime);
            }              g.Dispose();        }
         private void DrawBuffer(Graphics g)
        {            // Create Buffer
            Bitmap Buffer = new Bitmap(this.Width,this.Height);            Graphics BufferGraphics = Graphics.FromImage(Buffer);
             // Draw World Background
            SolidBrush brush = new SolidBrush(BackgroundColor);            BufferGraphics.FillRectangle(brush, 0, 0, 
                this.Width, this.Height);              // Net Pen            Pen pen = new Pen(CellLiveColor);
             // Get center
            float X = (this.Width >> 1) -                 (World.GridSize * World.Width >> 1);
            float Y = (this.Height >> 1) -                 (World.GridSize * World.Height >> 1);
             // Draw the cells
            int CellSize = World.GridSize - 2;              Cell.CellState status;            for (int x = 0; x < World.Width; x++)
            {                for (int y = 0; y < World.Height; y++)
                {                    status = World.Cells[x, y].Status;
                    switch (status)                    {
                        case Cell.CellState.Borned:                             brush.Color = CellBornedColor; 
                            break;                        case Cell.CellState.Dying: 
                            brush.Color = CellDyingColor;                             break;
                        case Cell.CellState.Live:                             brush.Color = CellLiveColor;
                            break;                        case Cell.CellState.Zombies:
                            brush.Color = CellZombieColor;                            break;
                        case Cell.CellState.Mutants:                            brush.Color = CellMutantColor;
                            break;                    }
                    if (status != Cell.CellState.Dead)                    {
                        BufferGraphics.FillEllipse(brush,                             (x * World.GridSize) + X + 
                            World.Cells[x, y].XOffset,                             (y * World.GridSize) + Y, 
                            CellSize, CellSize);                    }
                }            }
            try            {
                // Draw me                g.DrawImage(Buffer, 0, 0);
            }            catch { }
             World.Refresh();
                        pen.Dispose();
            brush.Dispose();            BufferGraphics.Dispose();
            Buffer.Dispose();        }
         protected override void OnPaint(PaintEventArgs e)
        {            if(!Simulate)  base.OnPaint(e);
        }          protected override void OnPaintBackground(PaintEventArgs e)        {
            if(!Simulate) base.OnPaintBackground(e);        }
         protected override void OnClosing(
            System.ComponentModel.CancelEventArgs e)        {
            Simulate = false;            base.OnClosing(e);
        }      }      public class Cell    {
        [Flags]        public enum CellState 
        {             Dead = 2, 
            Borned = 4,             Live = 8, 
            Dying = 16,             Zombies = 32, 
            Mutants = 64,            AllLiving = Borned | Live | Mutants,
            AllDead = Dead | Dying        };
         // Properties
        internal int IsZombie        {
            get            {
                return (Status | CellState.Zombies)                    == CellState.Zombies ?
                    1 : 0;            }
        }          internal int IsMutant        {
            get            {
                return (Status | CellState.Mutants)                    == CellState.Mutants ?
                    1 : 0;            }
        }          private bool IsAlwaysDead = true;          private CellState _status;        public CellState Status
        {             get{ return _status; }
        }          private Cells _parent;        public Cells Parent
        { get { return _parent; } }          private int _x;        public int X { get { return _x; } }
         private int _y;
        public int Y { get { return _y; } }          public float XOffset        { 
            get             {
                if (Parent.HexagonGrid)                    return Y % 2 == 1 ? 
                        Parent.Parent.GridSize >> 1 : 0;                else
                    return 0;            } 
        }          public Cell(Cells parent, int x, int y)        {
            _parent = parent;            _x = x;
            _y = y;              // All Cells are always dead in the beginning            _status = CellState.Dead;
             // All Cells along the sides are always dead...
            // This is just easier to check them for neighbors            // cause no Errors will occure in this case
            if ((X > 0 && X < Parent.Parent.Width-1) &&                (Y > 0 && Y < Parent.Parent.Height-1))
                IsAlwaysDead = false;            else
                IsAlwaysDead = true;        }
         ~Cell()
        {            _parent = null;
        }          internal bool HasAttribute(CellState cellState)        {
            if ((Status & cellState) == cellState)                return true;
            else if ((Status | cellState) == cellState)                return true;
            else                return false;
        }          public void Live()        {
            if (Status == CellState.Dead)            {
                Parent.PopulationInternal++;                _status = CellState.Borned;
            }            else if (Status == CellState.Dying)
            {                Parent.PopulationInternal++;
                _status = CellState.Live;            }
            else if (Status == CellState.Borned)                _status = CellState.Live;
        }          public void Die()        {
            if (Status == CellState.Live ||                Status == CellState.Mutants)
            {                Parent.PopulationInternal--;
                _status = CellState.Dying;            }
            else if (Status == CellState.Borned)            {
                Parent.PopulationInternal--;                _status = CellState.Dead;
            }            else if (Status == CellState.Dying || 
                Status == CellState.Zombies)                _status = CellState.Dead;
        }          public Cell RandomizeLife()        {
            if (Status != CellState.Live)            {
                if (Parent.Randomize.Next(1000) > 500)                {
                    Live();                    return this;
                }                else
                    return this;            }
            else                return this;
         }
         public void RefreshCellStatus()
        {            // skip this if we are "always dead"
            if(IsAlwaysDead) return;              #region Normal Cells            int Neighbors = CountNeighbors(CellState.AllLiving);
                        // Apply Rules
            // No 1. - Birth            if (HasAttribute(CellState.AllDead)
                 && Neighbors == 3)                Live();
             else if (HasAttribute(CellState.AllLiving))
            {                // No. 2 - Lonelyness
                if (Neighbors < 2)                    Die();
                // No. 3 - Live longer 🙂                else if (Neighbors > 1 && Neighbors < 4)
                    Live();                // No. 4 - Overpopulated
                // Mutants dont die on overpopulation                else if (Neighbors > 3 &&
                    Status != CellState.Mutants)                    Die();
            }            else if (Status == CellState.Dying)
                Die();              #endregion              #region Unusual Cells            if (Parent.Parent.Zombies || Parent.Parent.Mutants)
            {                  int ZombieNeighbor =                    CountNeighbors(CellState.Zombies);
                 int MutantNeighbor =
                    CountNeighbors(CellState.Mutants);                  #region Zombie Cells                // This is the Zombie special
                if (Parent.Parent.Zombies)                {
                     // The Zombie get beaten to death
                    // if enough cell are around it                    if (Status == CellState.Zombies &&
                        Neighbors > 3)                        Die();
                    // Dead Cell have a small chance                    // in turning to zombies
                    else if (Status == CellState.Dead)                    {
                        if (Parent.Randomize.Next(1, 1000) > 992)                            _status = CellState.Zombies;
                    }                    // if I am not a zombie and one
                    // of my neighbors is a zombie                    // I have a chance of getting
                    // bitten. The more zombies the higher                    // the chance
                    else if (Status != CellState.Zombies &&                        ZombieNeighbor > 0)
                    {                        if (Parent.Randomize.Next(1, 1000) >
                            (999 >> ZombieNeighbor))                            _status = CellState.Zombies;
                    }                    // if there are too many zombies neighbors
                    // My neighbor will eat me                    else if (Status == CellState.Zombies &&
                        ZombieNeighbor > 4)                        Die();
                     // A Zombie has a chance of dying
                    // if a mutant is one of his neighbors                    // The more mutants the higher the
                    // chance                    if (Status == CellState.Zombies &&
                        MutantNeighbor > 0)                    {
                        if (Parent.Randomize.Next(1, 1000) >                            (700 >> ZombieNeighbor))
                            Die();                    }
                 }
                #endregion                  #region Mutant Cells                // Mutant Cell Special
                if (Parent.Parent.Mutants)                {
                    // A living cell has a chance in                    // turning to a mutant
                    if (Status == CellState.Live &&                        Parent.Randomize.Next(1, 1000) > 800)
                        _status = CellState.Mutants;                    // A mutant cell will die if
                    // there are more than 4                    // zombies around him
                    else if (Status == CellState.Mutants &&                        ZombieNeighbor > 4)
                        Die();                    // A mutant will turn normal if
                    // there are too many Mutants around                    // him
                    else if (Status == CellState.Mutants &&                        MutantNeighbor > 3)
                        _status = CellState.Live;                    // Dead Cells will live if more than 2
                    // Mutants are around                    else if (HasAttribute(CellState.AllDead) &&
                        MutantNeighbor > 2)                    { Live(); Live(); }
                 }
                #endregion            }
            #endregion        }
         private int CountNeighbors(CellState cellStateToCount)
        {            int RetVal = 0;
            if(!IsAlwaysDead)            {
                RetVal += Parent[X + 1, Y].                    HasAttribute(cellStateToCount).ToInt();
                 RetVal += Parent[X, Y - 1].
                    HasAttribute(cellStateToCount).ToInt();                RetVal += Parent[X, Y + 1].
                    HasAttribute(cellStateToCount).ToInt();                  RetVal += Parent[X - 1, Y].                    HasAttribute(cellStateToCount).ToInt();
                 // We are arranging the cells in a hexagon
                // Thats why its less 2 neighbors to check                // But we have to swap neighbors every 2nd
                // line                if (!Parent.HexagonGrid)
                {                    RetVal += Parent[X - 1, Y - 1].
                        HasAttribute(cellStateToCount).ToInt();                    RetVal += Parent[X - 1, Y + 1].
                        HasAttribute(cellStateToCount).ToInt();                    RetVal += Parent[X + 1, Y - 1].
                        HasAttribute(cellStateToCount).ToInt();                    RetVal += Parent[X + 1, Y + 1].
                        HasAttribute(cellStateToCount).ToInt();                }
                else                {
                    if (XOffset > 0)                    {
                        RetVal += Parent[X + 1, Y - 1].                            HasAttribute(cellStateToCount).ToInt();
                        RetVal += Parent[X + 1, Y + 1].                            HasAttribute(cellStateToCount).ToInt();
                    }                    else
                    {                        RetVal += Parent[X - 1, Y - 1].
                            HasAttribute(cellStateToCount).ToInt();                        RetVal += Parent[X - 1, Y + 1].
                            HasAttribute(cellStateToCount).ToInt();                    }
                }            }
            return RetVal;        }
         // Overload the plus operator
        public static int operator +(int number, Cell cell)        {
            return (cell.Status == CellState.Live ||                cell.Status== CellState.Borned)? 
                number + 1 : number;        }
         // Overload the minus operator
        public static int operator -(int number, Cell cell)        {
            return (cell.Status == CellState.Live ||                cell.Status == CellState.Borned) ? 
                number - 1 : number;        }
         // Override ToString
        public override string ToString()        {
            return "Cell " + X + " " + Y + " " +                 Status.ToString();
        }    }
     public class Cells
    {        internal int PopulationInternal { get; set; }
        internal Random Randomize { get; set; }          public int Population { get { return PopulationInternal; } }        public bool HexagonGrid { get; set; }
         private Cell[,] Lifeforms;
        public Cell this[int x, int y]        {
            get { return Lifeforms[x, y]; }            set { Lifeforms[x, y] = value; }
        }          private World _parent;        public World Parent
        { get { return _parent; } }                public Cells(World world)        {
            _parent = world;            Lifeforms = new Cell[Parent.Width, Parent.Height];
            PopulationInternal = 0;              // Populate with cells            for (int x=0; x < Parent.Width; x++)
            {                for (int y=0; y < Parent.Height; y++)
                {                    Lifeforms[x, y] = new Cell(this, x, y);
                }            }
             // Initialize Randomizer
            int Seed;            if (!int.TryParse(
                DateTime.Now.ToString("ffffff"), out Seed))                Seed = DateTime.Now.Second;
            Randomize = new Random(Seed);        }
         // Make sure to kill the cells
        ~Cells()        {
            for (int x = 0; x < Parent.Width; x++)            {
                for (int y = 0; y < Parent.Height; y++)                {
                    Lifeforms[x, y] = null;                }
            }            _parent = null;
        }          // Randomly Populate        public void Populate()
        {            // Calculate abundancy of life
            int Abundancy = ((Parent.Width-2) *                (Parent.Height-2)) * 
                Parent.LifeAbundancy / 100;            // randomly scatter cells
            int RandomY;            while (Abundancy > 0)
            {                for (int x = 1; x < Parent.Width - 2; x++)
                {                    RandomY = Randomize.
                        Next(1, Parent.Height - 2);                    Abundancy -= 
                        this[x, RandomY].                        RandomizeLife();
                }            }
        }    }
     public class World
    {        public Cells Cells { get; set; }
        public int Width { get; set; }        public int Height { get; set; }
        public int GridSize { get; set; }        public int StableGenerations { get; set; }
        public bool Zombies { get; set; }        public bool Mutants { get; set; }
        public int StableGenerationCounter        { get { return StabilityCounter; } }
         private bool _isStable = false;
        public bool IsStable { get { return _isStable; } }          private int _lifeAbundancy = 10;        public int LifeAbundancy
        {            get { return _lifeAbundancy; }
            set            {
                if (value < 0)                    _lifeAbundancy = 0;
                else if (value > 100)                    _lifeAbundancy = 100;
                else                    _lifeAbundancy = value;
            }        }
         public World()
        {            StableGenerations = 10;
        }          public void Initialize()        {
            if (Width < 4) Width = 4;            if (Height < 4) Height = 4;
            if (GridSize < 4) GridSize = 4;            Cells = new Cells(this);
            Cells.Populate();        }
         private int StabilityCounter = 0;
        private int OldPopulation = 0;        public void Refresh()
        {            for(int y=1; y<Height-2;y++)
            {                for(int x =1;x<Width-2 ;x++)
                {                    Cells[x,y].RefreshCellStatus();
                }            }
             if (Cells.Population > OldPopulation - 3 && 
                Cells.Population < OldPopulation + 3)                StabilityCounter++;
            else                StabilityCounter = 0;
             if (StabilityCounter == StableGenerations)
                _isStable = true;              OldPopulation = Cells.Population;        }
    }      internal static class Extensions    {
        public static int ToInt(this bool value)        {
            if(value) return 1;            else return 0;
        }    }
} 
GeSHi 1.0.8.8
Advertisements
Kategorien:.NET, C# Schlagwörter:
  1. Es gibt noch keine Kommentare.
  1. No trackbacks yet.

Kommentar verfassen

Trage deine Daten unten ein oder klicke ein Icon um dich einzuloggen:

WordPress.com-Logo

Du kommentierst mit Deinem WordPress.com-Konto. Abmelden / Ändern )

Twitter-Bild

Du kommentierst mit Deinem Twitter-Konto. Abmelden / Ändern )

Facebook-Foto

Du kommentierst mit Deinem Facebook-Konto. Abmelden / Ändern )

Google+ Foto

Du kommentierst mit Deinem Google+-Konto. Abmelden / Ändern )

Verbinde mit %s

%d Bloggern gefällt das: