ようこそ。睡眠不足なプログラマのチラ裏です。

いまさらC#でライフゲームを書いてみた


どう書く?orgにて、C#で既に書いてらっしゃる方が多数おられますが、
どうもコードを短くすることに重点を置いて書いているような感じで、正直なんだか読みにくいなーと思い・・・。
冗長だけどわかりやすく、オブジェクト指向っぽくライフゲームを書いてみることにしました*1


ライフゲームについての説明は、ライフゲーム - Wikipediaを参照ください。


Cellクラス

まず、細胞ひとつひとつを表すCellクラスを作ることにします。
IsAliveによって、細胞が生きているかどうかがわかるようにします。

using System;

namespace LifeGameLibrary
{
    /// <summary>
    /// 細胞クラス
    /// </summary>
    [Serializable]
    public class Cell  
    {
        /// <summary>
        /// コンストラクタ
        /// </summary>
        public Cell() {
            this.Age = 0;
        }

        /// <summary>
        /// コンストラクタ
        /// </summary>
        public Cell(bool alive)
        {
            this.Age = 0;
            this.IsAlive = alive;
        }

        /// <summary>
        /// 生死
        /// </summary>
        public bool IsAlive { get; set; }

        /// <summary>
        /// 細胞年齢
        /// </summary>
        public long Age { get; set; }
    }
}

CellMapクラス

続きまして、細胞ひとつひとつの座標位置を表すマップを作ります。
まぁいろいろやってます、コードを読んでください。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace LifeGameLibrary
{
    /// <summary>
    /// 細胞マップ
    /// </summary>
    [Serializable]
    public class CellMap : ICloneable 
    {
        private Cell[,] map;
       
        /// <summary>
        /// コンストラクタ
        /// </summary>
        /// <param name="width">マップの幅</param>
        /// <param name="height">マップの高さ</param>
        public CellMap(int width, int height)
        {
            // 世代を初期化
            this.Generation = 0;

            this.map = new Cell[width, height];
            for (int x = 0; x < width; x++)
            {
                for (int y = 0; y < height; y++)
                {
                    this.map[x, y] = new Cell();
                }
            }
        }

        /// <summary>
        /// コンストラクタ
        /// </summary>
        /// <param name="mapString">マップ文字列</param>
        public CellMap(IEnumerable<string> mapString,char alive) {
            var mapInfo = from map in mapString
                          where map.Length == mapString.Max(x => x.Length)
                          let count = mapString.Count()
                          select new { column = mapString.Max(x => x.Length), row = count };

            foreach (var t in mapInfo)
            {
                this.map = new Cell[t.column, t.row];
                this.ColumnCount = t.column;
                this.RowCount = t.row; 

                var r = -1;
                foreach (var row in mapString) 
                {
                    r++;
                    //改行で分割して文字配列にしてセット
                    var record = row.ToCharArray();
                    for (int x = 0; x < t.column; x++)
                    {
                        this.map[x, r] = new Cell(record[x] == alive);
                    }
                }
            }
        }

        /// <summary>
        /// 列数
        /// </summary>
        public int ColumnCount { get; set; }

        /// <summary>
        /// 行数
        /// </summary>
        public int RowCount { get; set; }

        /// <summary>
        /// 指定した座標位置の細胞
        /// </summary>
        /// <param name="x">x座標</param>
        /// <param name="y">y座標</param>
        /// <returns>細胞</returns>
        public Cell this[int x, int y]
        {
            get { return this.map[x, y]; }
            set { this.map[x, y] = value; }
        }

        /// <summary>
        /// マップの幅
        /// </summary>
        public int Width
        {
            get { return this.map.GetLength(0); }
        }

        /// <summary>
        /// マップの高さ
        /// </summary>
        public int Height
        {
            get { return this.map.GetLength(1); }
        }

        /// <summary>
        /// マップの世代
        /// </summary>
        public long Generation { get; set; }

        /// <summary>
        /// 細胞マップを文字列で取得します。
        /// </summary>
        /// <param name="alive">生存を表す文字</param>
        /// <param name="dead">死亡を表す文字</param>
        /// <returns></returns>
        public string GetCellMapString(char alive,char dead)
        {
            var sb = new StringBuilder();
            for (int y = 0; y < this.RowCount; y++)
            {
                for (int x = 0; x < this.ColumnCount; x++){
                    sb.Append(this.map[x, y].IsAlive ? alive : dead );                
                }
                sb.Append((this.RowCount - 1 == y) ? "" : "\r\n");
            }
            return sb.ToString();
        }

        /// <summary>
        /// ディープコピークローンを取得します。
        /// </summary>
        /// <returns></returns>
        public CellMap DeepCopyClone()
        {
            return (CellMap)this.Clone();
        }

        #region ICloneable メンバ

        public object Clone()
        {
            return Serialization.DeepCopyClone<CellMap>(this);
        }

        #endregion
    }
}

