altos/pong: Don't let the ball skip over the paddle
[fw/altos] / src / pong / ao_pong.c
1 /*
2  * Copyright © 2011 Keith Packard <keithp@keithp.com>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful, but
10  * WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License along
15  * with this program; if not, write to the Free Software Foundation, Inc.,
16  * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
17  */
18
19 #include "ao.h"
20 #include <ao_exti.h>
21 #include <ao_event.h>
22 #include <ao_button.h>
23 #include <ao_boot.h>
24 #include <ao_vga.h>
25 #include "ao_pong_text.h"
26
27 // uint8_t ao_sensor_errors;
28
29 #define WIN_SCORE       11
30
31 #define BALL_WIDTH      5
32 #define BALL_HEIGHT     5
33
34 #define PADDLE_WIDTH    5
35 #define PADDLE_HEIGHT   25
36 #define PADDLE_OFFSET   20
37
38 struct point {
39         int     x, y;
40 };
41
42 struct rect {
43         int     x, y, w, h;
44 };
45
46 static struct ao_data   ao_data;
47
48 static const int        player_led[2] = { AO_LED_GREEN, AO_LED_BLUE };
49 static int              player_value[2];
50 static int              player_score[2];
51
52 #define player_filter(old, new) (((old) + (new)) >> 1)
53
54 static struct rect      player[2];
55
56 static struct rect      ball;
57
58 static int      ball_speed;
59 static int      ball_e;
60 static int      ball_dx, ball_dy;
61 static int      ball_step_x, ball_step_y;
62
63 static int      volley_count;
64
65 static int
66 intersect(struct rect *a, struct rect *b)
67 {
68         return (a->x <= b->x + b->w && b->x <= a->x + a->w &&
69                 a->y <= b->y + b->h && b->y <= a->y + a->h);
70 }
71
72 static int
73 ao_ball_step(void)
74 {
75         int     p;
76         int     s;
77
78         /* Move the ball */
79         for (s = 0; s < ball_speed; s++) {
80                 ball_e += ball_dy;
81                 ball.x += ball_step_x;
82                 while (ball_e >= ball_dx) {
83                         ball_e -= ball_dx;
84                         ball.y += ball_step_y;
85
86                         /* bounce off walls */
87                         if (ball.y < 0 || ball.y + ball.h > AO_VGA_HEIGHT) {
88                                 ball_step_y = -ball_step_y;
89                                 ball.y += ball_step_y;
90                         }
91                 }
92
93                 /* player missed */
94                 if (ball.x <= 0)
95                         return 1;
96
97                 if (ball.x >= AO_VGA_WIDTH - BALL_WIDTH)
98                         return -1;
99
100                 /* bounce off paddles */
101
102                 for (p = 0; p < 2; p++) {
103                         if (intersect(&ball, &player[p])) {
104
105                                 volley_count++;
106
107                                 ball_speed = 3 + volley_count / 4;
108
109                                 int     dy = (2 * (ball.y - player[p].y) + ball.h - player[p].h);
110                                 int     dx = 20;
111
112                                 ball_step_x = -ball_step_x;
113                                 ball.x += ball_step_x;
114                                 ball_step_y = 1;
115                                 if (dy < 0) {
116                                         ball_step_y = -1;
117                                         dy = -dy;
118                                 }
119                                 ball_e = 0;
120                                 ball_dx = dx;
121                                 ball_dy = dy;
122                         }
123                 }
124         }
125
126         return 0;
127 }
128
129 #define AO_ADC_MAX      4095
130
131 static void
132 ao_paddle_set(int p, int value)
133 {
134         int     pos = value * (AO_VGA_HEIGHT - PADDLE_HEIGHT) / AO_ADC_MAX;
135
136         player[p].y = pos;
137 }
138
139 enum pong_state {
140         pong_start,
141         pong_serve,
142         pong_volley,
143         pong_endgame,
144 };
145
146 static enum pong_state pong_state;
147 static int pong_timer;
148 static int pong_server;
149
150 #define PONG_SERVE_WAIT 90
151
152 static enum pong_state
153 ao_pong_start(void)
154 {
155         int p;
156
157         pong_timer = 1;
158
159         pong_server = ao_time() & 1;
160
161         for (p = 0; p < 2; p++) {
162                 ao_led_off(player_led[p]);
163                 player_score[p] = 0;
164         }
165
166         return pong_serve;
167 }
168
169 static enum pong_state
170 ao_pong_serve(void)
171 {
172         int seed;
173         if (--pong_timer > 0)
174                 return pong_serve;
175
176         seed = ao_time();
177
178         ball.y = (AO_VGA_HEIGHT - BALL_HEIGHT) / 2;
179
180         ball_speed = 3;
181         volley_count = 0;
182
183         if (pong_server) {
184                 ball.x = player[1].x - BALL_WIDTH;
185                 ball_step_x = -1;
186         } else {
187                 ball.x = player[0].x + PADDLE_WIDTH;
188                 ball_step_x = 1;
189         }
190
191         ball.w = BALL_WIDTH;
192         ball.h = BALL_HEIGHT;
193
194         ball_dx = 20;
195         ball_dy = (seed & 7) * 2;
196         ball_e = 0;
197
198         seed >>= 3;
199
200         if (seed & 1) {
201                 ball_step_y = 1;
202         } else {
203                 ball_step_y = -1;
204         }
205
206         return pong_volley;
207 }
208
209 static enum pong_state
210 ao_pong_volley(void)
211 {
212         int     miss = ao_ball_step();
213         int     point;
214
215         if (miss == 0)
216                 return pong_volley;
217
218         point = (miss + 1) >> 1;
219         player_score[point]++;
220         if (player_score[point] == WIN_SCORE) {
221                 ao_led_on(player_led[point]);
222                 return pong_endgame;
223         }
224
225         pong_server = point;
226         pong_timer = PONG_SERVE_WAIT;
227         return pong_serve;
228 }
229
230 static void
231 ao_pong_step(void)
232 {
233         int p;
234
235         /* Paddles are always active */
236         for (p = 0; p < 2; p++) {
237                 player_value[p] = player_filter(player_value[p], ao_data.adc.player[p]);
238                 ao_paddle_set(p, player_value[p]);
239         }
240         switch (pong_state) {
241         case pong_start:
242                 pong_state = ao_pong_start();
243                 break;
244         case pong_serve:
245                 pong_state = ao_pong_serve();
246                 break;
247         case pong_volley:
248                 pong_state = ao_pong_volley();
249                 break;
250         case pong_endgame:
251                 break;
252         }
253 }
254
255 static void
256 ao_pong_rect(struct rect *r, uint32_t fill)
257 {
258         ao_rect(&ao_vga_bitmap,
259                 r->x, r->y, r->w, r->h, fill, AO_COPY);
260 }
261
262 static void
263 ao_pong_score(int p, int value, uint32_t fill)
264 {
265         int     x = AO_VGA_WIDTH / 2 + (p * 2 - 1) * 50 - 24;
266         int     a, b;
267         char    c[4];
268
269         if (fill != 0) {
270                 a = value % 10;
271                 b = value / 10;
272                 if (b)
273                         c[0] = b + '0';
274                 else
275                         c[0] = ' ';
276                 c[1] = a + '0';
277                 c[2] = '\0';
278                 ao_pong_text(&ao_vga_bitmap, x, 30, c);
279         }
280 }
281
282 #define NET_WIDTH       2
283 #define NET_HEIGHT      8
284
285 static void
286 ao_pong_net(uint32_t fill)
287 {
288         int n;
289
290         if (fill == 0)
291                 return;
292
293         for (n = NET_HEIGHT/2; n < AO_VGA_HEIGHT - NET_HEIGHT; n += NET_HEIGHT * 2) {
294                 ao_rect(&ao_vga_bitmap,
295                         (AO_VGA_WIDTH - NET_WIDTH) >> 1,
296                         n,
297                         NET_WIDTH,
298                         NET_HEIGHT,
299                         1, AO_COPY);
300         }
301 }
302
303 static void
304 ao_pong_draw(uint32_t fill)
305 {
306         int p;
307
308         for (p = 0; p < 2; p++) {
309                 ao_pong_rect(&player[p], fill);
310                 ao_pong_score(p, player_score[p], fill);
311         }
312         if (fill == 0 || pong_state == pong_volley)
313                 ao_pong_rect(&ball, fill);
314         ao_pong_net(fill);
315 }
316
317 static void
318 ao_pong_redraw(void)
319 {
320         ao_pong_draw(0);
321         ao_pong_step();
322         ao_pong_draw(1);
323 }
324
325 static void
326 ao_pong_setup(void)
327 {
328         int     p;
329
330         ao_rect(&ao_vga_bitmap,
331                 0, 0, AO_VGA_WIDTH, AO_VGA_HEIGHT,
332                 0, AO_COPY);
333
334         ball.w = BALL_WIDTH;
335         ball.h = BALL_HEIGHT;
336
337         for (p = 0; p < 2; p++) {
338                 if (p)
339                         player[p].x = AO_VGA_WIDTH - PADDLE_OFFSET - PADDLE_WIDTH;
340                 else
341                         player[p].x = PADDLE_OFFSET;
342                 player[p].y = (AO_VGA_HEIGHT - PADDLE_HEIGHT) / 2;
343                 player[p].w = PADDLE_WIDTH;
344                 player[p].h = PADDLE_HEIGHT;
345         }
346
347         pong_state = pong_endgame;
348 }
349
350 static struct ao_task pong_task;
351
352 static void
353 ao_pong(void)
354 {
355         ao_pong_setup();
356         for (;;) {
357                 ao_vga_vblank = 0;
358                 while (ao_vga_vblank == 0)
359                         ao_sleep(&ao_vga_vblank);
360                 ao_data = ao_data_ring[ao_data_ring_prev(ao_data_head)];
361                 ao_pong_redraw();
362         }
363 }
364
365 static struct ao_task event_task;
366
367 static void
368 ao_event(void) {
369         struct ao_event event;
370
371         for (;;) {
372                 ao_event_get(&event);
373                 if (event.value == 0)
374                         pong_state = pong_start;
375         }
376 }
377
378 int
379 main(void)
380 {
381         ao_clock_init();
382
383         ao_task_init();
384
385         ao_led_init(LEDS_AVAILABLE);
386         ao_led_on(AO_LED_GREEN);
387         ao_led_off(AO_LED_BLUE);
388         ao_timer_init();
389         ao_dma_init();
390         ao_cmd_init();
391         ao_spi_init();
392         ao_exti_init();
393         ao_button_init();
394         ao_vga_init();
395
396         ao_timer_set_adc_interval(1);
397
398         ao_adc_init();
399         ao_usb_init();
400
401         ao_add_task(&pong_task, ao_pong, "pong");
402
403         ao_add_task(&event_task, ao_event, "event");
404
405         ao_vga_enable(1);
406
407         ao_start_scheduler();
408         return 0;
409 }