康威生命遊戲

November 30, 2021

1970 年 10 月的《Scientific American》的數學遊戲專欄中,John Conway 發表了生命遊戲,他建議你兩種顏色棋子要夠多,以及一個夠大的棋盤,棋盤中各方格的細胞與鄰居形成九宮格,也就是每個細胞的鄰居有八個,一開始各細胞會有個初始狀態,要不就活著要不就死亡。遊戲的規則是這樣的:

  1. 如果細胞的鄰居為二個或三個,該細胞在下一代(generation)的狀態會是存活(穩定)。
  2. 若細胞的鄰居小於一個或在四個以上,下一代將會死亡(孤單死或擁擠死)。
  3. 死亡狀態的細胞若有三個存活狀態的鄰居,下一代將會復活(繁殖)。

康威生命遊戲是一種細胞自動機(Cellular automaton),其他有趣且可構造複雜圖樣的細胞自動機,可以進一步參考蘭頓螞蟻或者是〈漫談細胞自動機〉。

解法思路

生命遊戲的規則可簡化為某位置有無細胞:

  1. 某位置之細胞鄰居數為 0、1、4、5、6、7、8 時,該位置下次狀態必無細胞存活。
  2. 某位置之細胞鄰居數為 2 時,該位置下次狀態保持不變(有細胞就有細胞,無細胞就無細胞)。
  3. 某位置之細胞鄰居數為 3 時,該位置下次狀態必有細胞存活。

網路上有許多生命遊戲的模擬程式,例如 Golly,有桌機或行動裝置的版本,下載回來後可用滑鼠點選各位置初始細胞狀態,模擬每一代的狀態變化,你可能會注意到其中首頁的文字跑馬燈,可以在 Golly 的 Life/Guns 找到 golly-ticker,生命遊戲能呈現一些重複模式,最有名的是 Conway 發現的滑行者(glider),以及在 Conway 的 50 英鎊懸賞下,由美國數學家 Bill Gosper 發表的槍(gun),可以不斷地發射出滑行者。

程式實作

#include <stdio.h> 
#define ROW 10 
#define COLUMN 10

void produce(int[][COLUMN], int[][COLUMN]);
void print(int[][COLUMN]);
void copy(int[][COLUMN], int[][COLUMN]);
int neighbors(int[][COLUMN], int, int);
int isDifferent(int[][COLUMN], int[][COLUMN]);

int main() { 
    int current[ROW][COLUMN] = {
       {0, 1, 0, 1, 0, 0, 0, 0, 1, 1},
       {0, 1, 0, 1, 0, 0, 0, 0, 1, 1},
       {0, 1, 0, 1, 0, 0, 0, 0, 1, 1},
       {0, 1, 1, 1, 0, 0, 1, 0, 1, 1},
       {0, 1, 1, 1, 0, 1, 0, 0, 1, 1},
       {0, 1, 0, 1, 1, 0, 0, 1, 1, 1},
       {0, 1, 0, 1, 0, 1, 0, 0, 1, 1},
       {0, 1, 0, 1, 0, 0, 1, 0, 1, 1},
       {0, 1, 0, 1, 0, 1, 0, 1, 1, 1},
       {0, 1, 0, 1, 1, 0, 0, 0, 1, 1}
    };
    int next[ROW][COLUMN] = {0};

    print(current);
    produce(current, next);
    while(isDifferent(current, next)) {
        copy(next, current);
        print(current);
        produce(current, next);
    }
   
    return 0; 
} 


void produce(int current[][COLUMN], int next[][COLUMN]) {
    int row; 
    for(row = 0; row < ROW; row++) {
        int column;
        for(column = 0; column < COLUMN; column++) {
            switch (neighbors(current, row, column)) {
               case 0: case 1: case 4:
                  next[row][column] = 0; break; 
               case 2: 
                  next[row][column] = current[row][column]; break; 
               case 3: 
                  next[row][column] = 1;
            } 
        }
    }
}