ILivingConditionインターフェイス

続きまして、細胞の生存条件を表すインターフェイスを定義します。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace LifeGameLibrary
{
    /// <summary>
    /// 生存条件インターフェイス
    /// </summary>
    public interface ILivingCondition
    {
        /// <summary>
        /// 細胞マップの各細胞について、次世代の生存確認を行います。
        /// </summary>
        /// <param name="cellMap"></param>
        void ConfirmationAlive(CellMap cellMap);

        /// <summary>
        /// 隣接する細胞たちを取得します。
        /// </summary>
        /// <param name="cellMap">細胞マップ</param>
        /// <param name="x">X座標</param>
        /// <param name="y">Y座標</param>
        /// <returns>隣接する細胞たち</returns>
        IEnumerable<Cell> GetNeighbors(CellMap cellMap, int x, int y);
    }
}


AbstractLivingConditionクラス

先ほど作成したILivingConditionインターフェイスを実装した、
生存条件抽象クラスを作成します。コードのとおりです。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace LifeGameLibrary
{
    /// <summary>
    /// 生存条件抽象クラス
    /// </summary>
    public abstract class AbstractLivingCondition : ILivingCondition
    {
        /// <summary>
        /// 対象の細胞のうち、生存している細胞の数を取得します。
        /// </summary>
        /// <param name="cells">対象細胞</param>
        /// <returns>生きている細胞の数</returns>
        public virtual int GetNumberOfCellsToBeAlive(IEnumerable<Cell> cells)
        {
            int count = 0;
            foreach (Cell cell in cells)
            {
                if (cell.IsAlive)
                {
                    count++;
                }
            }
            return count;
        }

        #region ILivingCondition メンバ

        /// <summary>
        /// 次の世代の生存確認を行います。
        /// </summary>
        /// <param name="cellMap"></param>
        public abstract void ConfirmationAlive(CellMap cellMap);

        /// <summary>
        /// 隣接する細胞のイテレータを提供
        /// </summary>
        /// <param name="cellMap">細胞マップ</param>
        /// <param name="x">x座標</param>
        /// <param name="y">y座標</param>
        /// <returns>隣接する細胞</returns>
        public virtual IEnumerable<Cell> GetNeighbors(CellMap cellMap, int x, int y)
        {
            int w = cellMap.Width;
            int h = cellMap.Height;
            foreach (int xn in new int[] { x - 1, x + 1, x })
            {
                foreach (int yn in new int[] { y - 1, y + 1, y })
                {
                    if ((xn != x) || (yn != y))
                    {
                        if ((xn >= 0) && (xn < w) && (yn >= 0) && (yn < h))
                        {
                            yield return cellMap[xn, yn];
                        }
                    }
                }
            }
            yield break;
        }
        #endregion
    }
}


StandardLivingConditionクラス

