88#define PLAYER_1 1
99#define PLAYER_2 2
1010
11+ const int BUSY_WAIT_FRAMECOUNTER = 2e7 ;
12+
1113const int WINDOW_HEIGHT = GRAPHICS_MAX_HEIGHT ;
1214const int WINDOW_WIDTH = GRAPHICS_MAX_WIDTH ;
1315
16+ const int BORDER = 5 ;
17+
1418const int BAT_HALF_HEIGHT = 15 ;
1519const int BAT_HALF_WIDTH = 2 ;
1620const int BAT_SPEED = 5 ;
1721
18- const int bat_miny = BAT_HALF_HEIGHT ;
19- const int bat_maxy = (WINDOW_HEIGHT - 1 )- BAT_HALF_HEIGHT ;
20-
2122const int p1_x = 10 ;
2223const int p2_x = (WINDOW_WIDTH - 1 )- p1_x ;
2324
24- const int BALL_SPEED = 2 ;
25+ const float BALL_SPEED_MAX = 10 ;
26+ const float BALL_SPEED_INIT = 5 ;
27+ const float BALL_SPEED_INC = 1.1 ;
2528const int BALL_RADIUS = 3 ;
2629
2730struct playerState {
28- int y ;
31+ float y ;
32+ float yspeed ;
2933};
3034
3135struct {
32- int x ,y ;
33- int xspeed , yspeed ;
36+ int top , bottom ;
37+ int left , right ;
38+ } field ;
39+
40+
41+ struct {
42+ float x , y , xprev , yprev ;
43+ float xspeed , yspeed ;
3444} ball ;
3545
3646struct {
3747 struct playerState p1 , p2 ;
48+ int turn ;
49+ int p1Score ;
50+ int p2Score ;
3851} gstate ;
3952
4053void frame_wait () {
4154 // busy wait
42- volatile int counter = 1e7 ;
55+ volatile int counter = BUSY_WAIT_FRAMECOUNTER ;
4356 while (counter -- );
4457}
4558
59+ void reset_ball () {
60+ ball .xprev = ball .x = WINDOW_WIDTH /2 ;
61+ ball .yprev = ball .y = WINDOW_HEIGHT /2 ;
62+ if (gstate .turn == PLAYER_1 ) {
63+ ball .xspeed = BALL_SPEED_INIT * 0.8 ;
64+ ball .yspeed = BALL_SPEED_INIT * 0.6 ;
65+ } else {
66+ ball .xspeed = - BALL_SPEED_INIT * 0.8 ;
67+ ball .yspeed = BALL_SPEED_INIT * 0.6 ;
68+ }
69+ }
70+
4671void game_init () {
47- gstate .p1 .y = (bat_miny + bat_maxy )/2 ;
72+ graphautoflush_disable ();
73+
74+ field .top = BORDER - 1 ;
75+ field .bottom = WINDOW_HEIGHT - BORDER ;
76+ field .left = BORDER - 1 ;
77+ field .right = WINDOW_WIDTH - BORDER ;
78+
79+ gstate .p1 .y = (field .top + field .bottom )/2 ;
4880 gstate .p2 = gstate .p1 ;
4981
50- ball .x = WINDOW_WIDTH /2 ;
51- ball .y = WINDOW_HEIGHT /2 ;
52- ball .xspeed = BALL_SPEED * 0.8 ;
53- ball .yspeed = BALL_SPEED * 0.6 ;
82+ gstate .p1Score = gstate .p2Score = 0 ;
83+ gstate .turn = PLAYER_1 ;
84+ reset_ball ();
85+ }
86+
87+ static void collision (float zm_border , float zm_prevx , float * zm_newx , float * zm_xspeed , float rigid_x ) {
88+ // assumes: zm is a square object
89+ // collison is written along x-coordinates but can be generalized.
90+ if (zm_prevx <=rigid_x - zm_border <=* zm_newx ) {
91+ // move object as per collision for object moving from left to right.
92+ * zm_newx = 2 * (rigid_x - zm_border ) - (* zm_newx );
93+ * zm_xspeed = -1 ;
94+ } else if (zm_prevx >=rigid_x + zm_border >=* zm_newx ) {
95+ // move object as per collision for object moving from right to left.
96+ * zm_newx = 2 * (rigid_x + zm_border ) - (* zm_newx );
97+ * zm_xspeed = -1 ;
98+ }
99+ }
100+
101+ void check_collision () {
102+ // wall
103+
104+ if (ball .y > field .bottom - BALL_RADIUS ) {
105+ ball .yspeed *= -1 ;
106+ ball .y = 2 * (field .bottom - BALL_RADIUS )- ball .y ;
107+ } else if (ball .y < field .top + BALL_RADIUS ) {
108+ ball .yspeed *= -1 ;
109+ ball .y = 2 * (field .top + BALL_RADIUS )- ball .y ;
110+ }
111+
112+ if (ball .x > p2_x - BAT_HALF_WIDTH - BALL_RADIUS && ball .y >=gstate .p2 .y - BAT_HALF_HEIGHT && ball .y <=gstate .p2 .y + BAT_HALF_HEIGHT ) {
113+ // right bat
114+ ball .xspeed *= -1 * BALL_SPEED_INC ;
115+ ball .x = 2 * (p2_x - BAT_HALF_WIDTH - BALL_RADIUS )- ball .x ;
116+ } else if (ball .x < p1_x + BAT_HALF_WIDTH + BALL_RADIUS && ball .y >=gstate .p1 .y - BAT_HALF_HEIGHT && ball .y <=gstate .p1 .y + BAT_HALF_HEIGHT ) {
117+ // left bat
118+ ball .xspeed *= -1 * BALL_SPEED_INC ;
119+ ball .x = 2 * (p1_x + BAT_HALF_WIDTH + BALL_RADIUS )- ball .x ;
120+ }
121+
122+
123+ if (ball .x > WINDOW_WIDTH - 1 ) {
124+ // player 2 missed ball
125+ gstate .p1Score += 1 ;
126+ gstate .turn = (gstate .turn == PLAYER_1 )?PLAYER_2 :PLAYER_1 ;
127+ reset_ball ();
128+ } else if (ball .x < 0 ) {
129+ // player 1 missed ball
130+ gstate .p2Score += 1 ;
131+ gstate .turn = (gstate .turn == PLAYER_1 )?PLAYER_2 :PLAYER_1 ;
132+ reset_ball ();
133+ }
54134}
55135
56136void draw_board () {
57137 // clear screen
58138 setbkcolor (GREEN );
59139 cleardevice ();
60140
141+ setcolor (BROWN );
142+ bar (0 , 0 , WINDOW_WIDTH , field .top );
143+ bar (0 , field .bottom , WINDOW_WIDTH - 1 , WINDOW_HEIGHT - 1 );
144+
61145 setcolor (BLUE );
62146 bar (p1_x - BAT_HALF_WIDTH , gstate .p1 .y - BAT_HALF_HEIGHT ,
63147 p1_x + BAT_HALF_WIDTH , gstate .p1 .y + BAT_HALF_HEIGHT );
@@ -68,6 +152,8 @@ void draw_board() {
68152
69153 setcolor (WHITE );
70154 fillellipse (ball .x , ball .y , BALL_RADIUS , BALL_RADIUS );
155+
156+ graphflush ();
71157}
72158
73159void move_bat (int player_id , int dir ) {
@@ -87,23 +173,33 @@ void move_bat(int player_id, int dir) {
87173 }
88174
89175 // keep bat in bounds
90- p -> y = max (bat_miny , min (bat_maxy , p -> y ));
176+ const int bat_miny = field .top + 1 + BAT_HALF_HEIGHT ;
177+ const int bat_maxy = field .bottom - 1 - BAT_HALF_HEIGHT ;
178+
179+ if (p -> y > bat_maxy ) {
180+ p -> y = bat_maxy ;
181+ } else if (p -> y < bat_miny ) {
182+ p -> y = bat_miny ;
183+ }
91184}
92185
93186void move_ball () {
187+ ball .xprev = ball .x ;
188+ ball .yprev = ball .y ;
94189 ball .x += ball .xspeed ;
95190 ball .y += ball .yspeed ;
96191}
97192
98193void game () {
99194 game_init ();
100195 while (1 ) {
196+ check_collision ();
101197 draw_board ();
102198
103199 move_ball ();
104200
201+ frame_wait ();
105202 if (!kbhit ()) {
106- frame_wait ();
107203 continue ;
108204 }
109205 char c = getch ();
0 commit comments