[Freeciv-Dev] (PR#2415) AI only autoattack patch
[Top] [All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index] [Thread Index]
This patch gives only the AI access to autoattack. Further expansion of
this (optional) feature to human players will come in separate patch(es).
It is a big step forward for the AI, yet only a 9k patch. But it is
complicated (tho nothing about the code is very AI-specific) and I need
someone else to read over it & commit it.
Changes:
- performance boost compared to previous versions
- a few more bugs busted
- when you aitoggle a player, all his units are immediately moved by AI
A word of explanation on the last item: It is possible to cheat using AI
only autoattack. When you know that a player is going to attack your
units, instead of waiting around and trying to outdo him in mouseclicks,
you can set yourself to 'hard' AI and let the AI counterattack with its
autoattack feature. This is still possible. However, since the AI will
immediately move your units around, chances are good that your carefully
placed defensive positions are now in complete disarray and your units
have no movements points left to counterattack with. If this isn't
sufficient, I could let the AI have go messing up your cities too ;)
The downside to this change is that "/aitoggle opponent" will now cause
complete disarray in the opponent's army. But then, how is this worse than
"/cut opponent"...
Finally, there has been an error "cut connection %s due to lagging player"
that has shown up now and then. I haven't been able to reproduce this
error with the current version of the patch, so I have fervent hopes that
it has gone away as mysteriously as it appeared.
- Per
Index: common/player.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/player.h,v
retrieving revision 1.92
diff -u -r1.92 player.h
--- common/player.h 2003/01/13 23:27:11 1.92
+++ common/player.h 2003/01/27 22:02:20
@@ -38,17 +38,17 @@
SEA_BARBARIAN = 2
};
+/* See doc/README.AI for documention on these */
enum handicap_type {
H_NONE=0, /* no handicaps */
H_DIPLOMAT=1, /* can't build offensive diplomats */
H_MAP=2, /* only knows map_get_known tiles */
H_LIMITEDHUTS=4, /* Can get only 25 gold and barbs from huts */
H_CITYBUILDINGS=8, /* doesn't know what buildings are in enemy cities */
- H_CITYUNITS=16, /* doesn't know what units are in enemy cities */
+ H_NOAUTOATTACK=16, /* cannot use autoattack */
H_DEFENSIVE=32, /* builds lots of defensive buildings without calculating
need */
H_EXPERIMENTAL=64, /* enable experimental AI features (for testing) */
H_SUB=128, /* doesn't know where subs may be lurking */
-/* below this point are milder handicaps that I can actually implement --
Syela */
H_RATES=256, /* can't set its rates beyond government limits */
H_TARGETS=512, /* can't target anything it doesn't know exists */
H_HUTS=1024, /* doesn't know which unseen tiles have huts on them */
Index: server/stdinhand.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/stdinhand.c,v
retrieving revision 1.270
diff -u -r1.270 stdinhand.c
--- server/stdinhand.c 2003/01/20 15:44:28 1.270
+++ server/stdinhand.c 2003/01/27 22:02:21
@@ -58,6 +58,7 @@
#include "timing.h"
#include "advmilitary.h" /* assess_danger_player() */
+#include "aiunit.h" /* ai_manage_units() */
#include "stdinhand.h"
@@ -1628,9 +1629,10 @@
H_NONE,
H_NONE,
/* easy */ H_RATES | H_TARGETS | H_HUTS | H_NOPLANES
- | H_DIPLOMAT | H_LIMITEDHUTS | H_DEFENSIVE,
+ | H_DIPLOMAT | H_LIMITEDHUTS | H_DEFENSIVE
+ | H_NOAUTOATTACK,
H_NONE,
- /* medium */ H_RATES | H_TARGETS | H_HUTS | H_DIPLOMAT,
+ /* medium */ H_RATES | H_TARGETS | H_HUTS | H_DIPLOMAT | H_NOAUTOATTACK,
H_NONE,
/* hard */ H_NONE,
H_NONE,
@@ -1706,10 +1708,15 @@
set_ai_level(caller, pplayer->name, pplayer->ai.skill_level);
/* the AI can't do active diplomacy */
cancel_all_meetings(pplayer);
- /* The following is sometimes necessary to avoid using
- uninitialized data... */
- if (server_state == RUN_GAME_STATE)
+ if (server_state == RUN_GAME_STATE) {
+ /* The following is sometimes necessary to avoid using
+ uninitialized data... */
assess_danger_player(pplayer);
+ /* This avoids cheating by setting a player to AI in order to
+ * get the benefits of lightning fast AI unit control
+ * (ie autoattack). */
+ ai_manage_units(pplayer);
+ }
} else {
cmd_reply(CMD_AITOGGLE, caller, C_OK,
_("%s is now under human control."), pplayer->name);
Index: server/unittools.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/unittools.c,v
retrieving revision 1.207
diff -u -r1.207 unittools.c
--- server/unittools.c 2003/01/27 19:34:53 1.207
+++ server/unittools.c 2003/01/27 22:02:21
@@ -54,6 +54,9 @@
#include "unittools.h"
+/* We need these global variables for our sort algorithm */
+static int autoattack_x;
+static int autoattack_y;
static void unit_restore_hitpoints(struct player *pplayer, struct unit *punit);
static void unit_restore_movepoints(struct player *pplayer, struct unit
*punit);
@@ -2724,8 +2727,130 @@
}
/*****************************************************************
-Will wake up any neighboring enemy sentry units or patrolling units
+ This function is passed to unit_list_sort() to sort a list of
+ units according to their win chance against autoattack_x|y.
+ If the unit is being transported, then push it to the front of
+ the list, since we wish to leave its transport out of combat
+ if at all possible.
*****************************************************************/
+static int compare_units(const void *p, const void *q)
+{
+ struct unit *p1 = (struct unit *)*(void**)p;
+ struct unit *q1 = (struct unit *)*(void**)q;
+ struct unit *p1def = get_defender(p1, autoattack_x, autoattack_y);
+ struct unit *q1def = get_defender(q1, autoattack_x, autoattack_y);
+ int p1uwc = unit_win_chance(p1, p1def);
+ int q1uwc = unit_win_chance(q1, q1def);
+
+ if (p1uwc < q1uwc || q1->transported_by > 0) {
+ return -1; /* q is better */
+ } else if (p1uwc == q1uwc) {
+ return 0;
+ } else {
+ return 1; /* p is better */
+ }
+}
+
+/*****************************************************************
+ Check if unit survives enemy autoattacks. We assume that any
+ unit that is adjacent to us can see us.
+*****************************************************************/
+static bool unit_survive_autoattack(struct unit *punit)
+{
+ struct unit_list autoattack;
+ int moves = punit->moves_left;
+ int sanity1 = punit->id;
+
+ /* Kludge to prevent attack power from dropping to zero during calc */
+ punit->moves_left = MAX(punit->moves_left, 1);
+
+ unit_list_init(&autoattack);
+ adjc_iterate(punit->x, punit->y, x, y) {
+ /* First add all eligible units to a unit list */
+ unit_list_iterate(map_get_tile(x, y)->units, penemy) {
+ struct player *enemyplayer = unit_owner(penemy);
+ enum diplstate_type ds =
+ pplayer_get_diplstate(unit_owner(punit), enemyplayer)->type;
+
+ if (enemyplayer->ai.control
+ && penemy->moves_left > 0
+ && ds == DS_WAR
+ && !ai_handicap(enemyplayer, H_NOAUTOATTACK)
+ && can_unit_attack_unit_at_tile(penemy, punit, punit->x, punit->y)) {
+ unit_list_insert(&autoattack, penemy);
+ }
+ } unit_list_iterate_end;
+ } adjc_iterate_end;
+
+ /* The unit list is now sorted according to win chance against punit */
+ autoattack_x = punit->x; /* global vars to pass to ... */
+ autoattack_y = punit->y; /* ... comparison function */
+ if (unit_list_size(&autoattack) >= 2) {
+ unit_list_sort(&autoattack, &compare_units);
+ }
+
+ unit_list_iterate_safe(autoattack, penemy) {
+ int sanity2 = penemy->id;
+ struct unit *enemy_defender = get_defender(punit, penemy->x, penemy->y);
+ struct unit *punit_defender = get_defender(penemy, punit->x, punit->y);
+ double punitwin = unit_win_chance(punit, enemy_defender);
+ double penemywin = unit_win_chance(penemy, punit_defender);
+ struct tile *ptile = map_get_tile(penemy->x, penemy->y);
+ double threshold = 0.25;
+
+ if (ptile->city && unit_list_size(&ptile->units) == 1) {
+ /* Don't leave city defenseless */
+ threshold = 0.90;
+ }
+
+ if ((penemywin > 1.0 - punitwin
+ || unit_flag(punit, F_DIPLOMAT)
+ || get_transporter_capacity(punit) > 0)
+ && penemywin > threshold) {
+#ifdef REALLY_DEBUG_THIS
+ freelog(LOG_NORMAL, "AA %s -> %s (%d,%d) %.2f > %.2f && > %.2f",
+ unit_type(penemy)->name, unit_type(punit)->name,
+ punit->x, punit->y, penemywin, 1.0 - punitwin, threshold);
+#endif
+
+ handle_unit_activity_request(penemy, ACTIVITY_IDLE);
+ (void) handle_unit_move_request(penemy, punit->x,
+ punit->y, FALSE, FALSE);
+ }
+#ifdef REALLY_DEBUG_THIS
+ else {
+ freelog(LOG_NORMAL, "!AA %s -> %s (%d,%d) %.2f > %.2f && > %.2f",
+ unit_type(penemy)->name, unit_type(punit)->name,
+ punit->x, punit->y, penemywin, 1.0 - punitwin, threshold);
+ continue;
+ }
+#endif
+
+ if (find_unit_by_id(sanity2)) {
+ send_unit_info(NULL, penemy);
+ }
+ if (find_unit_by_id(sanity1)) {
+ send_unit_info(NULL, punit);
+ } else {
+ return FALSE; /* done, gone */
+ }
+ } unit_list_iterate_safe_end;
+
+ unit_list_unlink_all(&autoattack);
+ if (find_unit_by_id(sanity1)) {
+ /* We could have lost movement in combat */
+ punit->moves_left = MIN(punit->moves_left, moves);
+ send_unit_info(NULL, punit);
+ return TRUE;
+ } else {
+ return FALSE;
+ }
+}
+
+/*****************************************************************
+ Will wake up any neighboring enemy sentry units or patrolling
+ units.
+*****************************************************************/
static void wakeup_neighbor_sentries(struct unit *punit)
{
/* There may be sentried units with a sightrange>3, but we don't
@@ -3014,6 +3139,9 @@
handle_unit_move_consequences(punit, src_x, src_y, dest_x, dest_y);
wakeup_neighbor_sentries(punit);
+ if (!unit_survive_autoattack(punit)) {
+ return FALSE;
+ }
maybe_make_first_contact(dest_x, dest_y, unit_owner(punit));
conn_list_do_unbuffer(&pplayer->connections);
[Prev in Thread] |
Current Thread |
[Next in Thread] |
- [Freeciv-Dev] (PR#2415) AI only autoattack patch,
Per I. Mathisen via RT <=
|
|