Click here to Skip to main content
15,887,027 members
Please Sign up or sign in to vote.
0.00/5 (No votes)
I'm currently working on a grid-based game in C where I have roads represented by dots ('.') and a player represented by 'P'. The goal is for the player to reach the destination ('G'). However, I'm facing a challenge in keeping the roads visible as the player moves through them.

Problem Description:

The grid is initialized with roads and borders, but the roads vanish when the player moves over them.
Despite my attempts to preserve road cells during player movement in the movePlayer function, the roads still disappear.

#include <stdio.h>
#include <stdlib.h>
#include <ncurses.h>
#include "traffic.h"

void initializeGame(char **grid, int rows, int cols) {
    int i, j;  /* Declare loop control variables */
    for (i = 0; i < rows; i++) {
        for (j = 0; j < cols; j++) {
            if (i == 0 || i == rows - 1 || j == 0 || j == cols - 1) {
                grid[i][j] = '*'; /* Set border cells to "*" */
            } else if ((i % 2 == 0) && (j > 0 && j < cols - 1)) {
                grid[i][j] = '.'; /* Set road cells to "." on even rows */
            } else {
                grid[i][j] = ' '; /* Set other cells inside borders to empty */
            }
        }
    }

    /* Place the player at the top-left corner and the goal at the bottom-right corner */
    grid[1][1] = 'P';
    grid[rows - 2][cols - 2] = 'G';
}


