细胞自动机! 用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); } }
效果如下
现在我们要写两个函数, 用格子的坐标就能给格子填充黑色或者白色. 或者说可以对指定格子设定生死.
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; }
哈哈,看起来不错
看起来很炫啊
这个有什么用
没啥用..