Subversion Repositories shark

Rev

Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | RSS feed

//////////////////////////////////////////////////////////////////
// Pong
// Written for CS 324, UIUC
// Spencer Hoke
// James Kim
// Tao Luo
//
// Last Modified: April 29, 2004

#include "pong.h"

///////////////////////////////////////
// sets initial values of global variables
void setglobals() {
    P_MASS = 100;
    kp = 30;
    kd = 20;                 // > sqrt(4*(XMAX - XMIN)* kp)
    AI_VIEWING_RANGE = 250;  // window = YMAX - this

    balllist = NULL;

    num_balls = 0;           // number of balls currently in system
    min_balls = 0;           // minimum number of balls in the system
    locked = 0;              // 1 if #balls kept constant
    ball_spd = 5;            // ballspeed (default = 5)

    p_speed = 10;            // # pixels to move each keypress
    p1_length = 150;         // size of the paddles
    p2_length = 75;          // size of the paddles

    p1_pos = (XMAX + XMIN - p1_length)/2;  // initialize starting paddle positions
    p2_pos = (XMAX + XMIN - p2_length)/2;
    p2_target_pos = p2_pos;  // initially no movement

    points1 = 0;             // scores for each side
    points2 = 0;
}

///////////////////////////////////////
// center at (x,y) with radius RAD and color c
void draw_ball(int x, int y, int c) {
    mutex_lock(&draw_mutex);
    grx_disc(x, y, RAD, c);
    mutex_unlock(&draw_mutex);
}

///////////////////////////////////////
// top left corner is (x,y), length is l,
// thickness is P_THICKNESS, and color is c
void draw_paddle(int x, int y, int l, int c) {
    mutex_lock(&draw_mutex);
    grx_box(x, y, x + l, y + P_THICKNESS, c);
    mutex_unlock(&draw_mutex);
}

///////////////////////////////////////
// draws a line where the computer's viewing window starts
void draw_viewwin(int col) {
    mutex_lock(&draw_mutex);
    grx_line(XMIN-RAD, AI_VIEWING_RANGE+YMIN, XMAX+RAD, AI_VIEWING_RANGE+YMIN, col);
    mutex_unlock(&draw_mutex);
}

///////////////////////////////////////
void print_scores() {
    char st1[15], st2[15];

    sprintf(st1, "Player 1:%6d", points1);
    sprintf(st2, "Player 2:%6d", points2);
    mutex_lock(&draw_mutex);
    grx_text(st1, XMIN-RAD-1, 20, blue, black);
    grx_text(st2, XMIN-RAD-1, 30, red, black);
    mutex_unlock(&draw_mutex);
}

///////////////////////////////////////
void print_numballs() {
    char st[15];

    sprintf(st, "Num balls:%3d", num_balls);
    mutex_lock(&draw_mutex);
    grx_text(st, XMAX-100, 20, magenta, black);
    mutex_unlock(&draw_mutex);
}

///////////////////////////////////////
void print_constants() {
    char st[15];

    sprintf(st, "kp:%3d, kd:%3d, mass:%4d", kp, kd, P_MASS);
    mutex_lock(&draw_mutex);
    grx_text(st, XMIN-RAD-1, YMAX+20, magenta, black);
    mutex_unlock(&draw_mutex);
}