int neighbors(int current[][COLUMN], int row, int column) {
    int dirs[8][2] = {{-1, 0}, {-1, 1}, {0, 1}, {1, 1},
                      {1, 0}, {1, -1}, {0, -1}, {-1, -1}};
    int count, i;
    for(count = 0, i = 0; i < 8 && count < 4; i++) {
        int r = row + dirs[i][0];
        int c = column + dirs[i][1];
        if(r > -1 && r < ROW && c > -1 && c < COLUMN && current[r][c]) {
             count++;
        }
    }
    return count;
} 

void print(int current[][COLUMN]) {
    printf("Status...\n");
    int row;    
    for(row = 0; row < ROW; row++) { 
        int column;
        for(column = 0; column < COLUMN; column++) {
            putchar(current[row][column] ? '*' : '~');
        }
        puts("");
    } 
} 

void copy(int from[][COLUMN], int to[][COLUMN]) {
    int row;
    for(row = 0; row < ROW; row++) {
        int column;
        for(column = 0; column < COLUMN; column++) {
            to[row][column] = from[row][column];
            from[row][column] = 0;
        }
    }
}  

int isDifferent(int current[][COLUMN], int next[][COLUMN]) {
    int row;
    for(row = 0; row < ROW; row++) {
        int column;
        for(column = 0; column < COLUMN; column++) {
            if(current[row][column] != next[row][column]) {
                return 1;
            }
        }
    }
    return 0;
}  
package cc.openhome;

import java.util.*;
import java.util.function.Consumer;

import static java.lang.System.out;

enum Rule implements Consumer<Cell> {
    Fatal  {
        @Override
        public void accept(Cell cell) {
            cell.isAlive = 0;
        }
    }, 
    
    Stable {
        @Override
        public void accept(Cell cell) {
            // nope
        }
    }, 
    
    Revivable {
        @Override
        public void accept(Cell cell) {
            cell.isAlive = 1;
        }
    };
    
    private static Map<Integer, Rule> envs = Map.of(2, Stable, 3, Revivable);

    public static Rule of(Cell[][] cells, Cell cell) {
        int[][] dirs = {{-1, 0}, {-1, 1}, {0, 1}, {1, 1},
                        {1, 0}, {1, -1}, {0, -1}, {-1, -1}};
        var count = 0;
        for(var i = 0; i < 8 && count < 4; i++) {
            var r = cell.i + dirs[i][0];
            var c = cell.j + dirs[i][1];
            if(r > -1 && r < cells.length && c > -1 && c < 
               cells[0].length && cells[r][c].isAlive != 0) {
                count++;
            }
        }
        
        return Rule.envs.getOrDefault(count, Fatal);
    }
}

class Cell {
    int i, j, isAlive;
    
    Cell(int i, int j, int isAlive) {
        this.i = i;
        this.j = j;
        this.isAlive = isAlive;
    }
    
    void nextGen(Cell[][] cells) {
        Rule.of(cells, this).accept(this);
    }
    
    Cell copy() {
        return new Cell(i, j, isAlive);
    }
}

public class LifeGame {
    public static Cell[][] cells(int[][] rawData) {
        var cells = new Cell[rawData.length][rawData[0].length];
        for(var i = 0; i < rawData.length; i++) {
            for(var j = 0; j < rawData[i].length; j++) {
                cells[i][j] = new Cell(i, j, rawData[i][j]);
            }
        }
        return cells;
    }
    
    public static Cell[][] copy(Cell[][] source) {
        Cell[][] cells = new Cell[source.length][source[0].length];
        for(var i = 0; i < source.length; i++) {
            for(var j = 0; j < source[i].length; j++) {
                cells[i][j] = source[i][j].copy();
            }
        }
        return cells;
    }
    
    public static void nextGen(Cell[][] cells) {
        Cell[][] currentCells = copy(cells);
        for(var row : cells) {
            for(var cell : row) {
                cell.nextGen(currentCells);
            }
        }
    }
    
    public static void print(Cell[][] cells) {
        out.println("Status...");
        for(var row : cells) {
            for(var cell : row) {
                out.print(cell.isAlive == 0 ? '~' : '*');
            }
            out.println();
        }
    }
    
