Края массива Game of Life ведут себя странно

Я увидел «Игру жизни» Конвея и решил сделать свою собственную. У меня есть логический массив для представления мира, но края (сверху и снизу) ведут себя странно, случайные ячейки становятся живыми.

В этом коде не печатает низ и верх мира, но это плохое решение. Мир «заворачивается» направо и налево, вызывая еще больше проблем, но это в другой раз.

#include <iostream>

const int height = 20;
const int width  = 20;

bool now_world[height][width];
bool then_world[height][width];

void clear_world();
void place_random_live_cells();
void then_world_initialization();
void print_world();
void generation_pass();
void update_worlds();

int main(int argc, const char * argv[])
{
using namespace std;
srand((unsigned)time(NULL));
int timer = 0;
int generation = 0;
clear_world();
place_random_live_cells();
then_world_initialization();
bool running = true;
while (running) {
    if (timer == 50000000) {
        cout << "Generation #" << generation << endl;
        print_world();
        generation_pass();
        update_worlds();
        ++generation;
        timer = 0;
    }
    ++timer;
}//While (running) ends here
return 0;
}

void place_random_live_cells()
{
int percent = 30;
int max_live_cells = ((height * width) / 100) * percent;
int current_live_cells = 0;
while (current_live_cells < max_live_cells) {
    int ycoords = 0 + (rand() % (height + 1));
    int xcoords = 0 + (rand() % (width  + 1));
    if (now_world[ycoords][xcoords] == false) {
        now_world[ycoords][xcoords] = true;
    } else {
        current_live_cells--;
    }
    ++current_live_cells;
}
}

//A generation pass and cells die and some cells come to life
void generation_pass()
{
using namespace std;
int neighbours = 0;
for (int iii = 0; iii < height; iii++) {
    for (int jjj = 0; jjj < width; jjj++) {
        //Count neighbouring cells that are alive
        if (now_world[iii+1][jjj+1] == true) {
            ++neighbours;
        }
        if (now_world[iii+1][jjj  ] == true) {
            ++neighbours;
        }
        if (now_world[iii+1][jjj-1] == true) {
            ++neighbours;
        }
        if (now_world[iii  ][jjj+1] == true) {
            ++neighbours;
        }
        if (now_world[iii  ][jjj-1] == true) {
            ++neighbours;
        }
        if (now_world[iii-1][jjj+1] == true) {
            ++neighbours;
        }
        if (now_world[iii-1][jjj  ] == true) {
            ++neighbours;
        }
        if (now_world[iii-1][jjj-1] == true) {
            ++neighbours;
        }
        //Apply rules to the cells
        //Dead cells with three live neighbours becomes alive
        if (then_world[iii][jjj] == false && neighbours == 3) {
            then_world[iii][jjj] =  true;
        }
        //Alive with fewer than two, they die
        if (then_world[iii][jjj] == true  && neighbours <  2) {
            then_world[iii][jjj] =  false;
        }
        //Alive with 2 or three live neighbours live on unchanged
        if (then_world[iii][jjj] == true  && neighbours == 2) {
            then_world[iii][jjj] =  true;
        }
        if (then_world[iii][jjj] == true  && neighbours == 3) {
            then_world[iii][jjj] =  true;
        }
        //Alive with more than three, they die
        if (then_world[iii][jjj] == true  && neighbours >  3) {
            then_world[iii][jjj] =  false;
        }
        //Dead cells without exactly three live neighbours remain dead
        //Reset neighbour value to zero
        neighbours = false;
    }
}
}

//Make next generation identical to current
//This is only called once
void then_world_initialization()
{
for (int iii = 0; iii < height; iii++) {
    for (int jjj = 0; jjj < width; jjj++) {
        then_world[iii][jjj] = now_world[iii][jjj];
    }
}
}

//Make the next generation be today
//This is called every generation
void update_worlds()
{
for (int iii = 0; iii < height; iii++) {
    for (int jjj = 0; jjj < width; jjj++) {
        now_world[iii][jjj] = then_world[iii][jjj];
    }
}
}

//Set all cells to dead
void clear_world()
{
for (long iii = 0; iii < height; iii++) {
    for (long jjj = 0; jjj < width; jjj++) {
        now_world[iii][jjj]  = false;
        then_world[iii][jjj] = false;
    }
}
}

