2009-12-22

用 GWT 寫個「生命遊戲」吧!

原文寫於 Java 版(ptt.cc)

以 GWT 教學系列來說,應該先講環境建制之類 blahblah
不過還是跳過那些繁文縟節,先講比較實在的部份
如果這樣子寫 JavaScript 的方式沒辦法引起興趣
還是會想要用 jQuery 之類的方法來寫程式
那開發環境安裝起來再快(例如 XAMPP [大笑]),也沒啥意義 :P
------------

寫一個 JavaScript 版的生命遊戲(Game of life)
要花多久? 有多難呢?

嗯? Java 版不是說好不提 JavaScript 嗎?
好好好,那拿出以前交作業的 Java 程式吧! [咦?]

首先包個「細胞」class
狀態只有兩種,要嘛活著要嘛死掉
不過得多個變數預知下一代會生會死
public class Cell{
  private boolean now;
  private boolean next;
  public Cell(){}

  public Cell(boolean alive) {now = alive;}

  public boolean isAlive(){return now;}

  public void setNextAlive(boolean alive){
   this.next = alive;
  }

  public void nextTurn() {now = next;}
 }
然後再來包個「世界」的 class
裡頭有一堆細胞,所以用個二維陣列來表示
至於 width, 跟 height 這兩個 field 純粹只是為了懶惰好看 [逃]
public class World{
  private int width;
  private int height;
  private Cell[][] cell;

  public World(int w, int h){
   width = w;
   height = h;

   cell = new Cell[w][h];
   for(int i=0; i<2; i++){
    tmpW = w+i;
    if( tmpW<0 || tmpW==width){
     continue;
    }
    for(int j=-1; j<2; j++){
     tmpH = h+j;
     if( tmpH<0 || tmpH==height){
      continue;
     }
     if(tmpH==h && tmpW==w){
      continue;
     }
     if(getCell(tmpW, tmpH).isAlive()){
      sum++;
     }
    }
   }

   switch(sum){
   case 0: case 1: case 4: case 5:
   case 6: case 7: case 8:
    getCell(w, h).setNextAlive(false);
    break;
   case 3:
    getCell(w, h).setNextAlive(true);
    break;
   }
  }

  public Cell getCell(int w, int h){
   return cell[w][h];
  }
 }
好了,物件都包好了,那麼現在該來解決畫面了
這次不用 Swing、也不用 SWT,試試傳說中的 GWT [奸笑]

「監視器」本身是個 panel,至於 vertical 在幹麼就跳過。
監視器要監看生命遊戲的世界,還要有一個棋盤來顯示狀況
所以 field 大概就是這樣啦~
public class Monitor extends VerticalPanel{
    private World world;
    private Grid board;
    private int width = 10, height = 10;
}
constructor 要把「世界」跟「棋盤」正式建立起來
「棋盤」要放在「監視器」上頭,還要有個按鈕讓人家可以點下一步
所以...
public Monitor(){
  world = new World(width, height);

  board = new Grid(width, height);
  this.add(board);

  Button next = new Button("下一世代");
  //點下一步要幹些什麼好事?
  next.addClickHandler(new ClickHandler(){
    @Override
    public void onClick(ClickEvent event) {
     nextTurn();
    }
  });
  this.add(next);

  //重新整理畫面
  refresh();
 }
這邊先跑出來兩個新的 method
nextTurn() 沒啥好講的,就是要 world 過個年,然後重新整理畫面
public void nextTurn(){
  world.nextTurn();
  refresh();
 }
至於 refresh(),我打算讓活著的細胞用「X」來表示
死掉的細胞的空空白白的什麼都沒有(其實是全形空白)
private void refresh() {
  for (int i = 0; i < width; i++) {
   for (int j = 0; j < height; j++) {
    if (world.getCell(i, j).isAlive()) {
     board.setText(i, j, "X");
    } else {
     board.setText(i, j, " ");
    }
   }
  }
 }
剩下的就是找到啟動的地方
在 GWT 裡頭是 EntryPoint 這個家族的 class 然後這樣寫:
public class Enter implements EntryPoint {
  public void onModuleLoad() {
   RootPanel.get().add(new Monitor());
  }
 }
好啦~ 恭喜你,只剩下 compile 以及找個網頁空間放 .js 檔
JavaScript 版的生命遊戲就... 寫 完 啦... \囧/

輕不輕鬆? 開不開心? [扭扭]
以前的 Java 程式碼可以直接拿來用...


阿? 什麼?
GWT 的開發環境要怎麼安裝?
程式碼要放在哪個目錄下?
怎麼指定 EntryPoint?
ㄜ... 請參照課本第 43 頁就可以了,好! 下課! [逃]