    public static void main(String[] args) {
        Cell[][] cells = cells(new int[][] {
                   {0, 1, 0, 1, 0, 0, 0, 0, 1, 1},
                   {0, 1, 0, 1, 0, 0, 0, 0, 1, 1},
                   {0, 1, 0, 1, 0, 0, 0, 0, 1, 1},
                   {0, 1, 1, 1, 0, 0, 1, 0, 1, 1},
                   {0, 1, 1, 1, 0, 1, 0, 0, 1, 1},
                   {0, 1, 0, 1, 1, 0, 0, 1, 1, 1},
                   {0, 1, 0, 1, 0, 1, 0, 0, 1, 1},
                   {0, 1, 0, 1, 0, 0, 1, 0, 1, 1},
                   {0, 1, 0, 1, 0, 1, 0, 1, 1, 1},
                   {0, 1, 0, 1, 1, 0, 0, 0, 1, 1}});
                   
        
        for(var g = 0; g < 35; g++) {
            print(cells);
            nextGen(cells);
        }
    }
}
class Cell:
    def __init__(self, i, j):
        self.position = (i, j)
    
    def seekToSurvive(self, cells):
        return self if self.isLivable(cells) else None
    
    def isLivable(self, cells):
        n = self.neighbors(cells) 
        return False if n in [0, 1, 4] else (True if n == 3 else 
                id(self) == id(cells[self.position[0]][self.position[1]]))

    def neighbors(self, cells):
        dirs  = [[-1, 0], [-1, 1], [0, 1], [1, 1],
                 [1, 0], [1, -1], [0, -1], [-1, -1]]
        count = 0;
        for i in range(8):
            if count == 4:
                break
            r = self.position[0] + dirs[i][0]
            c = self.position[1] + dirs[i][1]
            if r > -1 and r < len(cells) and c > -1 and c < len(cells[0]) \
                      and cells[r][c] != None:
                count += 1
        return count
        
    def __eq__(self, that):
        return False if that == None else self.position == that.position

    def __hash__(self):
        return hash(self.position)

def asCells(rowData):
    return [[Cell(i, j) if rowData[i][j] == 1 else None 
        for j in range(len(rowData[i]))] for i in range(len(rowData))]

def getOrNew(cells, i, j):
    return Cell(i, j) if cells[i][j] == None else cells[i][j]

def produce(current):
    return [[getOrNew(current, i, j).seekToSurvive(current) 
      for j in range(len(current[i]))] for i in range(len(current))]
    
def draw(cells):
    print('Status...')
    for row in cells:
        for cell in row:
            print('~' if cell == None else '*', end='')
        print()
    
current = asCells([[0, 1, 0, 1, 0, 0, 0, 0, 1, 1],
                   [0, 1, 0, 1, 0, 0, 0, 0, 1, 1],
                   [0, 1, 0, 1, 0, 0, 0, 0, 1, 1],
                   [0, 1, 1, 1, 0, 0, 1, 0, 1, 1],
                   [0, 1, 1, 1, 0, 1, 0, 0, 1, 1],
                   [0, 1, 0, 1, 1, 0, 0, 1, 1, 1],
                   [0, 1, 0, 1, 0, 1, 0, 0, 1, 1],
                   [0, 1, 0, 1, 0, 0, 1, 0, 1, 1],
                   [0, 1, 0, 1, 0, 1, 0, 1, 1, 1],
                   [0, 1, 0, 1, 1, 0, 0, 0, 1, 1]])

draw(current)
next = produce(current)
while(current != next):
    current = next
    draw(current);
    next = produce(current)
case class Cell(i: Int, j: Int) {
    private val dirs = List(
                         List(-1, 0), List(-1, 1), List(0, 1), List(1, 1),
                         List(1, 0), List(1, -1), List(0, -1), List(-1, -1)
                       )

    private val deathCounts = List(0, 1, 4)
    
    def seekToSurvive(cells: List[List[Cell]]) = if(isLivable(cells)) this 
                                                 else null
    
    def isLivable(cells: List[List[Cell]]) = {
        val n = neighbors(cells)
        if(deathCounts.contains(n)) false 
        else if(n == 3) true 
        else this eq cells(i)(j)
    }

    def increment(index: Int, count: Int, cells: List[List[Cell]]): Int = {
        if(index == 8 || count == 4) count
        else {
            val r = i + dirs(index)(0)
            val c = j + dirs(index)(1)
            if(r > -1 && r < cells.length && 
               c > -1 && c < cells(0).length &&
               cells(r)(c) != null) increment(index + 1, count + 1, cells)
            else increment(index + 1, count, cells)
        }
    }
    
    def neighbors(cells: List[List[Cell]]) = increment(0, 0, cells)
}

