C Игровые вопросы жизни

Я пытаюсь закодировать игру жизни на C. Я сижу 7 часов, задаваясь вопросом, как ее решить. Я зашел далеко, но есть что-то, из-за чего код не работает должным образом. Я считаю, что сделал что-то не так между следующей и текущей переменными в структуре. Может быть, неправильно считать, жив сосед или мертв. В любом случае буду признателен за любую помощь!

void checkField(const int rows, const int cols, cell field[rows][cols]) {
    int neighborCount;

    for (int r = 0; r < rows; r++) {
        for (int c = 0; c < cols; c++) {
                neighborCount = getNeighborCount(rows, cols, r, c, field);
                nextGeneration(rows, cols, r, c, neighborCount, field);
        }
    }
}

int getNeighborCount(const int rows, const int cols, 
    int r, int c, cell field[rows][cols]) {

    int neighborCount = 0;

    neighborCount += checkNeighbors(rows, cols, r - 1, c - 1, field);
    neighborCount += checkNeighbors(rows, cols, r - 1, c, field);
    neighborCount += checkNeighbors(rows, cols, r - 1, c + 1, field);
    neighborCount += checkNeighbors(rows, cols, r, c - 1, field);
    neighborCount += checkNeighbors(rows, cols, r, c + 1, field);
    neighborCount += checkNeighbors(rows, cols, r + 1, c - 1, field);
    neighborCount += checkNeighbors(rows, cols, r + 1, c, field);
    neighborCount += checkNeighbors(rows, cols, r + 1, c + 1, field);

    return neighborCount;
}


int checkNeighbors(const int rows, const int cols, 
    int r, int c, cell field[rows][cols]) {

    int neighborAlive;

    if (r < 0 || r > rows || c < 0 || c > cols || field[r][c].current != ALIVE){
        return neighborAlive = 0;
    }
    else {
        return neighborAlive = 1;
    }
}

void nextGeneration(const int rows, const int cols, 
    int r, int c, int neighborCount, cell field[rows][cols]) {

    for (int r = 0 ; r < rows ; r++) {
        for (int c = 0 ; c < cols ; c++) {
            field[r][c].current = DEAD;
            field[r][c].next = DEAD;
        }
    }

    if (neighborCount < 2){
        field[r][c].current = DEAD;
        field[r][c].next = DEAD;
    }

    if (neighborCount == 2 || neighborCount == 3) {
        field[r][c].current = ALIVE;
        field[r][c].next = ALIVE;

    }

    if ((field[r][c].current == DEAD) && neighborCount == 3) {
        field[r][c].current = ALIVE;
        field[r][c].next = ALIVE;
    }

    if (neighborCount >= 4) {
        field[r][c].current = DEAD;
        field[r][c].next = DEAD;
    }
}

person Cows42    schedule 17.10.2016    source источник
comment
эй @Cows42 не удаляйте вопрос, потому что будущие пользователи не смогут связать ответы на вопрос :)   -  person Cherubim    schedule 18.10.2016
comment
как вопрос об отладке моего кода с большим дампом кода и без четкого изложения проблемы получает так много голосов?   -  person bolov    schedule 18.10.2016
comment
@bolov Я четко указал, где, по моему мнению, были проблемы. Видимо, я был прав в обоих случаях. Я считаю, что сделал что-то не так между следующей и текущей переменными в структуре. Может быть, неправильно считать, жив сосед или мертв.   -  person Cows42    schedule 18.10.2016
comment
Пожалуйста, опубликуйте пример stackoverflow.com/help/mcve, а не фрагменты кода, так как вы можете скрыть проблему в части разреза код.   -  person Luis Colorado    schedule 19.10.2016


Ответы (3)


Теперь, когда условное выражение в функции checkNeighbors() работает, у вас есть несколько проблем при обновлении от одного поколения к другому. В функции nextGeneration() вам не нужно очищать массив ячеек, потому что вы все равно собираетесь перезаписать следующее поколение. И в каждом из тестов этой функции у вас есть, например:

if (neighborCount < 2){
    field[r][c].current = DEAD;
    field[r][c].next = DEAD;
}

Но вам нужно только field[r][c].next = DEAD;. Это ваша новая функция nextGeneration():

void nextGeneration(const int rows, const int cols, 
    int r, int c, int neighborCount, cell field[rows][cols]) {

    if (neighborCount < 2)
        field[r][c].next = DEAD;

    if (neighborCount == 2 || neighborCount == 3) 
        field[r][c].next = ALIVE;

    if ((field[r][c].current == DEAD) && neighborCount == 3)
        field[r][c].next = ALIVE;

    if (neighborCount >= 4)
        field[r][c].next = DEAD;
}

Затем в конце функции checkField() нужно скопировать следующее поколение в текущее поколение. Это ваша новая функция checkField():

void checkField(const int rows, const int cols, cell field[rows][cols]) {

    int neighborCount;
    int r, c;

    for (r = 0; r < rows; r++) {
        for (c = 0; c < cols; c++) {
                neighborCount = getNeighborCount(rows, cols, r, c, field);
                nextGeneration(rows, cols, r, c, neighborCount, field);
        }
    }
    /* Now, copy next generation into current */
    for (r = 0; r < rows; r++)
        for (c = 0; c < cols; c++)
            field[r][c].current = field[r][c].next;
}

С этими изменениями ваш код у меня работает, хотя я думаю, что может быть проблема с вашими правилами. Я попробовал планер, и он ведет себя не так, как я ожидал. Вот как выглядит сетка на втором поколении:

. . X . . . . . . . . . . . . . . . . . 
X . X X . . . . . . . . . . . . . . . . 
. X X X . . . . . . . . . . . . . . . . 
X X X . . . . . . . . . . . . . . . . . 
. . . . . . . . . . . . . . . . . . . . 
. . . . . . . . . . . . . . . . . . . . 
. . . . . . . . . . . . . . . . . . . . 
. . . . . . . . . . . . . . . . . . . . 
. . . . . . . . . . . . . . . . . . . . 
. . . . . . . . . . . . . . . . . . . . 
. . . . . . . . . . . . . . . . . . . . 
. . . . . . . . . . . . . . . . . . . . 
. . . . . . . . . . . . . . . . . . . . 
. . . . . . . . . . . . . . . . . . . . 
. . . . . . . . . . . . . . . . . . . . 
. . . . . . . . . . . . . . . . . . . . 
. . . . . . . . . . . . . . . . . . . . 
. . . . . . . . . . . . . . . . . . . . 
. . . . . . . . . . . . . . . . . . . . 
. . . . . . . . . . . . . . . . . . . . 

Осторожно, спойлеры:

Вот обновленная функция nextGeneration() с правилами, которые правильно реализуют игру жизни Конвея. Ваши первоначальные правила регенерировали мертвые клетки только с двумя соседями.

void nextGeneration(const int rows, const int cols, 
    int r, int c, int neighborCount, cell field[rows][cols]) {

    if (field[r][c].current == ALIVE) {
        if ((neighborCount < 2) || (neighborCount > 3)){
            field[r][c].next = DEAD;
        } else {
            field[r][c].next = ALIVE;
        }
    } else {
        if (neighborCount == 3) {
            field[r][c].next = ALIVE;
        } else {
            field[r][c].next = DEAD;
        }
    }
}

Обновлять:

После всего вышеперечисленного теперь я вижу, что вы пытались переключать дисплей между текущим и следующим поколениями. Это усложняет логику обновления, так как здесь тоже приходится чередовать поколения. Суть в том, что этот код просто сложнее, чем нужно. Вы можете реализовать это так, как вы хотели, но все еще было много проблем с обновлением, вызывающих проблемы, и ваш основной цикл в main() не мог чередоваться между поколениями. Вы можете сохранить предложенные мной изменения и удалить все ссылки на printCurrentField и printNextField. Это позволяет упростить функцию printField().

person ad absurdum    schedule 18.10.2016

int checkNeighbors(const int rows, const int cols, 
    int r, int c, cell field[rows][cols]) {
    int neighborAlive;

    if (r < 0 || r > rows || c < 0 || c > cols || field[r][c].current != ALIVE){
        return neighborAlive = 0;
    }
    else {
        return neighborAlive = 1;
    }
}

Предположим, что rows равно 7. Это означает, что имеется семь строк. Но этот код будет обращаться к восьми строкам: ноль, один, два, три, четыре, пять, шесть и семь. Этого не может быть.

person David Schwartz    schedule 17.10.2016
comment
rows - это константа, равная 20? Насколько я понимаю, функция if проверит, не выходит ли за границы переменная 'r' или 'c'. Если они таковы, то он просто добавляет 0 к счетчику, который ни на что не влияет. - person Cows42; 18.10.2016
comment
(r < 0 || r > rows || c < 0 || c > cols || field[r][c].current != ALIVE Я бы дважды проверил эти условия в отладчике - person tesseract; 18.10.2016
comment
@ Cows42 Cows42 Если вы прочитаете мой ответ, я объясню, почему он этого не делает. Если для rows установлено значение 20, это означает, что имеется 20 строк. Но этот тест позволит 21 строку. (Посчитай их.) - person David Schwartz; 18.10.2016
comment
Извините, я недавно начал изучать C. Как вы выполняете то, о чем просите? Или вы имеете в виду, что я пишу простой код и проверяю условия? - person Cows42; 18.10.2016
comment
Вы понимаете код, который вы вставили? Я пытаюсь выяснить, что вы знаете и чего не знаете, чтобы дать вам полезный ответ. Как вы думаете, сколько различных значений r удовлетворит этому if? Как вы думаете, сколько рядов? Эти числа равны? Должны ли они быть? - person David Schwartz; 18.10.2016
comment
@DavidSchwartz Думаю, я понимаю проблему. В условиях я включил 0 и 20. Тогда я думаю, что это решит это? - person Cows42; 18.10.2016
comment
г ‹ 0 || г ›= строки || с ‹ 0 || c ›= столбцы || поле[r][c].current != ЖИВОЕ - person Cows42; 18.10.2016
comment
@DavidSchwartz Я изменил условия на указанные выше. Так что это должно исправить проблему 0-19 = 20 строк. Однако он по-прежнему не выводит правильные значения на втором экране. Возможно, есть другие проблемы? - person Cows42; 18.10.2016

void loadCustom(const int rows, const int cols, cell field[rows][cols]) {

  printf("Give custom format string: ");
  do {
    int r, c;
    scanf("%d,%d", &r, &c);
    field[r][c].current = ALIVE;
  } while (getchar() != '\n');
}

scanf возвращает значение, попробуйте ИСПРАВИТЬ ошибку sig seg после неправильного ввода, я полагаю, что лучше прокомментировать код и использовать valgrind. Управление памятью плохое, поэтому вы выходите из массива, я думаю, есть проблема, попробуйте проверить это вручную.

person Matovidlo    schedule 17.10.2016