细胞自动机! 用OpenGL动态展示兰顿蚂蚁
兰顿蚂蚁算法
因为作业要用OpenGL画几条线, 所以我打算画一个简单的细胞自动机. 我们选择最简单的兰顿蚂蚁自动机. 这个自动机的核心算法非常简单:
- 若蚂蚁在黑格,右转90度,将该格改为白格,向前移一步;
- 若蚂蚁在白格,左转90度,将该格改为黑格,向前移一步.
首先下载一个glut包, 然后简单配置一下VS2013, 让他可以工作. 然后我们就开始工作.
绘图函数
首先我们要开始画格子
#define SCREEN_X 800
#define SCREEN_Y 800
#define MAP_MAX_X 30
#define MAP_MAX_Y 30
void drawLine(GLint a, GLint b, GLint c, GLint d) {
glBegin(GL_LINES);
glVertex2f((float)a / SCREEN_X, (float)b / SCREEN_Y);
glVertex2f((float)c / SCREEN_X, (float)d / SCREEN_Y);
glEnd();
}
void drawMap() {
int step_x = SCREEN_X / MAP_MAX_X * 2;
int step_y = SCREEN_Y / MAP_MAX_Y * 2;
for (int i = -SCREEN_X; i <= SCREEN_X; i += step_x) {
drawLine(i, -SCREEN_Y, i, SCREEN_Y);
}
for (int i = -SCREEN_Y; i <= SCREEN_Y; i += step_y) {
drawLine(-SCREEN_X, i, SCREEN_X, i);
}
}
效果如下2
现在我们要写两个函数, 用格子的坐标就能给格子填充黑色或者白色. 或者说可以对指定格子设定生死.
void setAlife(int x, int y) {
int step_x = SCREEN_X / MAP_MAX_X * 2;
int step_y = SCREEN_Y / MAP_MAX_Y * 2;
int xa = (x + 1)*step_x - SCREEN_X + 1;
int xb = xa + step_x - 2;
int ya = SCREEN_Y - ((y + 1) * step_y);
int yb = ya - step_y + 2;
glColor3f(0.0f, 0.7f, 0.7f);
glRectf((float)xa / SCREEN_X, (float)ya / SCREEN_Y, (float)xb / SCREEN_X, (float)yb / SCREEN_Y);
}
void setDead(int x, int y) {
int step_x = SCREEN_X / MAP_MAX_X * 2;
int step_y = SCREEN_Y / MAP_MAX_Y * 2;
int xa = (x + 1)*step_x - SCREEN_X + 1;
int xb = xa + step_x - 2;
int ya = SCREEN_Y - ((y + 1) * step_y);
int yb = ya - step_y + 2;
glColor3f(1.0f, 1.0f, 1.0f);
glRectf((float)xa / SCREEN_X, (float)ya / SCREEN_Y, (float)xb / SCREEN_X, (float)yb / SCREEN_Y);
}
这样我们简单的通过 setAlive(2, 12) 就能将第3行第13个格子涂成黑色(表示有生命存在); 用setDead(2, 13) 就成将他重新变成白色(没有生命存在的格子)
编程表示蚂蚁
主要有两个任务
- 建立一个地图数组, 表示地图当中某个格子是否有生命, True代表有, False代表没有. 同时设定蚂蚁的坐标和方向;
- 使用glutTimerFunc做出动画效果.
这两个任务的代码如下:
int antdirect = 0; // 0, 1, 2, 3 表示四个方向 up right down left
int antx = MAP_MAX_X / 2;
int anty = MAP_MAX_Y / 2;
bool lifemap[MAP_MAX_X][MAP_MAX_Y];
void autoFunc(int v) {
if (lifemap[antx][anty]) {
setDead(antx, anty);
antdirect++;
} else {
setAlife(antx, anty);
antdirect--;
}
antdirect = (antdirect + 4) % 4;
if (antdirect == 0) {
antx--;
} else if (antdirect == 1) {
anty++;
} else if (antdirect == 2) {
antx++;
} else if (antdirect == 3) {
anty--;
}
if (antx >= MAP_MAX_X) antx = 0;
else if (antx < 0) antx = MAP_MAX_X - 1;
if (anty >= MAP_MAX_Y) anty = 0;
else if (anty < 0) anty = MAP_MAX_Y - 1;
glFlush();
glutTimerFunc(200, &autoFunc, 0); // 回调函数
}
void Cellular_Automaton(void) {
glClear(GL_COLOR_BUFFER_BIT);
glColor3f(0.3, 0.3, 0.3);
drawMap();
glFlush();
glutTimerFunc(200, &autoFunc, 0); // 动画开始
}
运行!
完整代码如下:
#include <iostream>
#include <gl/glut.h>
using namespace std;
#define SCREEN_X 800
#define SCREEN_Y 800
#define MAP_MAX_X 100
#define MAP_MAX_Y 100
#define SPEED 1
int antdirect = 0; // 0, 1, 2, 3 表示四个方向 up right down left
int antx = MAP_MAX_X / 2;
int anty = MAP_MAX_Y / 2;
bool lifemap[MAP_MAX_X][MAP_MAX_Y];
void drawLine(GLint a, GLint b, GLint c, GLint d) {
glBegin(GL_LINES);
glVertex2f((float)a / SCREEN_X, (float)b / SCREEN_Y);
glVertex2f((float)c / SCREEN_X, (float)d / SCREEN_Y);
glEnd();
}
void drawMap() {
glColor3f(0.0f, 0.0f, 0.0f);
int step_x = SCREEN_X / MAP_MAX_X * 2;
int step_y = SCREEN_Y / MAP_MAX_Y * 2;
for (int i = -SCREEN_X + step_x; i < SCREEN_X + step_x; i += step_x) {
drawLine(i, -SCREEN_Y + step_y - 10, i, SCREEN_Y - step_y);
}
for (int i = SCREEN_Y; i > -SCREEN_Y; i -= step_y) {
drawLine(-SCREEN_X + step_x, i, SCREEN_X - step_x + 10, i);
}
}
/*
将一个格子变成黑的.
0 <= x < MAP_MAX_X
0 <= y < MAP_MAX_Y
所以左上角第一个格子的坐标是(0, 0)
*/
void setAlife(int x, int y) {
int step_x = SCREEN_X / MAP_MAX_X * 2;
int step_y = SCREEN_Y / MAP_MAX_Y * 2;
int xa = (x + 1)*step_x - SCREEN_X + 1;
int xb = xa + step_x - 2;
int ya = SCREEN_Y - ((y + 1) * step_y);
int yb = ya - step_y + 2;
glColor3f(0.0f, 0.7f, 0.7f);
glRectf((float)xa / SCREEN_X, (float)ya / SCREEN_Y, (float)xb / SCREEN_X, (float)yb / SCREEN_Y);
lifemap[x][y] = 1;
}
void setDead(int x, int y) {
int step_x = SCREEN_X / MAP_MAX_X * 2;
int step_y = SCREEN_Y / MAP_MAX_Y * 2;
int xa = (x + 1)*step_x - SCREEN_X + 1;
int xb = xa + step_x - 2;
int ya = SCREEN_Y - ((y + 1) * step_y);
int yb = ya - step_y + 2;
glColor3f(1.0f, 1.0f, 1.0f);
glRectf((float)xa / SCREEN_X, (float)ya / SCREEN_Y, (float)xb / SCREEN_X, (float)yb / SCREEN_Y);
lifemap[x][y] = 0;
}
void autoFunc(int v) {
if (lifemap[antx][anty]) {
setDead(antx, anty);
antdirect++;
} else {
setAlife(antx, anty);
antdirect--;
}
antdirect = (antdirect + 4) % 4;
if (antdirect == 0) {
antx--;
} else if (antdirect == 1) {
anty++;
} else if (antdirect == 2) {
antx++;
} else if (antdirect == 3) {
anty--;
}
if (antx >= MAP_MAX_X) antx = 0;
else if (antx < 0) antx = MAP_MAX_X - 1;
if (anty >= MAP_MAX_Y) anty = 0;
else if (anty < 0) anty = MAP_MAX_Y - 1;
glFlush();
glutTimerFunc(SPEED, &autoFunc, 0); // 回调函数
}
void Cellular_Automaton(void) {
glClear(GL_COLOR_BUFFER_BIT);
glColor3f(0.3, 0.3, 0.3);
drawMap();
glFlush();
glutTimerFunc(0, &autoFunc, 0); // 动画开始
}
int main(int argc, char* *argv) {
glutInit(&argc, argv);
glutInitDisplayMode(GLUT_RGB | GLUT_SINGLE);
glutInitWindowSize(SCREEN_X, SCREEN_Y);
glutInitWindowPosition(200, 100);
glutCreateWindow("Jecvay 的细胞自动机");
glClearColor(1, 1, 1, 0);
glutDisplayFunc(&Cellular_Automaton);
cout << "Start" << endl;
glutMainLoop();
cout << "The end." << endl;
return 0;
}