1
0
Fork 0
forked from len0rd/rockbox

Philpp Petermann's game wormlet

git-svn-id: svn://svn.rockbox.org/rockbox/trunk@1975 a1c6a512-1295-4272-9138-f99709370657
This commit is contained in:
Eric Linenberg 2002-08-26 03:32:39 +00:00
parent 9a47cc4e7c
commit b0a3984c5a
2 changed files with 807 additions and 0 deletions

779
apps/recorder/wormlet.c Normal file
View file

@ -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 <sprintf.h>
#include <stdlib.h>
#include <string.h>
#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
* <ul>
* <li>COLLISION_NONE
* <li>COLLISION_WORM
* <li>COLLISION_FOOD
* <li>COLLISION_ARGH
* <li>COLLISION_FIELD
* </ul>
*/
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;
}

28
apps/recorder/wormlet.h Normal file
View file

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