#
# comments start with a # sign
#
# each item in the make file consists of
# three things:  a product, its ingredients and how to make it
#
# gcc -o test test.c -lcurses


CC = gcc -g -Wall

pong: pong.o alarmlib.o paddle.o ball.o
	$(CC) pong.o alarmlib.o paddle.o ball.o -lcurses -o pong

pong.o: pong.c pong.h
	$(CC) -c pong.c
        
alarmlib.o: alarmlib.c alarmlib.h
	$(CC) -c alarmlib.c

paddle.o: paddle.c paddle.h
	$(CC) -c paddle.c

ball.o: ball.c ball.h
	$(CC) -c ball.c

______

/*//////
//
// pong.h
//
*/

#ifndef _PONG_H_
#define _PONG_H_

/* YES, and NO for clarity in function returns */
#define	YES	1
#define	NO	0

/* Error handling for fatal errors */
#define oops(errStr)  { perror(errStr); exit(1); }

#include        <unistd.h>
#include        <varargs.h>
#include	<curses.h>
#include	<stdio.h>
#include	<sys/time.h>
#include	<signal.h>


struct court  {int   left;
               int   top;
               int   right;
               int   bottom; };

#include        "alarmlib.h"
#include	"ball.h"
#include	"paddle.h"


/*
//     predefine all functions for smooth compile
*/     
void   set_up();
void   wrap_up();
void   update();
void   hideCursor();
void   drawCourt();
double getGravity();
void   hLine(int x, int y, int x2, char symbol);
void   vLine(int x, int y, int y2, char symbol);
double randomInt(int min, int max);
void   fatal(char *errStr);
int    getCourtRight();
int    getTopLimit();
int    getBottomLimit();
int    getLeftLimit();
int    getRightLimit();
int    getMidCourtX();
int    getMidCourtY();

#endif /* _PONG_H_ */



______


/*/////////
//
//  pong.c 
//
//      AUTHOR: Russell Lowke
//   CSCIE-215: Assignment Four
//
//       Usage: pong
//
//              use 'j' and 'k' keys to move paddle
//              use 'x' key to add more balls   (MAX 100)
//              use '<' and '>' keys to change gravity
//              use 'f' to speed game up
//              use 's' to slow game down
//              use 'q' to quit
//
// Description: Pong game!
//
//              A version of pong.
//              Objective is to get as many pong balls in play as possible.
//              Every four (4) times a ball is deflected by the paddle
//              a new pong ball is added.
//              How many pong balls can you keep in the air at one time?
//
//              If all balls get past the paddle 3 times, the game is over.
//
//
//      Method: Pong.c contains main, has all general fctns,
//              and also contains the parameters for the court (playing
//              area) in a static struct called mCourt.
//              key fctns are:
//
//                setUp     - establishes pong settings and vars
//                            sets an alarm to call update every [delay]
//                            milliseconds
//                main      - main loop simply reads keyboard input, letting
//                            alarm take care of the updates.
//                            on 'q' key pong wraps up all settings and
//                            memory via wrap_up.  ctrl 'c' is disabled to
//                            ensure wrap_up
//                wrap_up   - Cleans up all settings before exiting.
//                update    - calls updateBall and refresh
//                drawCourt - draws the playing court
//    
//              Ball.c, Paddle.c and alarmlib.c are modular segments of
//              code, each focusing on a specific task.
//
//              both ball.c and paddle.c use "get" functions to retrieve
//              information about the court from pong.c.   This way all vars
//              are protected and no globals used.
//
//              ball.c handles multiball, 
//              key fctns are:
//
//                updateball  - updates all balls in play
//                addBall     - adds a new ball to the field
//                removeBall  - removes ball from field
//                newBall     - mallocs memory for the ball
//                drawBall    - displays a ball
//                isBallOut   - checks if ball is out of the court
//
//
//              paddle.c handles the paddle control,
//              key fctns are:
//
//                init_paddle - prepares the paddle
//                up_paddle   - move paddle up
//                down_paddle - move paddle down
//                draw_paddle - displays the paddle
//                is_a_hit    - tells if the paddle hits a ball
//
//              alarmlib.c handles setting alarm, key fctns are:
//                set_ticker  - arranges for the interval timer to issue
//                              SIGALRM's at regular intervals
//
//       Notes: court size can easily be reconfigured by changing properties in mCourt
//              but if the court is too large, refresh issues may occur
*/


