0

I am creating a Snake game on Linux. I was using iostream to that point when I realized I need a getch() function included within the ncruses.h. I had a draw method which was printing all board elements to the console while using std::cout and every character was displaying correctly. I had the whole board. But when I switched to ncurses and rewrite all the std::cout to printf(), then the program starts to write just a parts of the board. It is all done in while cycle and every cycle board is redrawn.

I am a newbie so please be kind.

Here is my main.cpp

#include <iostream>
#include <chrono>
#include <thread>
#include <ncurses.h>
#include <stdio.h>

#include "board.h"
#include "consoledraw.h"
#include "snake.h"
#include "fruit.h"

void sleepThread()
{
static constexpr int SLEEP_MS = 1000;

std::this_thread::sleep_for(std::chrono::milliseconds(SLEEP_MS));
}

int main()
{
std::cout << "\e[8;30;140t"; // set console window size

Board _board;
Snake _snake;
consoledraw draw;
Fruit _fruit;

_board.setBoardSize(64, 36); // cca 16:9
_board.setDefaultItemType();
_board.makeSnake(_snake);
_board.setSnakeOnBoard(_snake);
_board.makeFruit(_fruit);
_board.setFruitOnBoard(_fruit);

int score = 0;

initscr();
setlocale(LC_ALL, "");
// raw();
cbreak();
curs_set(0);
noecho();
scrollok(stdscr, TRUE);
keypad(stdscr, TRUE);

while (true)
{
    draw.redraw(_board);
    // printf("Your score is: \r\v"); // TODO << score << std::endl;
    sleepThread();
    _board.clearBoard();
    _snake.moveSnake(_snake);

    if (_snake.checkSelfCollision(_snake) == true)
    {
        draw.clearConsole();
        printf("Game Over!\n");
        return 0;
    };

    if (_snake.eatFood(_fruit) == true)
    {
        _board.makeFruit(_fruit);
        _board.setFruitOnBoard(_fruit);
        score++;
    }
    _board.setSnakeOnBoard(_snake);
    _board.setFruitOnBoard(_fruit);
}
endwin();
return 0;
}

And here is my consoledraw.cpp

#include "consoledraw.h"

consoledraw::consoledraw()
{

}

void consoledraw::redraw(Board &board)
{
clearConsole();
drawBoard(board);
}

void consoledraw::clearConsole()
{
// Kompletne vymaze celou konzoli a nasrtavi kurzor do leveho horniho rohu.
refresh();
}

void consoledraw::drawBoard(Board &board)
{
auto b = board.getBoard();

// Top border line
drawHorizontalBorderLine(b[0].size(), true);

// Board
for (unsigned int y = 0; y < b.size(); y++)
{
    printf(LINE_VERTICAL); // border line

    for (unsigned int x = 0; x < b[y].size(); x++)
    {
        drawSymbol(b[y][x]);
    }
    printf(LINE_VERTICAL"\r\v"); // border line + newline
}

// Down border line
drawHorizontalBorderLine(b[0].size(), false);
}

void consoledraw::drawSymbol(ItemType itemType)
{
if (itemType == ItemType::Empty)
{
    printf(BLOCK_TRANSPARENT);
}
else if (itemType == ItemType::Tsss)
{
    printf(BLOCK_MEDIUM_SHADE);
}
else if (itemType == ItemType::Food)
{
    printf(BLOCK_LIGHT_SHADE);
}
else if (itemType == ItemType::TsssHead)
{
    printf(BLOCK_DARK_SHADE);
}
}

void consoledraw::drawHorizontalBorderLine(unsigned long boardWidth, bool isTop)
{
// Left corner
if (isTop)
{
    printf(LINE_UPLEFT_CORNER);
}
else
{
    printf(LINE_DOWNLEFT_CORNER);
}

// Line
for (unsigned int i = 0; i < boardWidth; i++)
{
    printf(LINE_HORIZONTAL);
}

// Right corner
if (isTop)
{
    printf(LINE_UPRIGHT_CORNER);
}
else
{
    printf(LINE_DOWNRIGHT_CORNER);
}

printf("\r\v");
}

Here is what I get when using ncurses:

enter image description here

And here is what I get using iostream:

enter image description here

Christophe
  • 54,708
  • 5
  • 52
  • 107
  • 4
    Do not use `iostream`! ncurses has its own input/output methods. Take a look at [this awesome tutorial](http://www.cs.ukzn.ac.za/~hughm/os/notes/ncurses.html#output) for more. – scohe001 Jan 07 '20 at 20:38
  • 2
    In general do not mix different methods of IO. Pick one and stick to it. It's not worth sorting out the bugs if two different IO methods clash, and it's not worth the performance hit if they spend time making sure that they don't clash. – user4581301 Jan 07 '20 at 20:51

1 Answers1

3

The C library (printf()) and the C++ library (std::cout) buffer their output -- although by default the C and C++ library synchronize their buffers.

The ncurses library does raw (unbuffered) output, sometimes wrapped with in-band control sequences. If you mix output to the same device through the C or C++ library and the ncurses library, you're going to get garbage on your screen. Just like you have.

You should use only either the ncurses library or the standard library to handle input/output from the terminal. Do not mix them.

Stephen M. Webb
  • 1,512
  • 9
  • 15
  • I thought that you can not mix (printf()) and (std::cout) only when printing into default "stdscr" window. I am using the std::cout just when resizing the console window and also doing that away from this mentioned window. Definitely will try your suggestion. Thank you! I will try to get rid of the iostream completely and then will post the result of my try and let you know if that works. – BadaBudaBudu Jan 07 '20 at 21:05
  • So I completely get rid of the iostream and still have the same issue. Please see the link: https://ibb.co/n3VpZhr – BadaBudaBudu Jan 07 '20 at 21:12
  • Edit: It will draw the whole board, but I am only able to see that in previous cycles. Not in the currently drawn board. – BadaBudaBudu Jan 07 '20 at 21:14
  • Either 1) You're calling `redraw(board)` at the beginning of your loop, not the end, or 2) You need to flush a buffer somewhere. – Mark Storer Jan 07 '20 at 21:25
  • I tried the first option to call redraw(board) as the end and it didn't work. Still getting those random part of the board on the screen. As for the second option, I think I understand what did you mean, but do not know how exactly to do that. I have found this page, where it is pretty good explained, but again, I can't use iostream which is sort of forbidden now due to ncureses use. https://www.geeksforgeeks.org/buffer-flush-means-c/ – BadaBudaBudu Jan 07 '20 at 21:36