0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

C#のフォームアプリでライフゲーム作成

Last updated at Posted at 2022-04-02

Youtubeで解説したC#で作成したライフゲームのソースコード。
下記URLで長々と解説していますが、結局はこのコードを最終的に書きました。
遊んでくれると嬉しいかも。
1回目:ライフゲーム作成するための考え方とか試行錯誤のお話だったはず。

2回目:クラス設計~完成までと、最後は小話。

LifeGame.cs
using System;
using System.Text;
using System.Drawing;
using System.Windows.Forms;

//クラスはFormを継承して作成
class LifeGame : Form
{
	private Button startBtn, resetBtn, randomBtn;
	private static Timer timer = new Timer();

    //ここで作成したMapクラスのオブジェクトを作成(タイマー操作の記述中でも使用したいため)
	private Map mp = new Map(40, 40);
	private Label lb;
	private int dotSize=20;

	public static void Main()
	{
		Application.Run(new LifeGame());
	}

	public LifeGame()
	{
		this.Text = "LifeGame";
		//ウィンドウサイズの指定(px)
		this.Width = 1200; this.Height = 1000;

		//タイマー発動間隔の指定(下記では100ms毎にタイマー発動)
		timer.Interval = 100;
		timer.Tick += new EventHandler(timer_Tick);

		lb = new Label();
        //テキストでグラフィック表現するため、フォントやフォントサイズをあらかじめ定めておく
        //Font(フォント名, フォントサイズ, フォントサイズをピクセル単位で指定)
		lb.Font = new Font("MS UI Gothic", dotSize, GraphicsUnit.Pixel);
		lb.Location = new Point(0,0);
		lb.Size = new Size(this.Width, this.Height - 100);

		lb.Text = mp.textMap();
		lb.Parent = this;
		
		lb.MouseClick += Form_MouseClick;

        //Startボタン
		startBtn = new Button();
		startBtn.Text = "▶";
		startBtn.Location = new Point(0,this.Height - 100);
		this.Controls.Add(startBtn);
		startBtn.Click += new EventHandler(startBtn_Click);

        //Resetボタン
		resetBtn = new Button();
		resetBtn.Text = "Reset";
		resetBtn.Location = new Point(startBtn.Location.X+startBtn.Width+10,this.Height - 100);
		this.Controls.Add(resetBtn);
		resetBtn.Click += new EventHandler(resetBtn_Click);

        //ランダム入力ボタン
		randomBtn = new Button();
		randomBtn.Text = "Random";
		randomBtn.Location = new Point(resetBtn.Location.X+resetBtn.Width+10,this.Height - 100);
		this.Controls.Add(randomBtn);
		randomBtn.Click += new EventHandler(randomBtn_Click);
	}

	private void Form_MouseClick(Object sender, MouseEventArgs e)
	{
		int pointX, pointY;
		if(e.Button==MouseButtons.Left)
		{
			pointX = (int)(e.X - 0.2*dotSize) / dotSize;
			pointY = (int)(e.Y - 0.025*dotSize) / dotSize;

			mp.dotReverse(pointX, pointY);
			lb.Text = mp.textMap();
			lb.Parent = this;
		}
	}

	private void startBtn_Click(Object sender, EventArgs e)
	{
		if(startBtn.Text == "▶")
		{
			startBtn.Text = "■";
			timer.Enabled = true;
		}
		else
		{
			startBtn.Text = "▶";
			timer.Enabled = false;
		}
	}

	private void resetBtn_Click(Object sender, EventArgs e)
	{
		mp.Reset();
		lb.Text = mp.textMap();
		lb.Parent = this;
	}

	private void randomBtn_Click(Object sender, EventArgs e)
	{
		mp.randomInput();
		lb.Text = mp.textMap();
		lb.Parent = this;
	}

	private void timer_Tick(Object sender, EventArgs e)
	{
		nextStep();
		lb.Text = mp.textMap();
		lb.Parent = this;
	}