///////////////////////////////////////
TASK ball(void *arg) {
    int     x, y;             // current position
    int     oldx, oldy;       // last position
    int     dx, dy;           // delta x, y
    int     theta, col;
    double  r;                // theta in radians
    int     p1len, p2len, p1pos, p2pos;   // temp storage of length and position
    int     outx;
    int     hitp1, hitp2;        // 1 if ball has hit paddle
    int     in1, in2;            // 1 if ball is currently inside paddle (error)
    struct  ballinfo myinfo;     // shared x/y/theta

    myinfo.x = x = oldx = XMIN;             // start in middle of the left
    myinfo.y = y = oldy = (YMIN+YMAX)/2;
    col = rand()%14 + 2;         // color ball

    // insert myinfo into global list
    mutex_lock(&list_mutex);
    if(balllist != NULL)   // update old first entry in list
        balllist->prev = &myinfo;
    myinfo.next = balllist;
    myinfo.prev = NULL;
    balllist = &myinfo;
    mutex_unlock(&list_mutex);

    //  starting angle = [ANGMIN,ANGMAX]
    theta = rand()%(ANGMAX - ANGMIN) + ANGMIN;
    if (rand()%2 == 1) // random starting direction
        theta *= -1;
    if (theta > 360) theta -= 360;
    if (theta < 0) theta += 360;
    myinfo.theta = theta;
    r = (double)theta * PI / 180.;

    while (1) {
        dx = (float)(ball_spd * cos(r));
        dy = (float)(ball_spd * sin(r));
        x += dx;
        y += dy;

        // check boundary conditions
        if (y >= YMAX) {  // end task and score point for player 2
            points1++;
            break;
        }
        if (y <= YMIN) {  // end task and score point for player 1
            points2++;
            break;
        }

        p1len = p1_length;  // copy globals
        p1pos = p1_pos;
        p2len = p2_length;
        p2pos = p2_pos;

        outx = (x >= XMAX) || (x <= XMIN);
        hitp1 = (y <= (YMIN + 2*RAD + P_THICKNESS)) // y is within range of paddle
                && (x >= (p1pos - RAD)) && (x <= (p1pos + p1len + RAD)); // x is within range of paddle
        hitp2 = (y >= (YMAX - 2*RAD - P_THICKNESS)) // y is within range of paddle
                && (x >= (p2pos - RAD)) && (x <= (p2pos + p2len + RAD)); // x is within range of paddle

        // need these in case ball goes in the side of the paddle
        in1 = (oldy <= (YMIN + 2*RAD + P_THICKNESS)) // oldy is within range of paddle
                && (oldx >= (p1pos - RAD)) && (oldx <= (p1pos + p1len + RAD)); // oldx is within range of paddle
        in2 = (oldy >= (YMAX - 2*RAD - P_THICKNESS)) // oldy is within range of paddle
                && (oldx >= (p2pos - RAD)) && (oldx <= (p2pos + p2len + RAD)); // oldx is within range of paddle

        if (outx || ((hitp1 || hitp2) && !in1 && !in2)) { // ball needs to bounce
            x -= dx;
            y -= dy;
            if (outx) theta = 180 - theta;                        // bounce off wall
            if ((hitp1 || hitp2) && !in1 && !in2) theta = -theta; // bounce off paddle

            if (theta > 360) theta -= 360;
            else if (theta < 0) theta += 360;

            r = (double)theta * PI / 180.;
            x += (float)(ball_spd * cos(r));
            y += (float)(ball_spd * sin(r));
        }

        draw_ball(oldx, oldy, black);       // clear old position
        draw_ball(x, y, col);               // draw new ball
        oldx = x; oldy = y;

        mutex_lock(&list_mutex);      // updates values in list
        myinfo.x = x;
        myinfo.y = y;
        myinfo.theta = theta;
        mutex_unlock(&list_mutex);

        task_endcycle();
    }

    // remove myinfo from list
    mutex_lock(&list_mutex);
    if(myinfo.next != NULL)
        (myinfo.next)->prev = myinfo.prev;

    if(myinfo.prev != NULL)
        (myinfo.prev)->next = myinfo.next;
    else
        balllist = myinfo.next;
    mutex_unlock(&list_mutex);

    draw_ball(oldx, oldy, 0); // clear ball
    print_scores();           // update score
    num_balls--;              // remove ball from system
    if(!locked)
        print_numballs();     // update count of balls
    return NULL;
}

///////////////////////////////////////
void addball() {
    HARD_TASK_MODEL m;
    PID pid;

    hard_task_default_model(m);
    hard_task_def_ctrl_jet (m);
    hard_task_def_wcet     (m, BALL_WCET);
    hard_task_def_mit      (m, BALL_PER);
    hard_task_def_group    (m, BALLGROUP);
    hard_task_def_usemath  (m);
    pid = task_create("ball", ball, &m, NULL);
    if (pid == NIL) {
        grx_close();
        sys_shutdown_message("Could not create task <ball> errno=%d", errno);
        sys_abort(errno);
    }
    task_activate(pid);
    num_balls++;
    print_numballs();
}

