Question

I am new to programming and wrote a small Pong game for an assignment using multiple threads, one thread for each ball. This programs works really well on Ubuntu but when I run in on Red Hat all sorts of crazy characters appear on the screen randomly. It seem to happening quicker the more balls I use.

Here is the code. It is run by using "pong a b c d" for example which will produce balls on the screen using the character symbols as the ball. I am pretty sure the issue is inside the animate function as that is where all the refreshing and moving is done. But don't know what the issue is.

#include    <stdio.h>
#include    <curses.h>
#include    <pthread.h>
#include    <stdlib.h>
#include    <unistd.h>
#include    <fcntl.h>
#include    "Pong.h"

#define MAXBALL 10              /* limit of balls   */

int game_over   = 0;            /* game over flag   */
int num_msg;                    /* number of balls  */
int select_ball;                /* select ball      */

struct ppball   balls[MAXBALL]; /* array of balls   */
struct paddle   the_paddle ;    /* paddle           */
struct window   the_window ;    /* window           */
pthread_t       thrds[MAXBALL]; /* the threads      */

void    set_ticker();           /* the animation    */
void    paddle_move(int move);  /* the animation    */
void    wrap_up();              /* the animation    */
void    *animate();             /* the animation    */

pthread_mutex_t mx = PTHREAD_MUTEX_INITIALIZER;

int main(int ac, char *av[]){

    int i; /* for loops */
    select_ball = 0;

    if ( ac == 1 ){
        printf("usage: Pong char ..\n"); 
        exit(1);
    }

    num_msg = setup(ac-1,av+1,balls);

    /* draw the window to the screen */
    the_window.win = newwin(20, 60, 1, 1);
    wborder(the_window.win, 0, ' ', 0, 0, 0, 0, 0, 0);
    refresh();
    wrefresh(the_window.win);


    /* create all the threads */
    for( i=0 ; i<num_msg; i++ ){
        if ( pthread_create(&thrds[i], NULL, animate, &balls[i]) ){
            fprintf(stderr,"error creating thread");
            endwin();
            exit(0);
        }
    }


    while( game_over != 1 ){    /* the main loop */
        int c = getch();        /* grab char */

        if( c == 'Q' ) {
            game_over = 1;
        }
    else if( c == 'S' ){
        for(i=0 ; i<num_msg; i++){
            if( balls[i].stimer > INCREMENT ){
                balls[i].stimer = balls[i].stimer + INCREMENT;
            }
        }
    }
    else if( c == 'F' ){
        for(i=0 ; i<num_msg; i++){
            if( balls[i].stimer > INCREMENT ){
                balls[i].stimer = balls[i].stimer - INCREMENT;
            }
        }
    }
    else if( c == 'a' ){
        if( the_paddle.mid_x_pos >= TOP_ROW + 2){
            paddle_move(0);
        }
    }
    else if( c == 'z' ){
        if( the_paddle.mid_x_pos <= BOT_ROW-2){
            paddle_move(1);
        }   
    }
}

for ( i=0; i<num_msg; i++ ){
    pthread_cancel(thrds[i]);
}

wrap_up();
return 0;
}


void wrap_up(){
curs_set(1);
clear();
endwin();
}

int setup(int nstrings, char *strings[], struct ppball ball[]){
int num_msg = ( nstrings > MAXBALL ? MAXBALL : nstrings );
int i;

/* assign positions and direction to each ball */
srand(getpid());
for(i=0 ; i<num_msg; i++){
    ball[i].stimer = START_TIMER;
    ball[i].y_pos = rand() % 10 + 2;
    ball[i].x_pos = rand() % 10 + 5;
    ball[i].y_dir = ((rand()%2)?1:-1);  ;
    ball[i].x_dir = ((rand()%2)?1:-1);  ;
    ball[i].symbol = *strings[i];
}

/* set up paddle */
the_paddle.y_pos = PADDLE_Y_INIT;
the_paddle.top_x_pos = PADDLE_X_INIT-1;
the_paddle.mid_x_pos = PADDLE_X_INIT;
the_paddle.bot_x_pos = PADDLE_X_INIT+1;
the_paddle.symbol = PADDLE_SYMBOL ;

/* set up curses */
initscr();
crmode();
noecho();
clear();
curs_set(0);

return num_msg;

}

