C ++中的元胞自动机

时间:2016-12-09 06:56:55

标签: c++

我试图用C ++编写着名的生命游戏代码。这是我到目前为止所得到的。当我运行它时,它给出了一个初始的随机细胞群,但下一代似乎不起作用。我的代码出了什么问题?

#include <iostream>
#include <cstdlib>
#include <time.h>

using namespace std;

int main(){

    //Number of rows and columns   
    const char live = '*';
    const char dead = ' ';
    const int rows = 10;
    const int cols = 10;
    char life[rows][cols];
    char life1[rows][cols];

    int ans=0;


    //create initial generation randomly

    srand (time(NULL));
    int cell;


    for(int r=0; r<rows; r++){

        for(int c=0; c<cols; c++){

            cell= rand()%10;
            if(cell >= 5){
                life[r][c] = live;
                }
            else {
                life[r][c] = dead;
            } 

        }
    }


    for(int r=0; r < rows; r++){
        for(int c = 0; c<cols;c++){
            cout << life[r][c] << " ";
        }
        cout << endl;
    }

    for(int k=0; k <10;k++){
        for(int r=0; r < rows; r++){
            for(int c=0;c<cols;c++){
                if(life[r][c] == live){
                    if((c-1) >=1 && (life[r][c-1] == live))
                        ans++;
                    if(c<cols && (life[r][c+1] == live))
                        ans++;
                    if(c<cols && r<rows && (life[r+1][c+1] == live))
                        ans++;
                    if(r<rows && (life[r+1][c] == live))
                        ans++;
                    if(c<cols && c >=0 && (life[r+1][c-1] == live))
                        ans++;
                    if(r>=0 && c >=0 && (life[r-1][c-1] == live))
                        ans++;
                    if(r>=0 && (life[r-1][c]==live))
                        ans++;
                    if(r>=0 && c<cols && (life[r-1][c+1] == live))
                        ans++;
                    if(ans==2 || ans==3)
                        life[r][c]= live;
                    if(ans>3)
                        life[r][c]= dead;
                    if(ans<2)
                        life[r][c]=live;

                }
                else {
                    if( life[r][c]==dead){
                        if(c>=0 && (life[r][c-1]==dead))
                            ans++;
                        if(c<cols && (life[r][c+1]==dead))
                            ans++;
                        if(r<rows && c<cols && (life[r+1][c+1]==dead))
                            ans++;
                        if(r<rows && (life[r][c]==life[r+1][c]))
                            ans++;
                        if(r<rows && c>0 && (life[r][c]==life[r+1][c-1]))
                            ans++;
                        if(r>=0 && c>=0 && (life[r][c]==life[r-1][c-1]))
                            ans++;
                        if(r>=0 &&(life[r][c]==life[r-1][c]))
                            ans++;
                        if(r>=0 && c<cols && (life[r][c] == life[r-1][c+1]))
                            ans++;
                        if(ans==3)
                            life[r][c]=live;

                    }
                }
            }
        }

        for(int r=0; r<rows; r++){
            for(int c=0; c< cols; c++){
                life[r][c]=life1[r][c];
            }
        }

        for(int r=0; r<rows;r++){
            for(int c =0; c<cols;c++){
                cout << life[r][c] << " ";

            }
            cout<<endl;
        }
    }

    return 0;
}

2 个答案:

答案 0 :(得分:2)

让我们从使代码工作所需的最小更改开始。

  • 每次迭代重置ans:在内循环开始时(超过cols),设置ans = 0;,否则计算在3以上,永远不会返回,一切都会停留{ {1}}
  • 保持您的生成:下一代是从dead数组计算的,因此在计算生成时不要更改此数组。而是将结果写入life。在每个外部(life1)迭代结束时,结果将被复制回k
  • 正确应用规则:在life案例结束时,少于2个邻居,单元格应按规则死亡。因此,请指定if (life[r][c] == live)而不是if (ans < 2) life1[r][c] = dead;
  • 使用live完成作业:对于其他情况(life1),请添加if (life[r][c] == dead)以确保完整初始化:else
  • 使用整个数组大小(从索引if(ans==3) life1[r][c] = live; else life1[r][c] = dead开始):0省略第一个索引。替换为if ((c - 1) >= 1 && (life[r][c - 1] == live))
  • 保持在数组范围内(以if (c >= 1 && (life[r][c - 1] == live))结尾)。 size-1超出范围,用if(c < cols && (life[r][c + 1] == live))替换以保持在界限范围内。
  • 请注意所有其他if((c + 1) < cols && (life[r][c + 1] == live))语句中的数组边界,方法与两个示例中描述的方式相同。