///////////////////////////////////////
// paddle 1 controller
TASK paddle1(void *arg) {
    int oldpos, newpos;
    int move, oldlength, length;

    oldpos = newpos = p1_pos;
    oldlength = -1;            // force draw initially

    while (1) {
        length = p1_length;    // need to copy in case they change
        move = p1_move;
        p1_move -= move;       // update global variable

        // get new position of ball
        newpos += move * p_speed;
        if(newpos < XMIN) // tried to move too far right
            newpos = XMIN;
        else if(newpos > (XMAX - length)) // tried to move too far left
            newpos = XMAX - length;

        if ((newpos != oldpos) || (length != oldlength)) { // need to redraw the paddle
            p1_pos = newpos;  // update global position
            draw_paddle(oldpos, YMIN + RAD, oldlength, black);  // clear old paddle
            draw_paddle(newpos, YMIN + RAD, length, blue);  // draw new one
            oldpos = newpos;
            oldlength = length;
        }
        else
            draw_paddle(newpos, YMIN + RAD, length, blue);  // refresh paddle

        task_endcycle();
    }
}

///////////////////////////////////////
// determines target position of paddle 2
TASK p2ai(void *arg) {
    int     proj;    // projected length to y-value of paddle
    int     closestBallY_Location = 0;
    int     closestBallX_Location = 0;
    int         closestBallY_Angle = 0;
    int     closestToPaddle;
    int         projectedX_Location = 0;
    int     foundBall;
    double  r;                  // angle in radians
    struct ballinfo *cur;
    char st[20];

     while(1) {
        if (num_balls != 0) { // if there are balls
            //finds the closest ball and its position and angle
            closestBallY_Location = AI_VIEWING_RANGE + YMIN;
            closestToPaddle = 1000000;       // large default number
            foundBall = 0;

            mutex_lock(&list_mutex);
            for (cur = balllist; cur != NULL; cur = cur->next) {
                if (cur->y > closestBallY_Location         // within window
                    && cur->y < (YMAX - RAD - P_THICKNESS) // not past paddle already
                    && cur->theta < 180)                   // going toward paddle
                {
                    r = (double)(cur->theta) * PI / 180.;
                    proj = (float)( (YMAX - RAD - P_THICKNESS - cur->y) / sin(r) );  // geometry
                    if(proj < closestToPaddle) { // this is the closest one
                        foundBall = 1;
                        closestBallY_Location = cur->y;
                        closestBallX_Location = cur->x;
                        closestBallY_Angle = cur->theta;
                        closestToPaddle = proj;
                    }
                }
            }
            mutex_unlock(&list_mutex);

            //finds where it needs to move to
            if (foundBall == 1) {
                r = (double)closestBallY_Angle * PI / 180.;
                projectedX_Location = (float)( (YMAX - RAD - P_THICKNESS - closestBallY_Location) / tan(r) );  // geometry
                projectedX_Location += closestBallX_Location;

                while(projectedX_Location < XMIN || projectedX_Location > XMAX) { // adjust for ball bounces
                    if(projectedX_Location < XMIN)
                        projectedX_Location = 2*XMIN - projectedX_Location;  // bounce off left side
                    if(projectedX_Location > XMAX)
                        projectedX_Location = 2*XMAX - projectedX_Location;  // bounce off right side
                }

                sprintf(st, "Proj X: %3d", projectedX_Location);
                mutex_lock(&draw_mutex);
                grx_text(st, XMAX-85, YMAX+20, magenta, black);
                mutex_unlock(&draw_mutex);

                p2_target_pos = projectedX_Location - p2_length/2;   // copy into target for p2
            }
        }

        task_endcycle();
    }
}

