altos/pong: Speed up ball as the volley continues
[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) * 7 + (new)) >> 3)
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
77         /* Move the ball */
78         ball_e += ball_dy * ball_speed;
79         ball.x += ball_step_x * ball_speed;
80         while (ball_e >= ball_dx) {
81                 ball_e -= ball_dx;
82                 ball.y += ball_step_y;
83
84                 /* bounce off walls */
85                 if (ball.y < 0 || ball.y + ball.h > AO_VGA_HEIGHT) {
86                         ball_step_y = -ball_step_y;
87                         ball.y += ball_step_y;
88                 }
89         }
90
91         /* player missed */
92         if (ball.x <= 0)
93                 return 1;
94
95         if (ball.x >= AO_VGA_WIDTH - BALL_WIDTH)
96                 return -1;
97
98         /* bounce off paddles */
99
100         for (p = 0; p < 2; p++) {
101                 if (intersect(&ball, &player[p])) {
102
103                         volley_count++;
104
105                         ball_speed = 3 + volley_count / 4;
106
107                         int     dy = (2 * (ball.y - player[p].y) + ball.h - player[p].h);
108                         int     dx = 20;
109
110                         ball_step_x = -ball_step_x;
111                         ball.x += ball_step_x * ball_speed;
112                         ball_step_y = 1;
113                         if (dy < 0) {
114                                 ball_step_y = -1;
115                                 dy = -dy;
116                         }
117                         ball_e = 0;
118                         ball_dx = dx;
119                         ball_dy = dy;
120                 }
121         }
122
123         return 0;
124 }
125
126 #define AO_ADC_MAX      4095
127
128 static void
129 ao_paddle_set(int p, int value)
130 {
131         int     pos = value * (AO_VGA_HEIGHT - PADDLE_HEIGHT) / AO_ADC_MAX;
132
133         player[p].y = pos;
134 }
135
136 enum pong_state {
137         pong_start,
138         pong_serve,
139         pong_volley,
140         pong_endgame,
141 };
142
143 static enum pong_state pong_state;
144 static int pong_timer;
145 static int pong_server;
146
147 #define PONG_SERVE_WAIT 90
148
149 static enum pong_state
150 ao_pong_start(void)
151 {
152         int p;
153
154         pong_timer = 1;
155
156         pong_server = ao_time() & 1;
157
158         for (p = 0; p < 2; p++) {
159                 ao_led_off(player_led[p]);
160                 player_score[p] = 0;
161         }
162
163         return pong_serve;
164 }
165
166 static enum pong_state
167 ao_pong_serve(void)
168 {
169         int seed;
170         if (--pong_timer > 0)
171                 return pong_serve;
172
173         seed = ao_time();
174
175         ball.y = (AO_VGA_HEIGHT - BALL_HEIGHT) / 2;
176
177         ball_speed = 3;
178         volley_count = 0;
179
180         if (pong_server) {
181                 ball.x = player[1].x - BALL_WIDTH;
182                 ball_step_x = -1;
183         } else {
184                 ball.x = player[0].x + PADDLE_WIDTH;
185                 ball_step_x = 1;
186         }
187
188         ball.w = BALL_WIDTH;
189         ball.h = BALL_HEIGHT;
190
191         ball_dx = 20;
192         ball_dy = (seed & 7) * 2;
193         ball_e = 0;
194
195         seed >>= 3;
196
197         if (seed & 1) {
198                 ball_step_y = 1;
199         } else {
200                 ball_step_y = -1;
201         }
202
203         return pong_volley;
204 }
205
206 static enum pong_state
207 ao_pong_volley(void)
208 {
209         int     miss = ao_ball_step();
210         int     point;
211
212         if (miss == 0)
213                 return pong_volley;
214
215         point = (miss + 1) >> 1;
216         player_score[point]++;
217         if (player_score[point] == WIN_SCORE) {
218                 ao_led_on(player_led[point]);
219                 return pong_endgame;
220         }
221
222         pong_server = point;
223         pong_timer = PONG_SERVE_WAIT;
224         return pong_serve;
225 }
226
227 static void
228 ao_pong_step(void)
229 {
230         int p;
231
232         /* Paddles are always active */
233         for (p = 0; p < 2; p++) {
234                 player_value[p] = player_filter(player_value[p], ao_data.adc.player[p]);
235                 ao_paddle_set(p, player_value[p]);
236         }
237         switch (pong_state) {
238         case pong_start:
239                 pong_state = ao_pong_start();
240                 break;
241         case pong_serve:
242                 pong_state = ao_pong_serve();
243                 break;
244         case pong_volley:
245                 pong_state = ao_pong_volley();
246                 break;
247         case pong_endgame:
248                 break;
249         }
250 }
251
252 static void
253 ao_pong_rect(struct rect *r, uint32_t fill)
254 {
255         ao_rect(&ao_vga_bitmap,
256                 r->x, r->y, r->w, r->h, fill, AO_COPY);
257 }
258
259 static void
260 ao_pong_score(int p, int value, uint32_t fill)
261 {
262         int     x = AO_VGA_WIDTH / 2 + (p * 2 - 1) * 50 - 24;
263         int     a, b;
264         char    c[4];
265
266         if (fill != 0) {
267                 a = value % 10;
268                 b = value / 10;
269                 if (b)
270                         c[0] = b + '0';
271                 else
272                         c[0] = ' ';
273                 c[1] = a + '0';
274                 c[2] = '\0';
275                 ao_pong_text(&ao_vga_bitmap, x, 30, c);
276         }
277 }
278
279 #define NET_WIDTH       2
280 #define NET_HEIGHT      8
281
282 static void
283 ao_pong_net(uint32_t fill)
284 {
285         int n;
286
287         if (fill == 0)
288                 return;
289
290         for (n = NET_HEIGHT/2; n < AO_VGA_HEIGHT - NET_HEIGHT; n += NET_HEIGHT * 2) {
291                 ao_rect(&ao_vga_bitmap,
292                         (AO_VGA_WIDTH - NET_WIDTH) >> 1,
293                         n,
294                         NET_WIDTH,
295                         NET_HEIGHT,
296                         1, AO_COPY);
297         }
298 }
299
300 static void
301 ao_pong_draw(uint32_t fill)
302 {
303         int p;
304
305         for (p = 0; p < 2; p++) {
306                 ao_pong_rect(&player[p], fill);
307                 ao_pong_score(p, player_score[p], fill);
308         }
309         if (fill == 0 || pong_state == pong_volley)
310                 ao_pong_rect(&ball, fill);
311         ao_pong_net(fill);
312 }
313
314 static void
315 ao_pong_redraw(void)
316 {
317         ao_pong_draw(0);
318         ao_pong_step();
319         ao_pong_draw(1);
320 }
321
322 static void
323 ao_pong_setup(void)
324 {
325         int     p;
326
327         ao_rect(&ao_vga_bitmap,
328                 0, 0, AO_VGA_WIDTH, AO_VGA_HEIGHT,
329                 0, AO_COPY);
330
331         ball.w = BALL_WIDTH;
332         ball.h = BALL_HEIGHT;
333
334         for (p = 0; p < 2; p++) {
335                 if (p)
336                         player[p].x = AO_VGA_WIDTH - PADDLE_OFFSET - PADDLE_WIDTH;
337                 else
338                         player[p].x = PADDLE_OFFSET;
339                 player[p].y = (AO_VGA_HEIGHT - PADDLE_HEIGHT) / 2;
340                 player[p].w = PADDLE_WIDTH;
341                 player[p].h = PADDLE_HEIGHT;
342         }
343
344         pong_state = pong_endgame;
345 }
346
347 static struct ao_task pong_task;
348
349 static void
350 ao_pong(void)
351 {
352         ao_pong_setup();
353         for (;;) {
354                 ao_vga_vblank = 0;
355                 while (ao_vga_vblank == 0)
356                         ao_sleep(&ao_vga_vblank);
357                 ao_data = ao_data_ring[ao_data_ring_prev(ao_data_head)];
358                 ao_pong_redraw();
359         }
360 }
361
362 static struct ao_task event_task;
363
364 static void
365 ao_event(void) {
366         struct ao_event event;
367
368         for (;;) {
369                 ao_event_get(&event);
370                 if (event.value == 0)
371                         pong_state = pong_start;
372         }
373 }
374
375 int
376 main(void)
377 {
378         ao_clock_init();
379
380         ao_task_init();
381
382         ao_led_init(LEDS_AVAILABLE);
383         ao_led_on(AO_LED_GREEN);
384         ao_led_off(AO_LED_BLUE);
385         ao_timer_init();
386         ao_dma_init();
387         ao_cmd_init();
388         ao_spi_init();
389         ao_exti_init();
390         ao_button_init();
391         ao_vga_init();
392
393         ao_timer_set_adc_interval(1);
394
395         ao_adc_init();
396         ao_usb_init();
397
398         ao_add_task(&pong_task, ao_pong, "pong");
399
400         ao_add_task(&event_task, ao_event, "event");
401
402         ao_vga_enable(1);
403
404         ao_start_scheduler();
405         return 0;
406 }