生存条件抽象クラスから派生し、ライフゲームの標準的な生存条件クラスを作成します。
ここで初めて、ConfirmationAliveメソッドが実装されています。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace LifeGameLibrary
{
    /// <summary>
    /// ライフゲームの標準的な生存条件
    /// </summary>
    public class StandardLivingCondition : AbstractLivingCondition
    {
        private IEnumerable<int> NumberOfNeighborsToAlive{get;set;}
        private IEnumerable<int> NumberOfNeighborsToBeBorn{get;set;}

        /// <summary>
        /// 条件:23/3
        /// </summary>
        public static readonly StandardLivingCondition Original = new StandardLivingCondition(new int[] { 2, 3 }, new int[] { 3 });

        /// <summary>
        /// 条件:23/36
        /// </summary>
        public static readonly StandardLivingCondition HighLife = new StandardLivingCondition(new int[] { 2, 3 }, new int[] { 3, 6 });

        #region コンストラクタ
        /// <summary>
        /// コンストラクタ
        /// </summary>
        /// <param name="numberOfNeighborsToAlive"></param>
        /// <param name="numberOfNeighborsToBeBorn"></param>
        public StandardLivingCondition(IEnumerable<int> numberOfNeighborsToAlive, IEnumerable<int> numberOfNeighborsToBeBorn)
        {
            this.NumberOfNeighborsToAlive = from num in numberOfNeighborsToAlive
                                            orderby num
                                            select num;
            this.NumberOfNeighborsToBeBorn = from num in numberOfNeighborsToBeBorn
                                             orderby num
                                             select num;
		}

        #endregion

        /// <summary>
        /// 細胞マップの各細胞について、次世代の生存確認を行います。
        /// </summary>
        /// <param name="cellMap">細胞マップ</param>
        public override void ConfirmationAlive(CellMap cellMap)
        {
            var oldCellMap = cellMap.DeepCopyClone();

            for (int x = 0; x < cellMap.Width; x++)
            {
                for (int y = 0; y < cellMap.Height; y++)
                {
                    var oldCell = oldCellMap[x, y];
                    var newCell = cellMap[x, y];
                    var neighbours = this.GetNeighbors(oldCellMap, x, y);
                    int aliveNeighborsCount = this.GetNumberOfCellsToBeAlive(neighbours);

                    if (oldCell.IsAlive)
                    {
                        newCell.IsAlive = Array.BinarySearch(this.NumberOfNeighborsToAlive.ToArray(), aliveNeighborsCount) >= 0;
                        newCell.Age = newCell.IsAlive ? ++newCell.Age : 0;
                        continue;
                    }
                    newCell.IsAlive = Array.BinarySearch(this.NumberOfNeighborsToBeBorn.ToArray(), aliveNeighborsCount) >= 0;
                }
            }
        }
    }
}


LifeGameクラス

ライフゲームを管理するクラスです。
マップ、生存条件、マップの描画などをつかさどります。

using System;
using System.Collections.Generic;

namespace LifeGameLibrary
{
    /// <summary>
    /// ライフゲーム
    /// </summary>
    public class LifeGame
    {
        #region コンストラクタ
        /// <summary>
        /// コンストラクタ
        /// </summary>
        /// <param name="lc">生存条件</param>
        /// <param name="width">マップの幅</param>
        /// <param name="height">マップの高さ</param>
        /// <param name="drw">描画するやつ</param>
        public LifeGame(ILivingCondition lc, int width, int height, ICellMapDrower drw)
            : this(lc, new CellMap(width, height), drw) { }

        /// <summary>
        /// コンストラクタ
        /// </summary>
        /// <param name="lc">生存条件</param>
        /// <param name="cm">細胞マップ</param>
        /// <param name="drw">描画するやつ</param>
        public LifeGame(ILivingCondition lc, CellMap cm, ICellMapDrower drw)
        {
            this.livingCondition = lc;
            this.CellMap = cm;
            this.cellMapDrawer = drw;
        }
        #endregion

        #region プロパティ
        /// <summary>
        /// 細胞マップを取得します。
        /// </summary>
        public CellMap CellMap { get; set; }
 
        /// <summary>
        /// 生存条件を取得します。
        /// </summary>
        private ILivingCondition livingCondition;
        public ILivingCondition LivingCondition
        {
            get { return this.livingCondition; }
        }

        private ICellMapDrower cellMapDrawer;
        public ICellMapDrower CellMapDrawer
        {
            get { return this.cellMapDrawer; }
        }
        #endregion

