首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >C (3模式)下康威的生命博弈

C (3模式)下康威的生命博弈
EN

Code Review用户
提问于 2018-12-04 02:58:41
回答 2查看 10.9K关注 0票数 4

这是我的生活游戏程序,我自己创建的,没有参考或同行评审,所以我需要一些评论和评论。我创建了3种模式:

用户模式:程序要求用户输入在板上放置活生物的坐标(x,y),直到用户输入一个负坐标。

自动模式:程序随机填充的生物,每个细胞有一个十分之一的机会包含一个生物。

混合模式:在自动填充板后,允许用户修改它。

我怎样才能改善这一点?我还在做一个检测“稳定”状态的函数。如果有人知道,请建议。

代码语言:javascript
复制
/**Conway's Game of Life**/
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <conio.h>

const int WIDTH = 67;
const int HEIGHT = 47;
const int TRUE = 1;
const int FALSE = 0;

void makingboundarylines (int board[HEIGHT][WIDTH])
{
    int cols, rows;
    for (cols =0; cols <WIDTH; cols++)
    {
        board[0][cols] = '_';
        board[HEIGHT -1][cols]= '_';
    }
    for (rows =1; rows <HEIGHT; rows++)
    {
        board[rows][0] = '|';
        board[rows][WIDTH -1] = '|';
    }
}
void fillarray(int board[HEIGHT][WIDTH])
{
    int i,j;
    for(i =1; i <HEIGHT-1; i++)
        for(j=1; j <WIDTH-1; j++)
            board[i][j] = ' ';
}
void fillarrayrandomly(int board[HEIGHT][WIDTH])
{
    int i, j, num;
    srand((unsigned)time(NULL));
    for(i =1; i <HEIGHT -1; i++)
        for(j=1; j <WIDTH -1;j++)
        { 
            num = rand()%11;
            if (num == 1)
                board[i][j] = 'O';
            else board[i][j] = ' ';
        }
}
void display2Darray(int board[HEIGHT][WIDTH])
{
int rows;
int cols;
    for (rows = 0; rows <HEIGHT; rows++)
    {
        for(cols =0; cols <WIDTH; cols++)
            printf ("%3c", board[rows][cols]);
        printf ("\n");
    }
}
void countneighbors (int board[HEIGHT][WIDTH])
{
    int neighbors;
    int rows;
    int cols;
    int a, b;
    for (rows =1; rows <HEIGHT; rows++)
    {
        for (cols = 1; cols <WIDTH; cols ++)
        {
            neighbors = 0;
            if (board[rows][cols] == 'O')
            {
                for (a = -1; a <2; a++)
                {
                    for (b = -1; b <2; b++)
                        if (((rows +a) == rows) && ((cols +b) == cols))
                             neighbors = neighbors;
                        else if ((board[rows +a][cols +b] == 'O') ||(board[rows +a][cols +b] == 1) || (board[rows +a][cols +b] == 0))
                            neighbors++;
                }
                if ((neighbors == 2) || (neighbors == 3))
                    board[rows][cols] = 1; /*live*/
                else if ((neighbors < 2) || (neighbors >= 4))
                    board[rows][cols] = 0; /*die*/
            }
        }
    }
}
void checknewborns (int board[HEIGHT][WIDTH])
{
    int neighbors;
    int rows;
    int cols;
    int a, b;
    for (rows =1; rows <HEIGHT -1; rows++)
    {
        for (cols = 1; cols <WIDTH -1; cols ++)
        {
            neighbors = 0;
            if (board[rows][cols] == ' ')
            {
                for (a = -1; a <2; a++)
                {
                    for (b = -1; b <2; b++)
                        if (((rows +a) == rows) && ((cols +b) == cols))
                            neighbors = neighbors;
                        else if ((board[rows +a][cols +b] == 'O') || (board[rows +a][cols +b] == 1) || (board[rows +a][cols +b] == 0))
                            neighbors++;
                }
                if ((neighbors == 3))
                    board[rows][cols] = 2; /*newborn*/
            }
        }
    }
}
void anewgeneration (int board[HEIGHT][WIDTH])
{
    int rows;
    int cols;
    for (rows =1; rows <HEIGHT -1; rows++)
    {
        for (cols = 1; cols <WIDTH -1; cols ++)
        {
            if (board[rows][cols] == 1)
                board[rows][cols] = 'O';
            else if (board[rows][cols] == 2)
                board[rows][cols] = 'O';
            else if (board[rows][cols] == 0)
                board[rows][cols] = ' ';
        }
    }
}  
int checkforexistence (int board[HEIGHT][WIDTH])
{
    int rows;
    int cols;
    int creatures =0;
    for (rows =1; rows <HEIGHT -1; rows++)
    {
        for (cols = 1; cols <WIDTH -1; cols ++)
        {
            if (board[rows][cols] == 'O')
                creatures++;
        }  
    }
    if (creatures == 0)
        return TRUE; /*all creatures died*/
    else return FALSE; /*there are still living creatures*/
}
void playgame (int board[HEIGHT][WIDTH], int numgeneration)
{
    int i, c, check;
    for (i = 1; (i <= numgeneration) && (c != 32); i++)
    {
        system("cls");
        countneighbors(board);
        checknewborns(board);
        anewgeneration(board);
        printf ("Generation: %i\n", i);
        printf ("Hit enter to move to the next generation\n");
        display2Darray(board);
        if(kbhit())
        {
            c = getch();
            if(c == 32)
                break;
        }
        check = checkforexistence(board);
        if ((check= checkforexistence(board)) ==TRUE)
        {
            printf ("\nALL CREATURES HAVE DIED: GAME OVER");
            c = 32;
        }
    }
}
int returnnumber(int anumber)
{
    if  ((anumber >50) || (anumber ==0))
    { 
        printf ("ERROR! The number must be between 1 and 50\n");
        printf ("Please enter a valid number!: ");
        scanf ("%i", &anumber);
        returnnumber(anumber);
    }
    else
        return anumber;
}
void entercoordinates (int board[HEIGHT][WIDTH])
{
    int rows = 1;
    int cols =1;
    while ((rows >0) && (cols >0))
    {
        printf ("\nPlease enter x coordinate(a number from 1 to 30): ");
        scanf("%i", &cols);
        cols = returnnumber(cols);
        if (cols >0)
        {
            printf ("Please enter y coordinate(a number from 1 to 50): ");
            scanf("%i", &rows);
            rows = returnnumber(rows);
            if ((rows >0) && (cols >0))
                board[rows][cols] = 'O';
        }
    }
}
void creatingpatterntypes(int board[HEIGHT][WIDTH])
{
    int type;
    printf ("\nEnter <1> for creating  a BOX");
    printf ("\nEnter <2> for creating  a BEEHIVE");
    printf ("\nEnter <3> for creating  a TOAD");
    printf ("\nEnter <4> for creating  a SHIP");
    printf ("\nEnter <5> for creating  a GLIDER");
    printf ("\nEnter <6> for creating  a QUEEN BEE SHUTTLE");
    printf ("\nEnter <7> for creating  a PULSAR");
    printf ("\nEnter <8> for creating  a BLINKER");
    printf ("\nEnter <9> for creating  a PENTADECATHLON\n");
    scanf ("%i", &type);

if (type == 1)
{
    board[10][10] = 'O';
    board[10][11] = 'O';
    board[11][10] = 'O';
    board[11][11] = 'O';
}
else if (type == 9)
{
    board[15][10] = 'O';
    board[15][11] = 'O';
    board[15][12] = 'O';
    board[15][13] = 'O';
    board[15][14] = 'O';
    board[15][15] = 'O';
    board[15][16] = 'O';
    board[15][17] = 'O';
    board[15][18] = 'O';
    board[15][19] = 'O';
}
else if (type == 5)
{
    board[28][3] = 'O';
    board[27][4] = 'O';
    board[26][4] = 'O';
    board[27][5] = 'O';
    board[28][5] = 'O';
}
else if (type == 3)
{
    board[18][12] = 'O';
    board[18][13] = 'O';
    board[18][14] = 'O';
    board[19][11] = 'O';
    board[19][12] = 'O';
    board[19][13] = 'O';
}
else if (type == 6)
{
    board[20][28] = 'O';
    board[20][29] = 'O';
    board[21][30] = 'O';
    board[22][31] = 'O';
    board[23][31] = 'O';
    board[24][31] = 'O';
    board[25][30] = 'O';
    board[26][29] = 'O';
    board[26][28] = 'O';
}
else if (type == 7)
{
    board[12][14] = 'O';
    board[13][13] = 'O';
    board[13][14] = 'O';
    board[13][15] = 'O';
    board[14][13] = 'O';
    board[14][15] = 'O';
    board[15][13] = 'O';
    board[15][14] = 'O';
    board[15][15] = 'O';
    board[16][14] = 'O';
}
else if (type == 8)
{
    board[12][12] = 'O';
    board[12][13] = 'O';
    board[12][14] = 'O';
}
else if (type == 4)
{
    board[12][12] = 'O';
    board[12][13] = 'O';
    board[13][12] = 'O';
    board[14][13] = 'O';
    board[14][14] = 'O';
    board[13][14] = 'O';
}
else if (type ==2)
{
    board[12][14] = 'O';
    board[13][13] = 'O';
    board[13][15] = 'O';
    board[14][13] = 'O';
    board[15][14] = 'O';
    board[14][15] = 'O';
    }
}
 void usermode(int board[HEIGHT][WIDTH])
{
    int i, generation, c, choice;
    printf ("\nDo you want to insert a particular pattern type? <1> for yes and <0> for no: ");
    scanf ("%i", &choice);
    if (choice == 1)
    {
        creatingpatterntypes(board);
        display2Darray(board);
    }
    else
    {
        entercoordinates(board);
        display2Darray(board);
    }

    printf ("\nPlease select a number of generation: ");
    scanf ("%i", &generation);

    playgame(board, generation);
}
void automaticmode(int board[HEIGHT][WIDTH])
{
    int i, generation, c;
    fillarrayrandomly(board);
    display2Darray(board);
    printf ("\nPlease select a number of generation: ");
    scanf ("%i", &generation);

    playgame(board, generation);
}
void hybridmode (int board[HEIGHT][WIDTH])
{
    int i, generation, c;
    fillarrayrandomly(board);
    display2Darray(board);
    entercoordinates(board);
    system("cls");
    display2Darray(board);

    printf ("\nPlease select a number of generation: ");
    scanf ("%i", &generation);

    playgame(board, generation);
}

