altos/pong: Add initial pong implementation
[fw/altos] / src / pong / ao_pong.c
diff --git a/src/pong/ao_pong.c b/src/pong/ao_pong.c
new file mode 100644 (file)
index 0000000..174db29
--- /dev/null
@@ -0,0 +1,392 @@
+/*
+ * Copyright © 2011 Keith Packard <keithp@keithp.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ */
+
+#include "ao.h"
+#include <ao_exti.h>
+#include <ao_event.h>
+#include <ao_button.h>
+#include <ao_boot.h>
+#include <ao_vga.h>
+#include "ao_pong_text.h"
+
+// uint8_t ao_sensor_errors;
+
+#define BALL_SPEED     3
+
+#define WIN_SCORE      11
+
+#define BALL_WIDTH     5
+#define BALL_HEIGHT    5
+
+#define PADDLE_WIDTH   5
+#define PADDLE_HEIGHT  25
+#define PADDLE_OFFSET  20
+
+struct point {
+       int     x, y;
+};
+
+struct rect {
+       int     x, y, w, h;
+};
+
+static struct ao_data  ao_data;
+
+static int             player_value[2];
+static int             player_score[2];
+
+#define player_filter(old, new)        (((old) * 7 + (new)) >> 3)
+
+static struct rect     player[2];
+
+static struct rect     ball;
+
+static int     ball_e;
+static int     ball_dx, ball_dy;
+static int     ball_step_x, ball_step_y;
+
+static int
+intersect(struct rect *a, struct rect *b)
+{
+       return (a->x <= b->x + b->w && b->x <= a->x + a->w &&
+               a->y <= b->y + b->h && b->y <= a->y + a->h);
+}
+
+static int
+ao_ball_step(void)
+{
+       int     p;
+
+       /* Move the ball */
+       ball_e += ball_dy;
+       ball.x += ball_step_x;
+       while (ball_e >= ball_dx) {
+               ball_e -= ball_dx;
+               ball.y += ball_step_y;
+       }
+
+       /* player missed */
+       if (ball.x <= 0)
+               return 1;
+
+       if (ball.x >= AO_VGA_WIDTH - BALL_WIDTH)
+               return -1;
+
+       /* bounce off walls */
+       if (ball.y < 0 || ball.y + ball.h > AO_VGA_HEIGHT) {
+               ball_step_y = -ball_step_y;
+               ball.y += ball_step_y;
+       }
+
+       /* bounce off paddles */
+
+       for (p = 0; p < 2; p++) {
+               if (intersect(&ball, &player[p])) {
+                       int     dy = 2 * (ball.y - player[p].y) + ball.h - player[p].h;
+                       int     dx = 20;
+
+                       ball_step_x = -ball_step_x;
+                       ball.x += ball_step_x;
+                       ball_step_y = 1;
+                       if (dy < 0) {
+                               ball_step_y = -1;
+                               dy = -dy;
+                       }
+                       ball_e = 0;
+                       ball_dx = dx;
+                       ball_dy = dy;
+               }
+       }
+
+       return 0;
+}
+
+#define AO_ADC_MAX     4095
+
+static void
+ao_paddle_set(int p, int value)
+{
+       int     pos = value * (AO_VGA_HEIGHT - PADDLE_HEIGHT) / AO_ADC_MAX;
+
+       player[p].y = pos;
+}
+
+enum pong_state {
+       pong_start,
+       pong_serve,
+       pong_volley,
+       pong_endgame,
+};
+
+static enum pong_state pong_state;
+static int pong_timer;
+static int pong_server;
+
+#define PONG_SERVE_WAIT        90
+
+static enum pong_state
+ao_pong_start(void)
+{
+       int p;
+
+       pong_timer = 1;
+
+       pong_server = ao_time() & 1;
+
+       for (p = 0; p < 2; p++)
+               player_score[p] = 0;
+
+       return pong_serve;
+}
+
+static enum pong_state
+ao_pong_serve(void)
+{
+       int seed;
+       if (--pong_timer > 0)
+               return pong_serve;
+
+       seed = ao_time();
+
+       ball.y = (AO_VGA_HEIGHT - BALL_HEIGHT) / 2;
+
+       if (pong_server) {
+               ball.x = player[1].x - BALL_WIDTH;
+               ball_step_x = -BALL_SPEED;
+       } else {
+               ball.x = player[0].x + PADDLE_WIDTH;
+               ball_step_x = BALL_SPEED;
+       }
+
+       ball.w = BALL_WIDTH;
+       ball.h = BALL_HEIGHT;
+
+       ball_dx = 100;
+       ball_dy = (seed & 7) * 10;
+       ball_e = 0;
+
+       seed >>= 3;
+
+       if (seed & 1) {
+               ball_step_y = BALL_SPEED;
+       } else {
+               ball_step_y = -BALL_SPEED;
+       }
+
+       return pong_volley;
+}
+
+static enum pong_state
+ao_pong_volley(void)
+{
+       int     miss = ao_ball_step();
+       int     point;
+
+       if (miss == 0)
+               return pong_volley;
+
+       point = (miss + 1) >> 1;
+       player_score[point]++;
+       if (player_score[point] == WIN_SCORE)
+               return pong_endgame;
+
+       pong_server = point;
+       pong_timer = PONG_SERVE_WAIT;
+       return pong_serve;
+}
+
+static void
+ao_pong_step(void)
+{
+       int p;
+
+       /* Paddles are always active */
+       for (p = 0; p < 2; p++) {
+               player_value[p] = player_filter(player_value[p], ao_data.adc.player[p]);
+               ao_paddle_set(p, player_value[p]);
+       }
+       switch (pong_state) {
+       case pong_start:
+               pong_state = ao_pong_start();
+               break;
+       case pong_serve:
+               pong_state = ao_pong_serve();
+               break;
+       case pong_volley:
+               pong_state = ao_pong_volley();
+               break;
+       case pong_endgame:
+               break;
+       }
+}
+
+static void
+ao_pong_rect(struct rect *r, uint32_t fill)
+{
+       ao_rect(&ao_vga_bitmap,
+               r->x, r->y, r->w, r->h, fill, AO_COPY);
+}
+
+static void
+ao_pong_score(int p, int value, uint32_t fill)
+{
+       int     x = AO_VGA_WIDTH / 2 + (p * 2 - 1) * 50 - 24;
+       int     a, b;
+       char    c[4];
+
+       if (fill != 0) {
+               a = value % 10;
+               b = value / 10;
+               if (b)
+                       c[0] = b + '0';
+               else
+                       c[0] = ' ';
+               c[1] = a + '0';
+               c[2] = '\0';
+               ao_pong_text(&ao_vga_bitmap, x, 30, c);
+       }
+}
+
+#define NET_WIDTH      2
+#define NET_HEIGHT     8
+
+static void
+ao_pong_net(uint32_t fill)
+{
+       int n;
+
+       if (fill == 0)
+               return;
+
+       for (n = NET_HEIGHT/2; n < AO_VGA_HEIGHT - NET_HEIGHT; n += NET_HEIGHT * 2) {
+               ao_rect(&ao_vga_bitmap,
+                       (AO_VGA_WIDTH - NET_WIDTH) >> 1,
+                       n,
+                       NET_WIDTH,
+                       NET_HEIGHT,
+                       1, AO_COPY);
+       }
+}
+
+static void
+ao_pong_draw(uint32_t fill)
+{
+       int p;
+
+       for (p = 0; p < 2; p++) {
+               ao_pong_rect(&player[p], fill);
+               ao_pong_score(p, player_score[p], fill);
+       }
+       if (fill == 0 || pong_state == pong_volley)
+               ao_pong_rect(&ball, fill);
+       ao_pong_net(fill);
+}
+
+static void
+ao_pong_redraw(void)
+{
+       ao_pong_draw(0);
+       ao_pong_step();
+       ao_pong_draw(1);
+}
+
+static void
+ao_pong_setup(void)
+{
+       int     p;
+
+       ao_rect(&ao_vga_bitmap,
+               0, 0, AO_VGA_WIDTH, AO_VGA_HEIGHT,
+               0, AO_COPY);
+
+       ball.w = BALL_WIDTH;
+       ball.h = BALL_HEIGHT;
+
+       for (p = 0; p < 2; p++) {
+               if (p)
+                       player[p].x = AO_VGA_WIDTH - PADDLE_OFFSET - PADDLE_WIDTH;
+               else
+                       player[p].x = PADDLE_OFFSET;
+               player[p].y = (AO_VGA_HEIGHT - PADDLE_HEIGHT) / 2;
+               player[p].w = PADDLE_WIDTH;
+               player[p].h = PADDLE_HEIGHT;
+       }
+
+       pong_state = pong_endgame;
+}
+
+static struct ao_task pong_task;
+
+static void
+ao_pong(void)
+{
+       ao_pong_setup();
+       for (;;) {
+               ao_vga_vblank = 0;
+               while (ao_vga_vblank == 0)
+                       ao_sleep(&ao_vga_vblank);
+               ao_data = ao_data_ring[ao_data_ring_prev(ao_data_head)];
+               ao_pong_redraw();
+       }
+}
+
+static struct ao_task event_task;
+
+static void
+ao_event(void) {
+       struct ao_event event;
+
+       for (;;) {
+               ao_event_get(&event);
+               if (event.value == 0)
+                       pong_state = pong_start;
+       }
+}
+
+int
+main(void)
+{
+       ao_clock_init();
+
+       ao_task_init();
+
+       ao_led_init(LEDS_AVAILABLE);
+       ao_led_on(AO_LED_GREEN);
+       ao_led_off(AO_LED_BLUE);
+       ao_timer_init();
+       ao_dma_init();
+       ao_cmd_init();
+       ao_spi_init();
+       ao_exti_init();
+       ao_button_init();
+       ao_vga_init();
+
+       ao_timer_set_adc_interval(1);
+
+       ao_adc_init();
+       ao_usb_init();
+
+       ao_add_task(&pong_task, ao_pong, "pong");
+
+       ao_add_task(&event_task, ao_event, "event");
+
+       ao_vga_enable(1);
+
+       ao_start_scheduler();
+       return 0;
+}