This repository has been archived on 2025-03-02. You can view files and clone it, but cannot push or open issues or pull requests.
avr-snake/snake.c

341 lines
4.9 KiB
C
Raw Permalink Normal View History

2012-12-11 23:06:23 +01:00
#include "Charliplexing.h"
#include "kbd.h"
#include <avr/io.h>
#include <avr/interrupt.h>
#include <util/delay.h>
#include <stdbool.h>
2012-12-11 23:06:23 +01:00
2013-03-15 16:27:24 +01:00
#define NUMBER_WIDTH 3
#define NUMBER_HEIGHT 7
static const uint8_t NUMBERS[10][NUMBER_HEIGHT][NUMBER_WIDTH] = {
{
{1, 1, 1},
{1, 0, 1},
{1, 0, 1},
{1, 0, 1},
{1, 1, 1},
},
{
{0, 0, 1},
{0, 0, 1},
{0, 0, 1},
{0, 0, 1},
{0, 0, 1},
},
{
{1, 1, 1},
{0, 0, 1},
{1, 1, 1},
{1, 0, 0},
{1, 1, 1},
},
{
{1, 1, 1},
{0, 0, 1},
{1, 1, 1},
{0, 0, 1},
{1, 1, 1},
},
{
{1, 0, 1},
{1, 0, 1},
{1, 1, 1},
{0, 0, 1},
{0, 0, 1},
},
{
{1, 1, 1},
{1, 0, 0},
{1, 1, 1},
{0, 0, 1},
{1, 1, 1},
},
{
{1, 1, 1},
{1, 0, 0},
{1, 1, 1},
{1, 0, 1},
{1, 1, 1},
},
{
{1, 1, 1},
{0, 0, 1},
{0, 0, 1},
{0, 0, 1},
{0, 0, 1},
},
{
{1, 1, 1},
{1, 0, 1},
{1, 1, 1},
{1, 0, 1},
{1, 1, 1},
},
{
{1, 1, 1},
{1, 0, 1},
{1, 1, 1},
{0, 0, 1},
{1, 1, 1},
},
};
2012-12-11 23:06:23 +01:00
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 kbdir;
static volatile bool start;
static volatile uint16_t lfsr = 0xace1;
2012-12-11 23:06:23 +01:00
static uint8_t dir;
2013-03-15 16:27:24 +01:00
static uint8_t score;
2012-12-11 23:06:23 +01:00
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) {
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_mix(uint16_t mix) {
lfsr ^= mix;
2013-03-15 15:34:52 +01:00
rand();
}
2012-12-11 23:06:23 +01:00
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) {
LedSignClear(0);
2012-12-11 23:06:23 +01:00
rand_q();
kbdir = dir = EAST;
2012-12-11 23:06:23 +01:00
tail = (point_t){7, 4};
history_len = 0;
history_pos = 0;
2013-03-15 16:27:24 +01:00
score = 0;
2012-12-11 23:06:23 +01:00
unsigned i;
for (i = 0; i < 2; i++)
move(EAST, true);
}
static void set_led(uint8_t x, uint8_t y, uint8_t val) {
LedSignSet(13-x, 8-y, val);
}
2013-03-15 16:27:24 +01:00
static void set_number(uint8_t x, uint8_t y, uint8_t val) {
uint8_t i, j;
for (i = 0; i < NUMBER_WIDTH; i++) {
for (j = 0; j < NUMBER_HEIGHT; j++) {
set_led(x+i, y+j, NUMBERS[val][j][i] ? 7 : 0);
}
}
}
static void show_score(uint8_t score) {
LedSignClear(7);
_delay_ms(50);
LedSignClear(6);
_delay_ms(50);
LedSignClear(5);
_delay_ms(50);
LedSignClear(4);
_delay_ms(50);
LedSignClear(3);
_delay_ms(50);
LedSignClear(2);
_delay_ms(50);
LedSignClear(1);
_delay_ms(50);
LedSignClear(0);
_delay_ms(50);
set_number(1, 2, (score/100)%10);
set_number(NUMBER_WIDTH+2, 2, (score/10)%10);
set_number(2*NUMBER_WIDTH+3, 2, score%10);
}
2012-12-11 23:06:23 +01:00
void kbd_handle(uint16_t code, bool make) {
if (!make)
return;
switch(code) {
case KBD_CODE_SPACE:
start = true;
break;
2012-12-11 23:06:23 +01:00
case KBD_CODE_UP:
kbdir = NORTH;
2012-12-11 23:06:23 +01:00
break;
case KBD_CODE_LEFT:
kbdir = WEST;
2012-12-11 23:06:23 +01:00
break;
case KBD_CODE_DOWN:
kbdir = SOUTH;
2012-12-11 23:06:23 +01:00
break;
case KBD_CODE_RIGHT:
kbdir = EAST;
2012-12-11 23:06:23 +01:00
}
}
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 bool step(void) {
if (kbdir != rev(dir))
dir = kbdir;
2012-12-11 23:06:23 +01:00
uint8_t d = dir;
rand_mix(d);
set_led(tail.x, tail.y, 0);
2012-12-11 23:06:23 +01:00
int i;
point_t pt = tail;
set_led(pt.x, pt.y, 0);
2012-12-11 23:06:23 +01:00
for (i = 0; i < history_len; i++) {
go(&pt, get_dir(i));
set_led(pt.x, pt.y, 2);
2012-12-11 23:06:23 +01:00
}
go(&pt, d);
set_led(pt.x, pt.y, 3);
2012-12-11 23:06:23 +01:00
if (point_used(&pt, true))
return false;
2012-12-11 23:06:23 +01:00
if (points_equal(&pt, &q)) {
2013-03-15 16:27:24 +01:00
score++;
2012-12-11 23:06:23 +01:00
rand_q();
move(d, true);
}
else {
go(&tail, get_dir(0));
move(d, false);
}
if (point_used(&pt, false))
return false;
2012-12-11 23:06:23 +01:00
set_led(q.x, q.y, 7);
return true;
2012-12-11 23:06:23 +01:00
}
int main() {
LedSignInit(GRAYSCALE);
kbd_init();
sei();
kbd_send(KBD_CMD_RESET);
while(true) {
start = false;
while (!start) {}
reset();
while (step())
_delay_ms(100);
2013-03-15 16:27:24 +01:00
_delay_ms(200);
show_score(score);
2012-12-11 23:06:23 +01:00
}
return 0;
}