Author: sveinung
Date: Thu Nov  3 16:09:45 2016
New Revision: 34359

URL: http://svn.gna.org/viewcvs/freeciv?rev=34359&view=rev
Log:
autoattack: consider the odds of all actions.

An auto attack can result in "Capture Units", "Bombard" or "Attack". What
unit tries to auto attack first is determined by its odds. Consider the odds
of doing any of the actions.

See bug #25253

Modified:
    trunk/server/unittools.c

Modified: trunk/server/unittools.c
URL: 
http://svn.gna.org/viewcvs/freeciv/trunk/server/unittools.c?rev=34359&r1=34358&r2=34359&view=diff
==============================================================================
--- trunk/server/unittools.c    (original)
+++ trunk/server/unittools.c    Thu Nov  3 16:09:45 2016
@@ -106,8 +106,29 @@
   TYPED_LIST_ITERATE_REV(struct unit_move_data, _plist, _pdata)
 #define unit_move_data_list_iterate_rev_end LIST_ITERATE_REV_END
 
-/* We need this global variable for our sort algorithm */
-static struct tile *autoattack_target;
+/* This data structure lets the auto attack code cache each potential
+ * attacker unit's probability of success against the target unit during
+ * the checks if the unit can do autoattack. It is then reused when the
+ * list of potential attackers is sorted by probability of success. */
+struct autoattack_prob {
+  int unit_id;
+  struct act_prob prob;
+};
+
+#define SPECLIST_TAG autoattack_prob
+#define SPECLIST_TYPE struct autoattack_prob
+#include "speclist.h"
+
+#define autoattack_prob_list_iterate_safe(autoattack_prob_list, _aap_,     \
+                                          _unit_)                          \
+  TYPED_LIST_ITERATE(struct autoattack_prob, autoattack_prob_list, _aap_)  \
+  struct unit *_unit_ = game_unit_by_number(_aap_->unit_id);               \
+                                                                           \
+  if (_unit_ == NULL) {                                                    \
+    continue;                                                              \
+  }
+
+#define autoattack_prob_list_iterate_safe_end  LIST_ITERATE_END
 
 static void unit_restore_hitpoints(struct unit *punit);
 static void unit_restore_movepoints(struct player *pplayer, struct unit 
*punit);
@@ -3002,26 +3023,33 @@
 }
 
 /*****************************************************************
-  This function is passed to unit_list_sort() to sort a list of
-  units according to their win chance against autoattack_x|y.
+  Used when unit_survive_autoattack()'s autoattack_prob_list
+  autoattack frees its items.
+*****************************************************************/
+static void autoattack_prob_free(struct autoattack_prob *prob)
+{
+  free(prob);
+}
+
+/*****************************************************************
+  This function is passed to autoattack_prob_list_sort() to sort
+  a list of units and action probabilities according to their win
+  chance against the autoattack target.
+
   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 struct unit *const *p1,
-                         const struct unit *const *q1)
-{
-  struct unit *p1def = get_defender(*p1, autoattack_target);
-  struct unit *q1def = get_defender(*q1, autoattack_target);
-  int p1uwc = unit_win_chance(*p1, p1def);
-  int q1uwc = unit_win_chance(*q1, q1def);
-
-  if (p1uwc < q1uwc || unit_transport_get(*q1)) {
+static int compare_units(const struct autoattack_prob *const *p1,
+                         const struct autoattack_prob *const *q1)
+{
+  const struct unit *q1unit = game_unit_by_number((*q1)->unit_id);
+
+  if (unit_transport_get(q1unit)) {
     return -1; /* q is better */
-  } else if (p1uwc == q1uwc) {
-    return 0;
   } else {
-    return 1; /* p is better */
+    /* Assume the worst. */
+    return action_prob_cmp_pessimist((*p1)->prob, (*q1)->prob);
   }
 }
 
@@ -3031,7 +3059,7 @@
 *****************************************************************/
 static bool unit_survive_autoattack(struct unit *punit)
 {
-  struct unit_list *autoattack;
+  struct autoattack_prob_list *autoattack;
   int moves = punit->moves_left;
   int sanity1 = punit->id;
 
@@ -3039,42 +3067,46 @@
     return TRUE;
   }
 