        #region メソッド
        /// <summary>
        /// 世代イテレータを取得
        /// </summary>
        /// <returns></returns>
        public IEnumerable<CellMap> GetCellMapGeneration()
        {
            while (true)
            {
                this.CellMap.Generation++;
                this.LivingCondition.ConfirmationAlive(this.CellMap);
                this.CellMapDrawer.Draw(this.CellMap);
                yield return this.CellMap;
            }
        }

        /// <summary>
        /// 細胞マップをランダム生成して設定します。
        /// </summary>
        public void SetRamdomCellMap() { 
        	var rnd = new Random();
            int w = this.CellMap.Width;
            int h = this.CellMap.Height;
			for(int x = 0; x < w; x++){
				for(int y = 0; y < h; y++){
                    this.CellMap[x, y].IsAlive = (Math.Floor((double)rnd.Next(2)) == 1);
				}
			}
        }

        /// <summary>
        /// 細胞マップを描画します。
        /// </summary>
        /// <returns></returns>
        public void Draw()
        {
            this.cellMapDrawer.Draw(this.CellMap);
        }

        #endregion
    }
}


Serializationクラス

CellMapのクローンを作成するために、ジェネリクスなディープコピーのみが実装されています。
このクラスに限った話ではありませんが、クラス名は適当なのでつっ込まないでください。

using System.IO;
using System.Runtime.Serialization.Formatters.Binary;

namespace LifeGameLibrary
{
    public static class Serialization
    {
        /// <summary>
        /// 対象オブジェクトのディープコピークローンを取得します。
        /// </summary>
        /// <typeparam name="T">対象オブジェクトの型</typeparam>
        /// <param name="target">コピー対象オブジェクトを指定します。</param>
        /// <returns>ディープコピーのクローンを返します。</returns>
        /// <remarks>このメソッでディープコピーするには、対象クラスがシリアライズ可能である必要があります。</remarks>
        public static T DeepCopyClone<T>(T target)
        {
            object clone = null;
            using (MemoryStream stream = new MemoryStream())
            {
                //対象オブジェクトをシリアライズ
                BinaryFormatter formatter = new BinaryFormatter();
                formatter.Serialize(stream, target);
                stream.Position = 0;

                //シリアライズデータをデシリアライズ
                clone = formatter.Deserialize(stream);
            }
            return (T)clone;
        }
    }
}


ICellMapDrowerインターフェイス

細胞マップを描画するためのインターフェイス。
こいつ見てて、クラス設計がなんか微妙な感じなことに気付きましたが、
もっかい考えるの面倒なのでこのままにしました。

namespace LifeGameLibrary
{
    /// <summary>
    /// 細胞マップを描画するやつのインターフェイス
    /// </summary>
    public interface ICellMapDrower
    {
        void Draw(CellMap cellMap);
    }
}


ConsoleDrawerクラス

こいつが細胞マップをコンソール出力してくれます。

using System;

namespace LifeGameLibrary
{
    /// <summary>
    /// コンソール出力
    /// </summary>
    public class ConsoleDrawer : ICellMapDrower
    {
        public void Draw(CellMap cellMap)
        {
            Console.Clear();
            Console.WriteLine(cellMap.GetCellMapString('■','□'));
            Console.WriteLine("世代:{0}", cellMap.Generation);
        }
    }
}

Templateクラス

ライフゲームで広く知られているパターンのテンプレを定義しています。
Wikipediaからまんま持ってきました。これは別になくてもよいですね。

using System.Collections.Generic;

namespace LifeGameLibrary.Pattern
{
    /// <summary>
    /// ライフゲームのパターンテンプレ
    /// </summary>
    public static class Template
    {
        #region 固定型
        /// <summary>
        /// ブロック
        /// </summary>
        /// <returns></returns>
        public static IEnumerable<string> GetBlock()
        {
            yield return "□□□□";
            yield return "□■■□";
            yield return "□■■□";
            yield return "□□□□";
            yield break;
        }

