0

I'm programming a Snake Game in C++ in order to learn more about C ++. I made the game using the paradigms of object-oriented programming, but the design of the drawGame function is not working properly.

Testing the drawGame function, I'm getting this as result:

enter image description here

void Game::drawGame(int fruitxpos, int fruitypos, std::vector<int>& xposition, std::vector<int>& yposition, int snakesize){
    system("cls");
    int printedflag = 0;
    for(int j = 1; j <= ysize; j++){
        if(j == 1 || j == ysize){
            for(int i = 1; i <= xsize; i++){
                std::cout << "#";
            }
            std::cout << "\n";
        }       
        else{
            for(int i = 1; i <= xsize; i++){
                if(i == 1 || i == xsize){
                    std::cout << "#";
                }
                else{
                    for(int n = 0; n <= snakesize; n++){
                        if(i == xposition[n] && j == yposition[n]){
                            std::cout << "o";
                            printedflag = 1;
                        }
                        else{
                            printedflag = 0; 
                        }
                    }
                    if(!printedflag){
                        if(i == fruitxpos && j == fruitypos){
                            std::cout << "F";
                        }
                        else{
                            std::cout << " ";
                        }
                    }   
                }
            }
            std::cout << "\n";
        }
    }
}

As you can see, It is printing a blank space after every snake block. Could someone explain me what is wrong?

Steffen Moritz
  • 7,277
  • 11
  • 36
  • 55
  • 4
    Hi, welcome to Stack Overflow. Have you tried stepping through your code with a debugger? That's an essential programmer skill, and would probably give you the answer faster than typing up the question here. – Angew is no longer proud of SO Jul 16 '19 at 18:57
  • 2
    What he said ^. On the rare chance that a debugger didn't come with the compiler, strongly consider switching compilers. – user4581301 Jul 16 '19 at 18:57
  • 2
    Answer below shook another comment loose: C++ IO streams are a poor choice for doing work like this because they're streams and output like this doesn't make a good stream. Consider using a [terminal library like ncurses](https://en.wikipedia.org/wiki/Ncurses) and taking advantage of writing over stuff you want cleared "Dirty Rectangle" style. – user4581301 Jul 16 '19 at 19:15
  • @user4581301 If you draw into a matrix of characters, you can then use the C++ I/O streams with no problems, especially if an extra column is added for newlines and the last matrix cell is '\0'. – Thomas Matthews Jul 16 '19 at 19:32
  • @ThomasMatthews Yes, but then you have to put up with the old screen content being pushed up off the top of the screen by the new content or go outside Standard C++ to do a screen clear (and probably get a flickering problem). – user4581301 Jul 16 '19 at 19:51

2 Answers2

2

IMHO, your program would be better by using a 2d matrix of characters. Your main program will write into the matrix. A print function would print the matrix. This eliminates the worry of having to use X,Y positioning on the console.

If you design the matrix as a {contiguous} array of characters, you can add an extra column for the newline character. The last cell of the matrix would be the nul character, '\0'. This allows you to print the matrix as if it was one long C-Style string.

Some example code:

const unsigned int MAX_COLUMNS = 20U + 1U; // +1 column for newline
const unsigned int MAX_ROWS    = 20U;
char game_board[MAX_ROWS][MAX_COLUMNS];

void Clear_Board()
{
    // Fill the board with spaces (blanks).
    memset((char *) &game_board[0][0], ' ', sizeof(game_board));

    // Draw the top and bottom borders
    for (unsigned int column = 0; column < MAX_COLUMNS; ++column)
    {
        game_board[0][column] = '#';
        game_board[MAX_ROWS - 1][column] = '#';
    }

    // Draw the side borders
    const unsigned int LEFT_COLUMN = 0U;
    const unsigned int RIGHT_COLUMN = MAX_COLUMNS - 2U;
    const unsigned int NEWLINE_COLUMN = MAX_COLUMNS - 1U;
    for (unsigned int row = 0; row < MAX_ROWS; ++row)
    {
        game_board[row][LEFT_COLUMN] = '#';
        game_board[row][RIGHT_COLUMN] = '#';
        game_board[row][NEWLINE_COLUMN] = '\n';
    }

    // Set the terminating nul character
    game_board[MAX_ROWS - 1][MAX_COLUMNS - 1] = '\0';
}

Printing the board:

std::cout << game_board;

or

std::cout.write(&game_board[0][0], sizeof(game_board) - 1U); // Don't print terminating nul.

Checking for fruit encounter

unsigned int snake_head_row = 10U;    // Example position.
unsigned int snake_head_column = 5u;  
const char FRUIT_CHAR = 'F';
//...
if (game_board[snake_head_row][snake_head_column] == FRUIT_CHAR)
{
  //...
}

Notice that the fruit encounter doesn't need printing.

IMHO, you should have the snake as a container of coordinates (row, column). Each body cell is an item in the container. If the snake grows, append a coordinate to the container. Drawing the snake involves traversing the container, placing snake characters at the appropriate game_board positions (then drawing the board).

A game board helps remember the position of the snake body and any other items on the board. You can use a console positioning library and put the characters at their coordinates, too.

Thomas Matthews
  • 56,849
  • 17
  • 98
  • 154
-1

Suggesting to split your Game::drawGame into sub-functions. Also, probably the method should not get all the parameters you pass to it as they all should better be class members.

void Game::drawGame() {
    drawBoard();
    drawSnake();
    drawFruits();
}

Then you may want to use gotoxy that can be found on the web, e.g. here, e.g. for windows:

void gotoxy(int x, int y) {
    HANDLE hc = GetStdHandle(STD_OUTPUT_HANDLE);  // get console handle 
    COORD cursor = { x, y };
    SetConsoleCursorPosition(hc, cursor);  // set new cursor position
}
Amir Kirsh
  • 12,564
  • 41
  • 74