/* include header */
#include        "pong.h"

static int    delay;          /* delay  controls speed of game */
static struct court  mCourt;  /* mCourt struct (also used by paddle and ball) */
static double gravity;        /* strength of gravity on balls */


/*
//  set_up
//
//     initialization handlers for pong.
//     set all values for pong game
//     sets the alarm to trigger [update] every [delay] milliseconds
//
*/
void set_up()
{
    void update();
    
    /* gereral initialize */
    initscr();                        /* init screen for drawing */
    crmode();                         /* ctrlmode */
    noecho();                         /* no keyboard echo */
    clear();                          /* clear screen */
    srand(getpid());                  /* set random seed */
    signal( SIGINT , SIG_IGN   );     /* ignore ctrl C, - so pong
                                         always gets to cleanup */
    /* set pong values */
    gravity       = 0.01;             /* gravity is 0.01 per delay */
    delay         = 25;               /* 1000 ms = 1 second */
    mCourt.left   = 9;                /* top left at     9,4 */
    mCourt.top    = 4;
    mCourt.right  = 70;               /* bottom right at 70,41 */
    mCourt.bottom = 21;
    /* mCourt.bottom = LINES - 4;        make court screen size */
    /* mCourt.right  = COLS  - 10;       (sometimes has refresh problem) */
    
    /* setup game */
    drawCourt();                      /* draw the playing court */
    init_paddle();                    /* Init the paddle */
    addBall();                        /* throw a ball into the court */

    /* set alarm */
    signal(SIGALRM, update );         /* set alarm to trigger update...*/
    set_ticker( delay );              /* every delay milliseconds. */
}


/*
//  main
//
//     Calls setup - then goes into a main loop which 
//     checks for keys to respond to.
//     Screen refresh is handled by an alarm, 
//     which triggers every [delay] milliseconds.
//     On 'Q' key the main loop is exited - then wrapup called.
*/
int main()
{
    int key, 
        doQuit = 0;

    /* setup for pong */
    set_up(); 

    while(! doQuit)
    {
        /* keyboard control, get the key pressed */
        key = getch();
                
        switch (key) {
            case 'F': case 'f':                         /* Faster */
                if (delay > 2) {
                    delay = delay/2;
                    set_ticker(delay);
                }
                break;
            case 'S': case 's': case 'D': case 'd':     /* Slower */
                delay = delay * 2;
                set_ticker(delay);
                break;
            case 'J': case 'j': case 'A': case 'a':     /* Paddle Up */
                up_paddle(&mCourt);
                break;
            case 'K': case 'k': case 'Z': case 'z':     /* Paddle Down */
                down_paddle(&mCourt);
                break;
            case 'X': case 'x':                         /* Extra Ball */
                addBall(&mCourt);
                break;
            case ',': case '<': case 'G': case 'g':     /* More Gravity */
                gravity += 0.01;
                break;
            case '.': case '>': case 'L': case 'l':     /* Less Gravity */
                gravity -= 0.01;
                break;
            case 'Q': case 'q':                         /* Quit */
                doQuit = 1;
                break;
        }

        if (getLivesLeft() == 0)
            doQuit = 1;
    }
    
    /* wrapup pong */
    wrap_up();
    
    return (0);
                        
}


/*
//  wrap_up
//
//     Cleans up all settings before exiting.
//     fix memory for balls
//     and fix terminal settings.
//
*/
void wrap_up()
{    
    /* House cleaning */
    disposeBalls();                          /* clear memory of balls */
    set_ticker( 0 );                         /* reset ticker          */
    endwin();                                /* back to normal        */   
}


/*
//  update
//
//     Called via alarm "signal(SIGALRM, update)"
//     as found in function set_up()
//
//     This function is called every [delay] milliseconds.
//     All methods which need to be updated before refresh are here.
//     
*/
void update()
{
    
    signal(SIGALRM, SIG_IGN);                /* ignore signals during update */
    
    updateBall();                            /* update all balls */
    hideCursor();                            /* hide cursor before
                                                refresh */
    
    refresh();                               /* now show changes */
    signal(SIGALRM, update);	             /* reset alarm  */ 

}