///////////////////////////////////////
// paddle 2 controller - applies forces and draws paddle
TASK paddle2(void *arg) {
    int     oldpos, newpos;
    int     positionErrorX, oldlength, length;
    int     force = 0, acceleration = 0, velocity = 0;    // paddle movement

    oldpos = newpos = p2_pos;
    oldlength = -1;            // force draw initially

    while (1) {
        length = p2_length;    // need to copy in case they change
        positionErrorX = p2_target_pos - oldpos;

        //Applying force
        force = kp * positionErrorX - kd * velocity;
        acceleration = force / P_MASS;

        velocity += acceleration;      // new velocity
        newpos = oldpos + velocity;    // using new velocity, so incorporates acceleration

        if(newpos < XMIN) // tried to move too far right
            newpos = XMIN;
        else if(newpos > (XMAX - length)) // tried to move too far left
            newpos = XMAX - length;

        if ((newpos != oldpos) || (length != oldlength)) { // need to redraw the paddle
            p2_pos = newpos;  // update global position
            draw_paddle(oldpos, YMAX - P_THICKNESS - RAD, oldlength, black);  // clear old paddle
            draw_paddle(newpos, YMAX - P_THICKNESS - RAD, length, red);  // draw new one
            oldpos = newpos;
            oldlength = length;
        }
        else
            draw_paddle(newpos, YMAX - P_THICKNESS - RAD, length, red);  // refresh paddle

        task_endcycle();
    }
}

///////////////////////////////////////
TASK countmon(void *arg) {
    while(1) {
        while(num_balls < min_balls)
            addball();
        task_endcycle();
    }
}

///////////////////////////////////////
// called when the system exits
void byebye(void *arg) {
    grx_close();
    cprintf("Thanks for playing!\n");
}

