#include <stdio.h>
#include <ncurses.h>
#include <stdlib.h>
#include "game.h"
void initializeGame(char **grid, int rows, int cols, FILE *file, int *playerRow, int *playerCol, int *win, int *collision) {
int i, j, val;
*win = 0;
*collision = 0;
for (i = 0; i < rows; i++) {
for (j = 0; j < cols; j++) {
if (fscanf(file, "%d", &val) != 1) {
exit(EXIT_FAILURE);
}
switch (val) {
case 0:
grid[i][j] = EMPTY_SPACE;
break;
case 1:
grid[i][j] = ROAD;
break;
case 2:
grid[i][j] = CAR_RIGHT;
break;
case 3:
grid[i][j] = PLAYER;
*playerRow = i;
*playerCol = j;
break;
case 4:
grid[i][j] = GOAL;
break;
case 5:
grid[i][j] = CAR_LEFT;
break;
case 6:
grid[i][j] = CAR_UP;
break;
case 7:
grid[i][j] = CAR_DOWN;
break;
}
}
}
for (i = 0; i < rows; i++) {
grid[i][0] = BORDER;
grid[i][cols - 1] = BORDER;
}
for (j = 0; j < cols; j++) {
grid[0][j] = BORDER;
grid[rows - 1][j] = BORDER;
}
}
void displayGame(char **grid, int rows, int cols) {
int i, j;
clear();
for (i = 0; i < rows; i++) {
for (j = 0; j < cols; j++) {
move(i, j);
printw("%c", grid[i][j]);
}
}
mvprintw(rows, 0, "Press 'w' to move up");
mvprintw(rows + 1, 0, "Press 'a' to move left");
mvprintw(rows + 2, 0, "Press 's' to move down");
mvprintw(rows + 3, 0, "Press 'd' to move right");
refresh();
}
void moveCars(char **grid, int rows, int cols) {
int i, j;
for (i = 0; i < rows; i++) {
for (j = 0; j < cols; j++) {
char cell = grid[i][j];
if (cell == CAR_RIGHT || cell == CAR_LEFT || cell == CAR_UP || cell == CAR_DOWN) {
int nextI = i, nextJ = j;
char nextDirection = cell;
switch (cell) {
case CAR_RIGHT:
if (j + 1 < cols && (grid[i][j + 1] == ROAD || grid[i][j + 1] == PLAYER)) nextJ++;
else if (i + 1 < rows && grid[i + 1][j] == ROAD) { nextI++; nextDirection = CAR_DOWN; }
else if (i - 1 >= 0 && grid[i - 1][j] == ROAD) { nextI--; nextDirection = CAR_UP; }
break;
case CAR_LEFT:
if (j - 1 >= 0 && (grid[i][j - 1] == ROAD || grid[i][j - 1] == PLAYER)) nextJ--;
else if (i + 1 < rows && grid[i + 1][j] == ROAD) { nextI++; nextDirection = CAR_DOWN; }
else if (i - 1 >= 0 && grid[i - 1][j] == ROAD) { nextI--; nextDirection = CAR_UP; }
break;
case CAR_DOWN:
if (i + 1 < rows && (grid[i + 1][j] == ROAD || grid[i + 1][j] == PLAYER)) nextI++;
else if (j + 1 < cols && grid[i][j + 1] == ROAD) { nextJ++; nextDirection = CAR_RIGHT; }
else if (j - 1 >= 0 && grid[i][j - 1] == ROAD) { nextJ--; nextDirection = CAR_LEFT; }
break;
case CAR_UP:
if (i - 1 >= 0 && (grid[i - 1][j] == ROAD || grid[i - 1][j] == PLAYER)) nextI--;
else if (j + 1 < cols && grid[i][j + 1] == ROAD) { nextJ++; nextDirection = CAR_RIGHT; }
else if (j - 1 >= 0 && grid[i][j - 1] == ROAD) { nextJ--; nextDirection = CAR_LEFT; }
break;
}
if (nextI != i || nextJ != j) {
grid[nextI][nextJ] = nextDirection;
grid[i][j] = EMPTY_SPACE;
}
}
}
}
}
int collision = 0;
void movePlayer(char **grid, int rows, int cols, char move, int *win, int *playerRow, int *playerCol, int *collision) {
int newPlayerRow = *playerRow;
int newPlayerCol = *playerCol;
switch (move) {
case 'w': newPlayerRow -= (newPlayerRow > 0) ? 1 : 0; break;
case 'a': newPlayerCol -= (newPlayerCol > 0) ? 1 : 0; break;
case 's': newPlayerRow += (newPlayerRow < rows - 1) ? 1 : 0; break;
case 'd': newPlayerCol += (newPlayerCol < cols - 1) ? 1 : 0; break;
}
if (newPlayerRow >= 0 && newPlayerRow < rows && newPlayerCol >= 0 && newPlayerCol < cols) {
if (grid[newPlayerRow][newPlayerCol] == EMPTY_SPACE || grid[newPlayerRow][newPlayerCol] == ROAD) {
grid[*playerRow][*playerCol] = EMPTY_SPACE;
*playerRow = newPlayerRow;
*playerCol = newPlayerCol;
grid[newPlayerRow][newPlayerCol] = PLAYER;
*collision = 0;
} else if (grid[newPlayerRow][newPlayerCol] == GOAL) {
*win = 1;
} else {
*collision = 1;
}
} else {
*collision = 1;
}
}
#include "game.h"
#include <ncurses.h>
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char *argv[]) {
FILE *file;
int mapRows, mapCols;
char **gameGrid;
int move;
int win = 0, collision = 0;
int playerRow = -1, playerCol = -1;
int i;
if (argc != 2) {
printf("Usage: %s <config_file>\n", argv[0]);
return 1;
}
file = fopen(argv[1], "r");
if (file == NULL) {
perror("Error opening file");
return 1;
}
if (fscanf(file, "%d %d", &mapRows, &mapCols) != 2) {
printf("Invalid file format. The first line must contain two integers.\n");
fclose(file);
return 1;
}
if (mapRows < 3 || mapCols < 5) {
printf("Invalid map size. Please ensure <map_row> is >= 3 and <map_col> is >= 5.\n");
fclose(file);
return 1;
}
gameGrid = (char **)malloc(mapRows * sizeof(char *));
if (!gameGrid) {
fprintf(stderr, "Memory allocation failed for gameGrid.\n");
return 1;
}
for (i = 0; i < mapRows; i++) {
gameGrid[i] = (char *)malloc(mapCols * sizeof(char));
if (!gameGrid[i]) {
fprintf(stderr, "Memory allocation failed for gameGrid row %d.\n", i);
while (--i >= 0) {
free(gameGrid[i]);
}
free(gameGrid);
return 1;
}
}
initializeGame(gameGrid, mapRows, mapCols, file, &playerRow, &playerCol, &win, &collision);
fclose(file);
initscr();
cbreak();
noecho();
keypad(stdscr, TRUE);
curs_set(0);
do {
displayGame(gameGrid, mapRows, mapCols);
move = getch();
if (move == 'w' || move == 'a' || move == 's' || move == 'd') {
movePlayer(gameGrid, mapRows, mapCols, move, &win, &playerRow, &playerCol, &collision);
displayGame(gameGrid, mapRows, mapCols);
}
collision = 0;
if (collision) {
clear();
printw("You have failed to clear the level.\nPress any key to exit.\n");
refresh();
getch();
break;
} else if (win) {
clear();
printw("Congratulations! You have cleared the level.\nPress any key to exit.\n");
refresh();
getch();
break;
}
napms(100);
} while (move != 'q');
endwin();
for (i = 0; i < mapRows; i++) {
free(gameGrid[i]);
}
free(gameGrid);
return 0;
}
#ifndef GAME_H
#define GAME_H
#include <stdio.h>
#define CAR_LEFT '<' /* Define symbol for car facing left */
#define CAR_RIGHT '>' /* Define symbol for car initially facing east and for general rightward movement */
#define CAR_UP '^' /* Define symbol for car facing upwards */
#define CAR_DOWN 'V' /* Define symbol for car facing downwards */
#define EMPTY_SPACE ' ' /* Symbol for empty space */
#define ROAD '.' /* Symbol for road */
#define PLAYER 'P' /* Symbol for player */
#define GOAL 'G' /* Symbol for goal */
#define BORDER '*' /* Symbol to represent the border */
void initializeGame(char **grid, int rows, int cols, FILE *file, int *playerRow, int *playerCol, int *win, int *collision);
void displayGame(char **grid, int rows, int cols);
void moveCars(char **grid, int rows, int cols);
void movePlayer(char **grid, int rows, int cols, char move, int *win, int *playerRow, int *playerCol, int *collision);
#endif /* GAME_H */
Configuration file:
10 15
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 3 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 1 1 1 1 0 0 0 1 1 1 1 0 0
0 0 1 0 0 1 0 0 0 1 0 0 1 0 0
0 0 1 0 0 1 1 1 1 1 1 1 1 0 0
0 0 1 0 0 0 0 0 0 1 0 0 0 0 0
0 0 1 0 0 0 0 0 4 1 0 0 0 0 0
0 0 1 1 1 1 1 2 1 1 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
0 0 0 0 0 0 0 0 0 0 0 0 0 0 0
What I have tried:
I'm developing a terminal-based game in C using the ncurses library. The game involves moving a player character within a grid. However, I've encountered an issue where the game unexpectedly exits back to the terminal upon attempting player movement, without any error messages or a segmentation fault.
After initializing the game state and entering the main game loop, pressing any of the movement keys ('w', 'a', 's', 'd') causes the game to exit immediately. This occurs without triggering any of my collision detection or win condition checks. There are no error messages or segmentation faults; the game simply ends and returns to the terminal.
A few things I attempted:
Ensure variables playerRow, playerCol, win, and collision are initialized correctly.
Use GDB to debug, setting watchpoints on playerRow and playerCol. The watchpoints trigger as expected, but no segmentation fault or error message is observed.
Checked memory allocation for gameGrid and confirmed no issues with ncurses initialization.