现在继续进行代码设计:我建议你创建一个函数if,在其中放置所有逻辑来计算活的邻居单元格。然后替换主要的巨大int count_living_neighbors(char life[10][10], int rowPos, int colPos)级联:

if

关于for(int k = 0; k < 10; k++) { for(int r = 0; r < rows; r++) { for(int c = 0; c < cols; c++) { int count = count_living_neighbors(life, r, c); if(life[r][c] == live) { if(count == 2 || count == 3) life1[r][c] = live; else life1[r][c] = dead; } else { if(count == 3) life1[r][c]=live; else life1[r][c]= dead; } } } for(int r = 0; r < rows; r++) { for(int c = 0; c < cols; c++) { life[r][c] = life1[r][c]; } } for(int r = 0; r < rows; r++) { for(int c = 0; c < cols; c++) { cout << life[r][c] << " "; } cout << endl; } } 的说明:实际上最好将count_living_neighbors以及行和列大小作为参数。但它使得数组算术不太明显。

修改

将常量值移动到全局范围,char* life函数可能如下所示:

count_living_neighbors

答案 1 :(得分:2)

grek40已经给出了实际答案,但我认为给你一些关于编码风格的建议并不会有什么坏处。这个答案基于grek40的代码。

首先,如果您处理某些数据结构,这是一个明确的信号,表明您需要一个类。我也将摆脱数组(你想避免使用C ++中的数组)并使用枚举使单元格的状态更具可读性。

让我们从一个位于头文件中的界面开始。

#include <vector>
using std::vector;

enum CellState{ //replacing your char* with CellState
    dead, alive
};

class GameOfLife{

public:

    GameOfLife(const unsigned int rows, const unsigned int cols);
    virtual ~GameOfLife(){}; //can omit the virtual if no subclasses are guaranteed

    void iterate(const unsigned int iterations = 1); //can do several steps at once, one step at a time is the assumed default
    void print() const;

private:
    vector<vector<CellState> > state;

    void initialize(const unsigned int rows, const unsigned int cols); //does the randomization
    unsigned int neighbors(const unsigned int row, const unsigned int col) const;
}

该课程使您的主要功能看起来非常容易阅读:

#include "GameOfLife.h"

int main(){

    GameOfLife game(10,10);
    game.print(); //print initial configuration
    game.iterate(); //or iterate(10) or how many steps you want
    game.print(); //print configuration at the end

    return 0;
}

让我们继续这个类的实现,位于GameOfLife.cpp中。我现在将省略像iostream这样的必要包括。

让我们从简单的开始,打印:

inline char state_to_char(const CellState state){
    if(state == dead){
        return ' ';
    }
    return '*';
}

void GameOfLife::print() const{
    for(unsigned int r = 0; r < state.size(); r++){
        for(unsigned int c = 0; c < state[r].size(); c++){
            cout << state_to_char(state[r][c]) << " ";
        }
        cout << endl;
    }
}

现在进行初始化:

void GameOfLife::initialize(const unsigned int rows, const unsigned int cols){

    state.resize(rows);
    for(unsigned int r = 0, r < rows, r++){
        state[r].resize(cols);
    }

    insert your code of randomly assigning dead or alive with changed names
}

构造函数变为

GameOfLife::GameOfLife(const unsigned int rows, const unsigned int cols){
    initialize(rows, cols);
}

(创建初始化以便以后在需要时更容易引入新的构造函数)

unsigned int neighbors就像grek40设计的count_living_neighbors。

对于核心部分,迭代:

//function to resize any vector^2
template<class T>
void resize(vector<vector<T> >& target, const unsigned int dx, const unsigned int dy){
    target.resize(dx);
    for(unsigned int i=0; i<dx; i++){
        target[i].resize(dy);
    }
}

GameOfLife::iterate(const unsigned int iterations){

    unsigned int rows = state.size();
    unsigned int cells = 0;
    if(rows != 0){
        cells = state[0].size();
    }

    vector<vector<CellState> > new_state;
    resize(new_state, rows, cells);

    for(unsigned int iteration = 0; iteration < iterations; iteration++){

        for(unsigned int r = 0; r < rows; r++){
            for(unsigned int c = 0; c < cells; c++){

                unsigned int count = neighbors(r, c);
                if(state[r][c] == alive){
                    if(count == 2 || count == 3){
                        new_state[r][c] = alive;
                    }else{
                        new_state[r][c] = dead;
                }else{
                    if(count == 3){
                        new_state[r][c] = alive;
                    }else{
                        new_state[r][c] = dead;
                    }
                }
            }//end for c
        }//end for r

        state = new_state;

    }//end for iteration
}

现在,总而言之,这是比以前更多的代码,但对于它的任何部分,我们确切知道它的作用,可以轻松读取它,如果有错误,我们可以使用a轻松找到错误调试器。