I need help with my Game of Life implementation in C. Other posts on Stackoverflow lead me to believe that my problem was to do with dangling pointers, but after modifying my program to use a global 2D array for the game grid instead of passing it to functions which return new 2D arrays, I realized that it was a problem with my update function.
I have tried hard-coding a number of simple patterns, including gliders and oscillators, and the grid doesn't update correctly. The patterns do update the same way every time the program is run, so I don't think it's a problem of uninitialized memory causing problems. I also know that there are no cells which contain values greater than 1. Therefore, the problem must lie in my mechanisms for updating the grid.
Can someone help me find the problem? I can't find anything wrong with my code and I believe I have programmed the rules correctly.
Here are my neighbors and update functions, along with the relevant variable and constant declarations.
#define MAX_Y 10 /* height */
#define MAX_X 30 /* width */
int grid[MAX_Y][MAX_X];
int neighbors(int x, int y) {
int dx, dy, dstx, dsty;
int n = 0;
for (dy = -1; dy <= 1; ++dy) {
for (dx = -1; dx <= 1; ++dx) {
dsty = y + dy;
dstx = x + dx;
if (dsty >= 0 && dsty < MAX_Y && dstx >= 0 && dstx < MAX_X)
n += !!grid[dsty][dstx]; /* use !! so that non-zero values eval to 1 */
}
}
/* (n > 0) ? printf("Point (%d,%d) has %d neighbors!\n", x, y, n) : 0; */
return n;
}
void update(void) {
int new[MAX_Y][MAX_X];
memset(new, 0, sizeof(int) * MAX_Y * MAX_X);
int i, j, n;
for (i = 0; i < MAX_Y; ++i) {
for (j = 0; j < MAX_X; ++j) {
n = neighbors(i, j);
/* alive, 2 or 3 neighbors -> alive!
* dead, 3 neighbors -> alive!
* anything else -> dead :(
*/
if (grid[i][j] && (n == 2 || n == 3))
new[i][j] = 1;
else if (!grid[i][j] && n == 3)
new[i][j] = 1;
else
new[i][j] = 0;
}
}
memcpy(grid, new, sizeof grid);
}
In your neighbors function, you need to think carefully about the loop iteration where dx and dy are both zero. Conway's Game of Life does not consider a cell to be neighbor of itself, so you need to avoid counting it.
You're also confusing yourself by using the letters i and j. You're allowing j to go all the way up to MAX_X, but then you are using j as the y coordinate when you call neighbors, so that will cause overflows and incorrect calculations. (Starting with the easier case of a 10x10 grid would sometimes save you from bugs like this.)
You should adjust the neighbors() function to omit the cell itself.
Here is a modified version:
#define MAX_Y 10 /* height */
#define MAX_X 30 /* width */
unsigned char grid[MAX_Y][MAX_X];
int neighbors(int x, int y) {
int n = -!!grid[y][x];
for (int dy = -1; dy <= 1; ++dy) {
for (int dx = -1; dx <= 1; ++dx) {
int dsty = y + dy;
int dstx = x + dx;
if (dsty >= 0 && dsty < MAX_Y && dstx >= 0 && dstx < MAX_X && grid[dsty][dstx])
n++;
}
}
return n;
}
void update(void) {
int new[MAX_Y][MAX_X] = { 0 };
for (int y = 0; y < MAX_Y; ++y) {
for (int x = 0; x < MAX_X; ++x) {
int n = neighbors(y, x);
/* alive, 2 or 3 neighbors -> alive!
* dead, 3 neighbors -> alive!
* anything else -> dead :(
*/
new[y][x] = (grid[y][x] && n == 2) || n == 3;
}
}
memcpy(grid, new, sizeof grid);
}
The neighbors() function can be simplified with fewer tests:
int neighbors(int x, int y) {
int n = -(grid[y][x] != 0);
int x1 = x - (x > 0);
int x2 = x + (x < MAX_X - 1);
int y1 = y - (y > 0);
int y2 = y + (y < MAX_Y - 1);
for (y = y1; y <= y2; y++) {
for (x = x1; x <= x2; x++) {
n += grid[y][x] != 0;
}
}
return n;
}
Related
This question already has answers here:
Returning an array using C
(8 answers)
Can a local variable's memory be accessed outside its scope?
(20 answers)
C: returning local struct works but returning local array does not?
(2 answers)
Closed 1 year ago.
I'm trying to implement Game of Life in C. I'm certain I programmed the rules of the game correctly, but the cells do not update correctly when running the program. In the beginning of main, I hard-coded a glider onto the map, but it doesn't 'glide' southwest like it's supposed to. I think the source of the problem might be in the update() function, but I can't find it myself.
(This isn't homework, this is just a personal project.)
#include <stdio.h>
#include <string.h>
#define MAX_Y 10 /* height */
#define MAX_X 30 /* width */
int neighbors(int grid[MAX_Y][MAX_X], int x, int y) {
int dx, dy, dstx, dsty;
int n = 0;
for (dy = -1; dy < 2; ++dy) {
for (dx = -1; dx < 2; ++dx) {
dsty = y + dy;
dstx = x + dx;
if (dsty > 0 && dsty < MAX_Y && dstx > 0 && dstx < MAX_X) {
n += !!grid[dsty][dstx]; /* use !! so that non-zero values eval to 1 */
}
}
}
/* (n > 0) ? printf("Point (%d,%d) has %d neighbors!\n", x, y, n) : 0; */
return n;
}
int **update(int grid[MAX_Y][MAX_X]) {
int new[MAX_Y][MAX_X];
memset(new, 0, sizeof new);
int i, j, n;
for (i = 0; i < MAX_Y; ++i) {
for (j = 0; j < MAX_X; ++j) {
n = neighbors(grid, i, j);
/* alive, 2 or 3 neighbors -> alive!
* dead, 3 neighbors -> alive!
* anything else -> dead :(
*/
if (grid[i][j] && (n == 2 || n == 3))
new[i][j] = 1;
else if (!grid[i][j] && n == 3)
new[i][j] = 1;
else
new[i][j] = 0;
}
}
return new;
}
void draw(int grid[MAX_Y][MAX_X]) {
int i, j;
for (i = 0; i < MAX_Y; ++i) {
for (j = 0; j < MAX_X; ++j) {
putchar((grid[i][j]) ? '#' : '.');
/* printf("|%2d", grid[i][j]); */
}
putchar('\n');
}
}
int main(void) {
int map[MAX_Y][MAX_X];
memset(map, 0, sizeof map);
map[2][2] = 1;
map[3][2] = 1;
map[4][2] = 1;
map[4][3] = 1;
map[3][4] = 1;
printf( " Game of Life v1.0\n"
"Press ENTER to step.\n");
for (;;) {
draw(map);
memcpy(map, update(map), sizeof map);
getchar();
}
}
This question already has an answer here:
Minesweeper revealing cells in C
(1 answer)
Closed 1 year ago.
I want to make a minefield. First I made a matrix, asked the user to enter with the rows and columns.
I need to:
put the bombs
indicate the amount of bombs nearby.
#include <stdio.h>
#include <stdlib.h>
int main(){
int rows, columns, i, j, bombs, **matrix;
printf("\Enter the rows: ");
scanf("%d", &rows);
printf("Enter the columns:");
scanf("%d", &columns);
printf("Enter the bombs:");
scanf("%d", &bombs);
if (bombs>= (rows*colums)){
printf("Error");
}
if (bombs<= 0){
printf("Error);
}
for (i = 0; i < rows; i++){
for (j = 0; j < columns; j++){
matrix[i][j] = rand() % 10;
}
}
// show the map //
for (i = 0; i < rows; i++){
for (j = 0; j < columns; j++) {
printf("%d", matrix[i][j]);
}
printf("\n\n");
}
return 0;
}
``
Data Structures
Instead of using an int then remembering magical numbers like -1 indicates a mine, it's more natural to say what these mean explicitly. Then you can change your mind or add to it without worrying about rewriting your whole programme. In the whole minefield, it's useless to have int **m without cols and rows. This interdependence is a sign that you should also package them in a structure to pass as one variable.
struct tile { unsigned nearby : 4, is_mine : 1, is_revealed : 1; };
struct map { size_t x, y; struct tile **tile; };
These definitions are very similar to what you had before, but they express the intent of the code much better.
Arrays
I would forget your complicated alocMatrix and just use one allocation, but then you have to use tiles[y * map->x + x]. Personal preference; I think it's easier to have a constructor that returns one allocated block.
struct map { size_t x, y; struct tile *tile; };
...
if(!(map = malloc(sizeof *map + sizeof *tile * x * y)))
{ if(!errno) errno = ERANGE; return 0; }
...
map->tile = (struct tile *)(map + 1);
memset(map->tile, 0, sizeof *tile * x * y);
Mines
A method of laying mines uniformly at random is to repeatedly choose a random tile, throwing out the tiles you've seen. Another method is to visit all the tiles and calculate the probability of each, P(mines left / tiles left). It depends on the number of mines which will be faster.
const size_t x1 = map->x, y1 = map->y;
size_t x, y, squares = x1 * y1;
assert(map && mines < squares);
for(x = 0; x < x1; x++) {
for(y = 0; y < y1; y++) {
struct tile *tile = &map->tile[y * x1 + x];
tile->is_revealed = 0;
tile->is_mine = rand() < RAND_MAX / squares * mines;
mines -= tile->is_mine;
squares--;
}
}
for(x = 0; x < x1; x++) for(y = 0; y < y1; y++)
map->tile[y * x1 + x].nearby =
(x && y && map->tile[(y-1) * x1 + (x-1)].is_mine) +
...
(x && y < y1 - 1 && map->tile[(y+1) * x1 + (x-1)].is_mine);
How about using this function to place the bombs? Call it by passing the address of m. Keep in mind that marking the rest of the squares with the appropriate number has to happen after the bombs have been placed (and it shouldn't depend on a call to rand). Also, the map has to be initialized to avoid surprises.
void placeBombs(int*** map, int numOfRows, int numOfColumns, int numOfBombs)
{
while (numOfBombs > 0) {
int column = rand() % numOfColumns;
int row = rand() % numOfRows;
if ((*map)[row][column] != -1) {
(*map)[row][column] = -1;
--numOfBombs;
}
/* Else, if a bomb already exists in the cell,
* we iterate without placing a bomb
*/
}
}
When I run my code it throws a segmentation fault and I have tried rewriting the code several times. Still to no avail, it won't even run. The segmentation fault happens as soon as my program is launched. What it's supposed to do is print a path on screen using the ncurses library in Linux, from the given coordinates. Here is the problematic snippet with the lines where gdb said the segmentation fault was, also it (snippet) reproduces the problem.
EDIT: This will help explain what I'm trying to do, but using dynamic arrays. Breadth First Search
EDIT 2: The variable frontier is supposed to keep track of the X and Y values at a specific index. The add_neighbors function is there to add all four neighbors (providing they aren't already added) to the frontier and came_from arrays.
frontier[index][0] is X value.
frontier[index][1] is Y value.
The before the first while loop I set the start position x1 and y1. During the first while loop, it increments getting the new coordinates from the frontier, then processing and adding to came_from array.
For example:
(x1,y1) (x1+1,y1)
(x1,y1+1) (x1+1,y1+1)
(x1,y2) (x2,y2)
I'm trying to get from (x1,y1) to (x2,y2). Sure hope that explains it better. What I'm trying to implement is a Breadth First Search (BFS) algorithm. Using two arrays, one is frontier (keeps track of visited positions) and came_from (keeps track of X and Y the path from x1,y1 to x2,y2). Updated the code to reflect the first answer. Plus added a comment to explain where the error might be, not really sure but I've been debugging it. It looks like the came_from array never gets set with x and y.
The Code:
/*
* pathfind.c - Simple Breadth First Search algorithm implementation.
*
* Author: Philip R. Simonson
* Date : 05/17/2021
*
****************************************************************************
*/
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <ncurses.h>
#define MAXHEIGHT 24
#define MAXWIDTH 80
/* Add neighboring positions to the arrays.
*/
int add_neighbors(int **frontier, int ***came_from, int count, int x, int y)
{
// North
if(y > 0 && came_from[y - 1][x][0] < 0) {
frontier[count][0] = x;
frontier[count][1] = y;
count++;
came_from[y - 1][x][0] = x;
came_from[y - 1][x][1] = y;
}
// South
if(y < MAXHEIGHT-1 && came_from[y + 1][x][0] < 0) {
frontier[count][0] = x;
frontier[count][1] = y;
count++;
came_from[y + 1][x][0] = x;
came_from[y + 1][x][1] = y;
}
// West
if(x > 0 && came_from[y][x - 1][0] < 0) {
frontier[count][0] = x;
frontier[count][1] = y;
count++;
came_from[y][x - 1][0] = x;
came_from[y][x - 1][1] = y;
}
// East
if(x < MAXWIDTH-1 && came_from[y][x + 1][0] < 0) {
frontier[count][0] = x;
frontier[count][1] = y;
count++;
came_from[y][x + 1][0] = x;
came_from[y][x + 1][1] = y;
}
return count; // Return counter for frontier
}
/* Simple BFS algorithm for path finding.
*/
void path_finding(int x1, int y1, int x2, int y2)
{
int **frontier, ***came_from;
int index, count;
int i, j;
index = 0;
count = 0;
// Initialise frontier array
frontier = malloc(sizeof(int *) * MAXHEIGHT * MAXWIDTH);
for(i = 0; i < (MAXHEIGHT * MAXWIDTH); i++) {
frontier[i] = malloc(sizeof(int) * 2);
}
// Create came_from array
came_from = malloc(sizeof(int **) * MAXHEIGHT);
for(i = 0; i < MAXHEIGHT; i++) {
came_from[i] = malloc(sizeof(int *) * MAXWIDTH);
for(j = 0; j < MAXWIDTH; j++) {
came_from[i][j] = malloc(sizeof(int) * 2);
came_from[i][j][0] = -1;
came_from[i][j][1] = -1;
}
}
// Add start to came_from
came_from[y1][x1][0] = -9;
came_from[y1][x1][1] = -9;
// Add start to frontier
frontier[count][0] = x1;
frontier[count][1] = y1;
count++;
while(index < count) {
int x = frontier[index][0];
int y = frontier[index][1];
index++;
if(x == x2 && y == y2)
break;
count = add_neighbors(frontier, came_from, count, x, y);
}
// Set temp position variables to end position
{
int x = x2;
int y = y2;
while(x != x1 || y != y1) {
int tempy = y;
mvprintw(y, x, "*");
// Segmentation fault because came_from[tempy][x][1] and came_from[tempy][x][0]
// always equals -1 which is out of bounds. Not sure how to fix it, something
// is wrong with add_neighbors function I think.
y = came_from[tempy][x][1];
x = came_from[tempy][x][0];
}
}
// TODO: Return came_from array!
// Free all resources from both arrays
for(i = 0; i < MAXHEIGHT; i++) {
for(j = 0; j < MAXWIDTH; j++) {
free(came_from[i][j]);
}
free(came_from[i]);
}
free(came_from);
for(i = 0; i < (MAXHEIGHT * MAXWIDTH); i++) {
free(frontier[i]);
}
free(frontier);
}
int main(void)
{
initscr();
noecho();
clear();
path_finding(0, 2, 7, 8);
refresh();
getch();
endwin();
return 0;
}
Compile with: cc -o test test.c -lncurses
GDB output:
[philip#darkstar temp]$ gdb --batch --ex r --ex bt --ex q temp
Program received signal SIGSEGV, Segmentation fault.
0x000055555555599d in path_finding (x1=0, y1=2, x2=7, y2=8) at src/pathfind.c:117
117 y = came_from[tempy][x][1];
#0 0x000055555555599d in path_finding (x1=0, y1=2, x2=7, y2=8) at src/pathfind.c:117
#1 0x00005555555551ff in main () at src/main.c:11
A debugging session is active.
Inferior 1 [process 65294] will be killed.
Quit anyway? (y or n) [answered Y; input not from terminal]
[philip#darkstar temp]$
Some of the allocation sizes are incorrect:
frontier = malloc(sizeof(frontier) * MAXHEIGHT * MAXWIDTH); should be
frontier = malloc(sizeof(*frontier) * MAXHEIGHT * MAXWIDTH);
frontier[i] = malloc(sizeof(*frontier) * 2); should read:
frontier[i] = malloc(sizeof(frontier[i][0]) * 2);
These accesses are not properly protected:
if(y <= MAXHEIGHT && came_from[y + 1][x][0] < 0) should be
if (y < MAXHEIGHT-1 && came_from[y + 1][x][0] < 0)
if(x <= MAXWIDTH && came_from[y][x + 1][0] < 0) should be
if(x < MAXWIDTH-1 && came_from[y][x + 1][0] < 0)
the frontier array is not initialized. You should use calloc() to intialize the int arrays to 0 or run an initialization loop.
in the while (x != x1 || y != y1) loop, when you read y = came_from[tempy][x][1] you do not check that y >= 0. Since you initialized came_from[y1][x1][0] = -9; it way be negative and cause an out of bounds access during the next iteration as you set tempy = y.
the algorithm is not obvious from the code, you might want to comment more for the reader's sake.
I am taking an online course with very little support. I am trying to follow instructions and write a script that takes input and draws two rectangles. Unfortunately, it just repeats infinitely, and I don't know what I'm missing. Any guidance would really help! Thank you so much for your help and time, this is my first post and I apologize for any formatting errors.
#include <stdio.h>
#include <stdlib.h>
/*
* Determines if coord is in range between
* offset (INCLUSIVE) and offset + size (EXCLUSIVE)
*/
int isInRange(int coord, int offset, int size) {
// if coord is in range, return 1
if ((coord >= offset) && (coord < (offset + size))) {
return 1;
}
// else, return 0
else {
return 0;
}
return 0;
}
/*
* Determines if coord is at border of offset or
* offset + size
*/
int isAtBorder(int coord, int offset, int size) {
// if coord is equal to offest or offset + size
if (coord == offset || (offset + size)) {
return 1;
}
// return 1, else return 0
else {
return 0;
}
return 0;
}
void squares(int size1, int x_offset, int y_offset, int size2) {
//compute the max of size1 and (x_offset + size2). Call this w
int w = (size1 + (x_offset + size2));
//compute the max of size1 and (y_offset + size2). Call this h
int h = (size1 + (y_offset + size2));
//count from 0 to h. Call the number you count with y
for (int y = 0; y < h; h++) {
//count from 0 to w. Call the number you count with x
for (int x = 0; x < w; x++) {
//check if EITHER
// ((x is between x_offset and x_offset +size2) AND
if (((isInRange(x, x_offset, size2) == 1) &&
// y is equal to either y_offset OR y_offset + size2 - 1)
(isAtBorder(y, y_offset, size2 - 1) == 1))
// OR
||
// ((y is between y_offset and y_offset + size2) AND
((isInRange(y, y_offset, size2) == 1) &&
// x is equal to either x_offset OR x_offset + size2 -1)
(isAtBorder(x, x_offset, size2-1)))) {
// if so, print a *
printf ("*");
}
//if not,
// check if EITHER
// x is less than size1 AND (y is either 0 or size1-1)
else {
if (((x < size1) && (isAtBorder(y, 0, size1 - 1) == 1))
// OR
||
// y is less than size1 AND (x is either 0 or size1-1)
((y < size1) && (isAtBorder(x, 0, size1 - 1) == 1))) {
//if so, print a #
printf ("#");
}
//else print a space
else {
printf (" ");
}
}
}
//when you finish counting x from 0 to w,
//print a newline
printf ("\n");
}
}
The real problem is, I suppose, in your squares function. Check out your for loop in this snippet:
for (int y = 0; y < h; h++) {
//count from 0 to w. Call the number you count with x
for (int x = 0; x < w; x++) {
In the first loop you're incrementing h, not y. Your "y" variable always stays at value which is equal 0, thus it won't finish this loop, since it will ALWAYS be smaller than "h" variable, unless "h" is less or equal than 0. You probably were aiming to increment "y" variable, not h.
After that, please check out your isAtBorder function. The "or" logical operator:
int isAtBorder(int coord, int offset, int size) {
// if coord is equal to offest or offset + size
if (coord == offset || (offset + size)) {
return 1;
}
// return 1, else return 0
else {
return 0;
}
return 0;
}
in the if statement is always true, so basically this function always returns 1.
I'm trying to pass a recursive function that populates my 2D array of structs. My memory allocation is working fine, but when I try to do a recursion, I get the error: Segmentation fault (core dumped).
Any idea why this must be happening? I think I wrote my code so that no index out of bound occurs. I still don't know why this is happening. Any help is going to be appreciated. Thanks!
#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
typedef struct {
char val;
bool filled;
} elements;
void assign(elements ** elements, int row, int column, int x, int y, int limit);
int main(int argc, char** argv)
{
int row = 0;
int column = 0;
int x = 0;
int y = 0;
int limit = 0;
sscanf(argv[1], "%d", &row);
sscanf(argv[2], "%d", &column);
sscanf(argv[3], "%d", &x);
sscanf(argv[4], "%d", &y);
sscanf(argv[5], "%d", &limit);
elements **foo;
foo = (elements **)malloc(sizeof(elements *) * row);
for (int i = 0; i < column; i++)
foo[i] = (elements *)malloc( sizeof(elements) * row);
foo[y][x].val = 'C';
// printf("%c\n", foo[y][x].val);
assign(foo, row, column, x, y, limit);
for(int i = 0; i < row; i++)
{
for(int j = 0; j < column; j++)
{
// foo[i][j].val = '.';
printf("%d\t ", foo[i][j].filled);
}
printf("\n");
}
}
void assign(elements ** elements, int row, int column, int x, int y, int limit)
{
int tempX = x;
int tempY = y;
if(elements[y][x].filled != 0 )
{
//printf("reached.");
return;
}
else if(limit < 0)
{
//printf("reached.");
return;
}
else
{
if(elements[y][x].val != 'C')
elements[y][x].val = limit + '0';
elements[y][x].filled = true;
tempX = x - 1;
tempY = y;
if (!( x < 0 || y < 0 || x > column - 1 || y > row -1 ))
assign(elements, row, column, tempX, tempY, limit - 1); // go up
tempX = x;
tempY = y + 1;
if (!( x < 0 || y < 0 || x > column - 1 || y > row -1 ))
assign(elements, row, column, tempX, tempY, limit - 1); // go right
tempX = x + 1;
tempY = y;
if (!( x < 0 || y < 0 || x > column - 1 || y > row -1 ))
assign(elements, row, column, tempX, tempY, limit - 1); // go down
tempX = x;
tempY = y - 1;
if (!( x < 0 || y < 0 || x > column - 1 || y > row -1 ))
assign(elements, row, column, tempX, tempY, limit - 1); // go left
}
}
each of the if() code blocks in the last half of assign() are beginning with the same basic parameter values, except the limit changes.
so the total number of recursions (which the code seems to be limiting the the value in 'limit' is not actually limited,
because when limit is 0, it is not necessarily the last call to be made to assign() and once limit is <0 the code will recurse about 16 gig more times. (at least) This is probably why the program crashes
Suggest decrementing limit within assign() before any of the recursive calls to assign()