int main (void)
{
    //printf ("hello. world\n");
    int board[HEIGHT][WIDTH], mode;
    makingboundarylines(board);
    fillarray(board);

    printf ("CONWAY'S GAME OF LIFE\n\n");
    printf ("Please hit space to terminate the program, hit enter to move to the next generation\n");
    printf ("Please enter <1> for User Mode, <2> for Automatic Mode and <3> for Hybrid Mode: ");
    scanf ("%i", &mode);

if (mode == 1)
    usermode(board);
else if (mode == 2)
    automaticmode(board);
else if (mode ==3)
    hybridmode(board);

return 0;
}
EN

回答 2

Code Review用户

回答已采纳

发布于 2018-12-04 07:30:26

我将继续@chux停下来的地方,而不是重新处理已经提出的任何问题。

一般来说,您的函数名是相当具有描述性的。描述函数名使得您不需要那么多文档--让代码文档本身。然而,在这一职能方面却缺乏:

代码语言:javascript
复制
int checkforexistence (int board[HEIGHT][WIDTH])
{
    //...
    if (creatures == 0)
        return TRUE; /*all creatures died*/
    else return FALSE; /*there are still living creatures*/
}

当我调用这个函数:if (checkfirexistence(board)) ...时,不清楚我到底在测试什么。如果没有这两个注释,这个函数的TRUE/FALSE输出是没有意义的,但是当我调用该函数时,我看不到那些注释,所以我需要找到这个函数来找出它做了什么。我建议您将此函数命名为areallcreaturesdead()。现在,我们立即清楚了TRUE返回值的含义。