        /// <summary>
        /// 蜂の巣
        /// </summary>
        /// <returns></returns>
        public static IEnumerable<string> GetBeehive()
        {
            yield return "□■□";
            yield return "■□■";
            yield return "■□■";
            yield return "□■□";
            yield break;
        }

        /// <summary>
        /// ボート
        /// </summary>
        /// <returns></returns>
        public static IEnumerable<string> GetBoat()
        {
            yield return "□■□";
            yield return "■□■";
            yield return "□■■";
            yield break;
        }

        /// <summary>
        /// 船
        /// </summary>
        /// <returns></returns>
        public static IEnumerable<string> GetShip()
        {
            yield return "■■□";
            yield return "■□■";
            yield return "□■■";
            yield break;
        }

        /// <summary>
        /// 池
        /// </summary>
        /// <returns></returns>
        public static IEnumerable<string> GetPool()
        {
            yield return "□■■□";
            yield return "■□□■";
            yield return "■□□■";
            yield return "□■■□";
            yield break;
        }
        #endregion

        #region 振動型
        /// <summary>
        /// ブリンカー
        /// </summary>
        /// <returns></returns>
        public static IEnumerable<string> GetBlinker()
        {
            yield return "□■□";
            yield return "□■□";
            yield return "□■□";
            yield break;
        }

        /// <summary>
        /// ペンタデカスロン
        /// </summary>
        /// <returns></returns>
        public static IEnumerable<string> GetPentadecathlon()
        {
            yield return "□□□□□□□□□□□□□□□□";
            yield return "□□□□□□□□□□□□□□□□";
            yield return "□□□□□□□□□□□□□□□□";
            yield return "□□□□□■□□□□■□□□□□";
            yield return "□□□□■■□□□□■■□□□□";
            yield return "□□□□□■□□□□■□□□□□";
            yield return "□□□□□□□□□□□□□□□□";
            yield return "□□□□□□□□□□□□□□□□";
            yield return "□□□□□□□□□□□□□□□□";
            yield break;
        }

        /// <summary>
        /// ヒキガエル
        /// </summary>
        /// <returns></returns>
        public static IEnumerable<string> GetHikigaeru()
        { 
             yield return "□□■□";
             yield return "■□□■";
             yield return "■□□■";
             yield return "□■□□";
             yield break;
        }

        /// <summary>
        /// ビーコン
        /// </summary>
        /// <returns></returns>
        public static IEnumerable<string> GetBeecon()
        {
            yield return "□□■■";
            yield return "□□■■";
            yield return "■■□□";
            yield return "■■□□";
            yield break;
        }

        /// <summary>
        /// 時計
        /// </summary>
        /// <returns></returns>
        public static IEnumerable<string> GetClock()
        {
            yield return "□□□";
            yield return "■□■□";
            yield return "□■□■";
            yield return "□■□□";
            yield break;
        }
        #endregion

        #region 移動型
        /// <summary>
        /// グライダー
        /// </summary>
        /// <returns></returns>
        public static IEnumerable<string> GetGlider()
        {
            yield return "□■□";
            yield return "■□□";
            yield return "■■■";
            yield break;
        }

        /// <summary>
        /// 軽量級宇宙船
        /// </summary>
        /// <returns></returns>
        public static IEnumerable<string> GetSmallSpaceship()
        {
            yield return "□■□□■";
            yield return "■□□□□";
            yield return "■□□□■";
            yield return "■■■■□";
            yield break;
        }

        /// <summary>
        /// 中量級宇宙船
        /// </summary>
        /// <returns></returns>
        public static IEnumerable<string> GetMiddleSpaceship()
        {
            yield return "□□□■□□";
            yield return "□■□□□■";
            yield return "■□□□□□";
            yield return "■□□□□■";
            yield return "■■■■■□";
            yield break;
        }

        /// <summary>
        /// 重量級宇宙船
        /// </summary>
        /// <returns></returns>
        public static IEnumerable<string> GetLargeSpaceship()
        {
            yield return "□□□■■□□";
            yield return "□■□□□□■";
            yield return "■□□□□□□";
            yield return "■□□□□□■";
            yield return "■■■■■■□";
            yield break;
        }