def asCells(rowData: List[List[Int]]) = {
    (for(i <- 0 until rowData.length) yield 
        (for(j <- 0 until rowData(i).length) yield 
            if(rowData(i)(j) == 1) Cell(i, j) else null).toList).toList
}

def getOrNew(cells: List[List[Cell]], i: Int, j: Int) = {
    if(cells(i)(j) == null) Cell(i, j) else cells(i)(j)
}

def produce(current: List[List[Cell]]) = {
    (for(i <- 0 until current.length) yield 
        (for(j <- 0 until current(i).length) yield 
            getOrNew(current, i, j).seekToSurvive(current)).toList).toList
}

def showUntilSteady(cells: List[List[Cell]]) {
    println("Status...")
    for(row <- cells) {
        for(cell <- row) {
            print(if(cell == null) '~' else '*')
        }
        println
    }
    val next = produce(cells)
    if(next != cells) showUntilSteady(next)
}

showUntilSteady(asCells(
        List(
            List(0, 1, 0, 1, 0, 0, 0, 0, 1, 1),
            List(0, 1, 0, 1, 0, 0, 0, 0, 1, 1),
            List(0, 1, 0, 1, 0, 0, 0, 0, 1, 1),
            List(0, 1, 1, 1, 0, 0, 1, 0, 1, 1),
            List(0, 1, 1, 1, 0, 1, 0, 0, 1, 1),
            List(0, 1, 0, 1, 1, 0, 0, 1, 1, 1),
            List(0, 1, 0, 1, 0, 1, 0, 0, 1, 1),
            List(0, 1, 0, 1, 0, 0, 1, 0, 1, 1),
            List(0, 1, 0, 1, 0, 1, 0, 1, 1, 1),
            List(0, 1, 0, 1, 1, 0, 0, 0, 1, 1)
        )
    )
}
# encoding: UTF-8
class Cell
    def initialize(r, c)
        @position = {i: r, j: c}
    end
    
    def seekToSurvive(cells)
        if isLivable(cells); self else nil end
    end
    
    def isLivable(cells)
        n = neighbors(cells) 
        if [0, 1, 4].include? n; false
        elsif n == 3; true
        else self.object_id == cells[@position[:i]][@position[:j]].object_id
        end
    end

    def neighbors(cells)
        dirs  = [[-1, 0], [-1, 1], [0, 1], [1, 1],
                 [1, 0], [1, -1], [0, -1], [-1, -1]]
        count = 0;
        (0...8).each do |i|
            if count == 4
                break
            end
            r = @position[:i] + dirs[i][0]
            c = @position[:j] + dirs[i][1]
            if r > -1 && r < cells.size && 
               c > -1 && c < cells[0].size && cells[r][c] != nil
                count += 1
            end                      
        end
        count
    end
    
    def eq?(that)
        @positon == that.position
    end

    def hash
        @position.hash
    end
end

def asCells(rowData)
    (0...rowData.size).map { |i|
        (0...rowData[i].size).map { |j|
            if rowData[i][j] == 1; Cell.new(i, j) else nil end
        }
    }
end

def getOrNew(cells, i, j)
    if cells[i][j] == nil; Cell.new(i, j) else cells[i][j] end
end

def produce(current)
    (0...current.size).map { |i|
        (0...current[i].size).map { |j|
            getOrNew(current, i, j).seekToSurvive(current)
        }
    }
end
      
def draw(cells)
    puts('Status...')
    cells.each do |row|
        row.each do |cell|
            print(if cell == nil; '~' else '*' end)
        end
        puts
    end
end
    
