翁愷老師的細胞自動機

大家可能看完翁愷老師的課程會對於細胞自動機想要真正的源代碼,現在就展示各個類的源代碼。

cellmachine/CellMachine.java

package cellmachine;

import javax.swing.JFrame;

import cell.Cell;
import field.View;
import field.Field;

public class CellMachine {

	public static void main(String[] args) {
		Field field = new Field(30,30);
		for( int row = 0; row<field.getHeight(); row++ ) {
			for( int col = 0; col<field.getWidth(); col++ ) {
				field.place(row, col, new Cell());
			}
		}
		for( int row = 0; row<field.getHeight(); row++ ) {
			for( int col = 0; col<field.getWidth(); col++ ) {
				Cell cell = field.get(row, col);
				if( Math.random() < 0.2 ) {
					cell.reborn();
				}
			}
		}
		View view = new View(field);
		JFrame frame = new JFrame();
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frame.setResizable(false);
		frame.setTitle("Cells");
		frame.add(view);
		frame.pack();
		frame.setVisible(true);

		for( int i = 0; i<1000; i++ ) {
			for( int row = 0; row<field.getHeight(); row++ ) {
				for( int col = 0; col<field.getWidth(); col++ ) {
					Cell cell = field.get(row, col);
					Cell[] neighbour = field.getNeighbour(row, col);
					int numOfLive = 0;
					for( Cell c : neighbour ) {
						if( c.isAlive() ) {
							numOfLive++;
						}
					}
					System.out.print("["+row+"]"+"["+col+"]:");
					System.out.print(cell.isAlive()?"live":"dead");
					System.out.print(":"+numOfLive+"-->");
					if( cell.isAlive() ) {
						if( numOfLive<2 || numOfLive>3 ) {
							cell.die();
							System.out.print("die");
						}
					}else if( numOfLive == 3 ) {
						cell.reborn();
						System.out.print("reborn");
					}
					System.out.println();
				}
			}
			System.out.print("UPDATE");
			frame.repaint();
			try {
				Thread.sleep(200);
			}catch( InterruptedException e) {
				e.printStackTrace();
			}
		}
	}

}

cell/Cell.java

package cell;

import java.awt.Graphics;

public class Cell {
	private boolean alive = false;
	
	public void die() { alive = false; }
	public void reborn() { alive = true; }
	public boolean isAlive() { return alive; }

	public void draw(Graphics g, int x, int y, int size) {
		g.drawRect(x, y, size, size);
		if( alive ) {
			g.fillRect(x, y, size, size);
		}
	}

}

field/View.java

package field;

import java.awt.Dimension;

public class View extends JPanel {
	private static final long serialVersionUID = -5258995676212660595L;
	private static final int GRID_SIZE = 16;
	private Field theField;

	public View(Field field) {
		theField = field;
	}

	@Override
	public void paint(Graphics g) {
		super.paint(g);
		for( int row = 0; row<theField.getHeight(); row++ ) {
			for( int col = 0; col<theField.getWidth(); col++ ) {
				Cell cell = theField.get(row, col);
				if( cell != null) {
					cell.draw(g, col*GRID_SIZE, row*GRID_SIZE, GRID_SIZE);
				}
			}
		}
	}

	@Override
	public Dimension getPreferredSize() {
		return new Dimension(theField.getWidth()*GRID_SIZE+1, theField.getHeight()*GRID_SIZE+1);
	}
	
	public static void main(String[] args) {
		Field field = new Field(10,10);
		for( int row = 0; row<field.getHeight(); row++ ) {
			for( int col = 0; col<field.getWidth(); col++ ) {
				field.place(row, col, new Cell());
			}
		}
		View view = new View(field);
		JFrame frame = new JFrame();
		frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
		frame.setResizable(false);
		frame.setTitle("Cells");
		frame.add(view);
		frame.pack();
		frame.setVisible(true);
	}

}

field/Field.java

package field;

import java.util.ArrayList;

import cell.Cell;

public class Field {
	private int width;
	private int height;
	private Cell[][] field;

	public Field (int width,int height) {
		this.width = width;
		this.height = height;
		field = new Cell[height][width];
	}

	public int getHeight() { return height; }
	
	public int getWidth() { return width; }
	
	public Cell place(int row, int col, Cell o) {
		Cell ret = field[row][col];
		field[row][col] = o;
		return ret;
	}

	public Cell[] getNeighbour(int row, int col) {
		ArrayList<Cell> list = new ArrayList<Cell>();
		for( int i = -1; i<2; i++ ) {
			for( int j = -1; j<2; j++ ) {
				int r = row+i;
				int c = col+j;
				if( r>-1 && r<height && c>-1 && c<width && !(r == row && c == col)) {
					list.add(field[r][c]);
				}
			}
		}
		return list.toArray(new Cell[list.size()]);
	}

	public Cell get(int row, int col) {
		return field[row][col];
	}

	public void clear() {
		for( int i = 0; i<height; i++ ) {
			for( int j = 0; j<width; j++ ) {
				field[i][j] = null;
			}
		}
	}

}

老師在最後還說到了幾個點:
數據與表現分離
程序業務邏輯與表現無關
表現可以是圖形的也可以是文本的
表現可以是當地的也可以是遠程的

View和Field的關係
表現與數據的關係
View只管根據Field畫出圖形
Field只管數據的存放
一旦數據更新以後,通知View重新畫出整個畫面
不去精心設計哪個局部需要更新
這樣簡化了程序邏輯
是在計算機運算速度提高的基礎上實現的

責任驅動的設計
將程序要實現的功能分配到合適的類/對象中去是設計中非常重要的一環

網格化
圖形界面本身有更多的解析度
但是將畫面格式化以後,數據就更容易處理了

同時,也提出了幾個問題:
問題0
爲什麼不是在Cell中提供setAlive(boolean)函數?
衛視採用複雜的die()、reborn()兩個函數?

問題1
爲什麼Field.getNeighbour()不直接看Cell。isAlive來返回一個數字,而是要返回一個數組讓外面來數數?

問題2
爲什麼不是由Cell自己判斷自己的鄰居的情況來決定自己是否應該被die或reborn?

本人現在還是沒有弄清這幾個問題的解,還希望各位知道的小夥伴們能夠在評論區中指點迷津。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章