Complete.Org: Mailing Lists: Archives: freeciv-ai: April 2003:
[freeciv-ai] findjob and manage units
Home

[freeciv-ai] findjob and manage units

[Top] [All Lists]

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index] [Thread Index]
To: freeciv-ai@xxxxxxxxxxx
Subject: [freeciv-ai] findjob and manage units
From: "Per I. Mathisen" <per@xxxxxxxxxxx>
Date: Mon, 28 Apr 2003 17:01:30 +0000 (GMT)

I've been thinking on how to improve the ai_manage_units() thing.

Currently it is a real mess, working only because we forget everything
every turn and then recalculate all tasks, and call ai_manage_units()
twice per turn. Since we are moving away from this scheme, we need to
fundamentally rethink the way units are being given tasks. In particular,
ai_manage_military() and ai_military_findjob().

There are several goals I want to reach:
 - We need to be able to keep an assignment over multiple turns.
 - If a part of code relinquishes a unit assigned to it, it should be
given other work in the same turn.
 - We should keep trying to do something with a unit until we has
exhausted all possible avenues for action.
 - A unit should be able to do multiple different tasks in the same turn.
 - A unit should be able to do the same task multiple times in the same
turn.
 - Some tasks should be tested for all units before other tasks. For
example, we want manage charges before we manage their bodyguards, we want
to give ferries a chance to reach the shore before we manage their cargo.
 - On turn end, all units have specific roles assigned.

(However, we can assme that once a part of the code says it cannot use a
unit, we can assume this holds for the rest of the turn.)

The current task distribution is very crude and done at several layers
without much logic behind it. Air units, for example, are all snatched at
a higher level than other military units. findjob tries to prioritize some
tasks, but uses very crude metrics to distribute them.

This is what I suggest instead:

Different units roles/tasks are split up into modules - separate files -
as far as possible. To some extent this has already begun (aiair,
aidiplomat, settler). Each module has its own API set of

        /* Try do something useful with punit. If ai_role is set
         * to something other than AIUNIT_NONE then we have reserved
         * this unit. If we completed our task, set it to AIUNIT_NONE.
         * Return TRUE if mission successfully completed, objective
         * reached and we can try another mission. FALSE otherwise. */
        bool ai_*_manage(pplayer, punit)

        /* Estimate a want for the best we could build in this city,
         * and replace contents of *choice if our want is higher than
         * existing want. */
        void ai_*_choice(pplayer, pcity, choice)

Then we have something akin to this:

        void ai_manage_units(pplayer)
        {
          int role = 0;

          /* First try to complete previous tasks. */
          unit_list_iterate_safe(pplayer->units, punit) {
            (void) ai_manage_unit(pplayer, punit, punit->ai.ai_role);
          } unit_list_iterate_safe_end;

          /* Now ensure no unit is left behind. */
          while (role++ != AIUNIT_LAST) {
            unit_list_iterate_safe(pplayer->units, punit) {
              /* Call this manage func until it is exhausted */
              if (punit->ai.ai_role == AIUNIT_NONE) {
                while (ai_manage_unit(pplayer, punit, &role));
              }
            } unit_list_iterate_safe_end;
          }
        }

        bool ai_manage_unit(pplayer, punit, role)
        {
          switch (*role) {
          case AIUNIT_DIPLOMAT_ATTACK:
          case AIUNIT_DIPLOMAT_DEFEND:
            *role++; /* We have two roles so don't check twice */
            return ai_diplomat_manage(pplayer, punit);
          case AIUNIT_ARMY:
            return ai_army_manage(pplayer, punit);
          case AIUNIT_AUTO_SETTLER:
            return ai_worker_manage(pplayer, punit);
          case AIUNIT_BUILD_CITY:
            return ai_settler_manage(pplayer,p unit);
          /* etc */
          default:
            punit->ai.ai_role = AIUNIT_DEFEND_HOME;
            break;
        }

What we want to achieve is to get useful but well-handled "recursion". It
should be handled in one place, instead of leaving it up to each manage
function to do it. We know from past experience (air, rampage, attack)
that recursion is easy to get subtly wrong with un-subtle results. In this
case, all we need to be sure of is that we only return TRUE after
_successful and non-recursively-possible_ completion of mission (resetting
role to AIUNIT_NONE).

It is possible we should iterate over all roles until all units have been
given a role instead. It is more dangerous, but the disadvantage with this
model is that if get finished with our task, we can't try to do a
lower-number task. This problem can be reduced by sorting the roles well,
perhaps.

  - Per



[Prev in Thread] Current Thread [Next in Thread]
  • [freeciv-ai] findjob and manage units, Per I. Mathisen <=