Mercurial > MadButterfly
diff examples/tank/enemy.c @ 1100:fb79175e6cc3
Add AI for enemy tanks
author | Thinker K.F. Li <thinker@codemud.net> |
---|---|
date | Sun, 05 Dec 2010 12:23:29 +0800 |
parents | |
children | dbea3e42bf93 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/examples/tank/enemy.c Sun Dec 05 12:23:29 2010 +0800 @@ -0,0 +1,272 @@ +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "tank.h" + +struct _enemy { + tank_rt_t *tank_rt; + tank_t *tank; + int memory[4]; +}; +typedef struct _enemy enemy_t; + +/* \brief Fire and move to the target tank if they are in a row or column. + */ +static int +try_fire(tank_t *me, tank_t *target, tank_rt_t *tank_rt) { + int x, y; + int *tracer = NULL; + int target_value; + int target_dir; + + if(me->map_x == target->map_x) { /* In a row */ + tracer = &y; + target_value = target->map_y; + if(me->map_y < target->map_y) + target_dir = TD_DOWN; + else + target_dir = TD_UP; + } + if(me->map_y == target->map_y) { /* In a column */ + tracer = &x; + target_value = target->map_x; + if(me->map_x < target->map_x) + target_dir = TD_RIGHT; + else + target_dir = TD_LEFT; + } + + if(tracer == NULL) + return 0; /* Not in a row or column */ + + /* Check obstacles between tanks */ + x = me->map_x; + y = me->map_y; + if(*tracer < target_value) { + while(++*tracer < target_value) { + if(map[y][x] != MUD) + break; + } + } else { + while(--*tracer > target_value) { + if(map[y][x] != MUD) + break; + } + } + + if(*tracer == target_value) { /* No any obstacle between tanks */ + /* Fire and move to target */ + if(me->direction == target_dir && me->bullet == NULL) + tank_fire_bullet(tank_rt, me); + tank_move(me, target_dir, tank_rt); + return 1; + } + + return 0; /* Find one or more obstacles */ +} + +#define NOTHING 0 +#define SOMETHING 1 + +static void +move_tank(enemy_t *enemy, tank_rt_t *tank_rt) { + tank_t *me; + tank_t **tanks, *tank; + static const int shift_xy[4][2] = { + {1, 0}, {0, 1}, + {-1, 0}, {0, -1}}; + int status[4]; + int x, y; + int i, tank_i; + int dir, chk_dir; + int possibles; + int which_dir; + + me = enemy->tank; + tanks = tank_rt->tanks; + + /* Collect status */ + for(i = 0; i < 4; i++) { + x = me->map_x + shift_xy[i][0]; + y = me->map_y + shift_xy[i][1]; + + /* Check obstacles */ + if(x == -1 || y == -1 || x >= MAP_W || y >= MAP_H) { + /* Out of range */ + status[i] = SOMETHING; + continue; + } + if(map[y][x] == MUD) + status[i] = NOTHING; + else + status[i] = SOMETHING; + + /* Check tanks */ + for(tank_i = 0; tank_i < tank_rt->n_tanks; tank_i++) { + tank = tanks[tank_i]; + if(tank->map_x == x && tank->map_y == y) { + status[i] = SOMETHING; + break; + } + } + } + + /* Try the same direction if status is not changed. */ + for(i = 0; i < 4; i++) { + if(status[i] != enemy->memory[i]) + break; + } + if(i == 4) { /* Status is not changed */ + tank_move(me, me->direction, tank_rt); + return; + } + + memcpy(enemy->memory, status, sizeof(int) * 4); + + /* Check possible directions except backward. */ + switch(me->direction) { + case TD_UP: + dir = 3; + break; + case TD_RIGHT: + dir = 0; + break; + case TD_DOWN: + dir = 1; + break; + case TD_LEFT: + dir = 2; + break; + } + + possibles = 0; + for(i = 0; i < 3; i++) { + chk_dir = (dir - 1 + i) % 4; + if(status[chk_dir] == NOTHING) + possibles++; + } + + if(possibles == 0) { /* Only can move backward */ + switch(me->direction) { + case TD_UP: + tank_move(me, TD_DOWN, tank_rt); + break; + case TD_RIGHT: + tank_move(me, TD_LEFT, tank_rt); + break; + case TD_DOWN: + tank_move(me, TD_UP, tank_rt); + break; + case TD_LEFT: + tank_move(me, TD_RIGHT, tank_rt); + break; + } + return; + } + + which_dir = rand() % possibles; + for(i = 0; i < 3; i++) { + chk_dir = (dir - 1 + i) % 4; + if(status[chk_dir] == NOTHING) { + if(which_dir == 0) + break; + which_dir--; + } + } + switch(chk_dir) { + case 0: + tank_move(me, TD_RIGHT, tank_rt); + break; + case 1: + tank_move(me, TD_DOWN, tank_rt); + break; + case 2: + tank_move(me, TD_LEFT, tank_rt); + break; + case 3: + tank_move(me, TD_UP, tank_rt); + break; + } +} + +static void +_drive_enemy_tank(enemy_t *enemy) { + tank_rt_t *tank_rt; + tank_t *me, *tank1, *tank2; + int r; + + tank_rt = enemy->tank_rt; + tank1 = tank_rt->tank1; + tank2 = tank_rt->tank2; + me = enemy->tank; + + r = try_fire(me, tank1, tank_rt); + if(r) + return; + r = try_fire(me, tank2, tank_rt); + if(r) + return; + + move_tank(enemy, tank_rt); +} + +static enemy_t *enemies = NULL; +static mb_timer_man_t *timer_man; + +/*! \brief Drive every enemy tanks. + */ +static void +enemy_tank_driver(int hdl, const mb_timeval_t *tmo, + const mb_timeval_t *now, void *data) { + tank_rt_t *tank_rt = (tank_rt_t *)data; + int n_enemy; + mb_timeval_t timeout, addend; + int i; + + n_enemy = tank_rt->n_enemy; + for(i = 0; i < n_enemy; i++) { + _drive_enemy_tank(enemies + i); + } + + get_now(&timeout); + MB_TIMEVAL_SET(&addend, 0, 300000); + MB_TIMEVAL_ADD(&timeout, &addend); + mb_timer_man_timeout(timer_man, &timeout, enemy_tank_driver, tank_rt); +} + +/*! \brief Start a timer for enemy tank driver. + */ +static void +start_enemy_tank_timer(tank_rt_t *tank_rt) { + mb_timeval_t timeout, addend; + + timer_man = mb_runtime_timer_man(tank_rt->mb_rt); + + get_now(&timeout); + MB_TIMEVAL_SET(&addend, 0, 300000); + MB_TIMEVAL_ADD(&timeout, &addend); + mb_timer_man_timeout(timer_man, &timeout, enemy_tank_driver, tank_rt); +} + +void +init_enemies(tank_rt_t *tank_rt) { + int n_enemy; + tank_t **tank_enemies; + tank_t *tank; + int i, j; + + n_enemy = tank_rt->n_enemy; + tank_enemies = tank_rt->tank_enemies; + + enemies = (enemy_t *)malloc(sizeof(enemy_t) * n_enemy); + + for(i = 0; i < n_enemy; i++) { + tank = tank_enemies[i]; + enemies[i].tank_rt = tank_rt; + enemies[i].tank = tank; + for(j = 0; j < 4; j++) + enemies[i].memory[j] = SOMETHING; + } + + start_enemy_tank_timer(tank_rt); +}