consolidated both snake trees, saving ~950 bytes

This commit is contained in:
Christian Kroll 2011-02-23 10:33:27 +00:00
parent 291c90ccf6
commit 3a0d8799bd
6 changed files with 416 additions and 285 deletions

View File

@ -1,131 +1,9 @@
#include "../games/snake/snake_game.h"
#include "../config.h" /**
#include "../pixel.h" * Snake in demo mode.
#include "../util.h" */
#include "../random/prng.h" void snake_animation(void)
#include "snake.h" {
snake_engine(1);
#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;j<apple_num;j++){
unsigned char i;
for(i=0;i<4;i++){
if ( (next_pixel(old_head, i).x == apples[j].x) && (next_pixel(old_head, i).y == apples[j].y) ){
apple_found = 1;
dir = i;
for(;j<apple_num-1;j++){
apples[j]=apples[j+1];
}
apple_num--;
goto apple_se;
}
}
}
apple_se:
if(apple_found){
}else{
while(get_next_pixel(old_head, dir)){
if((dead_cnt++)==4){
dead = 1;
break;
}
dir = direction_r(dir);
}
}
if(!dead){
*head = next_pixel(old_head, dir);
setpixel(*head, 3);
if((RANDOM8()&0xff)<80){
unsigned char j;
unsigned char nextapple=0, distx, disty, shortdist=255, xy=0;
if(!apple_num){
dir = RANDOM8()%4;
}else{
for(j=0;j<apple_num;j++){
if(head->x > 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<apple_num;j++){
if(x%2){
setpixel(apples[j], 2);
}else{
#ifdef CAN_SUPPORT
clearpixel(apples[j]);
#endif
}
}
wait (SNAKE_DELAY);
}
} }

View File

@ -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_ */

View File

@ -4,6 +4,7 @@
#include "config.h" #include "config.h"
#include "scrolltext/scrolltext.h" #include "scrolltext/scrolltext.h"
#include "animations/snake.h"
#include "animations/programm.h" #include "animations/programm.h"
#include "animations/matrix.h" #include "animations/matrix.h"
#include "animations/gameoflife.h" #include "animations/gameoflife.h"
@ -76,7 +77,7 @@ void display_loop(){
#ifdef ANIMATION_SNAKE #ifdef ANIMATION_SNAKE
case 4: case 4:
snake(); snake_animation();
break; break;
#endif #endif

View File

@ -11,6 +11,12 @@ ifeq ($(GAME_SNAKE),y)
SUBDIRS += games/snake SUBDIRS += games/snake
endif endif
ifeq ($(ANIMATION_SNAKE),y)
ifneq ($(GAME_SNAKE),y)
SUBDIRS += games/snake
endif
endif
ifeq ($(GAME_BREAKOUT),y) ifeq ($(GAME_BREAKOUT),y)
SUBDIRS += games/breakout SUBDIRS += games/breakout
endif endif

View File

@ -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;
// zeichne Rahmen
for (x = 0; x < NUM_COLS; x++) /**
* 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]};
}
/**
* 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);
}
}
}
x = 0;
while (1)
{
x++;
old_head = *head;
if (++head == pixels + 64)
head = pixels;
#ifdef GAME_SNAKE_NEWCONTROL
if (joy_cmd != 0xff)
{
if ((dir == left && joy_cmd != right) || (dir == right && joy_cmd
!= left) || (dir == up && joy_cmd != down) || (dir == down
&& joy_cmd != up))
dir = joy_cmd;
} }
#else #else
if (joy_cmd == right) for (uint8_t x = MAX_COLS; x--;)
{ {
dir = direction_r(dir); setpixel((pixel){x, 0}, SNAKE_COLOR_BORDER);
joy_cmd = 0xff; setpixel((pixel){x, NUM_ROWS - 1}, SNAKE_COLOR_BORDER);
} }
else if (joy_cmd == left) for (uint8_t y = MAX_ROWS; y--;)
{ {
dir = direction_r(dir); setpixel((pixel){0, y}, SNAKE_COLOR_BORDER);
dir = direction_r(dir); setpixel((pixel){NUM_COLS -1, y}, SNAKE_COLOR_BORDER);
dir = direction_r(dir);
joy_cmd = 0xff;
} }
#endif #endif
}
// kopf einen weiter bewegen
*head = next_pixel(old_head, dir);
apple_found = 0; /**
* Translates port information into directions.
// pr?fen ob man auf nen Apfel drauf ist * @return Current direction of the joystick
for (j = 0; j < apple_num; j++) */
static snake_dir_t snake_queryJoystick(void)
{
snake_dir_t dirJoystick;
if (JOYISUP)
{ {
if ((head->x == apples[j].x) && (head->y == apples[j].y)) dirJoystick = SNAKE_DIR_UP;
{
apple_found = 1;
for (; j < apple_num - 1; j++)
{
apples[j] = apples[j + 1];
}
apple_num--;
goto apple_se;
}
}
if (get_pixel(*head))
{
dead = 1;
}
apple_se:
if (!dead)
{
setpixel(*head, 3);
// 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))
{
apples[apple_num++] = new_apple;
}
}
// l?sche Ende
if (!apple_found && !dead)
{
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 < apple_num; j++)
{
if (x % 2)
{
setpixel(apples[j], 3);
}
else
{
clearpixel(apples[j]);
}
}
for (j = 0; j < 20; j++)
{
if (JOYISLEFT)
{
joy = left;
} }
else if (JOYISRIGHT) else if (JOYISRIGHT)
{ {
joy = right; dirJoystick = SNAKE_DIR_RIGHT;
#ifdef GAME_SNAKE_NEWCONTROL
}
else if (JOYISUP)
{
joy = up;
} }
else if (JOYISDOWN) else if (JOYISDOWN)
{ {
joy = down; dirJoystick = SNAKE_DIR_DOWN;
#endif }
else if (JOYISLEFT)
{
dirJoystick = SNAKE_DIR_LEFT;
} }
else else
{ {
joy = 0xff; dirJoystick = SNAKE_DIR_NONE;
} }
if (joy != joy_old)
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)
{ {
joy_cmd = joy; // valid transitions can only be uneven
if ((pprotSnake->dir + dirJoystick) & 0x01)
{
pprotSnake->dir = dirJoystick;
} }
joy_old = joy; }
wait(5); #else
if ((dirJoystick ^ *pdirLast) && (dirJoystick != SNAKE_DIR_NONE))
{ // only left or right movements are valid
if (dirJoystick & 0x01)
{
// rotate through directions (either clockwise or counterclockwise)
pprotSnake->dir = (pprotSnake->dir +
(dirJoystick == SNAKE_DIR_LEFT ? 3 : 1)) % 4;
}
}
*pdirLast = dirJoystick;
#endif
}
#endif
#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)
{
uint8_t nMinDist = UINT8_MAX;
for (uint8_t i = 0; i < pApples->nAppleCount; ++i)
{
uint8_t nDistX;
if (pxHead.x > pApples->aApples[i].x)
{
nDistX = pxHead.x - pApples->aApples[i].x;
}
else
{
nDistX = pApples->aApples[i].x - pxHead.x;
}
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;
}
if ((nDistX + nDistY) < nMinDist)
{
nMinDist = nDistX + nDistY;
nNextApple = i;
}
}
if (pprotSnake->dir ^ 0x01) // vertical direction?
{
pprotSnake->dir = pApples->aApples[nNextApple].x > pxHead.x ?
SNAKE_DIR_LEFT : SNAKE_DIR_RIGHT;
}
else
{
pprotSnake->dir = pApples->aApples[nNextApple].y > pxHead.y ?
SNAKE_DIR_DOWN : SNAKE_DIR_UP;
}
}
}
for (uint8_t i = 4; i--;)
{
pixel pxTest = snake_applyDirection(pxHead, pprotSnake->dir);
if (get_pixel(pxTest))
{
for (uint8_t j = pApples->nAppleCount; j--;)
{
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);
}
}
/**
* 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))
{
for (; i < pApples->nAppleCount; ++i)
{
pApples->aApples[i] = pApples->aApples[i + 1];
}
--pApples->nAppleCount;
return 1;
}
}
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))
{
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
// 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]))
{
snake_eliminateProtagonist(&protSnake);
return;
}
// 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);
}

9
games/snake/snake_game.h Normal file
View File

@ -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_ */