///////////////////////////////////////
int main(int argc, char **argv) {
    HARD_TASK_MODEL m;
    PID pid;

    char c;
    TIME seme;          // used to init the random seed
    PI_mutexattr_t a;   // for PI mutex init

    // Set the closing function
    sys_atrunlevel(byebye, NULL, RUNLEVEL_BEFORE_EXIT);

    // initialize global variables
    setglobals();

    // initialize PI mutexes
    PI_mutexattr_default(a);
    mutex_init(&draw_mutex, &a);
    mutex_init(&list_mutex, &a);

    // graphic card Initialization
    if (grx_init() < 1) {
        sys_abort(errno);
    }
    if (grx_open(640, 480, 8) < 0) {
        cprintf("GRX Error\n");
        sys_abort(errno);
    }

    // Draw initial screen layout and menu
    grx_rect(XMIN-RAD-1, YMIN-RAD-1, XMAX+RAD+1, YMAX+RAD+1, yellow);
    grx_text("Scores:",             XMIN-RAD-1, 10, magenta, black);

    grx_text("PONG",                       XMENU, YMENU, magenta, black);
    grx_text("Create a ball:       SPACE", XMENU, YMENU+15, redorange, black);
    grx_text("Quit program:        ESC",   XMENU, YMENU+25, redorange, black);
    grx_text("Move top paddle:     z x",   XMENU, YMENU+35, redorange, black);
    grx_text("Change top size:     a s",   XMENU, YMENU+45, redorange, black);
    grx_text("Change bottom size:  q w",   XMENU, YMENU+55, redorange, black);
    grx_text("Keep # Balls Const:  l",     XMENU, YMENU+65, redorange, black);
    grx_text("Change ball speed:   1 2",   XMENU, YMENU+75, redorange, black);
    grx_text("Change kp:           3 4",   XMENU, YMENU+85, redorange, black);
    grx_text("Change kd:           5 6",   XMENU, YMENU+95, redorange, black);
    grx_text("Change mass:         7 8",   XMENU, YMENU+105, redorange, black);
    grx_text("Change AI window:    9 0",   XMENU, YMENU+115, redorange, black);

    print_scores();
    print_numballs();
    print_constants();
    draw_viewwin(gray);

    // randomize for ball starting angle
    seme = sys_gettime(NULL);
    srand(seme);

    // set up the paddle tasks
    hard_task_default_model(m);
    hard_task_def_ctrl_jet (m);
    hard_task_def_wcet     (m, P_WCET);
    hard_task_def_mit      (m, P_PER);
    hard_task_def_group    (m, PGROUP);
    hard_task_def_usemath  (m);
    pid = task_create("paddle1", paddle1, &m, NULL);
    if (pid == NIL) {
        grx_close();
        sys_shutdown_message("Could not create task <paddle1> errno=%d", errno);
        sys_abort(errno);
    }

    hard_task_default_model(m);
    hard_task_def_ctrl_jet (m);
    hard_task_def_wcet     (m, P_WCET);
    hard_task_def_mit      (m, P_PER);
    hard_task_def_group    (m, PGROUP);
    hard_task_def_usemath  (m);
    pid = task_create("p2ai", p2ai, &m, NULL);
    if (pid == NIL) {
        grx_close();
        sys_shutdown_message("Could not create task <p2ai> errno=%d", errno);
        sys_abort(errno);
    }

    hard_task_default_model(m);
    hard_task_def_ctrl_jet (m);
    hard_task_def_wcet     (m, P_WCET);
    hard_task_def_mit      (m, P_PER);
    hard_task_def_group    (m, PGROUP);
    hard_task_def_usemath  (m);
    pid = task_create("paddle2", paddle2, &m, NULL);
    if (pid == NIL) {
        grx_close();
        sys_shutdown_message("Could not create task <paddle2> errno=%d", errno);
        sys_abort(errno);
    }
    group_activate(PGROUP);

    #ifdef JET_ON
        scenario_jetcontrol();
        init_jetcontrol();
        group_activate(JET_GROUP);
    #endif

    hard_task_default_model(m);
    hard_task_def_ctrl_jet (m);
    hard_task_def_wcet     (m, BALL_WCET);
    hard_task_def_mit      (m, BALL_PER);
    hard_task_def_group    (m, BALLGROUP);
    hard_task_def_usemath  (m);
    pid = task_create("countmon", countmon, &m, NULL);
    if (pid == NIL) {
        grx_close();
        sys_shutdown_message("Could not create task <countmonitor> errno=%d", errno);
        sys_abort(errno);
    }
    task_activate(pid);

    while(1) { // read in commands ( exit handled in handle_key() )
        c = keyb_getch(BLOCK);
        switch (c) {
            case ESC: // quit program
                sys_abort(0);
                break;
            case ' ': // create a new ball
                addball();
                if(locked == 1)    // update min # balls if locked
                    min_balls++;
                break;
            case 'z': // move paddle1 left
                p1_move--;
                break;
            case 'x': // move paddle1 right
                p1_move++;
                break;
            case 's': // increase paddle1 length
                if (p1_length < (XMAX - XMIN - P_DLENGTH))
                    p1_length += P_DLENGTH;
                break;
            case 'a': // decrease paddle1 length
                if (p1_length > (1 + P_DLENGTH))
                     p1_length -= P_DLENGTH;
                break;
            case 'w': // increase paddle2 length
                if (p2_length < (XMAX - XMIN - P_DLENGTH))
                    p2_length += P_DLENGTH;
                break;
            case 'q': // decrease paddle2 length
                if (p2_length > (1 + P_DLENGTH))
                    p2_length -= P_DLENGTH;
                break;
            case 'l': // lock number of balls
                if((locked = !locked)) {
                    min_balls = num_balls;
                    mutex_lock(&draw_mutex);
                    grx_text("   Locked", XMAX-100, 30, redorange, black);
                    mutex_unlock(&draw_mutex);
                }
                else {
                    min_balls = 0;
                    mutex_lock(&draw_mutex);
                    grx_text("         ", XMAX-100, 30, redorange, black);
                    mutex_unlock(&draw_mutex);
                }
                break;
            case '1': // decrease ball speed
                ball_spd--;
                break;
            case '2': // increase ball speed
                ball_spd++;
                break;
            case '3': // decrease kp
                if(kp > 0)
                    kp--;
                print_constants();
                break;
            case '4': // increase kp
                kp++;
                print_constants();
                break;
            case '5': // decrease kd
                if(kd > 0)
                    kd--;
                print_constants();
                break;
            case '6': // increase kd
                kd++;
                print_constants();
                break;
            case '7': // decrease mass
                if(P_MASS > 5)
                    P_MASS -= 5;
                print_constants();
                break;
            case '8': // increase mass
                P_MASS += 5;
                print_constants();
                break;
            case '9': // decrease AI window
                draw_viewwin(black);    // clear old line
                AI_VIEWING_RANGE += 5;
                draw_viewwin(gray);     // draw new one
                break;
            case '0': // increase AI window
                draw_viewwin(black);    // clear old line
                AI_VIEWING_RANGE -= 5;
                draw_viewwin(gray);     // draw new one
                break;
            default:
                break;
        }
    }

    return 0;
}