/* Function to display the game grid with borders */
void displayGame(char **grid, int rows, int cols, int prevPlayerRow, int prevPlayerCol) {
    int i, j;  /* Declare loop control variables */
    for (i = 0; i < rows; i++) {
        for (j = 0; j < cols; j++) {
            /* Move the cursor to the current cell */
            move(i, j);

            /* Print the content of the grid */
            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(); /* Refresh the screen using ncurses */
}

void movePlayer(char **grid, int rows, int cols, int move, int *win, int *prevPlayerRow, int *prevPlayerCol) {
    int i, j, playerRow, playerCol;  /* Declare loop control variables */

    /* Find the current position of the player */
    for (i = 0; i < rows; i++) {
        for (j = 0; j < cols; j++) {
            if (grid[i][j] == 'P') {
                playerRow = i;
                playerCol = j;
            }
        }
    }

    /* Clear the player's previous position */
    grid[*prevPlayerRow][*prevPlayerCol] = ' ';

    /* Move the player based on the user's input */
    switch (move) {
        case 'w':
            if (playerRow > 1) {
                playerRow--;
            }
            break;
        case 'a':
            if (playerCol > 1) {
                playerCol--;
            }
            break;
        case 's':
            if (playerRow < rows - 2) {
                playerRow++;
            }
            break;
        case 'd':
            if (playerCol < cols - 2) {
                playerCol++;
            }
            break;
        default:
            /* Invalid move */
            break;
    }

    /* Set the player's new position */
    grid[playerRow][playerCol] = 'P';

    /* Update the previous player position */
    *prevPlayerRow = playerRow;
    *prevPlayerCol = playerCol;

    /* Check if the player reached the goal */
    if (playerRow == rows - 2 && playerCol == cols - 2) {
        *win = 1; /* Set win flag to 1 */
    }
}



/* Main function, entry point of the program */
int main(int argc, char *argv[]) {
    /* Check the number of command-line arguments */
    if (argc != 3) {
        printf("Usage: %s <map_row> <map_col>\n", argv[0]);
        return 1;
    }

    /* Parse command-line arguments */
    int mapRows = atoi(argv[1]);
    int mapCols = atoi(argv[2]);

    /* Check for valid map size */
    if (mapRows < 3 || mapRows % 2 == 0 || mapCols < 5) {
        printf("Invalid map size. Please ensure <map_row> is an odd number >= 3 and <map_col> is >= 5.\n");
        return 1;
    }

    /* Dynamically allocate memory for the 2D char array */
    char **gameGrid = (char **)malloc(mapRows * sizeof(char *));
    int i;  /* Declare loop control variable */
    for (i = 0; i < mapRows; i++) {
        gameGrid[i] = (char *)malloc(mapCols * sizeof(char));
    }

    /* Initialize the game grid with "*" and place the player and goal inside borders */
    initializeGame(gameGrid, mapRows, mapCols);

    initscr(); /* Initialize ncurses */

    timeout(0); /* Set non-blocking input */

    char move;
    int win = 0; /* Win flag */
    int prevPlayerRow = 1; /* Initialize with player's initial row */
    int prevPlayerCol = 1; /* Initialize with player's initial column */

    do {
        /* Display the updated game grid with borders */
        displayGame(gameGrid, mapRows, mapCols, prevPlayerRow, prevPlayerCol);

        /* Get user input for player movement */
        move = getch(); /* Get the key without waiting for 'Enter' */

        /* Move the player based on user input */
        movePlayer(gameGrid, mapRows, mapCols, move, &win, &prevPlayerRow, &prevPlayerCol);

        /* Check if the player has won */
        if (win) {
            clear(); /* Clear the screen */
            printw("Congratulations! You have cleared the level.\n");
            printw("Press any key to exit.\n");
            refresh(); /* Refresh the screen to display the message */
            getch(); /* Wait for a key press before ending the program */
        }

    } while (move != 'q' && !win); /* Loop until the user enters 'q' or wins */

    endwin(); /* End ncurses */

    /* Free the dynamically allocated memory */
    for (i = 0; i < mapRows; i++) {
        free(gameGrid[i]);
    }
    free(gameGrid);
    

    return 0; /* Properly reach the end of the main function */
}


What I have tried:

I've reviewed the movePlayer function to ensure that the road cells are not modified during player
I attempted to separate the logic for road cells and player movement but still face the disappearing roads issue.
Posted

Create a global variable called lastSurfaceType and initialise it to "road".
When you move a player, set the square it is on to lastSurfaceType before you do the move, then set the variable to the type the player is about to move to.

That way, next time you move the player you have the right surface type ready to replace the "P" you are about to move.
 
Share this answer
 
Comments
DosNecro 21-Feb-24 4:44am    
I tried implementing this, however, now every time the player moves, a player (P) is printed on the previous position the player was in.
OriginalGriff 21-Feb-24 5:24am    
That's because you are saving the lastSurfaceType *after* you move not before, or not restoring it before you save the new location - without your code I can't tell!

Use the debugger to look at both locations, and follow exactly what you code is doing while it runs.
Quote:
How do I prevent roads from disappearing when the player moves through them in a grid-based game? - C programming

Replace
C++
/* Clear the player's previous position */
grid[*prevPlayerRow][*prevPlayerCol] = ' ';

with
C++
/* Clear the player's previous position */
grid[*prevPlayerRow][*prevPlayerCol] = '.';

When you remove the player from a cell, make it a road rather than emptying it.

[Edit]
Quote:
I tried implementing this. However, this time, every time the player moves, a road ('.') is printed on the grid of the previous position of the player.

The most simple solution is to not store the position of player on the grid. Just store coordinates of player.
When you display grid:
Java
' pseudocode
for each cell
  check if coordinates match player position
    display player
  else
    display background
  end if
next
 
Share this answer
 
v2
Comments
DosNecro 21-Feb-24 4:41am    
I tried implementing this. However, this time, every time the player moves, a road ('.') is printed on the grid of the previous position of the player.
Patrice T 21-Feb-24 4:50am    
Because the player can walk off roads ?
When did you tell us ?
merano99 21-Feb-24 16:51pm    
In fact, he wrote: "as the player moves through them."
Obviously, according to the source code in movePlayer(), the roads do not play a role in relation to the player, as the player should be able to move through them.
It is therefore background. This means that the previous state should be saved and restored when the player moves away.
This can be realized with a static variable in movePlayer without major changes.
C
void movePlayer(char** grid, int rows, int cols, int move, int* win, int* prevPlayerRow, int* prevPlayerCol) 
{
  static char p_bgnd = ' ';

  // ...

  /* Clear the player's previous position */
  grid[*prevPlayerRow][*prevPlayerCol] = p_bgnd;

  // ...
  
  /* Set the player's new position */
  p_bgnd = grid[playerRow][playerCol];
  grid[playerRow][playerCol] = 'P';

  // ...
}

By the way, I have tested the game with the pdcurses lib and VS under Windows and it works.
 
Share this answer
 

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