changeset 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 c18ad321844d
children e1a76d10953e
files examples/tank/Makefile.am examples/tank/enemy.c examples/tank/tank.h examples/tank/tank_main.c
diffstat 4 files changed, 380 insertions(+), 79 deletions(-) [+]
line wrap: on
line diff
--- a/examples/tank/Makefile.am	Sat Dec 04 16:22:11 2010 +0800
+++ b/examples/tank/Makefile.am	Sun Dec 05 12:23:29 2010 +0800
@@ -4,7 +4,7 @@
 #EXTRA_DIST = 
 svg_sources = brick.c bullet.c bush.c mud.c rock.c \
 	tank1.c tank2.c tank_en.c bang.c
-tank_SOURCES = tank_main.c
+tank_SOURCES = tank_main.c enemy.c
 nodist_tank_SOURCES = svgs.h \
 	$(svg_sources) $(svg_sources:.c=.h) $(svg_sources:.c=.mb)
 tank_CPPFLAGS = $(APPCFLAGS)
--- /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);
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/examples/tank/tank.h	Sun Dec 05 12:23:29 2010 +0800
@@ -0,0 +1,99 @@
+#ifndef __TANK_H_
+#define __TANK_H_
+
+#include <mb.h>
+#include "svgs.h"
+
+/*! \ingroup tank
+ * @{
+ */
+/*! \brief Tile types in a map. */
+enum { MUD, ROC, BRI, BSH };
+
+/*! \brief Map of the game. */
+extern char map[12][16];
+
+#define MAP_W 16
+#define MAP_H 12
+/* @} */
+
+/*! \defgroup bullet_elf Bullet Elf
+ * \ingroup tank
+ * @{
+ */
+/*! \brief Information about bullet elf
+ */
+struct _tank_bullet {
+    redraw_man_t *rdman;
+    coord_t *coord_pos;
+    coord_t *coord_rot;
+    bullet_t *bullet_obj;
+    int start_map_x, start_map_y;
+    int direction;
+    mb_progm_t *progm;
+    mb_timeval_t start_time;
+    observer_t *observer_redraw;
+    int hit_tmr;
+    mb_timer_man_t *timer_man;
+};
+typedef struct _tank_bullet tank_bullet_t;
+/*! \brief The direction a bullet is going.
+ */
+enum { BU_UP = 0, BU_RIGHT, BU_DOWN, BU_LEFT };
+/* @} */
+
+/*! \defgroup tank_elf Tank Elf
+ * \brief Tank elf module provides control functions of tanks in game.
+ * \ingroup tank
+ * @{
+ */
+/*! \brief Information about a tank elf. */
+struct _tank {
+    coord_t *coord_pos;		/*!< \brief coordinate for position */
+    coord_t *coord_rot;		/*!< \brief coordinate for rotation */
+    coord_t *coord_center;
+    int map_x, map_y;
+    int direction;
+    mb_progm_t *progm;
+    tank_bullet_t *bullet;
+    struct _tank_rt *tank_rt;	/*!< \brief for bullet to check
+				 * hitting on tanks.
+				 */
+};
+typedef struct _tank tank_t;
+enum { TD_UP = 0, TD_RIGHT, TD_DOWN, TD_LEFT };
+
+/* @} */
+
+/*
+ * \ingroup tank
+ * @{
+ */
+typedef struct _tank_rt tank_rt_t;
+
+/*! \brief Runtime information for tank, this game/example.
+ */
+struct _tank_rt {
+    tank_t *tank1;
+    tank1_t *tank1_o;
+    tank_t *tank2;
+    tank2_t *tank2_o;
+    int n_enemy;
+    tank_t *tank_enemies[10];
+    tank_en_t *tank_enemies_o[10];
+    tank_t *tanks[12];
+    int n_tanks;
+    void *map[12][16];
+    mb_rt_t *mb_rt;
+    observer_t *kb_observer;
+};
+
+extern void tank_move(tank_t *tank, int direction, tank_rt_t *tank_rt);
+extern void tank_fire_bullet(tank_rt_t *tank_rt, tank_t *tank);
+
+/* From enemy.c */
+extern void init_enemies(tank_rt_t *tank_rt);
+
+/* @} */
+
+#endif /* __TANK_H_ */
--- a/examples/tank/tank_main.c	Sat Dec 04 16:22:11 2010 +0800
+++ b/examples/tank/tank_main.c	Sun Dec 05 12:23:29 2010 +0800
@@ -3,16 +3,13 @@
 #include <string.h>
 #include <mb.h>
 #include <mb_tools.h>