//Print world
void print_world()
{
using namespace std;
char live = 'X';
char dead = '.';
for (long iii = height; iii > 0; iii--) {
    for (long jjj = width; jjj > 0; jjj--) {
        if (iii != 0 && iii != height) {
            if (now_world[iii][jjj]) {
                cout << live;
            } else {
                cout << dead;
            }
            cout << " ";
        }
    }
    cout << endl;
}
cout << endl;
}

person Lemonizer    schedule 09.06.2013    source источник
comment
это так очевидно... что, по-твоему, такого особенного в краях?   -  person Karoly Horvath    schedule 10.06.2013
comment
@KarolyHorvath, что их не существует. Но как это исправить?   -  person Lemonizer    schedule 10.06.2013
comment
Каково ваше предполагаемое поведение для краев? Обернуть вокруг? Никогда не меняется?   -  person zch    schedule 10.06.2013
comment
Сверху и снизу программа пытается получить доступ к world[-1][something], но это не сработает.   -  person Lemonizer    schedule 10.06.2013
comment
Я знаю. Что вы хотите, чтобы произошло?   -  person zch    schedule 10.06.2013
comment
Затем проверьте, не выходит ли [-1] за пределы, и обработайте, была ли это мертвая ячейка... или живая, если хотите.   -  person Appleshell    schedule 10.06.2013
comment
@zch Я хочу проигнорировать значение, выходящее за границы, и продолжить. Обычная клетка состоит из восьми смежных частей, ребро должно иметь пять, а угол — три.   -  person Lemonizer    schedule 10.06.2013
comment
Хорошо, ребята, я исправил это. Я просто сделал массив на одну длиннее в каждом направлении, затем сделал все эти ячейки мертвыми каждый ход, а затем не печатал их. Это все исправило, спасибо за вашу помощь.   -  person Lemonizer    schedule 10.06.2013


Ответы (2)


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

Если вы посмотрите на операторы if во вложенном цикле for, я думаю, вы обнаружите некоторые проблемы. Например, в этом случае, что происходит, когда iii равно (высота-1) или jjj равно (ширина-1)?

for (int iii = 0; iii < height; iii++) {
    for (int jjj = 0; jjj < width; jjj++) {
        //Count neighbouring cells that are alive
        if (now_world[iii+1][jjj+1] == true) {
            ++neighbours;

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

Убедитесь, что вы также обрабатываете такие случаи:

if (now_world[iii-1][jjj+1] == true) {
            ++neighbours;
        }

Что, если iii равно нулю?

Надеюсь, это поможет.

person Brent Writes Code    schedule 09.06.2013
comment
Я подозревал, что это так, и я думаю, что если они попытаются прочитать значения за пределами массива, они прочитают их как истинные? в противном случае не было бы живых ячеек на краю. Также я не вижу разницы в наших операторах if, кроме пробелов. - person Lemonizer; 10.06.2013
comment
@Lemonizer Избегайте использования == true , == false и т. Д. В логических условиях / выражениях. Это избыточно. Это не критика, это всего лишь совет по стилю :) - person Manu343726; 10.06.2013
comment
@Lemonizer - я ничего не менял в вашем коде, я просто скопировал/вставил его в качестве примера. Если вы выходите за пределы своего массива, поведение просто не определено. Он может вернуться истинным, может вернуться ложным или может рухнуть. Вот почему очень важно оставаться в пределах границ массива; потому что в противном случае результаты непредсказуемы. - person Brent Writes Code; 10.06.2013
comment
@BrentNash Понятно. Спасибо. Пока тебя не было, я исправил проблему и заставил ее записывать мир в файл .txt в каждом поколении. Я хочу, чтобы он сравнивал себя с каждым предыдущим поколением, чтобы увидеть, вошло ли оно в цикл. Эта неделя точно будет веселой! - person Lemonizer; 10.06.2013

Вы пытаетесь получить доступ к запредельным индексам в вашем массиве.

Я не уверен, какое поведение вы ожидаете, но простой способ — не вычислять обновления для ячеек на краях.

Таким образом, в generation_pass петли должны идти от 1 до height-1.

person Karoly Horvath    schedule 09.06.2013
comment
Я не думаю, что это сработает, эти клетки не смогут погибнуть, так как они никогда не обрабатываются. - person Lemonizer; 10.06.2013
comment
не устанавливайте их, не показывайте их и т.д... это все же намного проще, чем проверять каждое граничное условие. - person Karoly Horvath; 10.06.2013