/*
//  hideCursor
//
//     "hide" the cursor by moving it into the bottom right corner
//     Bottom right corner is least obtrusive place.
*/
void hideCursor()
{
    /* move cursor to bottom right corner */
    move(LINES-1,COLS-1);
    curs_set(0);
}


/*
//  drawCourt
//
//     Draw playing court defined by [mCourt]
//     Use vLine and hLine to display top, left and bottom of court
//
//     args:  *mCourt         --  pts to court data
*/
void drawCourt()
{
    /* left wall */
    vLine(mCourt.left,
          mCourt.top,
          mCourt.bottom, '|');

    /* top wall */
    hLine(mCourt.left,
          mCourt.top,
          mCourt.right,  '-');

    /* bottom wall */
    hLine(mCourt.left,
          mCourt.bottom,
          mCourt.right,  '-');
}

/*
//  tells the right of court
*/
int getCourtRight()
{
    return mCourt.right;
}

/*
//  tells the top limit of court
*/
int getTopLimit()
{
    return mCourt.top + 1;
}

/*
//  tells the bottom limit of court
*/
int getBottomLimit()
{
    return mCourt.bottom - 1;
}

/*
//  tells the left limit of court
*/
int getLeftLimit()
{
    return mCourt.left + 1;
}

/*
//  tells the right limit of court
*/
int getRightLimit()
{
    return mCourt.right - 1;
}

/*
//  tells the mid x point of court
*/
int getMidCourtX()
{
    return mCourt.left + (mCourt.right  - mCourt.left) / 2;
}

/*
//  tells the mid y point of court
*/
int getMidCourtY()
{
    return mCourt.top  + (mCourt.bottom - mCourt.top)  / 2;
}

/*
//  getGravity
//
//     Tells ball.c what gravity is
*/
double getGravity()
{
    return gravity;
}


/*
//  hLine
//
//     Draw a horizontal line (text) from point [x,y] 
//                                   to point [x, y2]
//                                 using char symbol
//
//     args:  x         --  x location to draw line from
//            y         --  y location to draw line from
//            x2        --  x location to draw line to.
//            symbol    --  char symbol to draw with.
*/
void hLine(int x, int y, int x2, char symbol)
{

    int i;
    
    /* move from x to x2 at y, drawing symbol */
    for (i = x; i <= x2; i++) {
        move(y, i);
        addch(symbol);
    }
}


/*
//  vLine
//
//     Draw a vertical line (text) from point [x,y] 
//                                   to point [x, y2]
//                                 using char symbol
//
//     args:  x         --  x location to draw line from
//            y         --  y location to draw line from
//            y2        --  y location to draw line to.
//            symbol    --  char symbol to draw with.
*/
void vLine(int x, int y, int y2, char symbol)
{
    int i;

    /* move from y to y2 at x, drawing symbol */
    for (i = y; i <= y2; i++) {
        move(i, x);
        addch(symbol);
    }
}


/*
//  randomInt
//
//     Generate a random integer between [min] and [max]
//
//     args:  min         --  minimum value to return
//            max         --  maximum value to return
*/
double randomInt(int min, int max)
{
    int range = max - min;
    
    return rand()%range + min;
}


/*
//  fatal
//
//     Fatal error message to stderr then exit (never returns)
*/
void fatal(char *errStr)
{
    fprintf(stderr, "%s\n", errStr);
    exit(1);
}

______


/*//////
//
// paddle.h
//
*/

#ifndef _PADDLE_H_
#define _PADDLE_H_

#include        "pong.h"

struct paddle {int   top;
               int   bottom;
               int   size;
               char  symbol; };

/*
//     predefine all functions for smooth compile
*/
void init_paddle();
void up_paddle();
void down_paddle();
void draw_paddle();
int  is_a_hit(int x, int y);


#endif /* _PADDLE_H_ */


______

