I want a Game of Life program in the console.
The parameters to initialize the 'field' can be changed in the mainMenu(). When all is done, it returns 1, and the drawing begins. When we return from draw, we get back to mainMenu(). This happens for ever in an infinite while loop. All functions work when tested separately. Problematic ones are border(), init(), etc.
What am I missing? I'm a beginner in C.
(fileIn() doesn't work proper, but that's not the issue here.)
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <conio.h>
//structure for storing the state of each cell
typedef struct Cell{
bool state, nState;
}Cell;
char path[50];
int rule;
Cell** setup(int width, int height){
//create field
int size = (width+2)*(height+2);
Cell** field = malloc(size * sizeof(Cell));;
if (field == NULL){
printf("Malloc Error!");
exit(-1);
}
return field;
}
void clrscr(){
printf("\e[1;1H\e[2J");
}
void welcome(){
clrscr();
printf("This program was created by:\033[1;31m\n U+0466.\033[1;m\n");
sleep(2);
}
void display(Cell* c, int width, int height){
clrscr();
int size = (width+2)*(height+2);
printf("\n");
for (int i = 0; i <(width*2-32)/2; i++)
printf(" ");
printf("\033[1;31mThe Game of Life on a %dx%d grid.\033[1;m", width, height);
//draw the field
for (int i = 0; i < size; i++){
if (i % (width+2) == 0)
printf("\n");
//skip border
if (i < width+2){
printf("X ");
continue;}
else if (i >= (size-(width+2))){
printf("X ");
continue;}
else if ((i+1) % (width+2) == 0){
printf("X ");
continue;}
else if ((i) % (width+2) == 0){
printf("X ");
continue;}
(c[i].state)? printf("O ") : printf(" ");
}
}
int fileIn(int* width, int *height){
//get path from user
printf("\033[1;31m");
printf("Please enter file path: ");
printf("\033[1;m");
scanf("%s", path);
//open file
FILE *fptr = fopen(path, "r");
if (fptr == NULL){
return -1;
}
//find the dimenisons of the field
char ch;
int h = 0;
int w = 0;
int wmax = 0;
for (ch = getc(fptr); ch != EOF; ch = getc(fptr)){
//0 or 1
if ((ch == 48)|(ch == 49)){
w++;
}
//linebreak
else if (ch == 10){
if (w!=0)
h++;
if (w > wmax){
wmax = w;
}
w = 0;
}
}
if ((wmax > 0)|(height > 0)){
*width = wmax;
*height = h + 1;
}
return 0;
}
void fileOut(Cell* c, int width, int height){
FILE *fptr = NULL;
fptr = fopen("out.txt", "w+");
//create a header
for (int i = 1; i <= width; i++)
fprintf(fptr, "-");
fprintf(fptr, "\nThe Game of Life, by U+O466\n");
for (int i = 1; i <= width; i++)
fprintf(fptr, "-");
//write file
int size = (width+2)*(height+2);
for (int i = 0; i < size; i++){
//skip border
if (i < width+2)
continue;
else if (i >= (size-(width+2)))
continue;
else if ((i+1) % (width+2) == 0)
continue;
else if ((i-3) % (width+2) == 0)
continue;
//write states
else if (i % (width+2) == 0)
fprintf(fptr, "\n");
((c+i)->state)? fprintf(fptr, "1"):fprintf(fptr, "0");
}
fclose(fptr);
}
void initFile(Cell* c, int w, int h){
int size = (w+2)*(h+2);
FILE* fptr = fopen(path, "r");
char ch;
//init to 0, than fill with data
for (int i = 0; i < size; i++)
(c+i)->state = 0;
for (int j = 1; j < h+1; j++){
for (int i = 1; i < w+1; i++){
ch = getc(fptr);
if (ch == 49){
(c+j*(w+2)+i)->state = 1;
}
//linebreak - doesnt handle 'empty' rows properly
else if ((ch == 10)){
break;
}
}
}
}
void initAuto(Cell* c, int width, int height){
//populate first row with 0, set center as 1
for (int i = 0; i < width+1; i++)
(c+i)->state = 0;
(width%2 == 0)? ((c+((width+1)/2))->state = 1) : ((c+(width/2))->state = 1);
for (int j = 1; j < height+1; j++){
for (int i = 1; i < width+1; i++){
char pat = 0;
//find out current pattern
pat |= ((c+(j*(width+2))+i-1)->state << 2 );
pat |= ((c+(j*(width+2))+i)->state << 1 );
pat |= ((c+(j*(width+2))+i+1)->state);
//set next rows state, based on the rule
(c+((j+1)*(width+2))+i)->state = (rule >> pat) & 1;
}
}
}
void init(Cell* c, int w, int h, int mode){
int size = (w+2)*(h+2);
switch (mode){
case 0:
case 1:
for (int i = 0; i < size; i++)
(c+i)->state = mode;
break;
case 2:
for (int i = 0; i < size; i++)
(c+i)->state = rand()%2;
break;
case 3:
initAuto(c, w, h);
break;
case 4:
initFile(c, w, h);
break;
}
}
void border(Cell *c, int w, int h, int mode){
w+=2;
h+=2;
for (int j = 0; j < h; j++){
for (int i = 0; i < w; i++){
if ((i==0) | (i == w-1) | (j==0) | (j == h-1)){
switch (mode){
case 0: //dead
case 1: //alive
(c+(w*j)+i)->state = mode;
break;
case 2: //tiling - corners not handled
if (i==0)
(c+(w*j))->state = (c+(w*j)+i-1)->state;
else if (i == w-1)
(c+(w*j)+i)->state = (c+(w*j)+1)->state;
if (j==0)
(c+i)->state = (c+(w*(h-2))+i)->state;
else if (j == h-1)
(c+(w*(h-1))+i)->state = (c+w+i)->state;
break;
}
(c+(w*j)+i)->nState = (c+(w*j)+i)->state;
}
}
}
}
void update(Cell* c, int w, int h){
w = w + 2;
h = h + 2;
int size = w*h;
for (int j = 1; j < h - 1; j++){
for (int i = 1; i < w + 1; i++){
//reset counter, then count neighbors
int nbors = 0;
nbors +=(c+(j-1)*w+(i-1))->state + //up-left
(c+(j-1)*w+(i)) ->state + //up
(c+(j-1)*w+(i+1))->state + //up-right
(c+(j) *w+(i-1))->state + //left
(c+(j) *w+(i+1))->state + //right
(c+(j+1)*w+(i-1))->state + //down-left
(c+(j+1)*w+(i)) ->state + //down
(c+(j+1)*w+(i+1))->state; //down-right
//decide next state based on current (rule: B3/S23)
if ((c+(j*w)+i)->state)
(c+(j*w)+i)->nState = (nbors == 3 || nbors == 2);
else if (nbors == 3)
(c+(j*w)+i)->nState = 1;
else
(c+(j*w)+i)->nState = 0;
}
}
//update all cells
for (int i = 0; i < size; i++)
(c+i)->state = (c+i)->nState;
}
int menu(char* opts[], char msg[]){
system("cls");
printf("\033[1;31m");
printf("%s\n", msg);
printf("\033[1;m");
//print options
printf("(0) Return\n");
int i = 0;
while (opts[i] != NULL){
printf("(%d) %s\n", i+1, opts[i]);
i++;
}
//get answer
printf("\033[1;31mPlease choose an option! (0-%d):\033[1;m ", i);
int choice;
scanf("%d", &choice);
//if answer is not an option, try again
if (choice > i){
menu(opts, msg);
}
return choice-1;
}
int draw(Cell* c, int width, int height, int borderM){
char ch;
bool playing = false;
while(1){
display(c, width, height);
printf("\033[1;31m\n0: Return\nTab: Continue\nSpace: Play\nEsc: Exit\033[1;m ");
ch = getch();
if (ch == 27)
return -1;
else if(ch == 9){
update(c, width, height);
border(c, width, height, borderM);
continue;
}
else if (ch == 48){
return 0;
}
else if (ch == 32){
playing = true;
while (playing){
display(c, width, height);
update(c, width, height);
border(c, width, height, borderM);
printf("\033[1;31m\nPress [Space] to stop!\033[1;m ");
sleep(1);
if (kbhit()) {
ch = getch();
if (ch == 32)
playing = false;
}
}
}
}
return 1;
}
int mainMenu(int* width, int* height, int* borderM, int* initM){
//def menu options
char* initOpts[] = {"Dead", "Alive", "Random", "Automaton", "File", NULL};
char* borderOpts[] = {"Dead", "Alive", "Infinite", NULL};
//print main menu
clrscr();
printf("\033[1;31mMage of File by U+0466\n\033[1;m");
printf("(0) Exit\n");
printf("(1) Size: %d x %d\n", *width, *height);
if (*initM == 3) //automaton rule printing
printf("(2) Starting state: %s (Rule %d)\n", initOpts[*initM], rule);
else if (*initM == 4) //file path printing
printf("(2) Starting state: %s\n", path);
else
printf("(2) Starting state: %s\n", initOpts[*initM]);
printf("(3) Border mode: %s\n", borderOpts[*borderM]);
printf("(4) Play\n");
printf("\033[1;31mPlease choose an option! (0-4):\033[1;m ");
//get answers
int opt;
scanf(" %d", &opt);
int ans;
//create submenus
switch (opt){
case 0: return -1;
case 1: clrscr();
printf("\033[1;31mEnter Width and Height:\033[1;m \n");
scanf("%d %d", width, height);
break;
case 2:
ans = menu(initOpts, "How do you want to populate the field?");
if (ans == 3){
clrscr();
printf("\033[1;31mEnter rule for Cellular Automaton (0-255):\033[1;m ");
scanf("%d", &rule);
*initM = ans;
}
else if (ans == 4){
clrscr();
int tmp = fileIn(width, height);
while (tmp == -1){
printf("\033[1;31mInvalid file, try again?\033[1;m (Y/N) ");
char ans;
scanf("%c", &ans);
if (ans == 89)
tmp = fileIn(width, height);
else if(ans == 78)
break;
}
if (tmp != -1)
*initM = 4;
}
else
*initM = ans;
break;
case 3:
ans = menu(borderOpts, "How do you want to set the border?");
if (ans != -1)
*borderM = ans;
break;
case 4:
return 1;
}
return 0;
}
int main(){
int width = 20, height = 30;
int borderM = 1, initM = 2;
Cell** field;
field = NULL;
int state = 0;
//welcome();
while (1){
switch (state){
case -1:
free(field);
printf("Good Bye!");
return 0;
case 0:
state = mainMenu(&width, &height, &borderM, &initM);
if (state == 1){
free(field);
field = setup(width, height);
init(*field, width, height, initM);
border(*field, width, height, borderM);
}
break;
case 1:
state = draw(*field, width, height, borderM);
break;
}
}
return 0;
}
I want a Game of Life program in the console.
The parameters to initialize the 'field' can be changed in the mainMenu(). When all is done, it returns 1, and the drawing begins. When we return from draw, we get back to mainMenu(). This happens for ever in an infinite while loop. All functions work when tested separately. Problematic ones are border(), init(), etc.
What am I missing? I'm a beginner in C.
(fileIn() doesn't work proper, but that's not the issue here.)
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <conio.h>
//structure for storing the state of each cell
typedef struct Cell{
bool state, nState;
}Cell;
char path[50];
int rule;
Cell** setup(int width, int height){
//create field
int size = (width+2)*(height+2);
Cell** field = malloc(size * sizeof(Cell));;
if (field == NULL){
printf("Malloc Error!");
exit(-1);
}
return field;
}
void clrscr(){
printf("\e[1;1H\e[2J");
}
void welcome(){
clrscr();
printf("This program was created by:\033[1;31m\n U+0466.\033[1;m\n");
sleep(2);
}
void display(Cell* c, int width, int height){
clrscr();
int size = (width+2)*(height+2);
printf("\n");
for (int i = 0; i <(width*2-32)/2; i++)
printf(" ");
printf("\033[1;31mThe Game of Life on a %dx%d grid.\033[1;m", width, height);
//draw the field
for (int i = 0; i < size; i++){
if (i % (width+2) == 0)
printf("\n");
//skip border
if (i < width+2){
printf("X ");
continue;}
else if (i >= (size-(width+2))){
printf("X ");
continue;}
else if ((i+1) % (width+2) == 0){
printf("X ");
continue;}
else if ((i) % (width+2) == 0){
printf("X ");
continue;}
(c[i].state)? printf("O ") : printf(" ");
}
}
int fileIn(int* width, int *height){
//get path from user
printf("\033[1;31m");
printf("Please enter file path: ");
printf("\033[1;m");
scanf("%s", path);
//open file
FILE *fptr = fopen(path, "r");
if (fptr == NULL){
return -1;
}
//find the dimenisons of the field
char ch;
int h = 0;
int w = 0;
int wmax = 0;
for (ch = getc(fptr); ch != EOF; ch = getc(fptr)){
//0 or 1
if ((ch == 48)|(ch == 49)){
w++;
}
//linebreak
else if (ch == 10){
if (w!=0)
h++;
if (w > wmax){
wmax = w;
}
w = 0;
}
}
if ((wmax > 0)|(height > 0)){
*width = wmax;
*height = h + 1;
}
return 0;
}
void fileOut(Cell* c, int width, int height){
FILE *fptr = NULL;
fptr = fopen("out.txt", "w+");
//create a header
for (int i = 1; i <= width; i++)
fprintf(fptr, "-");
fprintf(fptr, "\nThe Game of Life, by U+O466\n");
for (int i = 1; i <= width; i++)
fprintf(fptr, "-");
//write file
int size = (width+2)*(height+2);
for (int i = 0; i < size; i++){
//skip border
if (i < width+2)
continue;
else if (i >= (size-(width+2)))
continue;
else if ((i+1) % (width+2) == 0)
continue;
else if ((i-3) % (width+2) == 0)
continue;
//write states
else if (i % (width+2) == 0)
fprintf(fptr, "\n");
((c+i)->state)? fprintf(fptr, "1"):fprintf(fptr, "0");
}
fclose(fptr);
}
void initFile(Cell* c, int w, int h){
int size = (w+2)*(h+2);
FILE* fptr = fopen(path, "r");
char ch;
//init to 0, than fill with data
for (int i = 0; i < size; i++)
(c+i)->state = 0;
for (int j = 1; j < h+1; j++){
for (int i = 1; i < w+1; i++){
ch = getc(fptr);
if (ch == 49){
(c+j*(w+2)+i)->state = 1;
}
//linebreak - doesnt handle 'empty' rows properly
else if ((ch == 10)){
break;
}
}
}
}
void initAuto(Cell* c, int width, int height){
//populate first row with 0, set center as 1
for (int i = 0; i < width+1; i++)
(c+i)->state = 0;
(width%2 == 0)? ((c+((width+1)/2))->state = 1) : ((c+(width/2))->state = 1);
for (int j = 1; j < height+1; j++){
for (int i = 1; i < width+1; i++){
char pat = 0;
//find out current pattern
pat |= ((c+(j*(width+2))+i-1)->state << 2 );
pat |= ((c+(j*(width+2))+i)->state << 1 );
pat |= ((c+(j*(width+2))+i+1)->state);
//set next rows state, based on the rule
(c+((j+1)*(width+2))+i)->state = (rule >> pat) & 1;
}
}
}
void init(Cell* c, int w, int h, int mode){
int size = (w+2)*(h+2);
switch (mode){
case 0:
case 1:
for (int i = 0; i < size; i++)
(c+i)->state = mode;
break;
case 2:
for (int i = 0; i < size; i++)
(c+i)->state = rand()%2;
break;
case 3:
initAuto(c, w, h);
break;
case 4:
initFile(c, w, h);
break;
}
}
void border(Cell *c, int w, int h, int mode){
w+=2;
h+=2;
for (int j = 0; j < h; j++){
for (int i = 0; i < w; i++){
if ((i==0) | (i == w-1) | (j==0) | (j == h-1)){
switch (mode){
case 0: //dead
case 1: //alive
(c+(w*j)+i)->state = mode;
break;
case 2: //tiling - corners not handled
if (i==0)
(c+(w*j))->state = (c+(w*j)+i-1)->state;
else if (i == w-1)
(c+(w*j)+i)->state = (c+(w*j)+1)->state;
if (j==0)
(c+i)->state = (c+(w*(h-2))+i)->state;
else if (j == h-1)
(c+(w*(h-1))+i)->state = (c+w+i)->state;
break;
}
(c+(w*j)+i)->nState = (c+(w*j)+i)->state;
}
}
}
}
void update(Cell* c, int w, int h){
w = w + 2;
h = h + 2;
int size = w*h;
for (int j = 1; j < h - 1; j++){
for (int i = 1; i < w + 1; i++){
//reset counter, then count neighbors
int nbors = 0;
nbors +=(c+(j-1)*w+(i-1))->state + //up-left
(c+(j-1)*w+(i)) ->state + //up
(c+(j-1)*w+(i+1))->state + //up-right
(c+(j) *w+(i-1))->state + //left
(c+(j) *w+(i+1))->state + //right
(c+(j+1)*w+(i-1))->state + //down-left
(c+(j+1)*w+(i)) ->state + //down
(c+(j+1)*w+(i+1))->state; //down-right
//decide next state based on current (rule: B3/S23)
if ((c+(j*w)+i)->state)
(c+(j*w)+i)->nState = (nbors == 3 || nbors == 2);
else if (nbors == 3)
(c+(j*w)+i)->nState = 1;
else
(c+(j*w)+i)->nState = 0;
}
}
//update all cells
for (int i = 0; i < size; i++)
(c+i)->state = (c+i)->nState;
}
int menu(char* opts[], char msg[]){
system("cls");
printf("\033[1;31m");
printf("%s\n", msg);
printf("\033[1;m");
//print options
printf("(0) Return\n");
int i = 0;
while (opts[i] != NULL){
printf("(%d) %s\n", i+1, opts[i]);
i++;
}
//get answer
printf("\033[1;31mPlease choose an option! (0-%d):\033[1;m ", i);
int choice;
scanf("%d", &choice);
//if answer is not an option, try again
if (choice > i){
menu(opts, msg);
}
return choice-1;
}
int draw(Cell* c, int width, int height, int borderM){
char ch;
bool playing = false;
while(1){
display(c, width, height);
printf("\033[1;31m\n0: Return\nTab: Continue\nSpace: Play\nEsc: Exit\033[1;m ");
ch = getch();
if (ch == 27)
return -1;
else if(ch == 9){
update(c, width, height);
border(c, width, height, borderM);
continue;
}
else if (ch == 48){
return 0;
}
else if (ch == 32){
playing = true;
while (playing){
display(c, width, height);
update(c, width, height);
border(c, width, height, borderM);
printf("\033[1;31m\nPress [Space] to stop!\033[1;m ");
sleep(1);
if (kbhit()) {
ch = getch();
if (ch == 32)
playing = false;
}
}
}
}
return 1;
}
int mainMenu(int* width, int* height, int* borderM, int* initM){
//def menu options
char* initOpts[] = {"Dead", "Alive", "Random", "Automaton", "File", NULL};
char* borderOpts[] = {"Dead", "Alive", "Infinite", NULL};
//print main menu
clrscr();
printf("\033[1;31mMage of File by U+0466\n\033[1;m");
printf("(0) Exit\n");
printf("(1) Size: %d x %d\n", *width, *height);
if (*initM == 3) //automaton rule printing
printf("(2) Starting state: %s (Rule %d)\n", initOpts[*initM], rule);
else if (*initM == 4) //file path printing
printf("(2) Starting state: %s\n", path);
else
printf("(2) Starting state: %s\n", initOpts[*initM]);
printf("(3) Border mode: %s\n", borderOpts[*borderM]);
printf("(4) Play\n");
printf("\033[1;31mPlease choose an option! (0-4):\033[1;m ");
//get answers
int opt;
scanf(" %d", &opt);
int ans;
//create submenus
switch (opt){
case 0: return -1;
case 1: clrscr();
printf("\033[1;31mEnter Width and Height:\033[1;m \n");
scanf("%d %d", width, height);
break;
case 2:
ans = menu(initOpts, "How do you want to populate the field?");
if (ans == 3){
clrscr();
printf("\033[1;31mEnter rule for Cellular Automaton (0-255):\033[1;m ");
scanf("%d", &rule);
*initM = ans;
}
else if (ans == 4){
clrscr();
int tmp = fileIn(width, height);
while (tmp == -1){
printf("\033[1;31mInvalid file, try again?\033[1;m (Y/N) ");
char ans;
scanf("%c", &ans);
if (ans == 89)
tmp = fileIn(width, height);
else if(ans == 78)
break;
}
if (tmp != -1)
*initM = 4;
}
else
*initM = ans;
break;
case 3:
ans = menu(borderOpts, "How do you want to set the border?");
if (ans != -1)
*borderM = ans;
break;
case 4:
return 1;
}
return 0;
}
int main(){
int width = 20, height = 30;
int borderM = 1, initM = 2;
Cell** field;
field = NULL;
int state = 0;
//welcome();
while (1){
switch (state){
case -1:
free(field);
printf("Good Bye!");
return 0;
case 0:
state = mainMenu(&width, &height, &borderM, &initM);
if (state == 1){
free(field);
field = setup(width, height);
init(*field, width, height, initM);
border(*field, width, height, borderM);
}
break;
case 1:
state = draw(*field, width, height, borderM);
break;
}
}
return 0;
}
Share
Improve this question
edited Nov 23, 2024 at 16:33
halfer
20.3k19 gold badges109 silver badges202 bronze badges
asked Nov 20, 2024 at 18:43
c51p152c51p152
92 bronze badges
7
|
Show 2 more comments
1 Answer
Reset to default 1In your setup function you use assign malloc(size * sizeof(Cell))
on a Cell**
type, if you wanted to make an array of Cell pointers you should use sizeof(Cell*)
or sizeof(*field)
.
发布者:admin,转转请注明出处:http://www.yc00.com/questions/1742336603a4424774.html
Cell** field = malloc(size * sizeof(Cell));;
- why does it have two semicolons? It seem to be allocating space for array ofCell
, but is assigning the result toCell**
instead ofCell*
. This type confusion alone can cause all sorts of bugs. – Eugene Sh. Commented Nov 20, 2024 at 18:49init(*field, ...)
? Why do you dereference the pointer here? It's the same asfield[0]
. And at that point, ``field[0]` doesn't actually point anywhere, so you pass an uninitialized pointer. I concur with @EugeneSh., you're using pointer-to-pointer in the wrong way. It should be onlyCell *
, notCell **
. – Some programmer dude Commented Nov 20, 2024 at 18:54