我正在尝试使用基本的元胞自动机概念进行简单的2D沙子模拟(有关更多信息,请观看:https://www.youtube.com/watch?v=VLZjd_Y1gJ8)并将其显示在控制台中,但由于某些原因,随机粒子出现在矩阵的底线附近,如果您对矩阵进行最轻微的修改,就会出现很多混乱(例如,如果您在中间水平绘制一条线,或者在较低的位置绘制一个随机点,则会出现完全不同的情况(,我相信这可能是由于我的边界检查if(grid[y][x] <= sizeY && grid[y][x] <=sizeX)
中的错误,但我不确定。
代码:
#include <stdio.h>
#include <stdlib.h>
#ifdef __unix__
# include <unistd.h>
#elif defined _WIN32
# include <windows.h>
#define sleep(x) Sleep(1000 * (x))
#endif
int sizeX = 20;
int sizeY = 20;
int alive = 1;
int dead = 0;
int grid[20][20] = {{0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1}};
int drawScreen(int x, int y) {
system("cls"); // Clear screen
for (x = 0; x <= sizeX; x++) {
printf("n");
for (y = 0; y <= sizeY; y++) {
if (grid[x][y] == alive) {
printf("@");
} else {
printf(" ");
}
}
}
}
int sand(int grid[sizeX][sizeY], int x, int y) {
if (grid[y + 1][x] == dead) {
grid[y][x] = dead;
grid[y + 1][x] = alive;
} else if (grid[y + 1][x - 1] == dead) {
grid[y][x] = dead;
grid[y + 1][x - 1] = alive;
} else if (grid[y + 1][x + 1] == dead) {
grid[y][x] = dead;
grid[y + 1][x + 1] = alive;
}
}
int main(void) {
int x, y, iterations;
for (iterations = 0; iterations < 1000; iterations++) {
for (y = sizeY - 1; y >= 0; y--) {
for (x = sizeX - 1; x >= 0; x--) {
if (grid[y][x] == alive) {
if (grid[y + 1][x] <= sizeY && grid[y][x + 1] <= sizeX) { // Bounds
sand(grid, x, y);
}
}
}
}
drawScreen(x, y);
}
}
您的问题归结为未定义的行为超出了读取数组内存的范围。
数组是零索引的,所以循环应该运行x < sizeX
,而不是x <= sizeX
。
if (grid[y + 1][x] <= size_y && grid[y][x + 1] <= size_x) { // Bounds
不是一个好的边界检查,因为它测试的是grid
值,而不是y
和x
。您可能打算测试y < size_y - 1
和x < size_x - 1
。
现在,边界检查应该是精确的。如果一个单元格在右侧,我们仍然应该将其向下或向左移动,但跳过x + 1
向右检查/移动。其他边界也是如此。考虑到这一点,我会将边界检查烘焙到sand()
函数中(因为函数是动作,所以我会将其重命名为simulate_sand()
(。
您还在drawScreen()
函数中将grid[y][x]
切换为grid[x][y]
。由于网格的高度和宽度相同,所以不会造成任何问题,但如果它们不相同,这将是越界的。
您可以使用gcc -Wall -ggdb3 your_file.c
进行编译,然后使用valgrind ./a
来清除此UB的错误消息。-Wall
告诉我们,当drawScreen
和sand
被键入以返回int
时,它们不返回任何内容。将其更改为void
。
我要提醒您不要在函数顶部定义循环控制变量的范围,这可能会导致错误。CCD_ 22不需要CCD_ 23和CCD_。这些可以是纯本地的。
对网格进行全局范围划分也不理想,但我会保留您的初衷。
#include <stdio.h>
#include <windows.h>
const int size_x = 20;
const int size_y = 20;
const int alive = 1;
const int dead = 0;
int grid[20][20] = {
{0,0,0,0,1,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0},
{1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,1,0,1,0,0,0,0,0,0,0,0,1,1,0,0,0,0,0},
{0,0,0,0,1,1,0,0,0,0,0,0,0,0,1,0,0,0,0,0},
{0,0,0,1,1,1,0,0,0,0,0,0,0,0,1,0,0,1,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0},
{0,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,1},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0},
{1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1},
};
void draw_screen() {
system("cls");
for (int y = 0; y < size_y; y++) {
for (int x = 0; x < size_x; x++) {
putc(grid[y][x] == alive ? '@' : ' ', stdout);
}
putc('n', stdout);
}
}
void simulate_sand(const int x, const int y) {
// do nothing if dead or already at the bottom of the screen
if (grid[y][x] == dead || y >= size_y - 1) {
return;
}
// move down if empty
else if (grid[y+1][x] == dead) {
grid[y][x] = dead;
grid[y+1][x] = alive;
}
// move down and left if empty
else if (x > 0 && grid[y+1][x-1] == dead) {
grid[y][x] = dead;
grid[y+1][x-1] = alive;
}
// move down and right if empty
else if (x < size_x - 1 && grid[y+1][x+1] == dead) {
grid[y][x] = dead;
grid[y+1][x+1] = alive;
}
// else do nothing; sand is resting on sand cells beneath it
}
int main(void) {
for (int iterations = 0; iterations < 1000; iterations++) {
for (int y = size_y - 1; y >= 0; y--) {
for (int x = size_x - 1; x >= 0; x--) {
simulate_sand(x, y);
}
}
draw_screen();
}
}