-  autoattack = unit_list_new();
+  autoattack = autoattack_prob_list_new_full(autoattack_prob_free);
 
   /* Kludge to prevent attack power from dropping to zero during calc */
   punit->moves_left = MAX(punit->moves_left, 1);
 
   adjc_iterate(unit_tile(punit), ptile) {
-    /* First add all eligible units to a unit list */
+    /* First add all eligible units to a autoattack list */
     unit_list_iterate(ptile->units, penemy) {
-      if (action_auto_perf_unit_sel(AAPC_UNIT_MOVED_ADJ, penemy,
-                                    unit_owner(punit), NULL)
-          && (is_action_enabled_unit_on_units(ACTION_CAPTURE_UNITS,
-                                              penemy, unit_tile(punit))
-              || is_action_enabled_unit_on_units(ACTION_BOMBARD,
-                                                 penemy, unit_tile(punit))
-              || is_action_enabled_unit_on_tile(ACTION_ATTACK, penemy,
-                                                unit_tile(punit)))) {
-        unit_list_prepend(autoattack, penemy);
+      struct autoattack_prob *probability = fc_malloc(sizeof(*probability));
+      struct tile *tgt_tile = unit_tile(punit);
+
+      fc_assert_action(tgt_tile, continue);
+
+      probability->prob =
+          action_auto_perf_unit_prob(AAPC_UNIT_MOVED_ADJ,
+                                     penemy, unit_owner(punit), NULL,
+                                     tgt_tile, tile_city(tgt_tile),
+                                     punit);
+
+      if (action_prob_possible(probability->prob)) {
+        probability->unit_id = penemy->id;
+        autoattack_prob_list_prepend(autoattack, probability);
       }
     } unit_list_iterate_end;
   } adjc_iterate_end;
 
   /* The unit list is now sorted according to win chance against punit */
-  autoattack_target = unit_tile(punit); /* global variable */
-  if (unit_list_size(autoattack) >= 2) {
-    unit_list_sort(autoattack, &compare_units);
-  }
-
-  unit_list_iterate_safe(autoattack, penemy) {
+  if (autoattack_prob_list_size(autoattack) >= 2) {
+    autoattack_prob_list_sort(autoattack, &compare_units);
+  }
+
+  autoattack_prob_list_iterate_safe(autoattack, peprob, penemy) {
     int sanity2 = penemy->id;
     struct tile *ptile = unit_tile(penemy);
     struct unit *enemy_defender = get_defender(punit, ptile);
-    struct unit *punit_defender = get_defender(penemy, unit_tile(punit));
     double punitwin, penemywin;
     double threshold = 0.25;
-
-    fc_assert_action(NULL != punit_defender, continue);
+    struct tile *tgt_tile = unit_tile(punit);
+
+    fc_assert(tgt_tile);
 
     if (tile_city(ptile) && unit_list_size(ptile->units) == 1) {
       /* Don't leave city defenseless */
@@ -3087,15 +3119,26 @@
       /* 'penemy' can attack 'punit' but it may be not reciproque. */
       punitwin = 1.0;
     }
-    penemywin = unit_win_chance(penemy, punit_defender);
+
+    /* Previous attacks may have changed the odds. Recalculate. */
+    peprob->prob =
+        action_auto_perf_unit_prob(AAPC_UNIT_MOVED_ADJ,
+                                   penemy, unit_owner(punit), NULL,
+                                   tgt_tile, tile_city(tgt_tile),
+                                   punit);
+
+    if (!action_prob_possible(peprob->prob)) {
+      /* No longer legal. */
+      continue;
+    }
+
+    /* Assume the worst. */
+    penemywin = action_prob_to_0_to_1_pessimist(peprob->prob);
 
     if ((penemywin > 1.0 - punitwin
          || utype_acts_hostile(unit_type_get(punit))
          || get_transporter_capacity(punit) > 0)
         && penemywin > threshold) {
-      struct tile *tgt_tile = unit_tile(punit);
-
-      fc_assert(tgt_tile);
 
 #ifdef REALLY_DEBUG_THIS
       log_test("AA %s -> %s (%d,%d) %.2f > %.2f && > %.2f",
@@ -3124,12 +3167,12 @@
     if (game_unit_by_number(sanity1)) {
       send_unit_info(NULL, punit);
     } else {
-      unit_list_destroy(autoattack);
+      autoattack_prob_list_destroy(autoattack);
       return FALSE; /* moving unit dead */
     }
-  } unit_list_iterate_safe_end;
-
-  unit_list_destroy(autoattack);
+  } autoattack_prob_list_iterate_safe_end;
+
+  autoattack_prob_list_destroy(autoattack);
   if (game_unit_by_number(sanity1)) {
     /* We could have lost movement in combat */
     punit->moves_left = MIN(punit->moves_left, moves);


_______________________________________________
Freeciv-commits mailing list
Freeciv-commits@gna.org
https://mail.gna.org/listinfo/freeciv-commits

Reply via email to