summaryrefslogtreecommitdiff
path: root/apps/recorder/wormlet.c
diff options
context:
space:
mode:
Diffstat (limited to 'apps/recorder/wormlet.c')
-rw-r--r--apps/recorder/wormlet.c779
1 files changed, 779 insertions, 0 deletions
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 @@
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2002 Philipp Pertermann
11 *
12 * All files in this archive are subject to the GNU General Public License.
13 * See the file COPYING in the source tree root for full license agreement.
14 *
15 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
16 * KIND, either express or implied.
17 *
18 ****************************************************************************/
19
20#include <sprintf.h>
21#include <stdlib.h>
22#include <string.h>
23#include "lcd.h"
24#include "button.h"
25#include "kernel.h"
26#include "menu.h"
27
28#define MAX_WORM_LENGTH 500 // size of the ring of the worm
29#define INITIAL_WORM_LENGTH 10 // when the game starts
30#define WORM_PER_FOOD 7 // num of pixel the worm grows by eating a food
31
32// The worm is stored in a ring of xy coordinates
33short wormx[MAX_WORM_LENGTH];
34short wormy[MAX_WORM_LENGTH];
35
36int head; // index of the head within the buffer
37short headx;
38short heady;
39
40int tail; // index of the tail within the buffer
41int growing; // number of cyles the worm still keeps growing
42
43
44#define MAX_FOOD 5 // maximal number of food items
45#define FOOD_SIZE 3 // the width and height of a food
46short foodx[MAX_FOOD];
47short foody[MAX_FOOD];
48
49#define MAX_ARGH 100 // maximal number of argh items
50#define ARGH_SIZE 4 // the width and height of a argh
51#define ARGHS_PER_FOOD 2 // number of arghs produced each time a food was eaten
52short arghx[MAX_ARGH];
53short arghy[MAX_ARGH];
54int arghCount;
55
56// direction vector in which the worm moves
57short dirx; // only values -1 0 1 allowed
58short diry; // only values -1 0 1 allowed
59
60int speed = 10;
61
62// return values of checkCollision
63#define COLLISION_NONE 0
64#define COLLISION_WORM 1
65#define COLLISION_FOOD 2
66#define COLLISION_ARGH 3
67#define COLLISION_FIELD 4
68
69// size of the field the worm lives in
70#define FIELD_RECT_X 1
71#define FIELD_RECT_Y 1
72#define FIELD_RECT_WIDTH LCD_HEIGHT - 2
73#define FIELD_RECT_HEIGHT LCD_HEIGHT - 2
74
75/**
76 * Returns the current length of the worm.
77 * @return int a positive value
78 */
79int getWormLength(void) {
80 // initial simple calculation will be overwritten
81 // if wrong.
82 int retVal = head - tail;
83
84 // if the worm 'crosses' the boundaries of the ringbuffer
85 if (retVal < 0) {
86 retVal = head + MAX_WORM_LENGTH - tail;
87 }
88
89 return retVal;
90}
91
92/**
93 * Checks wether a specific food in the food arrays is at the
94 * specified coordinates.
95 * @param int foodIndex The index of the food in the food arrays
96 * @param int x the x coordinate.
97 * @param int y the y coordinate.
98 * @return Returns true if the coordinate hits the food specified by
99 * foodIndex.
100 */
101bool specificFoodCollision(int foodIndex, int x, int y) {
102 bool retVal = false;
103 if (x >= foodx[foodIndex] &&
104 x < foodx[foodIndex] + FOOD_SIZE &&
105 y >= foody[foodIndex] &&
106 y < foody[foodIndex] + FOOD_SIZE) {
107
108 retVal = true;
109 }
110 return retVal;
111}
112
113/**
114 * Returns the index of the food that is at the
115 * given coordinates. If no food is at the coordinates
116 * -1 is returned.
117 * @return int -1 <= value < MAX_FOOD
118 */
119int foodCollision(int x, int y) {
120 int i = 0;
121 int retVal = -1;
122 bool collisionDetected = false;
123 for (i = 0; i < MAX_FOOD && !collisionDetected; i++) {
124 if (specificFoodCollision(i, x, y)) {
125
126 collisionDetected = true;
127 retVal = i;
128 }
129 }
130 return retVal;
131}
132
133/**
134 * Checks wether a specific argh in the argh arrays is at the
135 * specified coordinates.
136 * @param int arghIndex The index of the argh in the argh arrays
137 * @param int x the x coordinate.
138 * @param int y the y coordinate.
139 * @return Returns true if the coordinate hits the argh specified by
140 * arghIndex.
141 */
142bool specificArghCollision(int arghIndex, int x, int y) {
143 bool retVal = false;
144 if (x >= arghx[arghIndex] &&
145 x < arghx[arghIndex] + ARGH_SIZE &&
146 y >= arghy[arghIndex] &&
147 y < arghy[arghIndex] + ARGH_SIZE) {
148
149 retVal = true;
150 }
151 return retVal;
152}
153
154/**
155 * Returns the index of the argh that is at the
156 * given coordinates. If no argh is at the coordinates
157 * -1 is returned.
158 * @param int x The x coordinate.
159 * @param int y The y coordinate.
160 * @return int -1 <= value < arghCount <= MAX_ARGH
161 */
162int arghCollision(int x, int y) {
163 int i = 0;
164 int retVal = -1;
165 bool collisionDetected = false;
166
167 // search for the argh that has the specified coords
168 for (i = 0; i < arghCount && !collisionDetected; i++) {
169 if (specificArghCollision(i, x, y)) {
170
171 collisionDetected = true;
172 retVal = i;
173 }
174 }
175 return retVal;
176}
177
178/**
179 * Checks wether the worm collides with the food at the specfied food-arrays.
180 * @param int foodIndex The index of the food in the arrays. Ensure the value is
181 * 0 <= foodIndex <= MAX_FOOD
182 * @return Returns true if the worm collides with the specified food.
183 */
184bool wormFoodCollision(int foodIndex) {
185 bool retVal = false;
186
187 // buffer wormLength because getWormLength is expensive
188 int wormLength = getWormLength();
189 int i;
190
191 // although all the worm gets iterated i is NOT the index
192 // of the worm arrays
193 for (i = 0; i < wormLength && !retVal; i++) {
194
195 // convert i to the true worm indices
196 int wormIndex = (tail + i) % MAX_WORM_LENGTH;
197
198 // The check
199 if (specificFoodCollision(foodIndex, wormx[wormIndex], wormy[wormIndex])) {
200 retVal = true;
201 }
202 }
203
204 return retVal;
205}
206
207/**
208 * Checks wether the worm collides with the argh at the specfied argh-arrays.
209 * @param int arghIndex The index of the argh in the arrays. Ensure the value is
210 * 0 <= arghIndex < arghCount <= MAX_ARGH
211 * @return Returns true if the worm collides with the specified argh.
212 */
213bool wormArghCollision(int arghIndex) {
214 bool retVal = false;
215
216 // buffer wormLength because getWormLength is expensive
217 int wormLength = getWormLength();
218 int i;
219
220 // although all the worm gets iterated i is NOT the index
221 // of the worm arrays
222 for (i = 0; i < wormLength && !retVal; i++) {
223
224 // convert i to the true worm indices
225 int wormIndex = (tail + i) % MAX_WORM_LENGTH;
226
227 // The check
228 if (specificArghCollision(arghIndex, wormx[wormIndex], wormy[wormIndex])) {
229 retVal = true;
230 }
231 }
232
233 return retVal;
234}
235
236/**
237 * Find new coordinates for the food stored in foodx[index], foody[index]
238 * that don't collide with any other food or argh
239 * @param int index
240 * Ensure that 0 <= index < MAX_FOOD.
241 */
242void makeFood(int index) {
243
244 int x = 0;
245 int y = 0;
246 bool collisionDetected = false;
247
248 do {
249 // make coordinates for a new food so that
250 // the entire food lies within the FIELD
251 x = rand() % (FIELD_RECT_WIDTH - FOOD_SIZE);
252 y = rand() % (FIELD_RECT_HEIGHT - FOOD_SIZE);
253
254 // Ensure that the new food doesn't collide with any
255 // existing foods or arghs.
256 // If one or more corners of the new food hit any existing
257 // argh or food a collision is detected.
258 collisionDetected =
259 foodCollision(x , y ) >= 0 ||
260 foodCollision(x + FOOD_SIZE - 1, y ) >= 0 ||
261 foodCollision(x , y + FOOD_SIZE - 1) >= 0 ||
262 foodCollision(x + FOOD_SIZE - 1, y + FOOD_SIZE - 1) >= 0 ||
263 arghCollision(x , y ) >= 0 ||
264 arghCollision(x + FOOD_SIZE - 1, y ) >= 0 ||
265 arghCollision(x , y + FOOD_SIZE - 1) >= 0 ||
266 arghCollision(x + FOOD_SIZE - 1, y + FOOD_SIZE - 1) >= 0;
267
268 // use coordinates for further testing
269 foodx[index] = x;
270 foody[index] = y;
271
272 // now test wether we accidently hit the worm with food ;)
273 collisionDetected |= wormFoodCollision(index);
274 }
275 while (collisionDetected);
276}
277
278/**
279 * Clears a food from the lcd buffer.
280 * @param int index The index of the food arrays under which
281 * the coordinates of the desired food can be found. Ensure
282 * that the value is 0 <= index <= MAX_FOOD.
283 */
284void clearFood(int index) {
285 // remove the old food from the screen
286 lcd_clearrect (foodx[index] + FIELD_RECT_X,
287 foody[index] + FIELD_RECT_Y,
288 FOOD_SIZE, FOOD_SIZE);
289}
290
291/**
292 * Draws a food in the lcd buffer.
293 * @param int index The index of the food arrays under which
294 * the coordinates of the desired food can be found. Ensure
295 * that the value is 0 <= index <= MAX_FOOD.
296 */
297void drawFood(int index) {
298 // draw the food object
299 lcd_fillrect (foodx[index] + FIELD_RECT_X,
300 foody[index] + FIELD_RECT_Y,
301 FOOD_SIZE, FOOD_SIZE);
302 lcd_clearrect(foodx[index] + FIELD_RECT_X + 1,
303 foody[index] + FIELD_RECT_Y + 1,
304 FOOD_SIZE - 2, FOOD_SIZE - 2);
305}
306
307/**
308 * Find new coordinates for the argh stored in arghx[index], arghy[index]
309 * that don't collide with any other food or argh.
310 * @param int index
311 * Ensure that 0 <= index < arghCount < MAX_ARGH.
312 */
313void makeArgh(int index) {
314 int x;
315 int y;
316 bool collisionDetected = false;
317
318 do {
319 // make coordinates for a new argh so that
320 // the entire food lies within the FIELD
321 x = rand() % (FIELD_RECT_WIDTH - ARGH_SIZE);
322 y = rand() % (FIELD_RECT_HEIGHT - ARGH_SIZE);
323 // Ensure that the new argh doesn't intersect with any
324 // existing foods or arghs.
325 // If one or more corners of the new argh hit any existing
326 // argh or food an intersection is detected.
327 collisionDetected =
328 foodCollision(x , y ) >= 0 ||
329 foodCollision(x + ARGH_SIZE - 1, y ) >= 0 ||
330 foodCollision(x , y + ARGH_SIZE - 1) >= 0 ||
331 foodCollision(x + ARGH_SIZE - 1, y + ARGH_SIZE - 1) >= 0 ||
332 arghCollision(x , y ) >= 0 ||
333 arghCollision(x + ARGH_SIZE - 1, y ) >= 0 ||
334 arghCollision(x , y + ARGH_SIZE - 1) >= 0 ||
335 arghCollision(x + ARGH_SIZE - 1, y + ARGH_SIZE - 1) >= 0;
336
337 // use the candidate coordinates to make a real argh
338 arghx[index] = x;
339 arghy[index] = y;
340
341 // now test wether we accidently hit the worm with argh ;)
342 collisionDetected |= wormArghCollision(index);
343 }
344 while (collisionDetected);
345}
346
347/**
348 * Draws an argh in the lcd buffer.
349 * @param int index The index of the argh arrays under which
350 * the coordinates of the desired argh can be found. Ensure
351 * that the value is 0 <= index < arghCount <= MAX_ARGH.
352 */
353void drawArgh(int index) {
354 // draw the new argh
355 lcd_fillrect (arghx[index] + FIELD_RECT_X,
356 arghy[index] + FIELD_RECT_Y,
357 ARGH_SIZE, ARGH_SIZE);
358}
359
360/**
361 * Initializes the worm-, food- and argh-arrays, draws a frame,
362 * makes some food and argh and display all that stuff.
363 */
364void initWormlet(void) {
365
366 // Needed when the game is restarted using BUTTON_ON
367 lcd_clear_display();
368
369 // Initialize all the worm coordinates to 0,0.
370 int i;
371 for (i = 0; i < MAX_WORM_LENGTH; i++){
372 wormx[i] = 0;
373 wormy[i] = 0;
374 }
375
376 // initialize the worm size
377 head = INITIAL_WORM_LENGTH;
378 tail = 0;
379
380 // initialize the worm start point
381 headx = 0;
382 heady = 0;
383
384 // set the initial direction the worm creeps to
385 dirx = 1;
386 diry = 0;
387
388 // make and display some food and argh
389 arghCount = MAX_FOOD;
390 for (i = 0; i < MAX_FOOD; i++) {
391 makeFood(i);
392 drawFood(i);
393 makeArgh(i);
394 drawArgh(i);
395 }
396
397 // draw the game field
398 lcd_invertrect (0 , 0 , FIELD_RECT_WIDTH + 2, FIELD_RECT_HEIGHT + 2);
399 lcd_invertrect(0 + 1, 0 + 1, FIELD_RECT_WIDTH , FIELD_RECT_HEIGHT);
400
401 // make everything visible
402 lcd_update();
403}
404
405/**
406 * Move the worm one step further.
407 * The direction in which the worm moves is taken
408 * from dirx and diry. If the
409 * worm crosses the boundaries of the field the
410 * worm is wrapped (it enters the field from the
411 * opposite side). moveWorm decreases growing if > 0.
412 */
413void moveWorm(void) {
414 // find the next array index for the head
415 head++;
416 if (head >= MAX_WORM_LENGTH){
417 head = 0;
418 }
419
420 // find the next array index for the tail
421 tail++;
422 if (tail >= MAX_WORM_LENGTH){
423 tail = 0;
424 }
425
426 // determine the new head position
427 headx += dirx;
428 heady += diry;
429
430 // Wrap the new head position if necessary
431 if (headx >= FIELD_RECT_WIDTH) {
432 headx = 0;
433 }
434
435 if (headx < 0){
436 headx = FIELD_RECT_WIDTH - 1;
437 }
438
439 if (heady >= FIELD_RECT_HEIGHT) {
440 heady = 0;
441 }
442
443 if (heady < 0) {
444 heady = FIELD_RECT_HEIGHT - 1;
445 }
446
447 // store the new head position in the
448 // worm arrays
449 wormx[head] = headx;
450 wormy[head] = heady;
451
452 // update the worms grow state
453 if (growing > 0) growing --;
454}
455
456/**
457 * Draws the head and clears the tail of the worm in
458 * the display buffer. lcd_update() is NOT called thus
459 * the caller has to take care that the buffer is displayed.
460 */
461void drawWorm(void) {
462 // draw the new head
463 lcd_drawpixel( wormx[head] + FIELD_RECT_X, wormy[head] + FIELD_RECT_Y);
464
465 // clear the space behind the worm
466 lcd_clearpixel(wormx[tail] + FIELD_RECT_X, wormy[tail] + FIELD_RECT_Y);
467}
468
469/**
470 * Checks wether the coordinate is part of the worm.
471 * @param x int The x coordinate
472 * @param y int The y coordinate
473 * @return int The index of the worm arrays that contain x, y.
474 * Returns -1 if the coordinates are not part of the worm.
475 */
476int wormCollision(int x, int y) {
477 int retVal = -1;
478
479 // getWormLength is expensive -> buffer the value
480 int wormLength = getWormLength();
481 int i;
482
483 // test each entry that is part of the worm
484 for (i = 0; i < wormLength && retVal == -1; i++) {
485
486 // The iteration iterates the length of the worm.
487 // Here's the conversion to the true indices within
488 // the worm arrays.
489 int trueIndex = (tail + i) % MAX_WORM_LENGTH;
490
491 // The check
492 if (wormx[trueIndex] == x && wormy[trueIndex] == y) {
493 retVal = trueIndex;
494 }
495 }
496 return retVal;
497}
498
499/**
500 * Returns true if the head of the worm just has
501 * crossed the field boundaries.
502 * @return bool true if the worm just has wrapped.
503 */
504bool fieldCollision(void) {
505 bool retVal = false;
506 if ((headx == FIELD_RECT_WIDTH - 1 && dirx == -1) ||
507 (headx == 0 && dirx == 1) ||
508 (heady == FIELD_RECT_HEIGHT - 1 && diry == -1) ||
509 (heady == 0 && diry == 1)) {
510
511 retVal = true;
512 }
513 return retVal;
514}
515
516/**
517 * Checks and returns wether the head of the worm
518 * is colliding with something currently.
519 * @return int One of the values
520 * <ul>
521 * <li>COLLISION_NONE
522 * <li>COLLISION_WORM
523 * <li>COLLISION_FOOD
524 * <li>COLLISION_ARGH
525 * <li>COLLISION_FIELD
526 * </ul>
527 */
528int checkCollision(void) {
529 int retVal = COLLISION_NONE;
530
531 if (wormCollision(headx, heady) >= 0) {
532 retVal = COLLISION_WORM;
533 }
534
535 if (foodCollision(headx, heady) >= 0) {
536 retVal = COLLISION_FOOD;
537 }
538
539 if (arghCollision(headx, heady) >= 0) {
540 retVal = COLLISION_ARGH;
541 }
542
543 if (fieldCollision()) {
544 retVal = COLLISION_FIELD;
545 }
546
547 return retVal;
548}
549
550/*
551void debugOutput(void) {
552 char buf[40];
553
554 // head
555 snprintf(buf, sizeof(buf), "h:%d(%d;%d) ", head, wormx[head], wormy[head]);
556 lcd_putsxy(FIELD_RECT_WIDTH + 3, 0, buf, 0);
557
558 // tail
559 snprintf(buf, sizeof(buf), "t:%d(%d;%d) ", tail, wormx[tail], wormy[tail]);
560 lcd_putsxy(FIELD_RECT_WIDTH + 3, 8, buf, 0);
561
562 // speed
563 snprintf(buf, sizeof(buf), "div:%d ", speed);
564 lcd_putsxy(FIELD_RECT_WIDTH + 3, 16, buf, 0);
565
566 // collision
567 switch (checkCollision()) {
568 case COLLISION_NONE: snprintf(buf, sizeof(buf), "free "); break;
569 case COLLISION_WORM: snprintf(buf, sizeof(buf), "worm "); break;
570 case COLLISION_FOOD: snprintf(buf, sizeof(buf), "food "); break;
571 case COLLISION_ARGH: snprintf(buf, sizeof(buf), "argh "); break;
572 case COLLISION_FIELD: snprintf(buf, sizeof(buf), "field "); break;
573 }
574 lcd_putsxy(FIELD_RECT_WIDTH + 3, 24, buf, 0);
575}
576*/
577
578/**
579 * Prints out the score board with all the status information
580 * about the game.
581 */
582void scoreBoard(void) {
583 char buf[15];
584 char buf2[15];
585
586 // Title
587 snprintf(buf, sizeof (buf), "Wormlet");
588 lcd_putsxy(FIELD_RECT_WIDTH + 3, 0, buf, 0);
589
590 // length
591 snprintf(buf, sizeof (buf), "length:");
592 lcd_putsxy(FIELD_RECT_WIDTH + 3, 12, buf, 0);
593 snprintf(buf, sizeof (buf), "%d ", getWormLength() - growing);
594 lcd_putsxy(FIELD_RECT_WIDTH + 3, 20, buf, 0);
595
596 switch (checkCollision()) {
597 case COLLISION_NONE:
598 snprintf(buf, sizeof(buf), "I'm hungry! ");
599 if (growing > 0) {
600 snprintf(buf2, sizeof(buf2), "growing");
601 }
602 else {
603 snprintf(buf2, sizeof(buf2), " ");
604 }
605 break;
606
607 case COLLISION_WORM:
608 snprintf(buf, sizeof(buf), "Ouch! I bit");
609 snprintf(buf2, sizeof(buf2), "myself! ");
610 break;
611
612 case COLLISION_FOOD:
613 snprintf(buf, sizeof(buf), "Yummy! ");
614 snprintf(buf2, sizeof(buf2), "growing");
615 break;
616
617 case COLLISION_ARGH:
618 snprintf(buf, sizeof(buf), "Argh! I'm ");
619 snprintf(buf2, sizeof(buf2), "poisoned! ");
620 break;
621
622 case COLLISION_FIELD:
623 snprintf(buf, sizeof(buf), "Boing! ");
624 snprintf(buf2, sizeof(buf2), "Headcrash!");
625 break;
626 }
627 lcd_putsxy(FIELD_RECT_WIDTH + 3, 32, buf, 0);
628 lcd_putsxy(FIELD_RECT_WIDTH + 3, 40, buf2, 0);
629}
630
631/**
632 * Checks for collisions of the worm and its environment and
633 * takes appropriate actions like growing the worm or killing it.
634 * @return bool Returns true if the worm is dead. Returns
635 * false if the worm is healthy, up and creeping.
636 */
637bool processCollisions(void) {
638 bool wormDead = false;
639 int index = -1;
640
641 wormDead = fieldCollision();
642
643 if (!wormDead) {
644
645 // check if food was eaten
646 index = foodCollision(headx, heady);
647 if (index != -1){
648 clearFood(index);
649 makeFood(index);
650 drawFood(index);
651
652 int i = 0;
653
654 for (i = 0; i < ARGHS_PER_FOOD; i++) {
655 arghCount++;
656 if (arghCount > MAX_ARGH) {
657 arghCount = MAX_ARGH;
658 }
659 makeArgh(arghCount - 1);
660 drawArgh(arghCount - 1);
661 }
662
663 tail -= WORM_PER_FOOD;
664 growing += WORM_PER_FOOD;
665 if (tail < 0) {
666 tail += MAX_WORM_LENGTH;
667 }
668
669 drawWorm();
670 }
671
672 // check if argh was eaten
673 else {
674 index = arghCollision(headx, heady);
675 if (index != -1) {
676 wormDead = true;
677 }
678 else {
679 if (wormCollision(headx, heady) != -1) {
680 wormDead = true;
681 }
682 }
683 }
684 }
685 return wormDead;
686}
687
688/**
689 * The main loop of the game.
690 * @return bool Returns true if the game ended
691 * with a dead worm. Returns false if the user
692 * aborted the game manually.
693 */
694bool run(void) {
695 int button = 0;
696 int wormDead = false;
697
698 // initialize the board and so on
699 initWormlet();
700
701 // change the direction of the worm
702 while (button != BUTTON_OFF && ! wormDead)
703 {
704 switch (button) {
705 case BUTTON_UP:
706 diry = -1;
707 dirx = 0;
708 break;
709
710 case BUTTON_DOWN:
711 diry = 1;
712 dirx = 0;
713 break;
714
715 case BUTTON_LEFT:
716 dirx = -1;
717 diry = 0;
718 break;
719
720 case BUTTON_RIGHT:
721 dirx = 1;
722 diry = 0;
723 break;
724
725 case BUTTON_F2:
726 speed--;
727 break;
728
729 case BUTTON_F3:
730 speed ++;
731 break;
732 }
733
734 moveWorm();
735// debugOutput();
736 wormDead = processCollisions();
737 drawWorm();
738 scoreBoard();
739 lcd_update();
740 button = button_get_w_tmo(HZ/speed);
741 }
742 return wormDead;
743}
744
745/**
746 * Main entry point from the menu to start the game control.
747 */
748Menu wormlet(void)
749{
750 bool wormDead = false;
751 int button;
752 do {
753
754 // button state will be overridden if
755 // the game quits with the death of the worm.
756 // Initializing button to BUTTON_OFF ensures
757 // that the user can hit BUTTON_OFF during the
758 // game to return to the menu.
759 button = BUTTON_OFF;
760
761 // start the game
762 wormDead = run();
763
764 // if worm isn't dead the game was quit
765 // via BUTTON_OFF -> no need to wait for
766 // buttons.
767 if (wormDead) {
768 do {
769 button = button_get(true);
770 }
771 // BUTTON_ON -> start new game
772 // BUTTON_OFF -> back to game menu
773 while (button != BUTTON_OFF && button != BUTTON_ON);
774 }
775 }
776 while (button != BUTTON_OFF);
777
778 return MENU_OK;
779}