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;
}