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