    //ライフゲームのルールから1世代後のマップ情報計算
	private void nextStep()
	{
		int[,] nowStatus = new int[mp.Width,mp.Height];
		int[,] nextStatus = new int[mp.Width,mp.Height];
		int cellSum;

        //現在情報取得
		for(int j=0; j<mp.Height; j++)
		{
			for(int i=0; i<mp.Width; i++)
			{
				nowStatus[i,j] = mp.dotStatus(i, j);
			}
		}

        //ライフゲームのルールから次世代生死ドットの計算
		for(int j=0; j<mp.Height; j++)
		{
			for(int i=0; i<mp.Width; i++)
			{
				cellSum = 0;
				//マップの外周部は無視
				if(i>0 && j>0 && i <mp.Width-1 && j <mp.Height-1)
				{
					for(int aj=j-1; aj<j+2; aj++)
					  for(int ai=i-1; ai<i+2; ai++)
					    cellSum += nowStatus[ai, aj];
					cellSum -= nowStatus[i, j];
				}
				if(nowStatus[i,j]==0 && cellSum==3) nextStatus[i, j] = 1;
				else if(nowStatus[i,j]==1)
				{
					if(cellSum==2 || cellSum==3) nextStatus[i, j] = 1;
					if(cellSum <2 || 3< cellSum) nextStatus[i, j] = 0;
				}
			}
		}

        //現在マップ情報と次世代マップ情報で差分あればドット反転
		for(int j=0; j<mp.Height; j++)
		{
			for(int i=0; i<mp.Width; i++)
			{
				if((nowStatus[i,j]-nextStatus[i,j])!=0) mp.dotReverse(i, j);
			}
		}
	}
}

class Map
{
	private string strT="■", strF="□", strN="\n";

	public string text;
	public string mapString;
	public StringBuilder sbmap;
	public int Width, Height;
	public int maxLength;

    //オブジェクト生成時に縦横のドット数からテキストマップ情報をstringbuilderに格納する
	public Map(int m, int n)
	{
		Width = m;
		Height = n;
		maxLength = m * n - 1;

		text = "";
		for(int i=0; i<n; i++)
		{
			for(int j=0; j<m; j++)
			{
				text += strF;
			}
			text += strN;
		}

		mapString = text;
		sbmap = new StringBuilder(mapString);
	}

    //指定したドット位置(x方向, y方向)を反転させるメソッド
	public void dotReverse(int m, int n)
	{
        //エラー処理
		if(m>Width-1 || n>Height-1) return;
		if(0>m || 0>n) return;

		int changePoint = (Width+1) * n + m;

		if(sbmap.ToString()[changePoint] == strF[0])
		{
			sbmap.Insert(changePoint, strT, 1);
		}
		else
		{
			sbmap.Insert(changePoint, strF, 1);	
		}
		sbmap.Remove(changePoint+1, 1);
	}

    //指定されたドット情報(■か□)を判定し1or0で返す
	public int dotStatus(int m, int n)
	{
        //エラー処理
		if(m>Width-1 || n>Height-1) return 0;
		if(0>m || 0>n) return 0;

		int getPoint = (Width+1) * n + m;

		if(sbmap.ToString()[getPoint] == strT[0]) return 1;
		else return 0;
	}

    //テキストのマップ情報をリセット
	public void Reset()
	{
		text = "";
		for(int i=0; i<Height; i++)
		{
			for(int j=0; j<Width; j++)
			{
				text += strF;
			}
			text += strN;
		}

		mapString = text;
		sbmap = new StringBuilder(mapString);
	}

    //マップの情報をランダムに入力するメソッド
	public void randomInput()
	{
		Random r = new Random();
		int randomN;

		text = "";
		for(int i=0; i<Height; i++)
		{
			for(int j=0; j<Width; j++)
			{
				randomN = (int)r.Next(0,2);

				if(randomN == 0) text += strF;
				else text += strT;
			}
			text += strN;
		}

		mapString = text;
		sbmap = new StringBuilder(mapString);
	}

    //操作は全てstringbuilderで行ったため、string型を返すメソッドも用意しておく
    //他のメソッドで全て返り値stringを返すようにしてしまってもいいかもしれない。。。
	public string textMap()
	{
		return sbmap.ToString();
	}
}
0
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?