current = asCells([[0, 1, 0, 1, 0, 0, 0, 0, 1, 1],
                   [0, 1, 0, 1, 0, 0, 0, 0, 1, 1],
                   [0, 1, 0, 1, 0, 0, 0, 0, 1, 1],
                   [0, 1, 1, 1, 0, 0, 1, 0, 1, 1],
                   [0, 1, 1, 1, 0, 1, 0, 0, 1, 1],
                   [0, 1, 0, 1, 1, 0, 0, 1, 1, 1],
                   [0, 1, 0, 1, 0, 1, 0, 0, 1, 1],
                   [0, 1, 0, 1, 0, 0, 1, 0, 1, 1],
                   [0, 1, 0, 1, 0, 1, 0, 1, 1, 1],
                   [0, 1, 0, 1, 1, 0, 0, 0, 1, 1]])

draw(current)
newGen = produce(current)
while(current != newGen)
    current = newGen
    draw(current);
    newGen = produce(current)
end
var produce = function() {
    function range(start, end) {
        var r = [];
        for(var i = start; i < end; i++) {
            r.push(i);
        }
        return r;
    }
    
    function liveOrDie(current, row, column) {
        switch(neighbors(current, row, column)) {
            case 0: case 1: case 4: return 0;
            case 2: return current[row][column];
        }
        return 1;
    }
    
    function neighbors(current, row, column) {
        var dirs = [[-1, 0], [-1, 1], [0, 1], [1, 1],
                    [1, 0], [1, -1], [0, -1], [-1, -1]];
        var count = 0;
        for(var i = 0; i < 8 && count < 4; i++) {
            var r = row + dirs[i][0];
            var c = column + dirs[i][1];
            if(r > -1 && r < current.length && 
               c > -1 && c < current[0].length && current[r][c]) {
                count++;
            }
        }
        return count;
    }
    
    return function(current) {
        return range(0, current.length).map(function(row) {
            return range(0, current[0].length).map(function(column) {
                return liveOrDie(current, row, column);
            });
        });
    };
}();

var draw = function(cells) {
    print('Status...\n');
    for(var row = 0; row < cells.length; row++) {
        for(var column = 0; column < cells[0].length; column++) {
            print(cells[row][column] ? '*' : '~');
        }
        print('\n');
    }
};

var isDifferent = function(current, next) {
    for(var row = 0; row < current.length; row++) {
        for(var column = 0; column < current[0].length; column++) {
            if(current[row][column] !== next[row][column]) {
                return true;
            }
        }
    }
    return false;
};

var current = [[0, 1, 0, 1, 0, 0, 0, 0, 1, 1],
               [0, 1, 0, 1, 0, 0, 0, 0, 1, 1],
               [0, 1, 0, 1, 0, 0, 0, 0, 1, 1],
               [0, 1, 1, 1, 0, 0, 1, 0, 1, 1],
               [0, 1, 1, 1, 0, 1, 0, 0, 1, 1],
               [0, 1, 0, 1, 1, 0, 0, 1, 1, 1],
               [0, 1, 0, 1, 0, 1, 0, 0, 1, 1],
               [0, 1, 0, 1, 0, 0, 1, 0, 1, 1],
               [0, 1, 0, 1, 0, 1, 0, 1, 1, 1],
               [0, 1, 0, 1, 1, 0, 0, 0, 1, 1]];

draw(current);
var next = produce(current)
while(isDifferent(current, next)) {
    current = next;
    draw(current);
    next = produce(next);
}
import Control.Monad

produce current = [[liveOrDie current row column | 
    column <- [0..columnLength - 1]] | row <- [0..rowLength - 1]]
    where rowLength = length current
          columnLength = length $ current !! 0

liveOrDie current row column = 
    if n `elem` [0, 1, 4] then 0
    else if n == 2 then current !! row !! column
         else 1
    where n = neighbors current row column
    
neighbors cells row column = increment cells 0 0
    where increment cells index count =
              if index == 8 || count == 4 then count
              else if r > -1 && r < rowLength && 
                      c > -1 && c < columnLength && cells !! r !! c /= 0 
                      then increment cells (index + 1) (count + 1)
                   else increment cells (index + 1) count
              where rowLength = length cells
                    columnLength = length $ cells !! 0
                    dirs = [[-1, 0], [-1, 1], [0, 1], [1, 1], 
                            [1, 0], [1, -1], [0, -1], [-1, -1]]
                    r = row + dirs !! index !! 0
                    c = column + dirs !! index !! 1
                    
showUntilSteady cells = do
    putStrLn "Status..."
    forM [0..rowLength - 1] (\row -> do
        forM [0..columnLength - 1] (\column -> do
            putChar (if cells !! row !! column == 0 then '~' else '*'))
        putStrLn "")
    when (next /= cells) $ do
        showUntilSteady next
    where next = produce cells
          rowLength = length cells
          columnLength = length $ cells !! 0

main = showUntilSteady [[0, 1, 0, 1, 0, 0, 0, 0, 1, 1],
                        [0, 1, 0, 1, 0, 0, 0, 0, 1, 1],
                        [0, 1, 0, 1, 0, 0, 0, 0, 1, 1],
                        [0, 1, 1, 1, 0, 0, 1, 0, 1, 1],
                        [0, 1, 1, 1, 0, 1, 0, 0, 1, 1],
                        [0, 1, 0, 1, 1, 0, 0, 1, 1, 1],
                        [0, 1, 0, 1, 0, 1, 0, 0, 1, 1],
                        [0, 1, 0, 1, 0, 0, 1, 0, 1, 1],
                        [0, 1, 0, 1, 0, 1, 0, 1, 1, 1],
                        [0, 1, 0, 1, 1, 0, 0, 0, 1, 1]]
state(Neighbors, _, 0) :- between(0, 1, Neighbors), !.
state(Neighbors, _, 0) :- between(4, 8, Neighbors), !.
state(2, Value, Value).
state(3, _, 1).

block(Blocks, Row, Column, Value) :-
    nth0(Row, Blocks, Values), nth0(Column, Values, Value).
    
upper_neighbor(Blocks, Row, Column) :-
    Row > 0, R is Row - 1,
    block(Blocks, R, Column, 1).
    
down_neighbor(Blocks, Row, Column) :-
    length(Blocks, ROWS), R is Row + 1, R < ROWS,
    block(Blocks, R, Column, 1).
    
left_neighbor(Blocks, Row, Column) :-
    Column > 0, C is Column - 1,
    block(Blocks, Row, C, 1).
    
right_neighbor(Blocks, Row, Column) :-
    nth0(0, Blocks, Values), 
    length(Values, Columns), C is Column + 1, C < Columns,
    block(Blocks, Row, C, 1).
    
left_upper_neighbor(Blocks, Row, Column) :-
    Row > 0, R is Row - 1,
    Column > 0, C is Column - 1,
    block(Blocks, R, C, 1).
    
left_down_neighbor(Blocks, Row, Column) :-
    length(Blocks, ROWS), R is Row + 1, R < ROWS,
    Column > 0, C is Column - 1,
    block(Blocks, R, C, 1).
    
right_upper_neighbor(Blocks, Row, Column) :-
    Row > 0, R is Row - 1,
    nth0(0, Blocks, Values), 
    length(Values, Columns), C is Column + 1, C < Columns,
    block(Blocks, R, C, 1).
    
right_down_neighbor(Blocks, Row, Column) :-
    length(Blocks, ROWS), R is Row + 1, R < ROWS,
    nth0(0, Blocks, Values), 
    length(Values, Columns), C is Column + 1, C < Columns,
    block(Blocks, R, C, 1).
    
truthes([P|T], N) :- P, !, truthes(T, Acc), N is Acc + 1.
truthes([_|T], Acc) :- truthes(T, Acc).
truthes([], 0).

neighbors(Blocks, Row, Column, N) :-
    truthes([
        upper_neighbor(Blocks, Row, Column),
        down_neighbor(Blocks, Row, Column),
        left_neighbor(Blocks, Row, Column),
        right_neighbor(Blocks, Row, Column),
        left_upper_neighbor(Blocks, Row, Column),
        left_down_neighbor(Blocks, Row, Column),
        right_upper_neighbor(Blocks, Row, Column),
        right_down_neighbor(Blocks, Row, Column)
    ], N).

forRow(Row, Blocks) :-
    length(Blocks, Rows), Row < Rows, !,
    forElem(Row, 0, Blocks), NR is Row + 1,
    forRow(NR, Blocks).
forRow(_, _) :- nl.    

forElem(Row, Column, Blocks) :-
    nth0(0, Blocks, Values), length(Values, Columns),
    Column < Columns, !, 
    block(Blocks, Row, Column, Value),    
    neighbors(Blocks, Row, Column, N),
    state(N, Value, ToValue),
    write(ToValue),
    NC is Column + 1,
    forElem(Row, NC, Blocks).
forElem(_, _, _) :- nl.
    
main(_) :- 
    Blocks = [
        [0, 1, 0, 1, 0, 0, 0, 0, 1, 1],
        [0, 1, 0, 1, 0, 0, 0, 0, 1, 1],
        [0, 1, 0, 1, 0, 0, 0, 0, 1, 1],
        [0, 1, 1, 1, 0, 0, 1, 0, 1, 1],
        [0, 1, 1, 1, 0, 1, 0, 0, 1, 1],
        [0, 1, 0, 1, 1, 0, 0, 1, 1, 1],
        [0, 1, 0, 1, 0, 1, 0, 0, 1, 1],
        [0, 1, 0, 1, 0, 0, 1, 0, 1, 1],
        [0, 1, 0, 1, 0, 1, 0, 1, 1, 1],
        [0, 1, 0, 1, 1, 0, 0, 0, 1, 1]
    ],
    forRow(0, Blocks).
# 我寫的玩具語言 https://github.com/JustinSDK/toy_lang

ROW = 10
COLUMN = 10

(DIRS = [
    [-1, 0], [-1, 1], [0, 1],  [1, 1],
    [1, 0],  [1, -1], [0, -1], [-1, -1]
])

def produce(current, next) {
    (iterate(0, ROW).forEach(row -> 
        iterate(0, COLUMN).forEach(column -> produceRC(row, column, current, next))
    ))
}

def produceRC(row, column, current, next) {
    switch neighbors(current, row, column) {
        case 0, 1, 4
            next.get(row).set(column, 0) 
        case 2
            next.get(row).set(column, current.get(row).get(column)) 
        case 3
            next.get(row).set(column, 1) 
    }         
}

def neighbors(current, row, column) {
    count = 0
    i = 0
    while i < 8 and count < 4 {
        r = row + DIRS.get(i).get(0)
        c = column + DIRS.get(i).get(1)
        if r > -1 and r < ROW and c > -1 and c < COLUMN and current.get(r).get(c) {
            count += 1
        }
        i += 1
    }

    return count
} 

def copy(from, to) {
    (iterate(0, ROW).forEach(row -> 
        iterate(0, COLUMN).forEach(column -> 
            copyRC(from, to, row, column)
        )
    ))
}  

def copyRC(from, to, row, column) {
    to.get(row).set(column, from.get(row).get(column))
    from.get(row).set(column, 0)
}

def isDifferent(current, next) {
    (return range(0, ROW)
              .any(row -> isDiffByRow(current.get(row), next.get(row))))
}  

def isDiffByRow(currentRow, nextRow) {
    (return range(0, COLUMN)
              .any(column -> currentRow.get(column) != nextRow.get(column)))
}

def printCells(current) {
    println('Status...')
    current.forEach(printRow)
    println()
} 

def printRow(row) {
    row.forEach(column -> print('*' if column != 0 else '~'))
    println()
} 

(current = [
    [0, 1, 0, 1, 0, 0, 0, 0, 1, 1],
    [0, 1, 0, 1, 0, 0, 0, 0, 1, 1],
    [0, 1, 0, 1, 0, 0, 0, 0, 1, 1],
    [0, 1, 1, 1, 0, 0, 1, 0, 1, 1],
    [0, 1, 1, 1, 0, 1, 0, 0, 1, 1],
    [0, 1, 0, 1, 1, 0, 0, 1, 1, 1],
    [0, 1, 0, 1, 0, 1, 0, 0, 1, 1],
    [0, 1, 0, 1, 0, 0, 1, 0, 1, 1],
    [0, 1, 0, 1, 0, 1, 0, 1, 1, 1],
    [0, 1, 0, 1, 1, 0, 0, 0, 1, 1]
])

next = range(0, ROW).map(_ -> range(0, COLUMN).fill(0))

printCells(current)
produce(current, next)

while isDifferent(current, next) {
    copy(next, current)
    printCells(current)
    produce(current, next)
}

分享到 LinkedIn 分享到 Facebook 分享到 Twitter