        /// <summary>
        /// グライダー・ガン
        /// </summary>
        /// <returns></returns>
        public static IEnumerable<string> GetGliderGun()
        {
            yield return "□□□□□□□□□□□□□□□□□□□□□□□□□■□□□□□□□□□□□□";
            yield return "□□□□□□□□□□□□□□□□□□□□□□□■□■□□□□□□□□□□□□";
            yield return "□□□□□□□□□□□□□■■□□□□□□■■□□□□□□□□□□□□■■□";
            yield return "□□□□□□□□□□□□■□□□■□□□□■■□□□□□□□□□□□□■■□";
            yield return "□■■□□□□□□□□■□□□□□■□□□■■□□□□□□□□□□□□□□□";
            yield return "□■■□□□□□□□□■□□□■□■■□□□□■□■□□□□□□□□□□□□";
            yield return "□□□□□□□□□□□■□□□□□■□□□□□□□■□□□□□□□□□□□□";
            yield return "□□□□□□□□□□□□■□□□■□□□□□□□□□□□□□□□□□□□□□";
            yield return "□□□□□□□□□□□□□■■□□□□□□□□□□□□□□□□□□□□□□□";
            yield return "□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□";
            yield return "□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□";
            yield return "□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□";
            yield return "□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□";
            yield return "□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□";
            yield return "□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□";
            yield return "□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□";
            yield return "□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□";
            yield return "□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□";
            yield return "□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□";
            yield return "□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□";
            yield return "□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□";
            yield return "□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□";
            yield return "□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□";
            yield return "□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□";
            yield return "□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□";
            yield return "□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□□";
            yield break;
        }

        /// <summary>
        /// 汽車ぽっぽ
        /// </summary>
        /// <returns></returns>
        public static IEnumerable<string> GetKishaPoppo()
        {
            yield return "□□□■□";
            yield return "□□□□■";
            yield return "■□□□■";
            yield return "□■■■■";
            yield return "□□□□□";
            yield return "□□□□□";
            yield return "□□□□□";
            yield return "■□□□□";
            yield return "□■■□□";
            yield return "□□■□□";
            yield return "□□■□□";
            yield return "□■□□□";
            yield return "□□□□□";
            yield return "□□□□□";
            yield return "□□□■□";
            yield return "□□□□■";
            yield return "■□□□■";
            yield return "□■■■■";
            yield break;
        }
        #endregion

        #region 長寿型
        /// <summary>
        /// ダイハード
        /// </summary>
        /// <returns></returns>
        public static IEnumerable<string> GetDieHard()
        {
            yield return "□□□□□□■□";
            yield return "■■□□□□□□";
            yield return "□■□□□■■■";
            yield break;
        }

        /// <summary>
        /// ドングリ
        /// </summary>
        /// <returns></returns>
        public static IEnumerable<string> GetDongri()
        {
            yield return "□■□□□□□";
            yield return "□□□■□□□";
            yield return "■■□□■■■";
            yield break;
        }
        #endregion
    }
}


Programクラス

最後に、LifeGameLibraryを用いて、コンソールアプリケーションでライフゲームを実行します。
パターンテンプレはペンタデカスロンを用いています。

using System;
using LifeGameLibrary;
using LifeGameLibrary.Pattern;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main()
        {
            //var lifeGame = new LifeGame(StandardLivingCondition.Original,3, 3, new ConsoleDrawer());
            //lifeGame.SetRamdomCellMap();

            var lifeGame = new LifeGame(StandardLivingCondition.Original,
                           new CellMap(Template.GetPentadecathlon(), '■'), new ConsoleDrawer());

            lifeGame.Draw();
            var s = Console.ReadLine();
            foreach (var map in lifeGame.GetCellMapGeneration())
            {
                s = Console.ReadLine();
                if (s == "exit") break;
            }
        }
    }
}

以上です。そこそこオブジェクト指向していますね。初心者にもわかりやすいでしょう?
気が向いたら、いろいろ拡張したりGUIとか整備しようかな。
どうもお疲れさまでした(・ω・)

*1:雑でいい加減だったりもしますが