Christian Kroll
14 years ago
6 changed files with 395 additions and 264 deletions
@ -1,8 +1,6 @@ |
|||
#ifndef SNAKE_H_ |
|||
#define SNAKE_H_ |
|||
|
|||
void snake(); |
|||
void snake_game(); |
|||
|
|||
void snake_animation(void); |
|||
|
|||
#endif /* SNAKE_H_ */ |
|||
|
@ -1,190 +1,429 @@ |
|||
#include <assert.h> |
|||
#include <stdint.h> |
|||
#include "../../config.h" |
|||
#include "../../compat/pgmspace.h" |
|||
#include "../../menu/menu.h" |
|||
#include "../../pixel.h" |
|||
#include "../../random/prng.h" |
|||
#include "../../util.h" |
|||
#include "../../joystick/joystick.h" |
|||
#include "../../menu/menu.h" |
|||
#include "snake_game.h" |
|||
|
|||
// if defined, joystick controls are NOT as "seen"
|
|||
// by the snake but absolute, that is, if pressing
|
|||
// up, snake goes up, etc.
|
|||
#define GAME_SNAKE_NEWCONTROL |
|||
|
|||
void snake_game(); |
|||
|
|||
#ifdef MENU_SUPPORT |
|||
// MSB is leftmost pixel
|
|||
#if defined MENU_SUPPORT && defined GAME_SNAKE |
|||
// snake icon (MSB is leftmost pixel)
|
|||
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"))) = |
|||
{&snake_game, icon}; |
|||
{ |
|||
&snake_game, |
|||
icon, |
|||
}; |
|||
#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}}; |
|||
pixel *head = &pixels[1]; |
|||
pixel *tail = &pixels[0]; |
|||
pixel old_head; |
|||
SNAKE_DIR_UP, //!< SNAKE_DIR_UP Snake is heading up.
|
|||
SNAKE_DIR_RIGHT,//!< SNAKE_DIR_RIGHT Snake is heading right.
|
|||
SNAKE_DIR_DOWN, //!< SNAKE_DIR_DOWN Snake is heading down.
|
|||
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++) |
|||
{ |
|||
if (((x == 0) || (x == NUM_COLS - 1)) || ((y == 0) || (y |
|||
== NUM_ROWS - 1))) |
|||
{ |
|||
setpixel((pixel) {x, y}, 3); |
|||
} |
|||
} |
|||
setpixel((pixel){i, 0}, SNAKE_COLOR_BORDER); |
|||
setpixel((pixel){i, NUM_ROWS - 1}, SNAKE_COLOR_BORDER); |
|||
setpixel((pixel){0, i}, SNAKE_COLOR_BORDER); |
|||
setpixel((pixel){NUM_COLS -1, i}, SNAKE_COLOR_BORDER); |
|||
} |
|||
#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++; |
|||
old_head = *head; |
|||
if (++head == pixels + 64) |
|||
head = pixels; |
|||
dirJoystick = SNAKE_DIR_DOWN; |
|||
} |
|||
else if (JOYISLEFT) |
|||
{ |
|||
dirJoystick = SNAKE_DIR_LEFT; |
|||
} |
|||
else |
|||
{ |
|||
dirJoystick = SNAKE_DIR_NONE; |
|||
} |
|||
|
|||
#ifdef GAME_SNAKE_NEWCONTROL |
|||
if (joy_cmd != 0xff) |
|||
return dirJoystick; |
|||
} |
|||
|
|||
|
|||
/**
|
|||
* 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 |
|||
!= left) || (dir == up && joy_cmd != down) || (dir == down |
|||
&& joy_cmd != up)) |
|||
dir = joy_cmd; |
|||
pprotSnake->dir = dirJoystick; |
|||
} |
|||
} |
|||
#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); |
|||
joy_cmd = 0xff; |
|||
} |
|||
else if (joy_cmd == left) |
|||
{ |
|||
dir = direction_r(dir); |
|||
dir = direction_r(dir); |
|||
dir = direction_r(dir); |
|||
joy_cmd = 0xff; |
|||
// rotate through directions (either clockwise or counterclockwise)
|
|||
pprotSnake->dir = (pprotSnake->dir + |
|||
(dirJoystick == SNAKE_DIR_LEFT ? 3 : 1)) % 4; |
|||
} |
|||
} |
|||
*pdirLast = dirJoystick; |
|||
#endif |
|||
} |
|||
#endif |
|||
|
|||
// kopf einen weiter bewegen
|
|||
*head = next_pixel(old_head, dir); |
|||
|
|||
apple_found = 0; |
|||
|
|||
// pr?fen ob man auf nen Apfel drauf ist
|
|||
for (j = 0; j < apple_num; j++) |
|||
#ifdef ANIMATION_SNAKE |
|||
/**
|
|||
* 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; |
|||
for (; j < apple_num - 1; j++) |
|||
uint8_t nDistX; |
|||
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) |
|||
{ |
|||
setpixel(*head, 3); |
|||
uint8_t nDistY; |
|||
if (pxHead.y > pApples->aApples[i].y) |
|||
{ |
|||
nDistY = pxHead.y - pApples->aApples[i].y; |
|||
} |
|||
else |
|||
{ |
|||
nDistY = pApples->aApples[i].y - pxHead.y; |
|||
} |
|||
|
|||
// setze neue ?pfel
|
|||
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)) |
|||
if ((nDistX + nDistY) < nMinDist) |
|||
{ |
|||
apples[apple_num++] = new_apple; |
|||
nMinDist = nDistX + nDistY; |
|||
nNextApple = i; |
|||
} |
|||
} |
|||
// l?sche Ende
|
|||
if (!apple_found && !dead) |
|||
if (pprotSnake->dir ^ 0x01) // vertical direction?
|
|||
{ |
|||
pprotSnake->dir = pApples->aApples[nNextApple].x > pxHead.x ? |
|||
SNAKE_DIR_LEFT : SNAKE_DIR_RIGHT; |
|||
} |
|||
else |
|||
{ |
|||
clearpixel(*tail); |
|||
if (++tail == pixels + 64) |
|||
tail = pixels; |
|||
pprotSnake->dir = pApples->aApples[nNextApple].y > pxHead.y ? |
|||
SNAKE_DIR_DOWN : SNAKE_DIR_UP; |
|||
} |
|||
} |
|||
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 ((++tail) > pixels + 64) |
|||
tail = pixels; |
|||
wait(60); |
|||
if ((pxTest.x == pApples->aApples[j].x) && |
|||
(pxTest.y == pApples->aApples[j].y)) |
|||
{ |
|||
return; |
|||
} |
|||
} |
|||
pprotSnake->dir = (pprotSnake->dir + 1) % 4; |
|||
} |
|||
else |
|||
{ |
|||
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) |
|||
{ |
|||
setpixel(apples[j], 3); |
|||
} |
|||
else |
|||
for (; i < pApples->nAppleCount; ++i) |
|||
{ |
|||
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) |
|||
{ |
|||
joy = left; |
|||
} |
|||
else if (JOYISRIGHT) |
|||
{ |
|||
joy = right; |
|||
#ifdef GAME_SNAKE_NEWCONTROL |
|||
} |
|||
else if (JOYISUP) |
|||
{ |
|||
joy = up; |
|||
} |
|||
else if (JOYISDOWN) |
|||
{ |
|||
joy = down; |
|||
pApples->aApples[pApples->nAppleCount++] = pxApple; |
|||
} |
|||
} |
|||
} |
|||
|
|||
|
|||
/**
|
|||
* The snake game. |
|||
*/ |
|||
void snake_engine(uint8_t bDemoMode) |
|||
{ |
|||
// init
|
|||
snake_protagonist_t protSnake; |
|||
snake_initGameProtagonist(&protSnake); |
|||
snake_apples_t apples; |
|||
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 |
|||
} |
|||
else |
|||
{ |
|||
joy = 0xff; |
|||
} |
|||
if (joy != joy_old) |
|||
|
|||
// actually move head
|
|||
pixel pxOldHead = protSnake.aSegments[protSnake.nHeadIndex]; |
|||
protSnake.nHeadIndex = (protSnake.nHeadIndex + 1) % SNAKE_MAX_LENGTH; |
|||
protSnake.aSegments[protSnake.nHeadIndex] = |
|||
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