意外と奥が深い!ライフゲーム作ってみました

By | 2016/07/24

皆さんはライフゲームはご存知ですか?ボードゲームの人生ゲームとか連想しそうですが、プログラミングに関係するライフゲームとなると、生命の誕生と死をシミュレーションするゲームを意味します。

ライフゲーム (Conway’s Game of Life[1]) は1970年にイギリスの数学者ジョン・ホートン・コンウェイ (John Horton Conway) が考案した生命の誕生、進化、淘汰などのプロセスを簡易的なモデルで再現したシミュレーションゲームである。単純なルールでその模様の変化を楽しめるため、パズルの要素を持っている。

ライフゲーム -Wikipedia

実際は下のイメージ図のように、緑色の点(生命)が条件に従って、誕生と消滅を繰り返していきます。無限ループに入る形状などがいくつかあるようです。

イメージ

lifegame

今回はJAVAでライフゲームを作成してみました。以下はサンプルコードになります。

サンプルコード

LifeGame.java

package lifeGame;
import java.awt.Color;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.JButton;
import javax.swing.JFrame;

public class LifeGame extends JFrame implements ActionListener, Runnable{
	private static final long serialVersionUID = 1L;
	private JButton button1,button2;
	private LifePanel lifePanel;
	private Thread t;
	private boolean flg,kissOfDeath;
	
	public static void main(String[] args) {
		new LifeGame().setVisible(true);
	}
	public LifeGame(){
		this.setSize(530, 500);//Panel全体の大きさ
		setBackground(Color.black);
		flg = false;
		kissOfDeath = false;//スレッドのフラグ 命名はジョークのようなもの
		setLayout(null);
		//Startボタン
		button1 = new JButton();
		button1.setText("Strat");
		button1.setBounds(10, 420, 70, 20);
		getContentPane().add(button1);
		//Clearボタン
		button2 = new JButton();
		button2.setText("Clear");
		button2.setBounds(440, 420, 70, 20);
		getContentPane().add(button2);
		//LifePanel
		lifePanel = new LifePanel(100, 80);//LifePanelの大きさ
		lifePanel.setBounds(10, 10, 500, 400);
		lifePanel.setBackground(Color.black);
		getContentPane().add(lifePanel);
		button1.addActionListener(this);
		button2.addActionListener(this);
		setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
	}
	
	public void actionPerformed(ActionEvent ev){
		if(ev.getSource() == button1){//StartまたはStopボタン押下
			//開始前または停止中
			if(!flg){
				button1.setText("Stop");
				kissOfDeath = true;
				t = new Thread(this);
				t.start();
			} else {//稼働中
				kissOfDeath = false;
				button1.setText("Start");
			}
			flg = !flg;//フラグ切り替え(スマートな書き方で結構好きw)
			return;
		} else {//Clearボタン押下
			lifePanel.initialData();
			lifePanel.repaint();
			return;
		}
	}
	
	public void run(){
		//ライフサイクル
		do {
			lifePanel.checkAllLife();//パターン判定
			lifePanel.repaint();
			try {
				Thread.sleep(100L);
			}
			catch(Exception _ex){}
		} while(kissOfDeath);
	}
}

Lifepanel.java

package lifeGame;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;

import javax.swing.JPanel;

public class LifePanel extends JPanel{
	private static final long serialVersionUID = 1L;
	private boolean data[][];
	private int dataWidth;
	private int dataHeight;
	private Image offScreen;
	private Graphics offg;
	
	public LifePanel(){
		this(25, 25);
	}
	//LifePanel
	public LifePanel(int i, int j){
		dataWidth = i;
		dataHeight = j;
		setPreferredSize(new Dimension(dataWidth * 5,dataHeight * 5));
		initialData();
		addMouseListener(new MouseAdapter(){
			public void mousePressed(MouseEvent mouseevent){
				doMouseDown(mouseevent);
			}
		});
	}
	//最初のLifePanelの状態
	public void initialData(){
		data = new boolean[dataWidth][dataHeight];
		for(int i = 0; i < dataWidth; i++){
			for(int j = 0; j < dataHeight; j++){
				data[i][j] = false;
			}
		}
	}
	//セルをLifePanelにセット
	public void doMouseDown(MouseEvent ev){
		int i = ev.getX() / 5;
		int j = ev.getY() / 5;
		if(i >= dataWidth || j >= dataHeight){
			return;
		} else {
			data[i][j] = !data[i][j];
			repaint();
			return;
		}
	}
	
	public void update(Graphics g){
		paint(g);
	}
	//セルの状態をLifePanelに反映
	public void paint(Graphics g){
		if(offScreen == null){
			offScreen = createImage(dataWidth * 5, dataHeight * 5);
			offg = offScreen.getGraphics();
		}
		offg.setColor(Color.BLACK);
		offg.fillRect(0, 0, dataWidth * 5, dataHeight * 5);
		offg.setColor(Color.GREEN);
		for(int i = 0; i < dataWidth; i++){
			for(int j = 0; j < dataHeight; j++){
				if(data[i][j]){
					offg.fillRect(i * 5, j * 5, 4, 4);
				}
			}
		}
		g.clearRect(0, 0, getSize().width, getSize().height);
		g.drawImage(offScreen, 0, 0, this);
	}
	//LifePanel内のすべてのセルをパターン判定
	public void checkAllLife(){
		boolean aflag[][] = new boolean[dataWidth][dataHeight];
		for(int j = 0; j < dataWidth; j++){
			for(int k = 0; k < dataHeight; k++){
				int i = checkLife(j, k);
				aflag[j][k] = false;
				if(i == 2 && data[j][k]){
					aflag[j][k] = true;
				}
				if(i == 3){
					aflag[j][k] = true;
				}
				if(i < 2 || i > 3){
					aflag[j][k] = false;
				}
			}
		}
		for(int l = 0; l < dataWidth; l++){
			for(int i1 = 0; i1 < dataHeight; i1++){
				data[l][i1] = aflag[l][i1];
			}
		}
		repaint();
	}
	//パターン判定(詳細はwikipediaなどを参照ください)
	public int checkLife(int i, int j){
		int k = 0;
		int l = dataWidth;
		int i1 = dataHeight;
		return k = (k = (k = (k = (k = (k = (k =
				//三項演算子で周囲のセルを判定(こういう時に綺麗にかけるのいいですよね)
				(k += data[(i + l + -1) % l][(j + i1 + -1) % i1] ? 1 : 0)//左上のセル
				+ (data[(i + l) % l][(j + i1 + -1) % i1] ? 1 : 0))//左
				+ (data[(i + l + 1) % l][(j + i1 + -1) % i1] ? 1 : 0))//左下
				+ (data[(i + l + -1) % l][(j + i1) % i1] ? 1 : 0))//上
				+ (data[(i + l + 1) % l][(j + i1) % i1] ? 1 : 0))//下
				+ (data[(i + l + -1) % l][(j + i1 + 1) % i1] ? 1 : 0))//右上
				+ (data[(i + l) % l][(j + i1 + 1) % i1] ? 1 : 0))//右
				+ (data[(i + l + 1) % l][(j + i1 + 1) % i1] ? 1 : 0);//右下
	}
}

上記のソースコードをコピペするだけですぐにライフゲームを作成することができます。生存パターンを少し変えてみたりすることで、オリジナルのライフゲームを楽しむのも面白いかもしれませんね。また、プログラミンングの練習にも使えると思うので、初心者の方にはコードを真似てみるのもいいかもしれません。余計なお世話かもしれませんが、適当にコメントも追加しておきました。

この記事が皆様のお役に立てれば、幸いです。

Pocket

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です