我还建议您在这些长名称中使用骆驼格,因为(至少对我来说)可以更容易地阅读它们。checkForExistenceareAllCreaturesDead等。

playgame()中,您有:

代码语言:javascript
复制
for (i = 1; (i <= numgeneration) && (c != 32); i++)
{
    //...
    check = checkforexistence(board);
    if ((check= checkforexistence(board)) ==TRUE)
    {
        printf ("\nALL CREATURES HAVE DIED: GAME OVER");
        c = 32;
    }
}

这里有一个冗余行、一个冗余赋值和一个冗余比较(if (checkforexistence(board))就足够了)。如果是TRUE,则打印一条消息,并设置c,然后继续循环。目前还不清楚为什么循环还在继续。要理解将c设置为32会破坏循环,就必须查看循环条件,它有两个不同的比较。为什么不使用break语句显式地中断循环呢?在相同的循环中,您也会更早地这样做:

代码语言:javascript
复制
for (i = 1; i <= numgeneration; i++)           // simple for loop, easy to read
{
    //...
    if (areAllCreaturesDead(board))            // simple test, obvious what it means
    {
        printf ("\nALL CREATURES HAVE DIED: GAME OVER");
        break;                                 // obvious break from the loop
    }
}

这里的关键字是“明显的”。您所做的一切都应该是显而易见的,这样您就不需要在代码中添加注释来解释它所做的事情,这样您就可以快速地阅读代码来理解结构和逻辑,这样but就不会隐藏,而是会被迫在公开的地方捕获它们。

下一个职能也相当不明确:

代码语言:javascript
复制
int returnnumber(int anumber)
{
    if  ((anumber >50) || (anumber ==0))
    { 
        printf ("ERROR! The number must be between 1 and 50\n");
        printf ("Please enter a valid number!: ");
        scanf ("%i", &anumber);
        returnnumber(anumber);
    }
    else
        return anumber;
}

它的名称如下:

代码语言:javascript
复制
printf ("\nPlease enter x coordinate(a number from 1 to 30): ");
scanf("%i", &cols);
cols = returnnumber(cols);

因此,您需要在这里使用scanf两次。我建议一个函数从用户那里获取一个数字并返回它:

代码语言:javascript
复制
int getNumber(const char* prompt) {
   int number = 0;
   printf(prompt);
   do {
      if (scanf("%i", &number) != 1) {
         exit(EXIT_FAILURE);
      }
   } while ((number > 50) || (number == 0));
   return number;
}

你这样称呼它:

代码语言:javascript
复制
cols = getNumber("\nEnter x coordinate (a number from 1 to 30, negative to finish): ");

在函数creatingpatterntypes()中,有一个很长的if ... else if ... else if ...列表。这是switch语句的完美候选:

代码语言:javascript
复制
switch (type) {
   case 1:
      //...
      break;
   case 2:
      //...
      break;
   case //... 
}

请注意,对类型代码进行排序也是有意义的,因此,如果需要修改或修复特定代码,很容易找到它。

main中也是如此,您可以打开一个mode值。

main中,在一行中定义了两个变量:

代码语言:javascript
复制
int board[HEIGHT][WIDTH], mode;

我认为在不同的行中将它们分开会更容易理解。特别是由于定义的两个变量类型并不完全相同,一个是int数组,一个是int。这样做更好:

代码语言:javascript
复制
int board[HEIGHT][WIDTH];
int mode;

您还可以使用typedef来避免在所有这些函数调用中重复使用int [HEIGHT][WIDTH]位:

代码语言:javascript
复制
typedef int Board[HEIGHT][WIDTH];
//...
Board board;
票数 2
EN

Code Review用户

发布于 2018-12-04 06:03:04

我怎样才能改善这一点?

格式好,文档

每个函数的注释对于描述函数的意图都有很大的帮助。

避免过多或太少文档的陷阱。医生应该传达功能的总体目标,等等。

避免非标准包含文件

#include <conio.h>调用一个非标准的C库包含文件。为了获得最大的可移植性,请避免这样做,然后编写围绕kbhit()的代码,等等。

无意义分配

为什么neighbors = neighbors;;

未使用参数

uint8_t ctx_id等不在static bool ctx_get_ip2(const char *qiact_reply, uint8_t ctx_id, struct ip_addr *ctx_ip), getch()中使用。为什么要把它传过去?

icusermode()automaticmode()中也是如此。

所有这些都是在我的编译器.

上启用大量警告的情况下检测到的。

节省时间!!

启用所有编译警告并生成警告自由编译--或者使用更好的编译器。

代码语言:javascript
复制
gcc -std=c11 -O3 -g3 -pedantic -Wall -Wextra -Wconversion

不可维护代码

认识到这60行代码是弱的。不知何故,测试应该更简洁地编码。

代码语言:javascript
复制
 if (type == 1) {
    board[10][10] = 'O';
    board[10][11] = 'O';
    .... // 50+ lines
    board[14][13] = 'O';
    board[15][14] = 'O';
    board[14][15] = 'O';

提示:任何时候代码都是重复的,请考虑一些循环。

避免失败的scanf()

下面是赤裸的。如果扫描失败,其余的代码就会被怀疑。

代码语言:javascript
复制
scanf("%i", &type);

相反,使用一些代码验证成功。

代码语言:javascript
复制
if (scanf("%i", &type) != 1) {
  puts("I'm going home now due to bad input.")'
  exit(EXIT_FAILURE);
}

你真的想要"%i"而不是"%d"吗?

使用#define是很好的

它很好地避免了神奇数字的陷阱。

srand()

通常,应该通过任意调用代码来调用srand() (通常只调用一次),而不是由助手函数(如fillarrayrandomly() )调用。

将代码与测试

分离

需要从测试代码和“生命游戏”代码中定义一个更亮的线条。

代码重用是编码过程中的首要任务。把其他人的“生命游戏”弄清楚。

避免输出依赖项

小调:下面假设输出是行缓冲的。高可移植性并不是假设。阅读前冲洗一下。

代码语言:javascript
复制
printf ("\nEnter <9> for creating  a PENTADECATHLON\n");
fflush(stdout);
scanf ("%i", &type);

\n在结尾处

与其在输出时使用'\n',不如在creatingpatterntypes()的末尾使用它。

没有理由不这么做

代码语言:javascript
复制
printf ("\n" \ 
  "Enter <1> for creating  a BOX\n"
  "Enter <2> for creating  a BEEHIVE\n"
  ...
  "Enter <9> for creating  a PENTADECATHLON\n");

const

void display2Darray(int board[HEIGHT][WIDTH])这样的函数应该是void display2Darray(const int board[HEIGHT][WIDTH]),以允许更大的应用程序、一些优化和更清晰的意图。

总体

正是这些小事情抑制了对代码的更大方面的良好审查。换句话说,我希望这些小问题不那么普遍,这样我们就可以把重点放在更大的问题上。

票数 3
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/208973

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档