/*//////////
//
//  paddle.c 
//
//      AUTHOR: Russell Lowke
//   CSCIE-215: Assignment Four
//
//
// Description: Modular Paddle
//
//      Method: Modualar paddle.c called from pong.c
//
//              init_paddle - sets the paddle
//              up_paddle & down_paddle - move the paddle
//              draw_paddle displays the paddle
//              is_a_hit - tells if the paddle hits a ball
*/

#include        "paddle.h"

static struct paddle mPaddle;

/*
//  init_paddle
//
//     initialize paddle data structure & draw it.
*/
void init_paddle()
{
    mPaddle.top    = 6;
    mPaddle.size   = 6;
    mPaddle.symbol = '#';
    
    /* note: paddle.bottom set in draw_paddle */

    draw_paddle();
}

/*
//  up_paddle
//
//     check if there is space to move the paddle and if so, do.
*/
void up_paddle()
{
    // check bounds
    if ( mPaddle.top > getTopLimit()) {

        // move the paddle
        mPaddle.top -= 1;
        draw_paddle();
    }
}

/*
//  down_paddle
//
//     Check if there is space to move the paddle, and if so, do.
*/
void down_paddle()
{
    if ( mPaddle.bottom < getBottomLimit()) {

        // move the paddle
        mPaddle.top += 1;
        draw_paddle();
    }
}

/*
//  draw_paddle
//
//     draw the paddle, removing trail for old paddle position
*/
void draw_paddle()
{
    // establish paddle bottom
    mPaddle.bottom = mPaddle.top + mPaddle.size - 1;

    // draw the paddle
    vLine(getCourtRight(), 
          mPaddle.top, 
          mPaddle.bottom, 
          mPaddle.symbol);

    /* erase area above and below paddle */
    /* removing the trail made by the old paddle */
    if (mPaddle.top > getTopLimit()) {            /* as above */
         move(mPaddle.top - 1, getCourtRight());
         addch(' ');
    }
    if (mPaddle.bottom < getBottomLimit()) {       /* so below */
         move(mPaddle.bottom + 1, getCourtRight());
         addch(' ');
    }

    /* ensure cursor hidden */
    hideCursor();
}

/*
//  is_a_hit
//
//     Check if loc [x,y] is a hit on the paddle
//
//     args:  x         --  x componant being checked
//            y         --  y componant being checked
*/
int is_a_hit(int x, int y)
{
   if (x >= getRightLimit()  &&                 /* right of court */
       y >= mPaddle.top      &&                 /* top of paddle */
       y <= mPaddle.bottom)                     /* bottom of paddle */
       return YES;                              /* ...is a hit */
   else
       return NO;
}

_____


/*//////
//
// ball.h
//
*/

#ifndef _BALL_H_
#define _BALL_H_

#include        "pong.h"

#define MAXBALLS     100
#define EXTRABALLAT  4

#include <stdlib.h>

struct ball   {double   xLoc; 
               double   yLoc; 
               double   xVel; 
               double   yVel; };

/*
//     predefine all functions for smooth compile
*/
void   updateBall();
void   addBall();
void   removeBall(struct ball *mBall);
struct ball *newBall(double x, double y, double xV, double yV);
void   drawBall(struct ball *mBall);
int    isBallOut(struct ball *mBall);
void   drawAt(double x, double y, char symbol);
void   disposeBalls();
int    getLivesLeft();


#endif /* _BALL_H_ */


_______

/*//////////
//
//  ball.c 
//
//      AUTHOR: Russell Lowke
//   CSCIE-215: Assignment Four
//
//
// Description: Modular Ball for pong
//
//      Method: Modular ball.c called from pong.c
//
//              updateball - updates all balls in play
//              addBall    - adds a new ball to the field
//              removeBall - removes ball from field
//              newBall    - mallocs memory for the ball
//              drawBall   - displays a ball
//              isBallOut  - checks if ball out of field
//            
//       Notes: balls[MAXBALLS] is a list of ptrs to all balls in play.
//              ball.c keeps track of total lives left [livesLeft]
*/

#include        "ball.h"

struct ball *balls[MAXBALLS];
static int livesLeft = 3;

