Click here to Skip to main content
15,887,267 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
/* Rasal Amarasurya */
/* 21304178 */

#include <stdio.h>
#include <ncurses.h>
#include <stdlib.h> /* For malloc, free */
#include "game.h"   /* Include the game header */

void initializeGame(char **grid, int rows, int cols, FILE *file, int *playerRow, int *playerCol, int *win, int *collision) {
    int i, j, val;

    *win = 0; /* Initialize game as not won */
    *collision = 0; /* Initialize as no collision */

    /* Read grid configuration from the file */
    for (i = 0; i < rows; i++) {
        for (j = 0; j < cols; j++) {
            if (fscanf(file, "%d", &val) != 1) {
                /* If reading the value fails, exit */
                exit(EXIT_FAILURE);
            }
            switch (val) {
                case 0:
                    grid[i][j] = EMPTY_SPACE; /* Empty space */
                    break;
                case 1:
                    grid[i][j] = ROAD; /* Road */
                    break;
                case 2:
                    /* Initially assuming the car faces right (east) as per initial requirements */
                    grid[i][j] = CAR_RIGHT; /* Car facing right (east) */
                    break;
                case 3:
                    grid[i][j] = PLAYER; /* Player's position */
                    *playerRow = i; /* Set player's row position */
                    *playerCol = j; /* Set player's column position */
                    break;
                case 4:
                    grid[i][j] = GOAL; /* Goal position */
                    break;
                /* Optionally, add cases for different car directions if they're specified in the file */
                case 5:
                    grid[i][j] = CAR_LEFT; /* Car facing left */
                    break;
                case 6:
                    grid[i][j] = CAR_UP; /* Car facing up */
                    break;
                case 7:
                    grid[i][j] = CAR_DOWN; /* Car facing down */
                    break;
                /* Add more cases as needed */
            }
        }
    }

    /* Set borders explicitly */
    for (i = 0; i < rows; i++) {
        grid[i][0] = BORDER; /* Set left border */
        grid[i][cols - 1] = BORDER; /* Set right border */
    }
    for (j = 0; j < cols; j++) {
        grid[0][j] = BORDER; /* Set top border */
        grid[rows - 1][j] = BORDER; /* Set bottom border */
    }
}




void displayGame(char **grid, int rows, int cols) {
    int i, j; /* Declaration at the top for C89 compliance */

    clear(); /* Clear the screen to avoid overlapping prints */
    for (i = 0; i < rows; i++) {
        for (j = 0; j < cols; j++) {
            move(i, j); /* Move cursor to the correct position */
            printw("%c", grid[i][j]); /* Print the character in the grid cell */
        }
    }

    /* Display controls below the grid */
    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(); /* Refresh the screen to reflect the updates */
}

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;

                /* Determine the next position and direction based on the current direction */
                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;
                }

                /* Update the grid if the car is moving */
                if (nextI != i || nextJ != j) {
                    grid[nextI][nextJ] = nextDirection;
                    grid[i][j] = EMPTY_SPACE;
                }
            }
        }
    }
}




/* Add a flag for collision detection */
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;

    /* Determine the new position based on input, with improved boundary checks */
    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;
    }

    /* Ensure new position is within the grid boundaries */
    if (newPlayerRow >= 0 && newPlayerRow < rows && newPlayerCol >= 0 && newPlayerCol < cols) {
        /* Check if the new position is not a collision (e.g., empty space, road, or goal) */
        if (grid[newPlayerRow][newPlayerCol] == EMPTY_SPACE || grid[newPlayerRow][newPlayerCol] == ROAD) {
            /* Valid move: update player's position on the grid */
            grid[*playerRow][*playerCol] = EMPTY_SPACE;  
            *playerRow = newPlayerRow;
            *playerCol = newPlayerCol;
            grid[newPlayerRow][newPlayerCol] = PLAYER;      
            *collision = 0;  
        } else if (grid[newPlayerRow][newPlayerCol] == GOAL) {
            /* Player reaches the goal */
            *win = 1;
        } else {
            /* Handle collision (e.g., obstacle or border) */
            *collision = 1;
        }
    } else {
        /* The new position is out of bounds, handle it as a collision */
        *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; /* Changed to int to conform with return type of getch() in C89 */
    int win = 0, collision = 0;
    int playerRow = -1, playerCol = -1; /* Initial values to be set by initializeGame */
    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;
        }
    }

    /* Adjusted call to initializeGame */
    initializeGame(gameGrid, mapRows, mapCols, file, &playerRow, &playerCol, &win, &collision);
    fclose(file);

    /* Basic ncurses setup */
    initscr();
    cbreak();
    noecho();
    keypad(stdscr, TRUE); /* Enable keyboard mapping */
    curs_set(0); /* Hide the cursor */

    do {
        displayGame(gameGrid, mapRows, mapCols);
        move = getch(); /* Get player input as int */

        if (move == 'w' || move == 'a' || move == 's' || move == 'd') {
            movePlayer(gameGrid, mapRows, mapCols, move, &win, &playerRow, &playerCol, &collision);
            displayGame(gameGrid, mapRows, mapCols); 
        }

        collision = 0; /* Ensure collision is correctly reset here only if needed */

        /* Collision handling */
        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); /* Delay to control game speed */

    } while (move != 'q');

    endwin(); /* End curses mode */

    for (i = 0; i < mapRows; i++) {
        free(gameGrid[i]);
    }
    free(gameGrid);

    return 0;
}


#ifndef GAME_H
#define GAME_H

#include <stdio.h> /* Include for FILE type */

/* Definitions for various game elements */
#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 */

/* Function prototypes */
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.
Posted
Updated 29-Mar-24 8:28am
v5
Comments
CPallini 28-Mar-24 7:15am    
Could you also provide a working configuration file, in order to enable us to run and test it?
DosNecro 28-Mar-24 7:29am    
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

the first line represents the dimensions of the grid (rows and columns)
1 represents the road (path the car will take)
2 represents the car
3 for the player
4 for the goal

You failed to properly initialize playerRow, playerRow variables.
Put a breakpoint at movePlayer function in order to see that live.
 
Share this answer
 
Comments
DosNecro 29-Mar-24 5:49am    
I updated the question with a few changes I made to the movePlayer function. However, the game logic is executing without errors, but the expected changes (movement of the player or updates to the game grid) aren't visually reflected on the terminal.
CPallini 29-Mar-24 7:16am    
You stated: "Ensure variables playerRow, playerCol, win, and collision are initialized correctly"
However I see no initialization taking place in initalizeGame. Did you update the posted code?
DosNecro 29-Mar-24 13:34pm    
My mistake, I updated the question now. I was able to get the player movement and initialization to work. However, the car movement that is supposed to happen on every player movement is not working.
CPallini is right! The comment in the source code says that the initializeGame() function should determine the player position.
C
int playerRow, playerCol; /* Removed the initialization here as it's set in initializeGame */

However, the prototype of the function suggests that this is not possible:
C
void initializeGame(char **grid, int rows, int cols, FILE *file);

I would expect something like this here:
C
void initializeGame(char** grid, int rows, int cols, int *player_x, int *player_y, FILE* file);
 
Share this answer
 
v2

This content, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)



CodeProject, 20 Bay Street, 11th Floor Toronto, Ontario, Canada M5J 2N8 +1 (416) 849-8900