Project

General

Profile

Feature #303 ยป 0031-README.AI-Remove-double-spaces.patch

Marko Lindqvist, 03/08/2024 07:51 PM

View differences:

doc/README.AI
The Freeciv AI is widely recognized as being as good as or better
military-wise as the AI of certain other games it is natural to compare
it with. It is, however, still too easy for experienced players.
it with. It is, however, still too easy for experienced players.
The code base used to be in a bad shape but it has gotten a lot
better. The reason for this is that the developer (Syela) who in a
few months put together a working AI had suddenly disappeared. His
bright ideas could only be matched by his inability to name variables
and to comment the code. Subsequent AI developers were not brave (or
better. The reason for this is that the developer (Syela) who in a
few months put together a working AI had suddenly disappeared.
His bright ideas could only be matched by his inability to name variables
and to comment the code. Subsequent AI developers were not brave (or
stupid?) enough to start from scratch, taking instead a small bite
here and there, trying hard not to break much, to understand Syela's
original design and only then to throw it away. Or perfect it.
original design and only then to throw it away. Or perfect it.
Code that implements the AI is divided between ai/ and server/advisors.
Latter is used also by human players for such automatic helpers
......
Speculation:
What is better, to receive 10$ annually starting in 5 years from now
or 5$ annually starting from this year? How can you take inflation
into account? The function amortize is meant to help you answer these
questions. To achieve this, it rescales the future benefit in terms of
or 5$ annually starting from this year? How can you take inflation
into account? The function amortize is meant to help you answer these
questions. To achieve this, it rescales the future benefit in terms of
todays money.
Suppose we have a constant rate of inflation, x percent. Then in five
Suppose we have a constant rate of inflation, x percent. Then in five
years time 10$ will buy as much as 10*(100/(100+x))^5 will buy today.
Denoting 100/(100+x) by q we get the general formula, N dollars Y
years from now will be worth N*q^Y in todays money. If we will
years from now will be worth N*q^Y in todays money. If we will
receive N every year starting Y years from now, the total amount
receivable (in todays money) is N*q^Y / (1-q) --- this is the sum of
infinite geometric series. This is exactly the operation that
infinite geometric series. This is exactly the operation that
amortize performs, the multiplication by some q < 1 raised to power Y.
Note that the factor 1/(1-q) does not depend on the parameters N and Y
and can be ignored. The connection between MORT constant and the
and can be ignored. The connection between MORT constant and the
inflation rate x is given by
(MORT - 1) / MORT = q = 100 / (100 + x).
Thus the current value of MORT = 24 corresponds to the inflation rate
......
==============================================
This estimation is implemented by kill_desire function (which isn't
perfect: multi-victim part is flawed) plus some corrections. In
general,
perfect: multi-victim part is flawed) plus some corrections.
In general,
Want = Operation_Profit * Amortization_Factor
where
......
= Shields_Lost_By_Enemy * Probability_To_Win
- Shields_Lost_By_Us * Probability_To_Lose
That is Battle_Profit is a probabilistic average. It answer the
That is Battle_Profit is a probabilistic average. It answer the
question "how much better off, on average, we would be from attacking
this enemy unit?"
......
========================
The code dealing with choosing military units to be built and targets
for them is especially messy. Here is what we've managed to decipher.
for them is especially messy. Here is what we've managed to decipher.
Military units are requested in military_advisor_choose_build
function. It first considers the defensive units and then ventures
into selection of attackers (if home is safe). There are 2
function. It first considers the defensive units and then ventures
into selection of attackers (if home is safe). There are 2
possibilities here: we just build a new attacker or we already have an
attacker which was forced, for some reason, to defend. In the second
attacker which was forced, for some reason, to defend. In the second
case it's easy: we calculate how good the existing attacker is and if
it's good, we build a defender to free it up.
Building a brand-new attacker is more complicated. Firstly,
Building a brand-new attacker is more complicated. Firstly,
ai_choose_attacker_* functions are charged to find the first
approximation to the best attacker that can be built here. This
approximation to the best attacker that can be built here. This
prototype attacker is selected using very simple attack_power * speed
formula. Then (already in kill_something_with) we search for targets
for the prototype attacker (using find_something_to_kill). Having
formula. Then (already in kill_something_with) we search for targets
for the prototype attacker (using find_something_to_kill). Having
found a target, we do the last refinement by calling
process_attacker_want to look for the best attacker type to take out
the target. This type will be our attacker choice. Note that the
the target. This type will be our attacker choice. Note that the
function process_attacker_want has side-effects wrt the tech selection.
Here is an example:
First ai_choose_attacker_land selects a Dragoon because it's strong
and fast. Then find_something_to_kill finds a victim for the
and fast. Then find_something_to_kill finds a victim for the
(virtual) Dragoon, an enemy Riflemen standing right next to the town.
Then process_attacker_want figures out that since the enemy is right
beside us, it can be taken out easier using an Artillery. It also
beside us, it can be taken out easier using an Artillery. It also
figures that a Howitzer would do this job even better, so bumps up our
desire for Robotics.
This is the idea, anyway. In practice, it is more complicated and
This is the idea, anyway. In practice, it is more complicated and
probably less efficient.
......
The ferry (i.e. boats transporting land units) system of Freeciv is
probably better described by statistical mechanics than by logic.
Both ferries and prospective passenger (PP) move around in what looks
like a random fashion, trying to get closer to each other. On
average, they succeed. This behaviour has good reasons behind it, is
like a random fashion, trying to get closer to each other. On
average, they succeed. This behaviour has good reasons behind it, is
hell to debug but means that small bugs don't affect overall picture
visibly (and stay unfixed as a result).
Each turn both boats and PPs forget all about prior arrangements
(unless the passenger is actually _in_ the boat). Then each will look
for the closest partner, exchange cards and head towards it. This is
(unless the passenger is actually _in_ the boat). Then each will look
for the closest partner, exchange cards and head towards it. This is
done in a loop which goes through all units in essentially random
order.
......
prior arrangements is the only good strategy -- it means that a boat
will not rely on the PP to notify it when it's not needed anymore.
This is not very effective but can only be changed when the PPs behave
more responsibly. See diplomat code for more responsible behaviour --
more responsibly. See diplomat code for more responsible behaviour --
they try to check if the old target is still good before trying to
find a new one.
When a boat has a passenger, it's a different story. The boat doesn't
When a boat has a passenger, it's a different story. The boat doesn't
do any calculations, instead one of the passengers is given full
control and it is the passenger who drives the boat.
......
>0 : id of it's passenger
When boat-building code stabilizes, it can be seen how many free boats
there are, on average, per PP. If there are more boats than PPs, it
makes sense that only PPs should look for boats. If boats are few,
they should be the ones choosing. This can be done both dynamically
there are, on average, per PP. If there are more boats than PPs, it
makes sense that only PPs should look for boats. If boats are few,
they should be the ones choosing. This can be done both dynamically
(both possibilities are coded and the appropriate is chosen every
turn) and statically (after much testing only one system remains).
Now they exist in parallel, although developed to a different degree.
......
attackers
* It would be nice for bodyguard and charge to meet en-route more
elegantly.
* struct choice should have a priority indicator in it. This will
* struct choice should have a priority indicator in it. This will
reduce the number of "special" want values and remove the necessity to
have want capped, thus reducing confusion.
    (1-1/1)