void *animate(void *arg)
{
struct ppball *info = arg;      /* point to info block  */
int i;
while( game_over != 1 ){

    usleep(info->stimer);

    if ( info->y_pos == TOP_ROW ){
        info->y_dir = 1 ; 
    } else if ( info->y_pos == BOT_ROW ){
        info->y_dir = -1 ;
    }

    /* check if ball hits paddle */
    if ( (info->y_pos == the_paddle.top_x_pos || info->y_pos == the_paddle.mid_x_pos || info->y_pos == the_paddle.bot_x_pos ) && info->x_pos == the_paddle.y_pos ){
        info->x_dir = -1;
    }

    if ( info->x_pos == LEFT_EDGE ){
        info->x_dir = 1 ;
    } else if ( info->x_pos == RIGHT_EDGE ){
        /* if ball hits right edge program ends */
        game_over = 1;
    }

    /* erase ball */
    mvaddch( info->y_pos, info->x_pos, BLANK );

    info->y_pos += info->y_dir ;    /* move */
    info->x_pos += info->x_dir ;    /* move */

    pthread_mutex_lock(&mx);        
    mvaddch( info->y_pos, info->x_pos, info->symbol );
    mvaddch( the_paddle.top_x_pos, the_paddle.y_pos, the_paddle.symbol );
    mvaddch( the_paddle.mid_x_pos, the_paddle.y_pos, the_paddle.symbol );
    mvaddch( the_paddle.bot_x_pos, the_paddle.y_pos, the_paddle.symbol );
    pthread_mutex_unlock(&mx);  

    mvprintw(LINES-3,3,"'a' and 'z' to move paddle");
    mvprintw(LINES-2,3,"'S' and 'F' dec/inc all ball speed.");
    mvprintw(LINES-1,3,"'s' and 'f' dec/inc %c speed, 'w' or 'r' to change ball",   balls[select_ball].symbol );

    refresh();  
}
    mvprintw(10,10,"Game Over");
}

void paddle_move(int move)
{
pthread_mutex_lock(&mx);
mvaddch( the_paddle.top_x_pos, the_paddle.y_pos, BLANK );
mvaddch( the_paddle.mid_x_pos, the_paddle.y_pos, BLANK );
mvaddch( the_paddle.bot_x_pos, the_paddle.y_pos, BLANK );

if( move == 0 && the_paddle.mid_x_pos >= TOP_ROW + 1){
    the_paddle.top_x_pos--;
    the_paddle.mid_x_pos--;
    the_paddle.bot_x_pos--;
    refresh();  
}
else if( move == 1 && the_paddle.mid_x_pos <= BOT_ROW-3){
    the_paddle.top_x_pos++;
    the_paddle.mid_x_pos++;
    the_paddle.bot_x_pos++;
}
mvaddch( the_paddle.top_x_pos, the_paddle.y_pos, the_paddle.symbol );
mvaddch( the_paddle.mid_x_pos, the_paddle.y_pos, the_paddle.symbol );
mvaddch( the_paddle.bot_x_pos, the_paddle.y_pos, the_paddle.symbol );
refresh();  
pthread_mutex_unlock(&mx);
}

I know it is a lot of code to read through. I am happy to cut it down.

here is what it looks like on Red Hat enter image description here

This is what it looks like on Ubuntu. enter image description here

Was it helpful?

Solution

Your code implicitly operates on the "current screen" in ncurses, a shared object, which is not thread safe. There is a mutex, but you are not consistently holding it across all calls to ncurses. This might not be the issue you're seeing, but it's a gaping problem.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top