📄 wormlet.c
字号:
/** * Returns the index of the food that is closest to the point * specified by x, y. This index may be used in the foodx and * foody arrays. * @param int x The x coordinate of the point * @param int y The y coordinate of the point * @return int A value usable as index in foodx and foody. */static int get_nearest_food(int x, int y){ int nearestfood = 0; int olddistance = FIELD_RECT_WIDTH + FIELD_RECT_HEIGHT; int deltax = 0; int deltay = 0; int foodindex; for (foodindex = 0; foodindex < MAX_FOOD; foodindex++) { int distance; deltax = foodx[foodindex] - x; deltay = foody[foodindex] - y; deltax = deltax > 0 ? deltax : deltax * (-1); deltay = deltay > 0 ? deltay : deltay * (-1); distance = deltax + deltay; if (distance < olddistance) { olddistance = distance; nearestfood = foodindex; } } return nearestfood;}/** * Returns wether the specified position is next to the worm * and in the direction the worm looks. Use this method to * test wether this position would be hit with the next move of * the worm unless the worm changes its direction. * @param struct worm *w - The worm to be investigated * @param int x - The x coordinate of the position to test. * @param int y - The y coordinate of the position to test. * @return Returns true if the worm will hit the position unless * it change its direction before the next move. */static bool is_in_front_of_worm(struct worm *w, int x, int y) { bool infront = false; int deltax = x - w->x[w->head]; int deltay = y - w->y[w->head]; if (w->dirx == 0) { infront = (w->diry * deltay) > 0; } else { infront = (w->dirx * deltax) > 0; } return infront;}/** * Returns true if the worm will collide with the next move unless * it changes its direction. * @param struct worm *w - The worm to be investigated. * @return Returns true if the worm will collide with the next move * unless it changes its direction. */static bool will_worm_collide(struct worm *w) { int x = w->x[w->head] + w->dirx; int y = w->y[w->head] + w->diry; bool retVal = !is_in_field_rect(x, y); if (!retVal) { retVal = (argh_collision(x, y) != -1); } if (!retVal) { retVal = (worm_collision(w, x, y) != NULL); } return retVal;}/** * This function * may be used to be stored in worm.fetch_worm_direction for * worms that are not controlled by humans but by artificial stupidity. * A direction is searched that doesn't lead to collision but to the nearest * food - but not very intelligent. The direction is written to the specified * worm. * @param struct worm *w - The worm of which the direction * is altered. */static void virtual_player(struct worm *w) { bool isright; int plana, planb, planc; /* find the next lunch */ int nearestfood = get_nearest_food(w->x[w->head], w->y[w->head]); /* determine in which direction it is */ /* in front of me? */ bool infront = is_in_front_of_worm(w, foodx[nearestfood], foody[nearestfood]); /* left right of me? */ int olddir = get_worm_dir(w); set_worm_dir(w, (olddir + 1) % 4); isright = is_in_front_of_worm(w, foodx[nearestfood], foody[nearestfood]); set_worm_dir(w, olddir); /* detect situation, set strategy */ if (infront) { if (isright) { plana = olddir; planb = (olddir + 1) % 4; planc = (olddir + 3) % 4; } else { plana = olddir; planb = (olddir + 3) % 4; planc = (olddir + 1) % 4; } } else { if (isright) { plana = (olddir + 1) % 4; planb = olddir; planc = (olddir + 3) % 4; } else { plana = (olddir + 3) % 4; planb = olddir; planc = (olddir + 1) % 4; } } /* test for collision */ set_worm_dir(w, plana); if (will_worm_collide(w)){ /* plan b */ set_worm_dir(w, planb); /* test for collision */ if (will_worm_collide(w)) { /* plan c */ set_worm_dir(w, planc); } }}/** * prints out the score board with all the status information * about the game. */static void score_board(void){ char buf[15]; char* buf2 = NULL; int i; int y = 0; rb->lcd_clearrect(FIELD_RECT_WIDTH + 2, 0, LCD_WIDTH - FIELD_RECT_WIDTH - 2, LCD_HEIGHT); for (i = 0; i < worm_count; i++) { int score = get_score(&worms[i]); /* high score */ if (worms[i].fetch_worm_direction != virtual_player){ if (highscore < score) { highscore = score; } } /* length */ rb->snprintf(buf, sizeof (buf),"Len:%d", score); /* worm state */ switch (check_collision(&worms[i])) { case COLLISION_NONE: if (worms[i].growing > 0) buf2 = "Growing"; else { if (worms[i].alive) buf2 = "Hungry"; else buf2 = "Wormed"; } break; case COLLISION_WORM: buf2 = "Wormed"; break; case COLLISION_FOOD: buf2 = "Growing"; break; case COLLISION_ARGH: buf2 = "Argh"; break; case COLLISION_FIELD: buf2 = "Crashed"; break; } rb->lcd_putsxy(FIELD_RECT_WIDTH + 3, y , buf); rb->lcd_putsxy(FIELD_RECT_WIDTH + 3, y+8, buf2); if (!worms[i].alive){ rb->lcd_invertrect(FIELD_RECT_WIDTH + 2, y, LCD_WIDTH - FIELD_RECT_WIDTH - 2, 17); } y += 19; } rb->snprintf(buf , sizeof(buf), "Hs: %d", highscore);#ifndef DEBUG_WORMLET rb->lcd_putsxy(FIELD_RECT_WIDTH + 3, LCD_HEIGHT - 8, buf);#else rb->lcd_putsxy(FIELD_RECT_WIDTH + 3, LCD_HEIGHT - 8, debugout);#endif}/** * 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. */static bool process_collisions(struct worm *w){ int index = -1; w->alive &= !field_collision(w); if (w->alive) { /* check if food was eaten */ index = food_collision(w->x[w->head], w->y[w->head]); if (index != -1){ int i; clear_food(index); make_food(index); draw_food(index); for (i = 0; i < ARGHS_PER_FOOD; i++) { argh_count++; if (argh_count > MAX_ARGH) argh_count = MAX_ARGH; make_argh(argh_count - 1); draw_argh(argh_count - 1); } add_growing(w, WORM_PER_FOOD); draw_worm(w); } /* check if argh was eaten */ else { index = argh_collision(w->x[w->head], w->y[w->head]); if (index != -1) { w->alive = false; } else { if (worm_collision(w, w->x[w->head], w->y[w->head]) != NULL) { w->alive = false; } } } } return !w->alive;}/** * 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. */ static bool run(void){ int button = 0; int wormDead = false; /* ticks are counted to compensate speed variations */ long cycle_start = 0, cycle_end = 0;#ifdef DEBUG_WORMLET int ticks_to_max_cycle_reset = 20; long max_cycle = 0; char buf[20];#endif /* initialize the board and so on */ init_wormlet(); cycle_start = *rb->current_tick; /* change the direction of the worm */ while (button != BUTTON_OFF && ! wormDead) { int i; long cycle_duration ; switch (button) { case BUTTON_UP: if (players == 1 && !use_remote) { player1_dir = NORTH; } break; case BUTTON_DOWN: if (players == 1 && !use_remote) { player1_dir = SOUTH; } break; case BUTTON_LEFT: if (players != 1 || use_remote) { player1_dir = (player1_dir + 3) % 4; } else { player1_dir = WEST; } break; case BUTTON_RIGHT: if (players != 1 || use_remote) { player1_dir = (player1_dir + 1) % 4; } else { player1_dir = EAST; } break; case BUTTON_F2: player2_dir = (player2_dir + 3) % 4; break; case BUTTON_F3: player2_dir = (player2_dir + 1) % 4; break; case BUTTON_RC_VOL_UP: player3_dir = (player3_dir + 1) % 4; break; case BUTTON_RC_VOL_DOWN: player3_dir = (player3_dir + 3) % 4; break; case BUTTON_PLAY: do { button = rb->button_get(true); } while (button != BUTTON_PLAY && button != BUTTON_OFF && button != BUTTON_ON); break; } for (i = 0; i < worm_count; i++) { worms[i].fetch_worm_direction(&worms[i]); } wormDead = true; for (i = 0; i < worm_count; i++){ struct worm *w = &worms[i]; move_worm(w); wormDead &= process_collisions(w); draw_worm(w); } score_board(); rb->lcd_update(); if (button == BUTTON_ON) { wormDead = true; } /* here the wormlet game cycle ends thus the current tick is stored as end time */ cycle_end = *rb->current_tick; /* The duration of the game cycle */ cycle_duration = cycle_end - cycle_start; cycle_duration = MAX(0, cycle_duration); cycle_duration = MIN(SPEED -1, cycle_duration);#ifdef DEBUG_WORMLET ticks_to_max_cycle_reset--; if (ticks_to_max_cycle_reset <= 0) { max_cycle = 0; } if (max_cycle < cycle_duration) { max_cycle = cycle_duration; ticks_to_max_cycle_reset = 20; } rb->snprintf(buf, sizeof buf, "ticks %d", max_cycle); set_debug_out(buf);#endif /* adjust the number of ticks to wait for a button. This ensures that a complete game cycle including user input runs in constant time */ button = rb->button_get_w_tmo(SPEED - cycle_duration); cycle_start = *rb->current_tick; } return wormDead;}#ifdef DEBUG_WORMLET/** * Just a test routine that checks that worm_food_collision works * in some typical situations. */static void test_worm_food_collision(void) { int collision_count = 0; int i; rb->lcd_clear_display(); init_worm(&worms[0], 10, 10); add_growing(&worms[0], 10); set_worm_dir(&worms[0], EAST); for (i = 0; i < 10; i++) { move_worm(&worms[0]); draw_worm(&worms[0]); } set_worm_dir(&worms[0], SOUTH); for (i = 0; i < 10; i++) { move_worm(&worms[0]); draw_worm(&worms[0]); } foodx[0] = 15; foody[0] = 12; for (foody[0] = 20; foody[0] > 0; foody[0] --) { char buf[20]; bool collision; draw_worm(&worms[0]); draw_food(0); collision = worm_food_collision(&worms[0], 0); if (collision) { collision_count++; } rb->snprintf(buf, sizeof buf, "collisions: %d", collision_count); rb->lcd_putsxy(0, LCD_HEIGHT -8, buf); rb->lcd_update(); } if (collision_count != FOOD_SIZE) { rb->button_get(true); } foody[0] = 15; for (foodx[0] = 30; foodx[0] > 0; foodx[0] --) { char buf[20]; bool collision; draw_worm(&worms[0]); draw_food(0); collision = worm_food_collision(&worms[0], 0); if (collision) { collision_count ++; } rb->snprintf(buf, sizeof buf, "collisions: %d", collision_count); rb->lcd_putsxy(0, LCD_HEIGHT -8, buf); rb->lcd_update(); } if (collision_count != FOOD_SIZE * 2) { rb->button_get(true); }}static bool expensive_worm_in_rect(struct worm *w, int rx, int ry, int rw, int rh){ int x, y; bool retVal = false; for (x = rx; x < rx + rw; x++){ for (y = ry; y < ry + rh; y++) { if (specific_worm_collision(w, x, y) != -1) { retVal = true; } } } return retVal;}static void test_worm_argh_collision(void) { int i; int dir; int collision_count = 0; rb->lcd_clear_display(); init_worm(&worms[0], 10, 10); add_growing(&worms[0], 40); for (dir = 0; dir < 4; dir++) { set_worm_dir(&worms[0], (EAST + dir) % 4); for (i = 0; i < 10; i++) { move_worm(&worms[0]); draw_worm(&worms[0]); } } arghx[0] = 12; for (arghy[0] = 0; arghy[0] < FIELD_RECT_HEIGHT - ARGH_SIZE; arghy[0]++){ char buf[20]; bool collision; draw_argh(0); collision = worm_argh_collision(&worms[0], 0); if (collision) { collision_count ++; } rb->snprintf(buf, sizeof buf, "collisions: %d", collision_count); rb->lcd_putsxy(0, LCD_HEIGHT -8, buf); rb->lcd_update(); } if (collision_count != ARGH_SIZE * 2) { rb->button_get(true);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -