康威生命遊戲
November 30, 20211970 年 10 月的《Scientific American》的數學遊戲專欄中,John Conway 發表了生命遊戲,他建議你兩種顏色棋子要夠多,以及一個夠大的棋盤,棋盤中各方格的細胞與鄰居形成九宮格,也就是每個細胞的鄰居有八個,一開始各細胞會有個初始狀態,要不就活著要不就死亡。遊戲的規則是這樣的:
- 如果細胞的鄰居為二個或三個,該細胞在下一代(generation)的狀態會是存活(穩定)。
- 若細胞的鄰居小於一個或在四個以上,下一代將會死亡(孤單死或擁擠死)。
- 死亡狀態的細胞若有三個存活狀態的鄰居,下一代將會復活(繁殖)。
康威生命遊戲是一種細胞自動機(Cellular automaton),其他有趣且可構造複雜圖樣的細胞自動機,可以進一步參考蘭頓螞蟻或者是〈漫談細胞自動機〉。
解法思路
生命遊戲的規則可簡化為某位置有無細胞:
- 某位置之細胞鄰居數為 0、1、4、5、6、7、8 時,該位置下次狀態必無細胞存活。
- 某位置之細胞鄰居數為 2 時,該位置下次狀態保持不變(有細胞就有細胞,無細胞就無細胞)。
- 某位置之細胞鄰居數為 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)
}