summaryrefslogtreecommitdiffstats
path: root/snake.c
diff options
context:
space:
mode:
Diffstat (limited to 'snake.c')
-rw-r--r--snake.c199
1 files changed, 199 insertions, 0 deletions
diff --git a/snake.c b/snake.c
new file mode 100644
index 0000000..19336eb
--- /dev/null
+++ b/snake.c
@@ -0,0 +1,199 @@
+#include "Charliplexing.h"
+#include "kbd.h"
+
+#include <avr/io.h>
+#include <avr/interrupt.h>
+#include <util/delay.h>
+
+
+typedef struct _point_t {
+ uint8_t x, y;
+} point_t;
+
+
+typedef enum _dir_t {
+ NORTH = 0,
+ WEST,
+ SOUTH,
+ EAST
+} dir_t;
+
+
+static volatile uint8_t dir;
+
+static point_t tail = {7, 4};
+
+#define HISTORY_MAX 128
+
+static uint8_t history[HISTORY_MAX >> 2];
+static uint8_t history_len = 0;
+static uint8_t history_pos = 0;
+
+static point_t q;
+
+static uint16_t rand(void) {
+ static uint16_t lfsr = 0xace1;
+ unsigned bit;
+
+ /* taps: 16 14 13 11; feedback polynomial: x^16 + x^14 + x^13 + x^11 + 1 */
+ bit = ((lfsr >> 0) ^ (lfsr >> 2) ^ (lfsr >> 3) ^ (lfsr >> 5)) & 1;
+ lfsr = (lfsr >> 1) | (bit << 15);
+
+ return lfsr;
+}
+
+static void rand_q(void) {
+ q.x = rand() % 14;
+ q.y = rand() % 9;
+}
+
+static inline uint8_t rev(uint8_t d) {
+ return d ^ 2;
+}
+
+static inline bool points_equal(const point_t *p1, const point_t *p2) {
+ return p1->x == p2->x && p1->y == p2->y;
+}
+
+static void move(uint8_t d, bool grow) {
+ uint8_t p = (history_pos+history_len) % HISTORY_MAX;
+
+ uint8_t n = p >> 2, shift = (p & 3) << 1;
+
+ history[n] &= ~(3 << shift);
+ history[n] |= d << shift;
+
+ if (grow)
+ history_len++;
+ else
+ history_pos = (history_pos+1) % HISTORY_MAX;
+}
+
+static uint8_t get_dir(uint8_t i) {
+ uint8_t p = (history_pos+i) % HISTORY_MAX;
+
+ uint8_t n = p >> 2, shift = (p & 3) << 1;
+
+ return (history[n] >> shift) & 3;
+}
+
+static void reset(void) {
+ rand_q();
+
+ dir = EAST;
+ tail = (point_t){7, 4};
+ history_len = 0;
+ history_pos = 0;
+
+ unsigned i;
+ for (i = 0; i < 2; i++)
+ move(EAST, true);
+}
+
+void kbd_handle(uint16_t code, bool make) {
+ if (!make)
+ return;
+
+ switch(code) {
+ case KBD_CODE_UP:
+ dir = NORTH;
+ break;
+
+ case KBD_CODE_LEFT:
+ dir = WEST;
+ break;
+
+ case KBD_CODE_DOWN:
+ dir = SOUTH;
+ break;
+
+ case KBD_CODE_RIGHT:
+ dir = EAST;
+ }
+}
+
+static void go(point_t *point, uint8_t d) {
+ switch (d) {
+ case NORTH:
+ point->y = (point->y+8)%9;
+ break;
+ case WEST:
+ point->x = (point->x+13)%14;
+ break;
+ case SOUTH:
+ point->y = (point->y+1)%9;
+ break;
+ case EAST:
+ point->x = (point->x+1)%14;
+ }
+}
+
+static bool point_used(const point_t *p, bool head) {
+ point_t pt = tail;
+
+ unsigned i;
+ for (i = 0; i < history_len - (head ? 0 : 1); i++) {
+ go(&pt, get_dir(i));
+
+ if (points_equal(&pt, p))
+ return true;
+ }
+
+ return false;
+}
+
+static void step(void) {
+ uint8_t d = dir;
+
+ LedSignSet(tail.x, tail.y, 0);
+
+ int i;
+ point_t pt = tail;
+
+ LedSignSet(pt.x, pt.y, 0);
+
+ for (i = 0; i < history_len; i++) {
+ go(&pt, get_dir(i));
+ LedSignSet(pt.x, pt.y, 2);
+ }
+
+ go(&pt, d);
+ LedSignSet(pt.x, pt.y, 3);
+
+ if (point_used(&pt, true)) {
+ while(true) {}
+ }
+
+ if (points_equal(&pt, &q)) {
+ rand_q();
+ move(d, true);
+ }
+ else {
+ go(&tail, get_dir(0));
+ move(d, false);
+ }
+
+ if (point_used(&pt, false)) {
+ while(true) {}
+ }
+
+ LedSignSet(q.x, q.y, 7);
+}
+
+int main() {
+ LedSignInit(GRAYSCALE);
+ kbd_init();
+
+ reset();
+
+ sei();
+
+ kbd_send(KBD_CMD_RESET);
+
+ while(true) {
+ step();
+ _delay_ms(100);
+ }
+
+ return 0;
+}