/* 
//  updateBalls
//  
//     Draw all balls in the [balls] list.
//     Called from update in pong.c
*/
void updateBall()
{
    int i;
        
    for (i = 0; balls[i]; i++)
        drawBall(balls[i]);
}

/*
//  addBall
//
//     Add a new ball to the balls list.
*/
void addBall()
{
    int i;

    /* count how many balls so far */
    for(i = 0; balls[i]; i++) {}

    /* no more than MAXBALLS balls */
    if (i < MAXBALLS-1) {

        /* get a new ball */
        balls[i] = newBall(getMidCourtX(),
                           getMidCourtY(), 
                           - randomInt(6, 10)/10,
                           - randomInt(1, 4)/10);
    }
}


/*
//  removeBall
//
//     Search through the list of balls and remove ball [mBall]
//     Free memory used by [mBall]
//
//     args: *mBall         --  pointer to ball to remove
*/
void removeBall(struct ball *mBall)
{
    int i;                                 /* general purpose counter */

    struct ball *xBall = mBall;            /* copy ball ptr */
    for (i = 0; balls[i]; i++){
        if (balls[i] == xBall){            /* is this the ball to remove? */
            xBall    = balls[i+1];         /* get the next ball */
            balls[i] = xBall;              /* replace ball[i] with next ball */
        }
    }
    free(mBall);                           /* free ball from memory */
}



/*
//  ball
//
//     create a new ball
//     two case blocks for switches,
//     flag type and arg value is read from table mFlagArgs
//
//     args:  x         --  pointer to struct being modifed.
//            y         --  index of arg in mFlagArgs table
//            xV        --  switch arg to OFF (0)  or ON (1)
//            yV
*/
struct ball *newBall(double x, double y, double xV, double yV)
{
    struct ball *mReturn;
    
    mReturn = malloc(sizeof(struct ball));  /* malloc memory for ball */

    if ( mReturn == NULL )                  /* check out of memory */
        fatal("out of memory in newBall");  /* ... error */
    
    mReturn->xLoc = x;                      /* store values*/
    mReturn->yLoc = y;
    mReturn->xVel = xV;
    mReturn->yVel = yV;
    
    return mReturn;                         /* return pointer */
}


/*
//  drawBall
//
//     Erase balls old position, move ball at velocity
//     add gravity, chheck edges of court, test for paddle,
//     draw balls new position.
//     
//     args: *mBall          --  pointer to ball being drawn
*/
void drawBall(struct ball *mBall)
{
    
    // erase ball from old location
    //drawAt(mBall->xLoc, mBall->yLoc, ' ');
    
    // move ball at velocity
    mBall->xLoc += mBall->xVel;
    mBall->yLoc += mBall->yVel;

    // gravity/
    mBall->yVel += getGravity();

    if (mBall->xVel > 1)
        mBall->xVel = 1;
    if (mBall->xVel < -1)
        mBall->xVel = -1;
    if (mBall->yVel > 1)
        mBall->yVel = 1;
    if (mBall->yVel < -1)
        mBall->yVel = -1;

    // bottom of court
    if (mBall->yLoc >= getBottomLimit()){
        mBall->yLoc =  getBottomLimit();      /* stop ball penitrating wall */
        mBall->yVel = 0 - mBall->yVel;        /* reverse y velocity */
    }
    // top of court
    if (mBall->yLoc <= getTopLimit()){
        mBall->yLoc =  getTopLimit();         /* stop ball penitrating wall */
        mBall->yVel = 0 - mBall->yVel;        /* reverse y velocity */
    }
    // left of court
    if (mBall->xLoc <= getLeftLimit()){
        mBall->xLoc =  getLeftLimit();        /* stop ball penitrating wall */
        mBall->xVel = 0 - mBall->xVel;        /* reverse x velocity */
    }
    
    // if ball out
    if (isBallOut(mBall)){

        /* remove the ball */
        removeBall(mBall);

        /* was that the last ball? */
        if (*balls == NULL) {
            livesLeft -= 1; 
            addBall();
        }
    }
    else
        drawAt(mBall->xLoc,
               mBall->yLoc, '*');   /* draw the ball */
}

