Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Confusing error in Game Of Life program

I have some working Game of Life code. It saves each population as a bitmap. Here's what the output looks like (cropped):

desired output

When cleaning the code, I found that if I commented out or otherwise remove line 60:

cout << "Survivor: " << x << ", " << y << "\n";

It completely messes up the program, and instead of producing a glider like it should, it produces this:

faulty output

I have poked around, trying to find out what might cause this, but I have been thus far unsuccessful. This is my current code:

//Bitmap Library from http://partow.net/programming/bitmap/
#include "bitmap_image.hpp"
#include <iostream>
#include <stdio.h>
#include <string.h>
#include <unistd.h>

using namespace std;

#define WIDTH 160
#define HEIGHT 128

bool population[WIDTH][HEIGHT];
bool survivors[WIDTH][HEIGHT];

int check_survivors();
int check_neighbors(int x, int y);
int write_population(char* file);

int main() {
    int i, populations;

    cout << "Enter number of populations: ";
    cin >> populations;

    //Glider
    survivors[28][100] = true;
    survivors[29][100] = true;
    survivors[29][101] = true;
    survivors[30][101] = true;
    survivors[28][102] = true;

    //Initial image generation
    write_population("population0.bmp");

    //populations
    for (i = 0; i < populations; i++) {
        char filename[17] = "population";
        char ii[3];
        sprintf(ii, "%d", i+1);

        strcat(filename, ii);
        strcat(filename, ".bmp");

        check_survivors();
        write_population(filename);
    }

    return 0;
}

int check_survivors() {
    //set x and y variables
    int x, y;

    for (x = 0; x < WIDTH; x++) {
        for (y = 0; y < HEIGHT; y++) {
            if (check_neighbors(x, y)) {
                survivors[x][y] = true;
                cout << "Survivor: " << x << ", " << y << "\n";
            } else {
                survivors[x][y] = false;
            }
        }
    }
    return 0;
}

int check_neighbors(int x, int y) {
    int neighbors = 0, survives;

    //I really need to rewrite this mess

    //Neighbors above
    if (population[x-1][y-1] == true && x != 0 && y != 0) {
        neighbors++;
    }
    if (population[x][y-1] == true && y != 0) {
        neighbors++;
    }
    if (population[x+1][y-1] == true && x != WIDTH-1 && y != 0) {
        neighbors++;
    }

    //Neighbors next to
    if (population[x-1][y] == true && x != 0 ) {
        neighbors++;
    }
    if (population[x+1][y] == true && x != WIDTH-1) {
        neighbors++;
    }

    //Neighbors below
    if (population[x-1][y+1] == true && x != 0  && y != HEIGHT-1) {
        neighbors++;
    }
    if (population[x][y+1] == true && y != HEIGHT-1) {
        neighbors++;
    }
    if (population[x+1][y+1] == true && x != WIDTH-1 && y != HEIGHT-1) {
        neighbors++;
    }

    //Determining life or death
    if (neighbors < 2 || neighbors > 3) {
        //Neighbors less than 2 or more than 3 is dead cell
        survives = 0; 
    } else if (neighbors == 3 && population[x][y] == false) {
        //Exactly 3 neighbors re-animates a cell
        survives = 1;
    } else if (population[x][y] == true) {
        //2 or 3 neighbors is survivor
        survives = 1;
    }

    return survives;
}

int write_population(char* file) {
    //Create Image
    bitmap_image image(WIDTH, HEIGHT);

    //Set background to white
    image_drawer draw(image);
    image.set_all_channels(255,255,255);

    //set x and y variables
    int x, y;

    //For every array point, check to see if it survives,
    //and transfer survivors to population
    for (x = 0; x < WIDTH; x++) {
        for (y = 0; y < HEIGHT; y++) {
            if (survivors[x][y] == true) {
                draw.pen_width(1);
                draw.pen_color(0,0,0);
                draw.plot_pixel(x, y);
            }
            population[x][y] = survivors[x][y];
        }
    }

    //Save image
    image.save_image(file);

    //return
    return 1;
}
like image 433
Quicksilver Avatar asked Nov 01 '14 02:11

Quicksilver


2 Answers

Things like this:

if (population[x-1][y-1] == true && x != 0 && y != 0)

need to be rewritten as:

if ( x > 0 && y > 0 && population[x-1][y-1] == true )

otherwise you'll be straight into undefined behavior territory when either x or y are 0 (as they will be several times when you call check_neighbors() from check_survivors()), and you can expect weird, inexplicable errors like this. You need to check for invalid array indices before you try to access those elements.

Also, here:

if (neighbors < 2 || neighbors > 3) {
    //Neighbors less than 2 or more than 3 is dead cell
    survives = 0; 
} else if (neighbors == 3 && population[x][y] == false) {
    //Exactly 3 neighbors re-animates a cell
    survives = 1;
} else if (population[x][y] == true) {
    //2 or 3 neighbors is survivor
    survives = 1;
}

it looks as if survives could be left with an indeterminate value if neighbors == 2 and population[x][y] == false, which would also cause undefined behavior if you were to access that value. It's not immediately clear from your code whether that combination of circumstances could ever be true, but if you're still at the debugging stage then at a minimum it's worth adding a condition check to verify whether or not it ever is.

If your program is exhibiting undefined behavior as this one is, then it's pretty much impossible to reason about it until after those issues are fixed.

like image 196
Crowman Avatar answered Nov 13 '22 09:11

Crowman


You don't always assign a value into the variable survives e.g. if population[x][y] is false and neighbors is 2. This leaves survives with a value of whatever is in memory at the time. When you add the cout call it probably happens to set that bit of stack memory to 0, masking your program's error.

Add an intial value to survives when you declare it.

like image 34
The Dark Avatar answered Nov 13 '22 07:11

The Dark