+#include "tank.h"
 #include "svgs.h"
 
 /*! \defgroup tank Example Tank
  * @{
  */
-/*! \brief Tile types in a map. */
-enum { MUD, ROC, BRI, BSH };
-
-/*! \brief Map of the game. */
-static char map[12][16] = {
+char map[12][16] = {
     { MUD, MUD, MUD, MUD, MUD, MUD, MUD, MUD,
       MUD, MUD, MUD, MUD, MUD, MUD, MUD, MUD},
     { MUD, ROC, ROC, ROC, MUD, BSH, BSH, ROC,
@@ -38,78 +35,8 @@
     { MUD, MUD, MUD, MUD, MUD, MUD, BRI, MUD,
       MUD, BRI, MUD, BRI, MUD, MUD, MUD, MUD}
 };
-
-#define MAP_W 16
-#define MAP_H 12
 /* @} */
 
-/*! \defgroup bullet_elf Bullet Elf
- * \ingroup tank
- * @{
- */
-/*! \brief Information about bullet elf
- */
-struct _tank_bullet {
-    redraw_man_t *rdman;
-    coord_t *coord_pos;
-    coord_t *coord_rot;
-    bullet_t *bullet_obj;
-    int start_map_x, start_map_y;
-    int direction;
-    mb_progm_t *progm;
-    mb_timeval_t start_time;
-    observer_t *observer_redraw;
-    int hit_tmr;
-    mb_timer_man_t *timer_man;
-};
-typedef struct _tank_bullet tank_bullet_t;
-/*! \brief The direction a bullet is going.
- */
-enum { BU_UP = 0, BU_RIGHT, BU_DOWN, BU_LEFT };
-/* @} */
-
-/*! \defgroup tank_elf Tank Elf
- * \brief Tank elf module provides control functions of tanks in game.
- * \ingroup tank
- * @{
- */
-/*! \brief Information about a tank elf. */
-struct _tank {
-    coord_t *coord_pos;		/*!< \brief coordinate for position */
-    coord_t *coord_rot;		/*!< \brief coordinate for rotation */
-    coord_t *coord_center;
-    int map_x, map_y;
-    int direction;
-    mb_progm_t *progm;
-    tank_bullet_t *bullet;
-    struct _tank_rt *tank_rt;	/*!< \brief for bullet to check
-				 * hitting on tanks.
-				 */
-};
-typedef struct _tank tank_t;
-enum { TD_UP = 0, TD_RIGHT, TD_DOWN, TD_LEFT };
-
-/* @} */
-
-typedef struct _tank_rt tank_rt_t;
-
-/*! \brief Runtime information for tank, this game/example.
- */
-struct _tank_rt {
-    tank_t *tank1;
-    tank1_t *tank1_o;
-    tank_t *tank2;
-    tank2_t *tank2_o;
-    int n_enemy;
-    tank_t *tank_enemies[10];
-    tank_en_t *tank_enemies_o[10];
-    tank_t *tanks[12];
-    int n_tanks;
-    void *map[12][16];
-    mb_rt_t *mb_rt;
-    observer_t *kb_observer;
-};
-
 /*! \ingroup tank_elf
  * @{
  */
@@ -165,8 +92,8 @@
 
 #define PI 3.1415926
 
-static void tank_move(tank_t *tank, int direction,
-		      tank_rt_t *tank_rt) {
+void
+tank_move(tank_t *tank, int direction, tank_rt_t *tank_rt) {
     mb_rt_t *mb_rt = tank_rt->mb_rt;
     redraw_man_t *rdman;
     mb_timer_man_t *timer_man;
@@ -488,7 +415,8 @@
 }
 
 /*! \brief To fire a bullet for a tank. */
-static void tank_fire_bullet(tank_rt_t *tank_rt, tank_t *tank) {
+void
+tank_fire_bullet(tank_rt_t *tank_rt, tank_t *tank) {
     mb_rt_t *mb_rt;
     redraw_man_t *rdman;
     int map_x, map_y;
@@ -738,6 +666,8 @@
 
     initial_tank(&tank_rt, rt);
     
+    /* init_enemies(&tank_rt); */
+    
     mb_runtime_event_loop(rt);
 
     mb_runtime_free(rt);