Christian Kroll
14 years ago
6 changed files with 395 additions and 264 deletions
@ -1,8 +1,6 @@ |
|||||
#ifndef SNAKE_H_ |
#ifndef SNAKE_H_ |
||||
#define SNAKE_H_ |
#define SNAKE_H_ |
||||
|
|
||||
void snake(); |
void snake_animation(void); |
||||
void snake_game(); |
|
||||
|
|
||||
|
|
||||
#endif /* SNAKE_H_ */ |
#endif /* SNAKE_H_ */ |
||||
|
@ -1,190 +1,429 @@ |
|||||
|
#include <assert.h> |
||||
|
#include <stdint.h> |
||||
#include "../../config.h" |
#include "../../config.h" |
||||
#include "../../compat/pgmspace.h" |
#include "../../compat/pgmspace.h" |
||||
#include "../../menu/menu.h" |
|
||||
#include "../../pixel.h" |
#include "../../pixel.h" |
||||
#include "../../random/prng.h" |
#include "../../random/prng.h" |
||||
#include "../../util.h" |
#include "../../util.h" |
||||
#include "../../joystick/joystick.h" |
#include "../../joystick/joystick.h" |
||||
|
#include "../../menu/menu.h" |
||||
|
#include "snake_game.h" |
||||
|
|
||||
// if defined, joystick controls are NOT as "seen"
|
#if defined MENU_SUPPORT && defined GAME_SNAKE |
||||
// by the snake but absolute, that is, if pressing
|
// snake icon (MSB is leftmost pixel)
|
||||
// up, snake goes up, etc.
|
|
||||
#define GAME_SNAKE_NEWCONTROL |
|
||||
|
|
||||
void snake_game(); |
|
||||
|
|
||||
#ifdef MENU_SUPPORT |
|
||||
// MSB is leftmost pixel
|
|
||||
static uint8_t icon[8] PROGMEM = |
static uint8_t icon[8] PROGMEM = |
||||
{0xff, 0x81, 0xbd, 0xa5, 0xa5, 0xad, 0xa1, 0xbf}; // Snake icon
|
{0xff, 0x81, 0xbd, 0xa5, 0xa5, 0xad, 0xa1, 0xbf}; |
||||
|
|
||||
game_descriptor_t snake_game_descriptor __attribute__((section(".game_descriptors"))) = |
game_descriptor_t snake_game_descriptor __attribute__((section(".game_descriptors"))) = |
||||
{&snake_game, icon}; |
{ |
||||
|
&snake_game, |
||||
|
icon, |
||||
|
}; |
||||
#endif |
#endif |
||||
|
|
||||
void snake_game() |
|
||||
|
// If defined, joystick controls are NOT as "seen" by the snake but absolute,
|
||||
|
// that is, if pressing up, snake goes up, etc.
|
||||
|
#define SNAKE_NEWCONTROL |
||||
|
|
||||
|
// limits
|
||||
|
#define SNAKE_MAX_LENGTH 64 |
||||
|
#define SNAKE_MAX_APPLES 10 |
||||
|
|
||||
|
// delays (in milliseconds)
|
||||
|
#define SNAKE_CYCLE_DELAY 100 |
||||
|
#define SNAKE_TERMINATION_DELAY 60 |
||||
|
|
||||
|
// colors
|
||||
|
#define SNAKE_COLOR_BORDER 3 |
||||
|
#define SNAKE_COLOR_PROTAGONIST 3 |
||||
|
#define SNAKE_COLOR_APPLE 3 |
||||
|
|
||||
|
|
||||
|
/**
|
||||
|
* Directions of the snake. |
||||
|
*/ |
||||
|
typedef enum snake_dir |
||||
{ |
{ |
||||
pixel pixels[64] = {{4, NUM_ROWS - 2}, {4, NUM_ROWS - 3}}; |
SNAKE_DIR_UP, //!< SNAKE_DIR_UP Snake is heading up.
|
||||
pixel *head = &pixels[1]; |
SNAKE_DIR_RIGHT,//!< SNAKE_DIR_RIGHT Snake is heading right.
|
||||
pixel *tail = &pixels[0]; |
SNAKE_DIR_DOWN, //!< SNAKE_DIR_DOWN Snake is heading down.
|
||||
pixel old_head; |
SNAKE_DIR_LEFT, //!< SNAKE_DIR_LEFT Snake is heading left.
|
||||
|
SNAKE_DIR_NONE //!< SNAKE_DIR_NONE Helper value for a "resting" joystick.
|
||||
|
} snake_dir_t; |
||||
|
|
||||
pixel apples[10]; |
|
||||
unsigned char apple_num = 0; |
|
||||
direction dir = up; |
|
||||
|
|
||||
clear_screen(0); |
/**
|
||||
|
* This structure represents the snake character itself. It keeps track of the |
||||
|
* snake's segments, its head and tail and the direction it is heading. |
||||
|
*/ |
||||
|
typedef struct snake_protagonist |
||||
|
{ |
||||
|
pixel aSegments[SNAKE_MAX_LENGTH]; /** All segments of the snake. */ |
||||
|
uint8_t nHeadIndex; /** Index of the head segment. */ |
||||
|
uint8_t nTailIndex; /** Index of the tail segment. */ |
||||
|
snake_dir_t dir; /** Direction of the snake. */ |
||||
|
} snake_protagonist_t; |
||||
|
|
||||
unsigned char apple_found = 0; |
|
||||
unsigned char j; |
|
||||
|
|
||||
unsigned char x, y, dead = 0; |
/**
|
||||
uint8_t joy, joy_old = 0xff, joy_cmd = 0xff; |
* This structure keeps track of all apples which are on the playing field. |
||||
|
*/ |
||||
|
typedef struct snake_apples |
||||
|
{ |
||||
|
pixel aApples[SNAKE_MAX_APPLES]; /** All apple positions */ |
||||
|
uint8_t nAppleCount; /** Counter of currently existing apples */ |
||||
|
} snake_apples_t; |
||||
|
|
||||
|
|
||||
|
/**
|
||||
|
* Moves a pixel to the given direction. |
||||
|
* @param pxNext pixel to be moved |
||||
|
* @param dir direction |
||||
|
*/ |
||||
|
static pixel snake_applyDirection(pixel pxNext, |
||||
|
snake_dir_t dir) |
||||
|
{ |
||||
|
assert(dir < 4); |
||||
|
static int8_t const nDelta[] = {0, -1, 0, 1, 0}; |
||||
|
return (pixel){pxNext.x + nDelta[dir], pxNext.y + nDelta[dir + 1]}; |
||||
|
} |
||||
|
|
||||
|
|
||||
// zeichne Rahmen
|
/**
|
||||
for (x = 0; x < NUM_COLS; x++) |
* This functions draws a border around the playing field. |
||||
|
*/ |
||||
|
static void snake_drawBorder(void) |
||||
|
{ |
||||
|
#if NUM_COLS == NUM_ROWS |
||||
|
for (uint8_t i = NUM_COLS; i--;) |
||||
{ |
{ |
||||
for (y = 0; y < NUM_ROWS; y++) |
setpixel((pixel){i, 0}, SNAKE_COLOR_BORDER); |
||||
{ |
setpixel((pixel){i, NUM_ROWS - 1}, SNAKE_COLOR_BORDER); |
||||
if (((x == 0) || (x == NUM_COLS - 1)) || ((y == 0) || (y |
setpixel((pixel){0, i}, SNAKE_COLOR_BORDER); |
||||
== NUM_ROWS - 1))) |
setpixel((pixel){NUM_COLS -1, i}, SNAKE_COLOR_BORDER); |
||||
{ |
} |
||||
setpixel((pixel) {x, y}, 3); |
#else |
||||
} |
for (uint8_t x = MAX_COLS; x--;) |
||||
} |
{ |
||||
|
setpixel((pixel){x, 0}, SNAKE_COLOR_BORDER); |
||||
|
setpixel((pixel){x, NUM_ROWS - 1}, SNAKE_COLOR_BORDER); |
||||
|
} |
||||
|
for (uint8_t y = MAX_ROWS; y--;) |
||||
|
{ |
||||
|
setpixel((pixel){0, y}, SNAKE_COLOR_BORDER); |
||||
|
setpixel((pixel){NUM_COLS -1, y}, SNAKE_COLOR_BORDER); |
||||
} |
} |
||||
|
#endif |
||||
|
} |
||||
|
|
||||
x = 0; |
|
||||
while (1) |
/**
|
||||
|
* Translates port information into directions. |
||||
|
* @return Current direction of the joystick |
||||
|
*/ |
||||
|
static snake_dir_t snake_queryJoystick(void) |
||||
|
{ |
||||
|
snake_dir_t dirJoystick; |
||||
|
if (JOYISUP) |
||||
|
{ |
||||
|
dirJoystick = SNAKE_DIR_UP; |
||||
|
} |
||||
|
else if (JOYISRIGHT) |
||||
|
{ |
||||
|
dirJoystick = SNAKE_DIR_RIGHT; |
||||
|
} |
||||
|
else if (JOYISDOWN) |
||||
{ |
{ |
||||
x++; |
dirJoystick = SNAKE_DIR_DOWN; |
||||
old_head = *head; |
} |
||||
if (++head == pixels + 64) |
else if (JOYISLEFT) |
||||
head = pixels; |
{ |
||||
|
dirJoystick = SNAKE_DIR_LEFT; |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
dirJoystick = SNAKE_DIR_NONE; |
||||
|
} |
||||
|
|
||||
#ifdef GAME_SNAKE_NEWCONTROL |
return dirJoystick; |
||||
if (joy_cmd != 0xff) |
} |
||||
|
|
||||
|
|
||||
|
/**
|
||||
|
* Initializes the structure which represents the snake itself. |
||||
|
* @param pprotSnake The protagonist structure to be initialized. |
||||
|
*/ |
||||
|
static void snake_initGameProtagonist(snake_protagonist_t *pprotSnake) |
||||
|
{ |
||||
|
pprotSnake->aSegments[0] = (pixel){4, NUM_ROWS - 2}; |
||||
|
pprotSnake->aSegments[1] = (pixel){4, NUM_ROWS - 3}; |
||||
|
pprotSnake->nTailIndex = 0; |
||||
|
pprotSnake->nHeadIndex = 1; |
||||
|
pprotSnake->dir = SNAKE_DIR_UP; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
#ifdef GAME_SNAKE |
||||
|
/**
|
||||
|
* Determines the next direction of the snake depending on joystick input. |
||||
|
* @param pprotSnake Protagonist structure to be controlled. |
||||
|
* @param pdirLast The last joystick movement to avoid key repeat. |
||||
|
*/ |
||||
|
static void snake_userControl(snake_protagonist_t *pprotSnake, |
||||
|
snake_dir_t *pdirLast) |
||||
|
{ |
||||
|
snake_dir_t dirJoystick = snake_queryJoystick(); |
||||
|
#ifdef SNAKE_NEWCONTROL |
||||
|
if (dirJoystick != SNAKE_DIR_NONE) |
||||
|
{ |
||||
|
// valid transitions can only be uneven
|
||||
|
if ((pprotSnake->dir + dirJoystick) & 0x01) |
||||
{ |
{ |
||||
if ((dir == left && joy_cmd != right) || (dir == right && joy_cmd |
pprotSnake->dir = dirJoystick; |
||||
!= left) || (dir == up && joy_cmd != down) || (dir == down |
|
||||
&& joy_cmd != up)) |
|
||||
dir = joy_cmd; |
|
||||
} |
} |
||||
|
} |
||||
#else |
#else |
||||
if (joy_cmd == right) |
if ((dirJoystick ^ *pdirLast) && (dirJoystick != SNAKE_DIR_NONE)) |
||||
|
{ // only left or right movements are valid
|
||||
|
if (dirJoystick & 0x01) |
||||
{ |
{ |
||||
dir = direction_r(dir); |
// rotate through directions (either clockwise or counterclockwise)
|
||||
joy_cmd = 0xff; |
pprotSnake->dir = (pprotSnake->dir + |
||||
} |
(dirJoystick == SNAKE_DIR_LEFT ? 3 : 1)) % 4; |
||||
else if (joy_cmd == left) |
|
||||
{ |
|
||||
dir = direction_r(dir); |
|
||||
dir = direction_r(dir); |
|
||||
dir = direction_r(dir); |
|
||||
joy_cmd = 0xff; |
|
||||
} |
} |
||||
|
} |
||||
|
*pdirLast = dirJoystick; |
||||
|
#endif |
||||
|
} |
||||
#endif |
#endif |
||||
|
|
||||
// kopf einen weiter bewegen
|
|
||||
*head = next_pixel(old_head, dir); |
|
||||
|
|
||||
apple_found = 0; |
|
||||
|
|
||||
// pr?fen ob man auf nen Apfel drauf ist
|
#ifdef ANIMATION_SNAKE |
||||
for (j = 0; j < apple_num; j++) |
/**
|
||||
|
* Approaches directions which may lead to an apple. |
||||
|
* @param pprotSnake The hungry protagonist. |
||||
|
* @param pApples A bunch of apples. |
||||
|
*/ |
||||
|
static void snake_autoRoute(snake_protagonist_t *pprotSnake, |
||||
|
snake_apples_t *pApples) |
||||
|
{ |
||||
|
pixel pxHead = pprotSnake->aSegments[pprotSnake->nHeadIndex]; |
||||
|
if (random8() < 80) |
||||
|
{ |
||||
|
uint8_t nNextApple = 0; |
||||
|
if (pApples->nAppleCount) |
||||
{ |
{ |
||||
if ((head->x == apples[j].x) && (head->y == apples[j].y)) |
uint8_t nMinDist = UINT8_MAX; |
||||
|
for (uint8_t i = 0; i < pApples->nAppleCount; ++i) |
||||
{ |
{ |
||||
apple_found = 1; |
uint8_t nDistX; |
||||
for (; j < apple_num - 1; j++) |
if (pxHead.x > pApples->aApples[i].x) |
||||
{ |
{ |
||||
apples[j] = apples[j + 1]; |
nDistX = pxHead.x - pApples->aApples[i].x; |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
nDistX = pApples->aApples[i].x - pxHead.x; |
||||
} |
} |
||||
apple_num--; |
|
||||
goto apple_se; |
|
||||
} |
|
||||
} |
|
||||
if (get_pixel(*head)) |
|
||||
{ |
|
||||
dead = 1; |
|
||||
} |
|
||||
apple_se: |
|
||||
|
|
||||
if (!dead) |
uint8_t nDistY; |
||||
{ |
if (pxHead.y > pApples->aApples[i].y) |
||||
setpixel(*head, 3); |
{ |
||||
|
nDistY = pxHead.y - pApples->aApples[i].y; |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
nDistY = pApples->aApples[i].y - pxHead.y; |
||||
|
} |
||||
|
|
||||
// setze neue ?pfel
|
if ((nDistX + nDistY) < nMinDist) |
||||
if ((apple_num < 9) && (random8() < 10)) |
|
||||
{ |
|
||||
pixel new_apple = (pixel) {(random8() % (NUM_COLS-2))+1, |
|
||||
(random8() % (NUM_ROWS-2))+1}; |
|
||||
if (!get_pixel(new_apple)) |
|
||||
{ |
{ |
||||
apples[apple_num++] = new_apple; |
nMinDist = nDistX + nDistY; |
||||
|
nNextApple = i; |
||||
} |
} |
||||
} |
} |
||||
// l?sche Ende
|
if (pprotSnake->dir ^ 0x01) // vertical direction?
|
||||
if (!apple_found && !dead) |
{ |
||||
|
pprotSnake->dir = pApples->aApples[nNextApple].x > pxHead.x ? |
||||
|
SNAKE_DIR_LEFT : SNAKE_DIR_RIGHT; |
||||
|
} |
||||
|
else |
||||
{ |
{ |
||||
clearpixel(*tail); |
pprotSnake->dir = pApples->aApples[nNextApple].y > pxHead.y ? |
||||
if (++tail == pixels + 64) |
SNAKE_DIR_DOWN : SNAKE_DIR_UP; |
||||
tail = pixels; |
|
||||
} |
} |
||||
} |
} |
||||
else |
} |
||||
|
|
||||
|
for (uint8_t i = 4; i--;) |
||||
|
{ |
||||
|
pixel pxTest = snake_applyDirection(pxHead, pprotSnake->dir); |
||||
|
if (get_pixel(pxTest)) |
||||
{ |
{ |
||||
while (tail != head) |
for (uint8_t j = pApples->nAppleCount; j--;) |
||||
{ |
{ |
||||
clearpixel(*tail); |
if ((pxTest.x == pApples->aApples[j].x) && |
||||
if ((++tail) > pixels + 64) |
(pxTest.y == pApples->aApples[j].y)) |
||||
tail = pixels; |
{ |
||||
wait(60); |
return; |
||||
|
} |
||||
} |
} |
||||
|
pprotSnake->dir = (pprotSnake->dir + 1) % 4; |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
break; |
break; |
||||
} |
} |
||||
|
} |
||||
|
} |
||||
|
#endif |
||||
|
|
||||
|
|
||||
|
/**
|
||||
|
* Small animation that lets the dying snake disappear. |
||||
|
* @param pprotSnake Pointer to the dying snake. |
||||
|
*/ |
||||
|
static void snake_eliminateProtagonist(snake_protagonist_t *pprotSnake) |
||||
|
{ |
||||
|
while (pprotSnake->nTailIndex != pprotSnake->nHeadIndex) |
||||
|
{ |
||||
|
clearpixel(pprotSnake->aSegments[pprotSnake->nTailIndex++]); |
||||
|
pprotSnake->nTailIndex %= SNAKE_MAX_LENGTH; |
||||
|
wait(SNAKE_TERMINATION_DELAY); |
||||
|
} |
||||
|
} |
||||
|
|
||||
for (j = 0; j < apple_num; j++) |
|
||||
|
/**
|
||||
|
* Initializes the structure that keeps track of all currently existing apples. |
||||
|
* @param pApples Pointer to the apples in question. |
||||
|
*/ |
||||
|
static void snake_initApples(snake_apples_t *pApples) |
||||
|
{ |
||||
|
pApples->nAppleCount = 0; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
/**
|
||||
|
* Checks for an apple at a given position and removes it if there is one. |
||||
|
* @param pApples The set of apples which are one the playing field |
||||
|
* @param pxHead The position to be tested. |
||||
|
* @return 0 if no apples were found, 1 otherwise |
||||
|
*/ |
||||
|
static uint8_t snake_checkForApple(snake_apples_t *pApples, pixel pxHead) |
||||
|
{ |
||||
|
for (uint8_t i = pApples->nAppleCount; i--;) |
||||
|
{ |
||||
|
if ((pxHead.x == pApples->aApples[i].x) && |
||||
|
(pxHead.y == pApples->aApples[i].y)) |
||||
{ |
{ |
||||
if (x % 2) |
for (; i < pApples->nAppleCount; ++i) |
||||
{ |
|
||||
setpixel(apples[j], 3); |
|
||||
} |
|
||||
else |
|
||||
{ |
{ |
||||
clearpixel(apples[j]); |
pApples->aApples[i] = pApples->aApples[i + 1]; |
||||
} |
} |
||||
|
--pApples->nAppleCount; |
||||
|
return 1; |
||||
} |
} |
||||
for (j = 0; j < 20; j++) |
} |
||||
|
return 0; |
||||
|
} |
||||
|
|
||||
|
|
||||
|
/**
|
||||
|
* Creates some new apples from time to time. |
||||
|
* @param pApples Pointer to the apple structure. |
||||
|
*/ |
||||
|
static void snake_spawnApples(snake_apples_t *pApples) |
||||
|
{ |
||||
|
if ((pApples->nAppleCount < SNAKE_MAX_APPLES) && (random8() < 10)) |
||||
|
{ |
||||
|
pixel pxApple = (pixel){(random8() % (NUM_COLS-2)) + 1, |
||||
|
(random8() % (NUM_ROWS - 2)) + 1}; |
||||
|
if (!get_pixel(pxApple)) |
||||
{ |
{ |
||||
if (JOYISLEFT) |
pApples->aApples[pApples->nAppleCount++] = pxApple; |
||||
{ |
} |
||||
joy = left; |
} |
||||
} |
} |
||||
else if (JOYISRIGHT) |
|
||||
{ |
|
||||
joy = right; |
/**
|
||||
#ifdef GAME_SNAKE_NEWCONTROL |
* The snake game. |
||||
} |
*/ |
||||
else if (JOYISUP) |
void snake_engine(uint8_t bDemoMode) |
||||
{ |
{ |
||||
joy = up; |
// init
|
||||
} |
snake_protagonist_t protSnake; |
||||
else if (JOYISDOWN) |
snake_initGameProtagonist(&protSnake); |
||||
{ |
snake_apples_t apples; |
||||
joy = down; |
snake_initApples(&apples); |
||||
|
snake_dir_t dirLast = SNAKE_DIR_NONE; |
||||
|
|
||||
|
// init screen
|
||||
|
clear_screen(0); |
||||
|
snake_drawBorder(); |
||||
|
|
||||
|
for (uint8_t nAppleColor = 0; 1; nAppleColor ^= SNAKE_COLOR_APPLE) |
||||
|
{ |
||||
|
// determine new direction
|
||||
|
#if defined ANIMATION_SNAKE && defined GAME_SNAKE |
||||
|
if (bDemoMode) |
||||
|
{ |
||||
|
snake_autoRoute(&protSnake, &apples); |
||||
|
} |
||||
|
else |
||||
|
{ |
||||
|
snake_userControl(&protSnake, &dirLast); |
||||
|
} |
||||
|
#elif defined ANIMATION_SNAKE |
||||
|
snake_autoRoute(&protSnake, &apples); |
||||
|
#else |
||||
|
snake_userControl(&protSnake, &dirLast); |
||||
#endif |
#endif |
||||
} |
|
||||
else |
// actually move head
|
||||
{ |
pixel pxOldHead = protSnake.aSegments[protSnake.nHeadIndex]; |
||||
joy = 0xff; |
protSnake.nHeadIndex = (protSnake.nHeadIndex + 1) % SNAKE_MAX_LENGTH; |
||||
} |
protSnake.aSegments[protSnake.nHeadIndex] = |
||||
if (joy != joy_old) |
snake_applyDirection(pxOldHead, protSnake.dir); |
||||
|
|
||||
|
// look if we have found an apple
|
||||
|
if (!snake_checkForApple(&apples, |
||||
|
protSnake.aSegments[protSnake.nHeadIndex])) |
||||
|
{ |
||||
|
// quit game if we hit something which is not an apple
|
||||
|
if (get_pixel(protSnake.aSegments[protSnake.nHeadIndex])) |
||||
{ |
{ |
||||
joy_cmd = joy; |
snake_eliminateProtagonist(&protSnake); |
||||
|
return; |
||||
} |
} |
||||
joy_old = joy; |
|
||||
wait(5); |
// remove last segment
|
||||
|
clearpixel(protSnake.aSegments[protSnake.nTailIndex]) |
||||
|
protSnake.nTailIndex = (protSnake.nTailIndex +1) % SNAKE_MAX_LENGTH; |
||||
|
|
||||
|
// new apples
|
||||
|
snake_spawnApples(&apples); |
||||
|
} |
||||
|
// draw new head
|
||||
|
setpixel(protSnake.aSegments[protSnake.nHeadIndex], |
||||
|
SNAKE_COLOR_PROTAGONIST); |
||||
|
|
||||
|
// draw apples
|
||||
|
for (uint8_t i = apples.nAppleCount; i--;) |
||||
|
{ |
||||
|
setpixel(apples.aApples[i], nAppleColor); |
||||
} |
} |
||||
|
|
||||
|
wait(SNAKE_CYCLE_DELAY); |
||||
} |
} |
||||
} |
} |
||||
|
|
||||
|
/**
|
||||
|
* Snake in game mode. |
||||
|
*/ |
||||
|
void snake_game(void) |
||||
|
{ |
||||
|
snake_engine(0); |
||||
|
} |
||||
|
@ -0,0 +1,9 @@ |
|||||
|
#ifndef SNAKE_GAME_H_ |
||||
|
#define SNAKE_GAME_H_ |
||||
|
|
||||
|
#include <stdint.h> |
||||
|
|
||||
|
void snake_engine(uint8_t bDemoMode); |
||||
|
void snake_game(void); |
||||
|
|
||||
|
#endif /* SNAKE_GAME_H_ */ |
Loading…
Reference in new issue