有没有办法只用一个数组来编写康威的生命游戏

Is there a way to program Conway's Game of Life with only one array

我已经用 C 语言编写了一个实现康威生命游戏的程序。

这是我的代码

#include <stdio.h>

int main(int argc, char *argv[]) {
    char field[20][20] = {0}; //game field
    char cpy[20][20] = {0}; //copy of game field
    int gens;   //number of generations
    scanf("%d", &gens);//input by user
    while(1){   //runs till 'break;'
        char c;
        scanf(" %c", &c); //read next char
        if(c == 'a'){//break at char 'e'
            int i,j;
            scanf("%d %d",&i,&j);//scan coordinates
            field[j][i] = 1;    //setting cell to state alive
        }else{
            break;
        }
    }
    //calculate and print generations
    for(int i = 0; i <= gens; i++){
        printf("-- Generation: %d\n",i);
        //iterating over each cell
        for(int k = 0; k < 20;k++){
            for(int l = 0; l < 20; l++){
                //print current generation
                if(field[k][l] == 1){
                    printf("%c",'#');//alive
                }else{
                    printf("%c",'.');//dead
                }
                
                //counting neighbors of field[k][l]
                int neighbors = 0;
                for(int y = -1; y < 2; y++){
                    for(int x = -1; x < 2; x++){
                        if(!(x == 0 && y == 0)){
                            if( k + y < 20 &&
                                k + y >= 0  &&
                                l + x < 20 &&
                                l + x >= 0){
                                    if(field[k+y][l+x] == 1){
                                        neighbors++;
                                    }
                                }
                        }
                    }
                }
//rules
//Any live cell with two or three live neighbours survives.
//Any dead cell with three live neighbours becomes a live cell.
//All other live cells die in the next generation. Similarly, all other dead cells stay dead.
                if(field[k][l] == 1 &&
                   (neighbors == 2 || neighbors == 3)){
                    cpy[k][l] = 1;       
                }else if(field[k][l] == 0 &&
                   neighbors == 3){
                    cpy[k][l] = 1;       
                }else if(field[k][l] == 1){
                    cpy[k][l] = 0;
                }
                
            }
            printf("\n");
        }
        
        //setting gamefield to new generation
        for(int a = 0; a < 20; a++){
            for(int b = 0; b < 20; b++){
                field[a][b] = cpy[a][b];
            }
        }
    }
    return 0;
}

用户输入看起来像这样

3
a 9 9
a 9 10
a 9 11
e

第一个数字表示要模拟多少代。之后,用户可以通过输入字符 a,然后输入单元格的 xy 坐标,将单个单元格设置为 1。判断输入结束,用户输入字符e.

如您所见,我的代码中有 2 个数组,一个代表当前一代,第二个代表下一代。

只是因为我感兴趣,有没有一种方法可以实现整个事情,这样你只需要一个数组?

而不是比较原始字段,例如field[k][l] == 1 你可以这样检查:(field[k][l] & 1) == 1。 现在你只检查每个字段的一位。

那么您可以 field[k][l] |= (1 << 1) a.k.a,而不是像您 (cpy[k][l] = 1) 那样在副本中设置值。设置第二位,同时将其与第一位的值合并。

最后,您将再次遍历所有字段,并执行 field[k][l] >>= 1。所有第二位都右移,所有第一位都被遗忘。

//setting gamefield to new generation
for(int a = 0; a < 20; a++){
    for(int b = 0; b < 20; b++){
        field[a][b] >>= 1;
    }
}

邻居的计数可以合并:每个单元将其 4 个上游邻居的计数加上 4 个下游邻居的计数 - 当您计算下行邻居时,您添加它们的邻居计数。

  --->

|  u u u
|  u * d
|  d d d 
v

所以只需要一个数组,但是第二个字段:

struct onecell {
    int alive;
    int neibs;
} **cells;

函数:

void do_row(struct onecell *row, struct onecell *rlow) {

    int j;
    int nbs;
    struct onecell *c, *n1;

    for (j = 1; j <= COLSDISP; j++) {

        c  =  &row[ j ]; 
        n1 =  &row[j+1];        // same row, to the right

        nbs = c->neibs + n1->alive;                                   // add the 4 neighbors to the right and below
        nbs += rlow[ j ].alive + rlow[j+1].alive + rlow[j-1].alive;

        if (c->alive) {

            n1->neibs++;                // tell downstream that we are alive  
            rlow[ j ].neibs++; 
            rlow[j+1].neibs++;
            rlow[j-1].neibs++;

            if (nbs < 2 || nbs > 3)
                c->alive = 0;

        } else
            if (nbs == 3)
                c->alive = 1;

        c->neibs = 0;                    // reset
    }
}

边界行必须单独处理;多线程看起来像这样:

while (loops++ < 1000) {

        for (i = CHUNK; i <= ROWSDISP; i += CHUNK)
             prepare_bottomrow(cells[i], cells[i+1]);

        #pragma omp parallel for num_threads(THREADS)

        for (i = 1; i <= ROWSDISP; i++)
            if (i % CHUNK != 0)
                do_row(cells[i], cells[i+1]);
            else
                do_bottomrow(cells[i]);

        // print_matrix();
        // usleep(40000); 

    }

...bottomrow() 完成了 do_row() 的一半工作; prepare_bottomrow()parallel之外,因为它跨了一个CHUNK。但即使没有 omp parallel 也会有最后一排。

行、线程和块大小必须是可整除的,例如200 行,4 个线程和 CHUNK=50.

这个算法很快。 rlow[j].neibsrlow[j].alive 并排。