From 3a0d8799bd7bdedb23cbec81c71d54e20c0ec7b2 Mon Sep 17 00:00:00 2001 From: Christian Kroll Date: Wed, 23 Feb 2011 10:33:27 +0000 Subject: [PATCH] consolidated both snake trees, saving ~950 bytes --- animations/snake.c | 138 +---------- animations/snake.h | 4 +- display_loop.c | 3 +- games/games.mk | 6 + games/snake/snake_game.c | 499 +++++++++++++++++++++++++++++---------- games/snake/snake_game.h | 9 + 6 files changed, 395 insertions(+), 264 deletions(-) create mode 100644 games/snake/snake_game.h diff --git a/animations/snake.c b/animations/snake.c index 7b38324..8333d79 100644 --- a/animations/snake.c +++ b/animations/snake.c @@ -1,131 +1,9 @@ - -#include "../config.h" -#include "../pixel.h" -#include "../util.h" -#include "../random/prng.h" -#include "snake.h" - -#define RANDOM8() (random8()) - -void snake(){ - pixel pixels[64]; - pixels[0] = (pixel){NUM_COLS/2, NUM_ROWS/2}; - pixels[1] = (pixel){NUM_COLS/2, (NUM_ROWS/2)-1}; - - pixel * head = &pixels[1]; - pixel * tail = &pixels[0]; - pixel old_head; - - pixel apples[10]; - unsigned char apple_num = 0; - - direction dir = up; - - clear_screen(0); - - unsigned char x=0, dead=0; - while(1){ - x++; - old_head = *head; - if(++head == pixels + 64) head = pixels; - - unsigned char dead_cnt=0; - - unsigned char apple_found = 0, j; - for(j=0;jx > apples[j].x){ - distx = head->x - apples[j].x; - }else{ - distx = apples[j].x - head->x; - } - if(head->y > apples[j].y){ - disty = head->y - apples[j].y; - }else{ - disty = apples[j].y - head->y; - } - if ((distx + disty) < shortdist){ - shortdist = distx + disty; - nextapple = j; - xy = (distx > disty)?1:0; - } - } - if(xy){ - dir = (apples[nextapple].x > head->x)?left:right; - }else{ - dir = (apples[nextapple].y > head->y)?down:up; - } - } - } - - if( (apple_num<9) && ((RANDOM8()&0xff)<10) ){ - pixel new_apple = (pixel){RANDOM8()%NUM_COLS,RANDOM8()%NUM_ROWS}; - if(!get_pixel(new_apple)){ - apples[apple_num++]=new_apple; - } - } - - if(!apple_found){ - clearpixel(*tail); - if(++tail == pixels + 64) tail = pixels; - } - }else{ - while(tail != head){ - clearpixel(*tail); - if((++tail)>pixels+64) tail = pixels; - wait (60); - } - break; - } - - for(j=0;j +#include #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); +} diff --git a/games/snake/snake_game.h b/games/snake/snake_game.h new file mode 100644 index 0000000..7853581 --- /dev/null +++ b/games/snake/snake_game.h @@ -0,0 +1,9 @@ +#ifndef SNAKE_GAME_H_ +#define SNAKE_GAME_H_ + +#include + +void snake_engine(uint8_t bDemoMode); +void snake_game(void); + +#endif /* SNAKE_GAME_H_ */