From b0a3984c5a3c76296b0fe84057b2d1051e11a699 Mon Sep 17 00:00:00 2001 From: Eric Linenberg Date: Mon, 26 Aug 2002 03:32:39 +0000 Subject: Philpp Petermann's game wormlet git-svn-id: svn://svn.rockbox.org/rockbox/trunk@1975 a1c6a512-1295-4272-9138-f99709370657 --- apps/recorder/wormlet.c | 779 ++++++++++++++++++++++++++++++++++++++++++++++++ apps/recorder/wormlet.h | 28 ++ 2 files changed, 807 insertions(+) create mode 100644 apps/recorder/wormlet.c create mode 100644 apps/recorder/wormlet.h (limited to 'apps') diff --git a/apps/recorder/wormlet.c b/apps/recorder/wormlet.c new file mode 100644 index 0000000000..ab8ef0aa91 --- /dev/null +++ b/apps/recorder/wormlet.c @@ -0,0 +1,779 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2002 Philipp Pertermann + * + * All files in this archive are subject to the GNU General Public License. + * See the file COPYING in the source tree root for full license agreement. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + +#include +#include +#include +#include "lcd.h" +#include "button.h" +#include "kernel.h" +#include "menu.h" + +#define MAX_WORM_LENGTH 500 // size of the ring of the worm +#define INITIAL_WORM_LENGTH 10 // when the game starts +#define WORM_PER_FOOD 7 // num of pixel the worm grows by eating a food + +// The worm is stored in a ring of xy coordinates +short wormx[MAX_WORM_LENGTH]; +short wormy[MAX_WORM_LENGTH]; + +int head; // index of the head within the buffer +short headx; +short heady; + +int tail; // index of the tail within the buffer +int growing; // number of cyles the worm still keeps growing + + +#define MAX_FOOD 5 // maximal number of food items +#define FOOD_SIZE 3 // the width and height of a food +short foodx[MAX_FOOD]; +short foody[MAX_FOOD]; + +#define MAX_ARGH 100 // maximal number of argh items +#define ARGH_SIZE 4 // the width and height of a argh +#define ARGHS_PER_FOOD 2 // number of arghs produced each time a food was eaten +short arghx[MAX_ARGH]; +short arghy[MAX_ARGH]; +int arghCount; + +// direction vector in which the worm moves +short dirx; // only values -1 0 1 allowed +short diry; // only values -1 0 1 allowed + +int speed = 10; + +// return values of checkCollision +#define COLLISION_NONE 0 +#define COLLISION_WORM 1 +#define COLLISION_FOOD 2 +#define COLLISION_ARGH 3 +#define COLLISION_FIELD 4 + +// size of the field the worm lives in +#define FIELD_RECT_X 1 +#define FIELD_RECT_Y 1 +#define FIELD_RECT_WIDTH LCD_HEIGHT - 2 +#define FIELD_RECT_HEIGHT LCD_HEIGHT - 2 + +/** + * Returns the current length of the worm. + * @return int a positive value + */ +int getWormLength(void) { + // initial simple calculation will be overwritten + // if wrong. + int retVal = head - tail; + + // if the worm 'crosses' the boundaries of the ringbuffer + if (retVal < 0) { + retVal = head + MAX_WORM_LENGTH - tail; + } + + return retVal; +} + +/** + * Checks wether a specific food in the food arrays is at the + * specified coordinates. + * @param int foodIndex The index of the food in the food arrays + * @param int x the x coordinate. + * @param int y the y coordinate. + * @return Returns true if the coordinate hits the food specified by + * foodIndex. + */ +bool specificFoodCollision(int foodIndex, int x, int y) { + bool retVal = false; + if (x >= foodx[foodIndex] && + x < foodx[foodIndex] + FOOD_SIZE && + y >= foody[foodIndex] && + y < foody[foodIndex] + FOOD_SIZE) { + + retVal = true; + } + return retVal; +} + +/** + * Returns the index of the food that is at the + * given coordinates. If no food is at the coordinates + * -1 is returned. + * @return int -1 <= value < MAX_FOOD + */ +int foodCollision(int x, int y) { + int i = 0; + int retVal = -1; + bool collisionDetected = false; + for (i = 0; i < MAX_FOOD && !collisionDetected; i++) { + if (specificFoodCollision(i, x, y)) { + + collisionDetected = true; + retVal = i; + } + } + return retVal; +} + +/** + * Checks wether a specific argh in the argh arrays is at the + * specified coordinates. + * @param int arghIndex The index of the argh in the argh arrays + * @param int x the x coordinate. + * @param int y the y coordinate. + * @return Returns true if the coordinate hits the argh specified by + * arghIndex. + */ +bool specificArghCollision(int arghIndex, int x, int y) { + bool retVal = false; + if (x >= arghx[arghIndex] && + x < arghx[arghIndex] + ARGH_SIZE && + y >= arghy[arghIndex] && + y < arghy[arghIndex] + ARGH_SIZE) { + + retVal = true; + } + return retVal; +} + +/** + * Returns the index of the argh that is at the + * given coordinates. If no argh is at the coordinates + * -1 is returned. + * @param int x The x coordinate. + * @param int y The y coordinate. + * @return int -1 <= value < arghCount <= MAX_ARGH + */ +int arghCollision(int x, int y) { + int i = 0; + int retVal = -1; + bool collisionDetected = false; + + // search for the argh that has the specified coords + for (i = 0; i < arghCount && !collisionDetected; i++) { + if (specificArghCollision(i, x, y)) { + + collisionDetected = true; + retVal = i; + } + } + return retVal; +} + +/** + * Checks wether the worm collides with the food at the specfied food-arrays. + * @param int foodIndex The index of the food in the arrays. Ensure the value is + * 0 <= foodIndex <= MAX_FOOD + * @return Returns true if the worm collides with the specified food. + */ +bool wormFoodCollision(int foodIndex) { + bool retVal = false; + + // buffer wormLength because getWormLength is expensive + int wormLength = getWormLength(); + int i; + + // although all the worm gets iterated i is NOT the index + // of the worm arrays + for (i = 0; i < wormLength && !retVal; i++) { + + // convert i to the true worm indices + int wormIndex = (tail + i) % MAX_WORM_LENGTH; + + // The check + if (specificFoodCollision(foodIndex, wormx[wormIndex], wormy[wormIndex])) { + retVal = true; + } + } + + return retVal; +} + +/** + * Checks wether the worm collides with the argh at the specfied argh-arrays. + * @param int arghIndex The index of the argh in the arrays. Ensure the value is + * 0 <= arghIndex < arghCount <= MAX_ARGH + * @return Returns true if the worm collides with the specified argh. + */ +bool wormArghCollision(int arghIndex) { + bool retVal = false; + + // buffer wormLength because getWormLength is expensive + int wormLength = getWormLength(); + int i; + + // although all the worm gets iterated i is NOT the index + // of the worm arrays + for (i = 0; i < wormLength && !retVal; i++) { + + // convert i to the true worm indices + int wormIndex = (tail + i) % MAX_WORM_LENGTH; + + // The check + if (specificArghCollision(arghIndex, wormx[wormIndex], wormy[wormIndex])) { + retVal = true; + } + } + + return retVal; +} + +/** + * Find new coordinates for the food stored in foodx[index], foody[index] + * that don't collide with any other food or argh + * @param int index + * Ensure that 0 <= index < MAX_FOOD. + */ +void makeFood(int index) { + + int x = 0; + int y = 0; + bool collisionDetected = false; + + do { + // make coordinates for a new food so that + // the entire food lies within the FIELD + x = rand() % (FIELD_RECT_WIDTH - FOOD_SIZE); + y = rand() % (FIELD_RECT_HEIGHT - FOOD_SIZE); + + // Ensure that the new food doesn't collide with any + // existing foods or arghs. + // If one or more corners of the new food hit any existing + // argh or food a collision is detected. + collisionDetected = + foodCollision(x , y ) >= 0 || + foodCollision(x + FOOD_SIZE - 1, y ) >= 0 || + foodCollision(x , y + FOOD_SIZE - 1) >= 0 || + foodCollision(x + FOOD_SIZE - 1, y + FOOD_SIZE - 1) >= 0 || + arghCollision(x , y ) >= 0 || + arghCollision(x + FOOD_SIZE - 1, y ) >= 0 || + arghCollision(x , y + FOOD_SIZE - 1) >= 0 || + arghCollision(x + FOOD_SIZE - 1, y + FOOD_SIZE - 1) >= 0; + + // use coordinates for further testing + foodx[index] = x; + foody[index] = y; + + // now test wether we accidently hit the worm with food ;) + collisionDetected |= wormFoodCollision(index); + } + while (collisionDetected); +} + +/** + * Clears a food from the lcd buffer. + * @param int index The index of the food arrays under which + * the coordinates of the desired food can be found. Ensure + * that the value is 0 <= index <= MAX_FOOD. + */ +void clearFood(int index) { + // remove the old food from the screen + lcd_clearrect (foodx[index] + FIELD_RECT_X, + foody[index] + FIELD_RECT_Y, + FOOD_SIZE, FOOD_SIZE); +} + +/** + * Draws a food in the lcd buffer. + * @param int index The index of the food arrays under which + * the coordinates of the desired food can be found. Ensure + * that the value is 0 <= index <= MAX_FOOD. + */ +void drawFood(int index) { + // draw the food object + lcd_fillrect (foodx[index] + FIELD_RECT_X, + foody[index] + FIELD_RECT_Y, + FOOD_SIZE, FOOD_SIZE); + lcd_clearrect(foodx[index] + FIELD_RECT_X + 1, + foody[index] + FIELD_RECT_Y + 1, + FOOD_SIZE - 2, FOOD_SIZE - 2); +} + +/** + * Find new coordinates for the argh stored in arghx[index], arghy[index] + * that don't collide with any other food or argh. + * @param int index + * Ensure that 0 <= index < arghCount < MAX_ARGH. + */ +void makeArgh(int index) { + int x; + int y; + bool collisionDetected = false; + + do { + // make coordinates for a new argh so that + // the entire food lies within the FIELD + x = rand() % (FIELD_RECT_WIDTH - ARGH_SIZE); + y = rand() % (FIELD_RECT_HEIGHT - ARGH_SIZE); + // Ensure that the new argh doesn't intersect with any + // existing foods or arghs. + // If one or more corners of the new argh hit any existing + // argh or food an intersection is detected. + collisionDetected = + foodCollision(x , y ) >= 0 || + foodCollision(x + ARGH_SIZE - 1, y ) >= 0 || + foodCollision(x , y + ARGH_SIZE - 1) >= 0 || + foodCollision(x + ARGH_SIZE - 1, y + ARGH_SIZE - 1) >= 0 || + arghCollision(x , y ) >= 0 || + arghCollision(x + ARGH_SIZE - 1, y ) >= 0 || + arghCollision(x , y + ARGH_SIZE - 1) >= 0 || + arghCollision(x + ARGH_SIZE - 1, y + ARGH_SIZE - 1) >= 0; + + // use the candidate coordinates to make a real argh + arghx[index] = x; + arghy[index] = y; + + // now test wether we accidently hit the worm with argh ;) + collisionDetected |= wormArghCollision(index); + } + while (collisionDetected); +} + +/** + * Draws an argh in the lcd buffer. + * @param int index The index of the argh arrays under which + * the coordinates of the desired argh can be found. Ensure + * that the value is 0 <= index < arghCount <= MAX_ARGH. + */ +void drawArgh(int index) { + // draw the new argh + lcd_fillrect (arghx[index] + FIELD_RECT_X, + arghy[index] + FIELD_RECT_Y, + ARGH_SIZE, ARGH_SIZE); +} + +/** + * Initializes the worm-, food- and argh-arrays, draws a frame, + * makes some food and argh and display all that stuff. + */ +void initWormlet(void) { + + // Needed when the game is restarted using BUTTON_ON + lcd_clear_display(); + + // Initialize all the worm coordinates to 0,0. + int i; + for (i = 0; i < MAX_WORM_LENGTH; i++){ + wormx[i] = 0; + wormy[i] = 0; + } + + // initialize the worm size + head = INITIAL_WORM_LENGTH; + tail = 0; + + // initialize the worm start point + headx = 0; + heady = 0; + + // set the initial direction the worm creeps to + dirx = 1; + diry = 0; + + // make and display some food and argh + arghCount = MAX_FOOD; + for (i = 0; i < MAX_FOOD; i++) { + makeFood(i); + drawFood(i); + makeArgh(i); + drawArgh(i); + } + + // draw the game field + lcd_invertrect (0 , 0 , FIELD_RECT_WIDTH + 2, FIELD_RECT_HEIGHT + 2); + lcd_invertrect(0 + 1, 0 + 1, FIELD_RECT_WIDTH , FIELD_RECT_HEIGHT); + + // make everything visible + lcd_update(); +} + +/** + * Move the worm one step further. + * The direction in which the worm moves is taken + * from dirx and diry. If the + * worm crosses the boundaries of the field the + * worm is wrapped (it enters the field from the + * opposite side). moveWorm decreases growing if > 0. + */ +void moveWorm(void) { + // find the next array index for the head + head++; + if (head >= MAX_WORM_LENGTH){ + head = 0; + } + + // find the next array index for the tail + tail++; + if (tail >= MAX_WORM_LENGTH){ + tail = 0; + } + + // determine the new head position + headx += dirx; + heady += diry; + + // Wrap the new head position if necessary + if (headx >= FIELD_RECT_WIDTH) { + headx = 0; + } + + if (headx < 0){ + headx = FIELD_RECT_WIDTH - 1; + } + + if (heady >= FIELD_RECT_HEIGHT) { + heady = 0; + } + + if (heady < 0) { + heady = FIELD_RECT_HEIGHT - 1; + } + + // store the new head position in the + // worm arrays + wormx[head] = headx; + wormy[head] = heady; + + // update the worms grow state + if (growing > 0) growing --; +} + +/** + * Draws the head and clears the tail of the worm in + * the display buffer. lcd_update() is NOT called thus + * the caller has to take care that the buffer is displayed. + */ +void drawWorm(void) { + // draw the new head + lcd_drawpixel( wormx[head] + FIELD_RECT_X, wormy[head] + FIELD_RECT_Y); + + // clear the space behind the worm + lcd_clearpixel(wormx[tail] + FIELD_RECT_X, wormy[tail] + FIELD_RECT_Y); +} + +/** + * Checks wether the coordinate is part of the worm. + * @param x int The x coordinate + * @param y int The y coordinate + * @return int The index of the worm arrays that contain x, y. + * Returns -1 if the coordinates are not part of the worm. + */ +int wormCollision(int x, int y) { + int retVal = -1; + + // getWormLength is expensive -> buffer the value + int wormLength = getWormLength(); + int i; + + // test each entry that is part of the worm + for (i = 0; i < wormLength && retVal == -1; i++) { + + // The iteration iterates the length of the worm. + // Here's the conversion to the true indices within + // the worm arrays. + int trueIndex = (tail + i) % MAX_WORM_LENGTH; + + // The check + if (wormx[trueIndex] == x && wormy[trueIndex] == y) { + retVal = trueIndex; + } + } + return retVal; +} + +/** + * Returns true if the head of the worm just has + * crossed the field boundaries. + * @return bool true if the worm just has wrapped. + */ +bool fieldCollision(void) { + bool retVal = false; + if ((headx == FIELD_RECT_WIDTH - 1 && dirx == -1) || + (headx == 0 && dirx == 1) || + (heady == FIELD_RECT_HEIGHT - 1 && diry == -1) || + (heady == 0 && diry == 1)) { + + retVal = true; + } + return retVal; +} + +/** + * Checks and returns wether the head of the worm + * is colliding with something currently. + * @return int One of the values + *
    + *
  • COLLISION_NONE + *
  • COLLISION_WORM + *
  • COLLISION_FOOD + *
  • COLLISION_ARGH + *
  • COLLISION_FIELD + *
+ */ +int checkCollision(void) { + int retVal = COLLISION_NONE; + + if (wormCollision(headx, heady) >= 0) { + retVal = COLLISION_WORM; + } + + if (foodCollision(headx, heady) >= 0) { + retVal = COLLISION_FOOD; + } + + if (arghCollision(headx, heady) >= 0) { + retVal = COLLISION_ARGH; + } + + if (fieldCollision()) { + retVal = COLLISION_FIELD; + } + + return retVal; +} + +/* +void debugOutput(void) { + char buf[40]; + + // head + snprintf(buf, sizeof(buf), "h:%d(%d;%d) ", head, wormx[head], wormy[head]); + lcd_putsxy(FIELD_RECT_WIDTH + 3, 0, buf, 0); + + // tail + snprintf(buf, sizeof(buf), "t:%d(%d;%d) ", tail, wormx[tail], wormy[tail]); + lcd_putsxy(FIELD_RECT_WIDTH + 3, 8, buf, 0); + + // speed + snprintf(buf, sizeof(buf), "div:%d ", speed); + lcd_putsxy(FIELD_RECT_WIDTH + 3, 16, buf, 0); + + // collision + switch (checkCollision()) { + case COLLISION_NONE: snprintf(buf, sizeof(buf), "free "); break; + case COLLISION_WORM: snprintf(buf, sizeof(buf), "worm "); break; + case COLLISION_FOOD: snprintf(buf, sizeof(buf), "food "); break; + case COLLISION_ARGH: snprintf(buf, sizeof(buf), "argh "); break; + case COLLISION_FIELD: snprintf(buf, sizeof(buf), "field "); break; + } + lcd_putsxy(FIELD_RECT_WIDTH + 3, 24, buf, 0); +} +*/ + +/** + * Prints out the score board with all the status information + * about the game. + */ +void scoreBoard(void) { + char buf[15]; + char buf2[15]; + + // Title + snprintf(buf, sizeof (buf), "Wormlet"); + lcd_putsxy(FIELD_RECT_WIDTH + 3, 0, buf, 0); + + // length + snprintf(buf, sizeof (buf), "length:"); + lcd_putsxy(FIELD_RECT_WIDTH + 3, 12, buf, 0); + snprintf(buf, sizeof (buf), "%d ", getWormLength() - growing); + lcd_putsxy(FIELD_RECT_WIDTH + 3, 20, buf, 0); + + switch (checkCollision()) { + case COLLISION_NONE: + snprintf(buf, sizeof(buf), "I'm hungry! "); + if (growing > 0) { + snprintf(buf2, sizeof(buf2), "growing"); + } + else { + snprintf(buf2, sizeof(buf2), " "); + } + break; + + case COLLISION_WORM: + snprintf(buf, sizeof(buf), "Ouch! I bit"); + snprintf(buf2, sizeof(buf2), "myself! "); + break; + + case COLLISION_FOOD: + snprintf(buf, sizeof(buf), "Yummy! "); + snprintf(buf2, sizeof(buf2), "growing"); + break; + + case COLLISION_ARGH: + snprintf(buf, sizeof(buf), "Argh! I'm "); + snprintf(buf2, sizeof(buf2), "poisoned! "); + break; + + case COLLISION_FIELD: + snprintf(buf, sizeof(buf), "Boing! "); + snprintf(buf2, sizeof(buf2), "Headcrash!"); + break; + } + lcd_putsxy(FIELD_RECT_WIDTH + 3, 32, buf, 0); + lcd_putsxy(FIELD_RECT_WIDTH + 3, 40, buf2, 0); +} + +/** + * Checks for collisions of the worm and its environment and + * takes appropriate actions like growing the worm or killing it. + * @return bool Returns true if the worm is dead. Returns + * false if the worm is healthy, up and creeping. + */ +bool processCollisions(void) { + bool wormDead = false; + int index = -1; + + wormDead = fieldCollision(); + + if (!wormDead) { + + // check if food was eaten + index = foodCollision(headx, heady); + if (index != -1){ + clearFood(index); + makeFood(index); + drawFood(index); + + int i = 0; + + for (i = 0; i < ARGHS_PER_FOOD; i++) { + arghCount++; + if (arghCount > MAX_ARGH) { + arghCount = MAX_ARGH; + } + makeArgh(arghCount - 1); + drawArgh(arghCount - 1); + } + + tail -= WORM_PER_FOOD; + growing += WORM_PER_FOOD; + if (tail < 0) { + tail += MAX_WORM_LENGTH; + } + + drawWorm(); + } + + // check if argh was eaten + else { + index = arghCollision(headx, heady); + if (index != -1) { + wormDead = true; + } + else { + if (wormCollision(headx, heady) != -1) { + wormDead = true; + } + } + } + } + return wormDead; +} + +/** + * The main loop of the game. + * @return bool Returns true if the game ended + * with a dead worm. Returns false if the user + * aborted the game manually. + */ +bool run(void) { + int button = 0; + int wormDead = false; + + // initialize the board and so on + initWormlet(); + + // change the direction of the worm + while (button != BUTTON_OFF && ! wormDead) + { + switch (button) { + case BUTTON_UP: + diry = -1; + dirx = 0; + break; + + case BUTTON_DOWN: + diry = 1; + dirx = 0; + break; + + case BUTTON_LEFT: + dirx = -1; + diry = 0; + break; + + case BUTTON_RIGHT: + dirx = 1; + diry = 0; + break; + + case BUTTON_F2: + speed--; + break; + + case BUTTON_F3: + speed ++; + break; + } + + moveWorm(); +// debugOutput(); + wormDead = processCollisions(); + drawWorm(); + scoreBoard(); + lcd_update(); + button = button_get_w_tmo(HZ/speed); + } + return wormDead; +} + +/** + * Main entry point from the menu to start the game control. + */ +Menu wormlet(void) +{ + bool wormDead = false; + int button; + do { + + // button state will be overridden if + // the game quits with the death of the worm. + // Initializing button to BUTTON_OFF ensures + // that the user can hit BUTTON_OFF during the + // game to return to the menu. + button = BUTTON_OFF; + + // start the game + wormDead = run(); + + // if worm isn't dead the game was quit + // via BUTTON_OFF -> no need to wait for + // buttons. + if (wormDead) { + do { + button = button_get(true); + } + // BUTTON_ON -> start new game + // BUTTON_OFF -> back to game menu + while (button != BUTTON_OFF && button != BUTTON_ON); + } + } + while (button != BUTTON_OFF); + + return MENU_OK; +} diff --git a/apps/recorder/wormlet.h b/apps/recorder/wormlet.h new file mode 100644 index 0000000000..347c6be737 --- /dev/null +++ b/apps/recorder/wormlet.h @@ -0,0 +1,28 @@ +/*************************************************************************** + * __________ __ ___. + * Open \______ \ ____ ____ | | _\_ |__ _______ ___ + * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ / + * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < < + * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \ + * \/ \/ \/ \/ \/ + * $Id$ + * + * Copyright (C) 2002 Philipp Pertermann + * + * All files in this archive are subject to the GNU General Public License. + * See the file COPYING in the source tree root for full license agreement. + * + * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY + * KIND, either express or implied. + * + ****************************************************************************/ + +#ifndef __WORMLET__ +#define __WORMLET__ + +#include "menu.h" + +Menu wormlet(void); + +#endif /*__WORMLET__ */ + -- cgit v1.2.3