<URL: http://bugs.freeciv.org/Ticket/Display.html?id=39835 >

Another problem found with PR#39562, this time on its stated purpose,
better choice asserts.  The assert() itself tests both ">= CT_NONE" and
"!= CT_NONE" -- the previous version had commented out "!= CT_NONE".

The assert() also doesn't use standardized *_count() access functions.

Why the "do {} while(0)"?  Oh well, I left that, it doesn't seem to do
any harm....

===

PR#39565 removed some error detection done by PR#39553 -- for
building/improvements -- in a section labeled (long before PR#39553):
   /* This should never happen? */

Obviously, it was happening!  But the logging didn't work, because the
old code (also before PR#39553) used the value directly without
checking for a valid improvement before calling the function:

- } else if (can_build_improvement(pcity,
-                                  punittype->impr_requirement)) {
+ } else if (can_city_build_improvement_now(pcity, impr_req)) {

Instead of finding and fixing the bug, remove the log?!?!  That's
counterproductive.  It was a simple (NULL != impr_req) fix.  Exactly
the kind of bug that copious error detection was intended to find!

There was more effort to stick 4 useless lines around the log and
generate a PR....  And even more effort by me to find and undo the
useless lines several months later.  Maybe now we can discover the
underlying problem(s).

===

So that this report isn't a completely painful waste of several hours
time, added ai_choice_rule_name() to replace 3 switches introduced in
PR#39827.  Wish I'd thought of it before....

Changed the name of ai_unit_task_rule_name() to conform to practice.

Check simple_ai_types[] for "A_NEVER != punittype->require_advance"
in its update function, rather than at every use of its iterator.
(Some didn't check.)




Index: common/city.h
===================================================================
--- common/city.h       (revision 13913)
+++ common/city.h       (working copy)
@@ -166,13 +166,13 @@
 #define ASSERT_CHOICE(c)                                                 \
   do {                                                                   \
     if ((c).want > 0) {                                                  \
-      assert((c).type >= 0 && (c).type < CT_LAST && (c).type != CT_NONE);\
+      assert((c).type > CT_NONE && (c).type < CT_LAST);                  \
       if ((c).type == CT_BUILDING) {                                     \
         int _iindex = improvement_index((c).value.building);             \
-        assert(_iindex >= 0 && _iindex < game.control.num_impr_types);   \
+        assert(_iindex >= 0 && _iindex < improvement_count());           \
       } else {                                                           \
         int _uindex = utype_index((c).value.utype);                      \
-        assert(_uindex >= 0 && _uindex < game.control.num_unit_types);   \
+        assert(_uindex >= 0 && _uindex < utype_count());                 \
       }                                                                  \
     }                                                                    \
   } while(0);
Index: ai/aitools.c
===================================================================
--- ai/aitools.c        (revision 13913)
+++ ai/aitools.c        (working copy)
@@ -59,9 +59,10 @@
 #include "aitools.h"
 
 /**************************************************************************
-  Return a string describing a unit's AI role.
+  Return the (untranslated) rule name of the ai_unit_task.
+  You don't have to free the return pointer.
 **************************************************************************/
-const char *get_ai_role_str(enum ai_unit_task task)
+const char *ai_unit_task_rule_name(const enum ai_unit_task task)
 {
   switch(task) {
    case AIUNIT_NONE:
@@ -83,11 +84,35 @@
    case AIUNIT_HUNTER:
      return "Hunter";
   }
+  /* no default, ensure all types handled somehow */
   assert(FALSE);
   return NULL;
 }
 
 /**************************************************************************
+  Return the (untranslated) rule name of the ai_choice.
+  You don't have to free the return pointer.
+**************************************************************************/
+const char *ai_choice_rule_name(const struct ai_choice *choice)
+{
+  switch (choice->type) {
+  case CT_NONE:
+    return "(nothing)";
+  case CT_BUILDING:
+    return improvement_rule_name(choice->value.building);
+  case CT_CIVILIAN:
+  case CT_ATTACKER:
+  case CT_DEFENDER:
+    return utype_rule_name(choice->value.utype);
+  case CT_LAST:
+    return "(unknown)";
+  };
+  /* no default, ensure all types handled somehow */
+  assert(FALSE);
+  return NULL;
+}
+
+/**************************************************************************
   Amortize a want modified by the shields (build_cost) we risk losing.
   We add the build time of the unit(s) we risk to amortize delay.  The
   build time is claculated as the build cost divided by the production
@@ -806,7 +831,8 @@
   assert(!unit_has_orders(punit));
 
   UNIT_LOG(LOG_DEBUG, punit, "changing role from %s to %s",
-           get_ai_role_str(punit->ai.ai_role), get_ai_role_str(task));
+           ai_unit_task_rule_name(punit->ai.ai_role),
+           ai_unit_task_rule_name(task));
 
   /* Free our ferry.  Most likely it has been done already. */
   if (task == AIUNIT_NONE || task == AIUNIT_DEFEND_HOME) {
Index: ai/aitools.h
===================================================================
--- ai/aitools.h        (revision 13913)
+++ ai/aitools.h        (working copy)
@@ -51,7 +51,8 @@
   double enemy_zoc_cost;
 };
 
-const char *get_ai_role_str(enum ai_unit_task task);
+const char *ai_unit_task_rule_name(const enum ai_unit_task task);
+const char *ai_choice_rule_name(const struct ai_choice *choice);
 
 int military_amortize(struct player *pplayer, struct city *pcity, 
                       int value, int delay, int build_cost);
Index: ai/aicity.c
===================================================================
--- ai/aicity.c (revision 13913)
+++ ai/aicity.c (working copy)
@@ -1383,24 +1383,10 @@
   }
 
   if (pcity->ai.choice.want != 0) {
-    const char *name = "(unknown)";
     ASSERT_CHOICE(pcity->ai.choice);
 
-    switch (pcity->ai.choice.type) {
-    case CT_CIVILIAN:
-    case CT_ATTACKER:
-    case CT_DEFENDER:
-      name = utype_rule_name(pcity->ai.choice.value.utype);
-      break;
-    case CT_BUILDING:
-      name = improvement_rule_name(pcity->ai.choice.value.building);
-      break;
-    case CT_NONE:
-    case CT_LAST:
-      break;
-    };
     CITY_LOG(LOG_DEBUG, pcity, "wants %s with desire %d.",
-            name,
+            ai_choice_rule_name(&pcity->ai.choice),
             pcity->ai.choice.want);
     
     /* parallel to citytools change_build_target() */
@@ -1541,7 +1527,6 @@
     int buycost;
     int limit = cached_limit; /* cached_limit is our gold reserve */
     struct city *pcity = NULL;
-    const char *name = "(unknown)";
 
     /* Find highest wanted item on the buy list */
     init_choice(&bestchoice);
@@ -1627,20 +1612,6 @@
        continue;
     }
 
-    switch (bestchoice.type) {
-    case CT_CIVILIAN:
-    case CT_ATTACKER:
-    case CT_DEFENDER:
-      name = utype_rule_name(bestchoice.value.utype);
-      break;
-    case CT_BUILDING:
-      name = improvement_rule_name(bestchoice.value.building);
-      break;
-    case CT_NONE:
-    case CT_LAST:
-      break;
-    };
-
     /* FIXME: Here Syela wanted some code to check if
      * pcity was doomed, and we should therefore attempt
      * to sell everything in it of non-military value */
@@ -1651,7 +1622,7 @@
             || (bestchoice.want > 200 && pcity->ai.urgency > 1))) {
       /* Buy stuff */
       CITY_LOG(LOG_BUY, pcity, "Crash buy of %s for %d (want %d)",
-               name,
+               ai_choice_rule_name(&bestchoice),
                buycost,
                bestchoice.want);
       really_handle_city_buy(pplayer, pcity);
@@ -1660,8 +1631,9 @@
                && assess_defense(pcity) == 0) {
       /* We have no gold but MUST have a defender */
       CITY_LOG(LOG_BUY, pcity, "must have %s but can't afford it (%d < %d)!",
-              name,
-              pplayer->economic.gold, buycost);
+               ai_choice_rule_name(&bestchoice),
+               pplayer->economic.gold,
+               buycost);
       try_to_sell_stuff(pplayer, pcity);
       if (pplayer->economic.gold - pplayer->ai.est_upkeep >= buycost) {
         CITY_LOG(LOG_BUY, pcity, "now we can afford it (sold something)");
Index: ai/advmilitary.c
===================================================================
--- ai/advmilitary.c    (revision 13913)
+++ ai/advmilitary.c    (working copy)
@@ -720,8 +720,8 @@
   memset(tech_desire, 0, sizeof(tech_desire));
   
   simple_ai_unit_type_iterate(punittype) {
-    int move_type = utype_move_type(punittype);
     int desire; /* How much we want the unit? */
+    int move_type = utype_move_type(punittype);
 
     /* Only consider proper defenders - otherwise waste CPU and
      * bump tech want needlessly. */
@@ -885,15 +885,9 @@
   }
 
   simple_ai_unit_type_iterate(punittype) {
-    Tech_type_id tech_req;
-    int tech_dist;
+    Tech_type_id tech_req = advance_number(punittype->require_advance);
+    int tech_dist = num_unknown_techs_for_goal(pplayer, tech_req);
     int move_type = utype_move_type(punittype);
-    
-    if (A_NEVER == punittype->require_advance) {
-      continue;
-    }
-    tech_req = advance_number(punittype->require_advance);
-    tech_dist = num_unknown_techs_for_goal(pplayer, tech_req);
 
     if ((move_type == LAND_MOVING || (move_type == SEA_MOVING && shore))
         && (tech_dist > 0 
@@ -913,8 +907,7 @@
       /* Cost (shield equivalent) of gaining these techs. */
       /* FIXME? Katvrr advises that this should be weighted more heavily in big
        * danger. */
-      int tech_cost = total_bulbs_required_for_goal(pplayer,
-                        advance_number(punittype->require_advance)) / 4
+      int tech_cost = total_bulbs_required_for_goal(pplayer, tech_req) / 4
                       / city_list_size(pplayer->cities);
       int move_rate = punittype->move_rate;
       int bcost_balanced = build_cost_balanced(punittype);
@@ -923,7 +916,6 @@
       int attack = unittype_att_rating(punittype, will_be_veteran,
                                        SINGLE_MOVE,
                                        punittype->hp);
-      struct impr_type *impr_req = punittype->need_improvement;
       
       /* Take into account reinforcements strength */
       if (acity) attack += acity->ai.attack;
@@ -1012,6 +1004,8 @@
                    (acity ? acity->name : utype_rule_name(victim_unit_type)),
                    TILE_XY(ptile));
         } else if (want > best_choice->want) {
+          struct impr_type *impr_req = punittype->need_improvement;
+
           if (can_city_build_unit_now(pcity, punittype)) {
             /* This is a real unit and we really want it */
 
@@ -1026,11 +1020,14 @@
             best_choice->value.utype = punittype;
             best_choice->want = want;
             best_choice->type = CT_ATTACKER;
+          } else if (NULL == impr_req) {
+            CITY_LOG(LOG_DEBUG, pcity, "cannot build unit %s",
+                     utype_rule_name(punittype));
           } else if (can_city_build_improvement_now(pcity, impr_req)) {
            /* Building this unit requires a specific type of improvement.
             * So we build this improvement instead.  This may not be the
             * best behavior. */
-            CITY_LOG(LOG_DEBUG, pcity, "building %s to build %s",
+            CITY_LOG(LOG_DEBUG, pcity, "building %s to build unit %s",
                      improvement_rule_name(impr_req),
                      utype_rule_name(punittype));
             best_choice->value.building = impr_req;
@@ -1038,13 +1035,9 @@
             best_choice->type = CT_BUILDING;
           } else {
            /* This should never happen? */
-            /* FIXME: Restore this when bug causing it to crash is fixed.
-             *        Commented out just as a critical fix to avoid crash. */
-#if 0
-            CITY_LOG(LOG_DEBUG, pcity, "cannot build %s or %s",
+            CITY_LOG(LOG_DEBUG, pcity, "cannot build %s or unit %s",
                      improvement_rule_name(impr_req),
                      utype_rule_name(punittype));
-#endif
          }
         }
       }
@@ -1492,23 +1485,8 @@
   if (choice->want <= 0) {
     CITY_LOG(LOGLEVEL_BUILD, pcity, "military advisor has no advice");
   } else {
-    const char *name = "(unknown)";
-
-    switch (pcity->ai.choice.type) {
-    case CT_CIVILIAN:
-    case CT_ATTACKER:
-    case CT_DEFENDER:
-      name = utype_rule_name(choice->value.utype);
-      break;
-    case CT_BUILDING:
-      name = improvement_rule_name(choice->value.building);
-      break;
-    case CT_NONE:
-    case CT_LAST:
-      break;
-    };
     CITY_LOG(LOGLEVEL_BUILD, pcity, "military advisor choice: %s (want %d)",
-             name,
+             ai_choice_rule_name(choice),
              choice->want);
   }
 }
Index: ai/aiunit.c
===================================================================
--- ai/aiunit.c (revision 13913)
+++ ai/aiunit.c (working copy)
@@ -2499,7 +2499,8 @@
   int i = 0;
 
   unit_type_iterate(punittype) {
-    if (!utype_has_flag(punittype, F_CIVILIAN)
+    if (A_NEVER != punittype->require_advance
+       && !utype_has_flag(punittype, F_CIVILIAN)
        && !uclass_has_flag(utype_class(punittype), UCF_MISSILE)
        && !utype_has_flag(punittype, F_NO_LAND_ATTACK)
         && utype_move_type(punittype) != AIR_MOVING
_______________________________________________
Freeciv-dev mailing list
Freeciv-dev@gna.org
https://mail.gna.org/listinfo/freeciv-dev

Reply via email to