/*
//  isBallOut?
//
//     if ball hit paddle teak velocity
//     return true if ball out
//     ...also keep track of the number of paddle hits
//        creating a new ball on very fourth paddle hit
//     
//     args: *mBall          --  pointer to ball being tested
*/
int isBallOut(struct ball *mBall)
{
    static int paddleHits = 0;
    
    // right side court
    if (mBall->xLoc >= getRightLimit()){
        mBall->xLoc =  getRightLimit();       /* stop ball penitrating*/
    
        // test for paddle
        if (is_a_hit( (int)(0.5+mBall->xLoc),
                      (int)(0.5+mBall->yLoc))) {
            
            mBall->xVel = 0 - mBall->xVel;    /* reverse x velocity */

            /* tweak the velocity by small random amount */
            mBall->xVel += (randomInt(1, 5)/10) - 0.3;
            mBall->yVel += (randomInt(1, 5)/10) - 0.3;

            /* time for an extra ball? */
            paddleHits += 1;
            if (paddleHits >= EXTRABALLAT) {
                addBall();
                paddleHits = 0;
            }
        }
        else {            
            return YES;
        }
    }

    return NO;
}


/*
//  drawAt
//
//     draw a char [symbol] at point [x,y]
//
//     args:  x         --  x location
//            y         --  y location
//            symbol    -- symbol to draw
*/
void drawAt(double x, double y, char symbol)
{
    move((int)(0.5+y), (int)(0.5+x));
    addch(symbol);
}


/*
//  disposeBalls
//
//     Clear memory of all balls.
*/
void disposeBalls()
{
    int i;
 
    for (i = 0; balls[i]; i++)          /* loop through the balls */
        free(balls[i]);                 /* free malloced memory   */
}


/*
//  getLivesLeft
//
//  return number of lives user has left
//  called from  main
//  [livesLeft] is a static var local to ball.c
*/
int getLivesLeft()
{
    return livesLeft;
}

_______


/*//////
//
// alarmlib.h
*/

#ifndef _ALARMLIB_H_
#define _ALARMLIB_H_

#include	<sys/time.h>
#include	<signal.h>
#include        <unistd.h>
#include	<stdio.h>

/*
//     predefine all functions for smooth compile
*/
void set_ticker( int n_msecs );
void millisleep( int n );

#endif /* __ALARMLIB_H_ */


______



/*//////////
//
//  alarmlib.c
//
//      AUTHOR: Russell Lowke
//   CSCIE-215: Assignment Four
//
//
// Description: timer functions for higher resolution clock
//              
//
//       Notes: adapted from Bruse's alarmlib (version 98.03.16)
*/


// include header
#include        "alarmlib.h"

extern int errno;


/*
//  set_ticker
//
//     arranges for the interval timer to issue
//     SIGALRM's at regular intervals
//
//     args:  n_msecs      --  in milliseconds, converted into micro seoncds
*/
void set_ticker( int n_msecs )
{
	struct itimerval new_timeset, old_timeset;
	long	n_sec, n_usecs;

	n_sec = n_msecs / 1000 ;
	n_usecs = ( n_msecs % 1000 ) * 1000L ;

	new_timeset.it_interval.tv_sec  = n_sec;        /* set reload */
	new_timeset.it_interval.tv_usec = n_usecs;	/* new ticker value */
	new_timeset.it_value.tv_sec     = n_sec  ;	/* store this */
	new_timeset.it_value.tv_usec    = n_usecs ;	/* and this */

	if ( setitimer( ITIMER_REAL, &new_timeset, &old_timeset ) != 0 ){
		printf("Error with timer..errno=%d\n", errno );
		exit(1);
	}

}



/*
//  millisleep
//
//     set a flag in the [*info] struct to ON or OFF
//
//     args:  n            --  period for millisleep
*/
void millisleep( int n )
{
        static void tickerOff();             /* handler for ticker off */

	signal( SIGALRM , tickerOff);	     /* set handler */
	set_ticker( n );                     /* set alarm timer */
        pause();                             /* wait for sigalrm */
}



/*
//  tickerOff
//
//     turns off ticker
*/
static void tickerOff()
{
	set_ticker( 0 );			        
}


______
