自动机(Automation)
一维细胞自动机 作为Windows编程的一个简单例子,下面展示了一个一维细胞自动机程序。 ca1d.c
// 一维细胞自动机:
// 在时刻t的内部状态(左,中央,右)111 110 101 100 011 010 001 000
// 在时刻t+1的中央细胞的内部状态 0 1 1 0 0 1 1 0
#include <windows.h>
#define WND_CLASS_NAME TEXT("Cell Automaton")
#define IMAX 300 // 行方向(Y轴)
#define JMAX 300 // 列方向(X轴)
int cell[IMAX][JMAX];
int rule[] = { 0, 1, 1, 0, 0, 1, 1, 0 }; // 从低位开始
void paint(HWND hwnd){
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
int i, j;
for (i = 0; i < IMAX; i++){
for (j = 0; j < JMAX; j++){
if (cell[i][j] == 1) {
SetPixel(hdc, j, i, RGB(0,0,0));
} // 在坐标(i,j)画一个1x1的正方形
}
}
EndPaint(hwnd, &ps);
}
void init() {
int i, j;
// 初始化
for (j = 0; j < JMAX; j++) {
cell[0][j] = rand() % 2;
}
// 计算
for (i = 1; i < IMAX; i++){ // 第1行~第(IMAX-1)行
for (j = 0; j < JMAX; j++){ // 第0列~第(JMAX-1)列
int length = JMAX;
int before = i - 1; // 前一行
// 确定两边
int l = (j + length - 1) % length; // left
int c = j; // center
int r = (j + 1) % length; // right
// 计算代码 code = 0 ~ 7
int code = cell[before][l] * 4
+ cell[before][c] * 2
+ cell[before][r] * 1;
// 设置细胞状态
cell[i][j] = rule[code];
}
}
}
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
switch (uMsg) {
case WM_CREATE:
init();
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_PAINT:
paint(hwnd);
break;
default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
return 0;
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
HWND hWnd;
WNDCLASS wcl;
wcl.hInstance = hInstance;
wcl.lpszClassName = WND_CLASS_NAME;
wcl.lpfnWndProc = WindowProc;
wcl.style = 0;
wcl.hIcon = NULL;
wcl.hCursor = LoadCursor(NULL, IDC_ARROW);
wcl.lpszMenuName = NULL;
wcl.cbClsExtra = 0;
wcl.cbWndExtra = 0;
wcl.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
if (!RegisterClass(&wcl)) return FALSE;
hWnd = CreateWindow(
WND_CLASS_NAME, // LPCTSTR lpClassName 注册的类名
TEXT("一维细胞自动机"), // LPCTSTR lpWindowName 窗口名
WS_OVERLAPPEDWINDOW|WS_VISIBLE, // DWORD dwStyle 窗口样式
20, 20, // int x, y 窗口的横向、纵向位置
305, 325, // int nWidth, nHeight 窗口的宽度、高度
NULL, // HWND hWndParent 父窗口或所有者窗口的句柄
NULL, // HMENU hMenu 菜单句柄或子标识符
hInstance, // HINSTANCE hInstance 应用程序实例的句柄
NULL); // LPVOID lpParam 窗口创建数据
if(!hWnd) return FALSE;
MSG msg;
while (GetMessage(&msg,NULL,0,0) > 0){
TranslateMessage(&msg);
DispatchMessage(&msg);
}
return msg.wParam;
}
此程序的执行结果如下图所示。
使用winlib库后,可以将ca1d.c改写如下。创建窗口时,类名和窗口名是相同的,所以用SetWindowText函数来设置窗口名。
ca1d2.c
// 一维细胞自动机:
// 时刻t的内部状态(左,中心,右) 111 110 101 100 011 010 001 000
// 时刻t+1的中心细胞的内部状态 0 1 1 0 0 1 1 0
// tcc ca1d2.c ../lib/winlib.o
#include <windows.h>
HWND crWindow(HINSTANCE, LPSTR, WNDPROC, int, int, int, int);
#define IMAX 300 // 行方向(Y轴)
#define JMAX 300 // 列方向(X轴)
int cell[IMAX][JMAX];
int rule[] = { 0, 1, 1, 0, 0, 1, 1, 0 }; // 从低位开始
void paint(HWND hwnd){
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
int i, j;
for (i = 0; i < IMAX; i++){
for (j = 0; j < JMAX; j++){
if (cell[i][j] == 1) {
SetPixel(hdc, j, i, RGB(0,0,0));
} // 在坐标(i,j)处画一个左上角为1,1的正方形
}
}
EndPaint(hwnd, &ps);
}
void init() {
int i, j;
// 初始化
for (j = 0; j < JMAX; j++) {
cell[0][j] = rand() % 2;
}
// 计算
for (i = 1; i < IMAX; i++){ // 第1行~第(IMAX-1)行
for (j = 0; j < JMAX; j++){ // 第0列~第(JMAX-1)列
int length = JMAX;
int before = i - 1; // 上一行
// 确定两侧
int l = (j + length - 1) % length; // left
int c = j; // center
int r = (j + 1) % length; // right
// 计算代码 code = 0 ~ 7
int code = cell[before][l] * 4
+ cell[before][c] * 2
+ cell[before][r] * 1;
// 设置细胞状态
cell[i][j] = rule[code];
}
}
}
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
switch (uMsg) {
case WM_CREATE:
init();
break;
case WM_DESTROY:
PostQuitMessage(0);
break;
case WM_PAINT:
paint(hwnd);
break;
default:
return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
return 0;
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
HWND hWnd = crWindow(hInstance, TEXT("Cell Automaton"), WindowProc, 20, 20, 305, 325);
if (!hWnd) return FALSE;
SetWindowText(hWnd, TEXT("一维细胞自动机"));
return msgLoop();
}
同样,以下展示了被称为规则30、90、110的著名一维细胞自动机程序及其执行结果。
rule30.c
/** 一维细胞自动机:规则 30
* 在时刻t的内部状态(左,中央,右) 111 110 101 100 011 010 001 000
* 时刻t+1中央细胞的内部状态 0 0 0 1 1 1 1 0
* 二进制的(0001 1110)在十进制中为30,因此称为规则30。
*/
#include <windows.h>
HWND crWindow(HINSTANCE, LPSTR, WNDPROC, int, int, int, int);
#define IMAX 200 // 行方向(Y轴)
#define JMAX 200 // 列方向(X轴)
int cell[IMAX][JMAX];
int rule[] = { 0, 1, 1, 1, 1, 0, 0, 0 }; // 从低位开始
void paint(HWND hwnd){
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
int i, j;
for (i = 0; i < IMAX; i++){
for (j = 0; j < JMAX; j++){
if (cell[i][j] == 1) {
SetPixel(hdc, j, i, RGB(0,0,0));
} // 在坐标(i,j)处画一个左上角为1x1的正方形
}
}
EndPaint(hwnd, &ps);
}
void init() {
int i, j;
// 初始化
cell[0][JMAX/2] = 1; // 仅第0行中间为1
// 计算
for (i = 1; i < IMAX; i++){ // 第1行~第(IMAX-1)行
for (j = 0; j < JMAX; j++){ // 第0列~第(JMAX-1)列
int length = JMAX;
int before = i - 1; // 上一行
// 确定两侧
int l = (j + length - 1) % length; // left
int c = j; // center
int r = (j + 1) % length; // right
// 计算代码 code = 0 ~ 7
int code = cell[before][l] * 4
+ cell[before][c] * 2
+ cell[before][r] * 1;
// 设置细胞状态
cell[i][j] = rule[code];
}
}
}
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
switch (uMsg) {
case WM_CREATE: init(); break;
case WM_DESTROY: PostQuitMessage(0); break;
case WM_PAINT: paint(hwnd); break;
default: return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
return 0;
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
HWND hWnd = crWindow(hInstance, TEXT("Rule30"), WindowProc, 20, 20, 205, 225);
if (!hWnd) return FALSE;
return msgLoop();
}
rule90.c
/** 一维细胞自动机:规则 90
* 时刻t的内部状态(左,中,右) 111 110 101 100 011 010 001 000
* 时刻t+1的中央细胞的内部状态 0 1 0 1 1 0 1 0
* 二进制的(0101 1010)转换为十进制等于90,因此称为规则90。
*/
#include <windows.h>
HWND crWindow(HINSTANCE, LPSTR, WNDPROC, int, int, int, int);
#define IMAX 200 // 行方向(Y轴)
#define JMAX 200 // 列方向(X轴)
int cell[IMAX][JMAX];
int rule[] = { 0, 1, 0, 1, 1, 0, 1, 0 }; // 从低位开始
void paint(HWND hwnd){
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
int i, j;
for (i = 0; i < IMAX; i++){
for (j = 0; j < JMAX; j++){
if (cell[i][j] == 1) {
SetPixel(hdc, j, i, RGB(0,0,0));
} // 在坐标(i,j)处绘制一个1x1的正方形
}
}
EndPaint(hwnd, &ps);
}
void init() {
int i, j;
// 初始化
cell[0][JMAX/2] = 1; // 仅第0行中间为1
// 计算
for (i = 1; i < IMAX; i++){ // 第1行~第(IMAX-1)行
for (j = 0; j < JMAX; j++){ // 第0列~第(JMAX-1)列
int length = JMAX;
int before = i - 1; // 上一行
// 确定左右两侧
int l = (j + length - 1) % length; // left
int c = j; // center
int r = (j + 1) % length; // right
// 计算代码 code = 0 ~ 7
int code = cell[before][l] * 4
+ cell[before][c] * 2
+ cell[before][r] * 1;
// 设置细胞状态
cell[i][j] = rule[code];
}
}
}
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
switch (uMsg) {
case WM_CREATE: init(); break;
case WM_DESTROY: PostQuitMessage(0); break;
case WM_PAINT: paint(hwnd); break;
default: return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
return 0;
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
HWND hWnd = crWindow(hInstance, TEXT("Rule90"), WindowProc, 20, 20, 205, 225);
if (!hWnd) return FALSE;
return msgLoop();
}
rule110.c
/** 一维细胞自动机:规则 110
* 在时刻t的内部状态(左,中心,右) 111 110 101 100 011 010 001 000
* 在时刻t+1的中心单元的内部状态 0 1 1 0 1 1 1 0
* 二进制的(01101110)转换为十进制等于110,因此称之为规则110。
*/
#include <windows.h>
HWND crWindow(HINSTANCE, LPSTR, WNDPROC, int, int, int, int);
#define IMAX 200 // 行方向(Y轴)
#define JMAX 200 // 列方向(X轴)
int cell[IMAX][JMAX];
int rule[] = { 0, 1, 1, 0, 1, 1, 1, 0 }; // 从低位开始
void paint(HWND hwnd){
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
int i, j;
for (i = 0; i < IMAX; i++){
for (j = 0; j < JMAX; j++){
if (cell[i][j] == 1) {
SetPixel(hdc, j, i, RGB(0,0,0));
} // 在坐标(i,j)处画一个左上角为1x1的正方形
}
}
EndPaint(hwnd, &ps);
}
void init() {
int i, j;
// 初始化
cell[0][JMAX/2] = 1; // 仅第0行的中心为1
// 计算
for (i = 1; i < IMAX; i++){ // 第1行~第(IMAX-1)行
for (j = 0; j < JMAX; j++){ // 第0列~第(JMAX-1)列
int length = JMAX;
int before = i - 1; // 上一行
// 确定两侧
int l = (j + length - 1) % length; // left
int c = j; // center
int r = (j + 1) % length; // right
// 计算代码 code = 0 ~ 7
int code = cell[before][l] * 4
+ cell[before][c] * 2
+ cell[before][r] * 1;
// 设置单元状态
cell[i][j] = rule[code];
}
}
}
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
switch (uMsg) {
case WM_CREATE: init(); break;
case WM_DESTROY: PostQuitMessage(0); break;
case WM_PAINT: paint(hwnd); break;
default: return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
return 0;
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
HWND hWnd = crWindow(hInstance, TEXT("Rule110"), WindowProc, 20, 20, 205, 225);
if (!hWnd) return FALSE;
return msgLoop();
}
通过菜单选择规则 前一节提到的程序算法相同,只是数据不同,因此非常容易统一。下面展示了程序和执行示例。
cellautomata.c
/** 一维细胞自动机:规则110的情况
* 时间t时的内部状态(左,中央,右) 111 110 101 100 011 010 001 000
* 时间t+1时中央的细胞的内部状态 0 1 1 1 0 1 1 0
* 二进制的(0110 1110)转换为十进制后为110,因此称之为规则110。
*/
#include <windows.h>
HWND crWindow(HINSTANCE, LPSTR, WNDPROC, int, int, int, int);
#define IMAX 200 // 行方向(Y轴)
#define JMAX 200 // 列方向(X轴)
int cell[IMAX][JMAX];
int rules[4][8] = {
{ 0, 1, 1, 0, 0, 1, 1, 0 }, // 规则102
{ 0, 1, 1, 1, 1, 0, 0, 0 }, // 规则30
{ 0, 1, 0, 1, 1, 0, 1, 0 }, // 规则90
{ 0, 1, 1, 1, 0, 1, 1, 0 } // 规则110
};
char *name[] = { "规则102", "规则30", "规则90", "规则110" };
int nRule = 0;
void paint(HWND hwnd){
PAINTSTRUCT ps;
HDC hdc = BeginPaint(hwnd, &ps);
int i, j;
char buf[128];
wsprintf(buf, "%s", name[nRule]);
SetWindowText(hwnd, buf);
// 初始化
cell[0][JMAX/2] = 1; // 第0行的中央设为1
// 计算
for (i = 1; i < IMAX; i++){ // 第1行~第(IMAX-1)行
for (j = 0; j < JMAX; j++){ // 第0列~第(JMAX-1)列
int length = JMAX;
int before = i - 1; // 前一行
// 确定两侧
int l = (j + length - 1) % length; // left
int c = j; // center
int r = (j + 1) % length; // right
// 计算代码 code = 0 ~ 7
int code = cell[before][l] * 4
+ cell[before][c] * 2
+ cell[before][r] * 1;
// 设置细胞状态
cell[i][j] = rules[nRule][code];
}
}
for (i = 0; i < IMAX; i++){
for (j = 0; j < JMAX; j++){
if (cell[i][j] == 1) {
SetPixel(hdc, j, i, RGB(0,0,0));
} // 在坐标(i,j)处绘制一个左上角为1x1大小的正方形
}
}
EndPaint(hwnd, &ps);
}
void init(HWND hWnd) {
HMENU hMenu = CreateMenu();
HMENU hSubMenu = CreatePopupMenu();
int i;
for (i = 0; i < 4; i++)
AppendMenu(hSubMenu, MFT_STRING, i, name[i]);
AppendMenu(hMenu, MF_POPUP | MFT_STRING, (UINT)hSubMenu, "规则");
SetMenu(hWnd, hMenu);
}
void command(HWND hWnd, UINT cmd) {
nRule = cmd;
InvalidateRect(hWnd, NULL, TRUE);
UpdateWindow(hWnd);
}
LRESULT CALLBACK WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam) {
switch (uMsg) {
case WM_CREATE: init(hwnd); break;
case WM_COMMAND: command(hwnd, wParam); break;
case WM_DESTROY: PostQuitMessage(0); break;
case WM_PAINT: paint(hwnd); break;
default: return DefWindowProc(hwnd, uMsg, wParam, lParam);
}
return 0;
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) {
HWND hWnd = crWindow(hInstance, TEXT("Cell Automaton"), WindowProc, 20, 20, 210, 250);
if (!hWnd) return FALSE;
return msgLoop();
}