[kde-doc-english] [konquest] /: Becai now plays quite well against Default Normal and Default Hard in

Nemanja Hirsl nemhirsl at gmail.com
Wed Oct 23 21:02:13 UTC 2013


Git commit 2349a7c1a36ba48fd576ce03dde7547384910d8b by Nemanja Hirsl, on behalf of Alexander Schuch.
Committed on 23/10/2013 at 20:57.
Pushed by nhirsl into branch 'master'.

Becai now plays quite well against Default Normal and Default Hard in
nearly all situations - 1on1 and FFA. It is mainly tested in games with
two other players, 22x22 board with 40 neutral planets with 0-2 neutral
production.
It works nicely in cumulative games as well.
The only known "issue" is that it uses neutral kill percentage for
target selection even in blind games, where the player cannot see the
kill percentage.
REVIEW:112747

M  +2    -0    CMakeLists.txt
M  +3    -0    dialogs/newgamedlg.cc
M  +10   -20   doc/index.docbook
A  +591  -0    players/ai/becai/becai.cpp     [License: GPL (v2+)]
C  +24   -10   players/ai/becai/becai.h [from: players/ai/example/example_gui.cpp - 062% similarity]
C  +6    -6    players/ai/becai/becai_gui.cpp [from: players/ai/example/example_gui.cpp - 073% similarity]
C  +10   -11   players/ai/becai/becai_gui.h [from: players/ai/example/example_gui.cpp - 074% similarity]
M  +1    -1    players/ai/default/hard_gui.cpp
M  +1    -1    players/ai/default/normal_gui.cpp
M  +1    -1    players/ai/default/weak_gui.cpp
M  +1    -1    players/ai/example/example_gui.cpp

http://commits.kde.org/konquest/2349a7c1a36ba48fd576ce03dde7547384910d8b

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 1eb87aa..3b321ad 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -37,6 +37,8 @@ set(konquest_SRCS
     dialogs/scoredlg.cc
     dialogs/fleetdlg.cc
 
+    players/ai/becai/becai.cpp
+    players/ai/becai/becai_gui.cpp
     players/ai/default/default.cpp
     players/ai/default/weak.cpp
     players/ai/default/weak_gui.cpp
diff --git a/dialogs/newgamedlg.cc b/dialogs/newgamedlg.cc
index c52f338..ad0d931 100644
--- a/dialogs/newgamedlg.cc
+++ b/dialogs/newgamedlg.cc
@@ -22,6 +22,8 @@
  */
 #include "newgamedlg.h"
 
+#include "../players/ai/becai/becai_gui.h"
+#include "../players/ai/becai/becai.h"
 #include "../players/ai/default/weak_gui.h"
 #include "../players/ai/default/normal_gui.h"
 #include "../players/ai/default/hard_gui.h"
@@ -357,6 +359,7 @@ NewGameDlg::NewGameDlg( QWidget *parent, Game *game)
     m_selectablePlayer.push_back(new AiDefaultNormalGui());
     m_selectablePlayer.push_back(new AiDefaultHardGui());
     // m_selectablePlayer.push_back(new AiExampleGui());
+    m_selectablePlayer.push_back(new AiBecaiGui());
 
     m_w = new NewGameDlgUI(this);
     m_w->map->setMap(m_game->map());
diff --git a/doc/index.docbook b/doc/index.docbook
index 4e73b1d..766dc6d 100644
--- a/doc/index.docbook
+++ b/doc/index.docbook
@@ -113,17 +113,19 @@ Listed below are the following parameters for the new game:
 <itemizedlist>
 
 <listitem><para>Players</para><itemizedlist>
-  
+
   <listitem><para>Amount of players with <guibutton>Add</guibutton> or <guibutton>Remove</guibutton> players</para></listitem>
   <listitem><para>Names of players</para></listitem>
   <listitem><para>Type of players (Computer versus Human)</para></listitem>
   <listitem><para>Difficulty of computer players (Low/Normal/Hard)</para></listitem>
   </itemizedlist>
-  <para>To change the name of the player, double-click in the <guilabel>Name</guilabel> 
-  column and edit it. To change the type of the player, open the drop-down list by 
+  <para>To change the name of the player, double-click in the <guilabel>Name</guilabel>
+  column and edit it. To change the type of the player, open the drop-down list by
   double-clicking in the <guilabel>Type</guilabel> column.</para>
-  <para>The type of AI player can be chosen from <guimenuitem>Default Weak</guimenuitem> (a player that is neither aggressive in attacks nor in defense), <guimenuitem>Default Normal/Offensive</guimenuitem> (an aggressive attacking player) and <guimenuitem>Default Hard/Defensive</guimenuitem> (a player with defensive strategy).</para>
-</listitem>  
+  <para>The type of AI player can be chosen from <guimenuitem>Default (Weak)</guimenuitem> (a player that is neither aggressive in attacks nor in defense), <guimenuitem>Default (Offensive)</guimenuitem> (an aggressive attacking player) and <guimenuitem>Default (Defensive)</guimenuitem> (a player with defensive strategy).</para>
+  <para>Besides these classic AI players, <guimenuitem>Becai (Balanced)</guimenuitem> is a balanced player taking most of the <link linkend="strattips" endterm="strattips.title"/> into account.</para>
+  <para>Keep in mind that the actual difficulty of any opponent depends on your very own strategy. If you play aggressively yourself, an aggressive AI player might be easier to defeat than a defensive player, and vice versa.</para>
+</listitem>
 
 <listitem><para>Map</para><itemizedlist>
   
@@ -222,9 +224,10 @@ ships.</para></listitem>
 </sect1>
 
 <sect1 id="strattips">
-<title>Strategies and Tips</title>
+<title id="strattips.title">Strategies and Tips</title>
 <itemizedlist>
-<listitem><para>Do not attack a planet with less than 10 ships.</para></listitem>
+<listitem><para>The game is all about ship production. Try to get as many planets as possible as fast as your can in order to build more ships than any of your opponents.</para></listitem>
+<listitem><para>Do not attack a planet with less than 10 ships, unless you can conquer it.</para></listitem>
 <listitem><para>Attack neutral planets before other players' planets.</para></listitem>
 <listitem><para>Split your ship storages across several planets.</para></listitem>
 <listitem><para>Plan your attacks in advance.</para></listitem>
@@ -398,19 +401,6 @@ Yes, you should however store them on multiple planets to be able to send them t
 <qandaentry>
 <question>
 <para>
-I have sent out all the ships on a planet, but when I move my mouse over the planet, it shows the ships still in place, why?
-</para>
-</question>
-<answer>
-<para>
-The number of ships on a planet is updated when a turn is ended.
-</para>
-</answer>
-</qandaentry>
-
-<qandaentry>
-<question>
-<para>
 Is it possible to change the theme?
 </para>
 </question>
diff --git a/players/ai/becai/becai.cpp b/players/ai/becai/becai.cpp
new file mode 100644
index 0000000..c40786b
--- /dev/null
+++ b/players/ai/becai/becai.cpp
@@ -0,0 +1,591 @@
+/*
+    Copyright 2013 Alexander Schuch <aschuch247 at gmail.com>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include "becai.h"
+
+#include <cfloat>
+#include <cmath>
+
+#include <KDebug>
+#include <QMap>
+#include <QMultiMap>
+#include <QtAlgorithms>
+
+#include "../../../game.h"
+#include "../../../planet.h"
+
+
+AiBecai::AiBecai(
+        Game *game,
+        const QString &newName,
+        const QColor &color)
+    : ComputerPlayer(
+        game,
+        newName,
+        color)
+{
+}
+
+
+/**
+ * This internal helper class is for attack target categorisation.
+ */
+
+class TargetPlanet
+{
+public:
+    TargetPlanet(Planet *planet, int minimumAttackFleetToConquerPlanet, double distance);
+    Planet* planet() const { return m_planet; }
+    int minimumAttackFleetToConquerPlanet() const { return m_minimumAttackFleetToConquerPlanet; }
+    double distance() const { return m_distance; }
+
+private:
+    Planet* m_planet;
+    int m_minimumAttackFleetToConquerPlanet;
+    double m_distance;
+};
+
+
+TargetPlanet::TargetPlanet(Planet *planet, int minimumAttackFleetToConquerPlanet, double distance) :
+    m_planet(planet),
+    m_minimumAttackFleetToConquerPlanet(minimumAttackFleetToConquerPlanet),
+    m_distance(distance)
+{
+}
+
+
+/**
+ * Get the minimum defence fleet size for the given planet.
+ *
+ * Protect planets with higher kill percentage better. Do the same for
+ * planets with higher production.
+ */
+
+int
+AiBecai::getMinimumDefenceFleetSize(Planet *planet, int minimumBaseDefenceFleetSize, double averageOwnKillPercentage, double averageOwnProduction)
+{
+    return std::ceil(minimumBaseDefenceFleetSize * planet->killPercentage() / averageOwnKillPercentage * planet->production() / averageOwnProduction);
+}
+
+
+void
+AiBecai::play()
+{
+    int totalTotalPlanets = 0;
+
+    int totalOwnProduction = 0;
+    int totalOwnPlanets = 0;
+    int totalOwnFleet = 0;
+
+    int totalEnemyProduction = 0;
+    int totalEnemyPlanets = 0;
+    int totalEnemyDefence = 0;
+
+    // The first and nearly most important part is to analyse the current game
+    // situation. For this, different metrics are calculated.
+
+    /**
+     * @todo Move these metrics into the general player class so that they can
+     * be calculated and displayed to the human player for its own empire one
+     * day. Or maybe but them into an own class.
+     */
+
+    foreach (Fleet *fleet, this->attackList()) {
+        totalOwnFleet += fleet->shipCount();
+    }
+
+    // We need to know the average planet defence fleet size. So we get a list
+    // of all planet defence fleet sizes, and based on that, calculate the
+    // average.
+
+    QList<int> nonOwnPlanetDefenceFleetSizeList;
+
+    // We like to protect high kill percentage planets more than others. So we
+    // need to know our own average.
+
+    double totalKillPercentage = 0;
+
+    foreach (Planet *planet, m_game->planets()) {
+        totalTotalPlanets += 1;
+
+        if (planet->player() == this) {
+
+            // We found a planet of us.
+
+            totalOwnProduction += planet->production();
+            totalOwnPlanets += 1;
+            totalOwnFleet += planet->fleet().shipCount();
+
+            totalKillPercentage += planet->killPercentage();
+        }
+        else {
+            if (planet->player()->isNeutral() != true) {
+
+                // We found a non-neutral enemy planet.
+
+                totalEnemyProduction += planet->production();
+                totalEnemyPlanets += 1;
+                totalEnemyDefence += planet->fleet().shipCount();
+            }
+
+            nonOwnPlanetDefenceFleetSizeList.push_back(planet->fleet().shipCount());
+        }
+    }
+
+    // Ignore the upper and lower 10% of non-own planet defence fleet sizes and
+    // create the arithmetic average. In an FFA game with multiple enemies, we
+    // have an advantage if we can keep defence at a higher level than others.
+
+    qSort(nonOwnPlanetDefenceFleetSizeList);
+
+    for (int i = std::floor((double) nonOwnPlanetDefenceFleetSizeList.size() / 10); i > 0; --i) {
+        nonOwnPlanetDefenceFleetSizeList.takeFirst();
+        nonOwnPlanetDefenceFleetSizeList.takeLast();
+    }
+
+    int averageNonOwnPlanetDefenceFleetSize = 0;
+
+    foreach (int fleetSize, nonOwnPlanetDefenceFleetSizeList) {
+        averageNonOwnPlanetDefenceFleetSize += fleetSize;
+    }
+
+    if (nonOwnPlanetDefenceFleetSizeList.size() > 0) {
+        averageNonOwnPlanetDefenceFleetSize = std::ceil((double) averageNonOwnPlanetDefenceFleetSize / nonOwnPlanetDefenceFleetSizeList.size());
+    }
+
+    if (totalOwnPlanets == 0) {
+
+        // It is perfectly valid to have no own planets, but to still be alive.
+        // This is the case of having at least one fleet still travelling.
+        // We can abort here as there is nothing we can do (and to avoid
+        // division by zero later on).
+
+        emit(donePlaying());
+        return;
+    }
+
+    double averageOwnKillPercentage = totalKillPercentage / totalOwnPlanets;
+    double averageOwnProduction = (double) totalOwnProduction / totalOwnPlanets;
+
+    // The minimum defence should depend on the game situation.
+    // Values that define the game situation are:
+    // - number of enemy/neutral planets (and their defence)
+    // - number of own planets compared to non-neutral enemy planets
+    // - number of enemies still active in the game
+
+    // The first idea is to allocate a certain fixed percentage of our total
+    // fleet for defence distributed across all our planets. But in the
+    // beginning, we need less defence, and later on, in mid-game, we need more,
+    // and in end-game, when we are about to finish off the remaining enemy
+    // planets, less again. So the percentage should be dynamic rather than
+    // fixed.
+
+    // But keep in mind that 1) having less defence in beginning makes us a good
+    // long-range target and 2) in non-cumultative games, lots of neutral
+    // planets are never conquered, in case of a higher (3+) neutral production!
+
+    // So use a fixed percentage of our total fleet for defence but adjust it
+    // with a dynamic factor based on game situation. Reduce it the more planets
+    // we have, compared to non-neutral planets (planets occupied by any player
+    // including us). Actually it is the production that counts and not the
+    // planets, but planets are good enough for us here for the moment.
+
+    int minimumBaseDefenceFleetSize = (int) std::ceil(((totalOwnFleet / 2) / totalOwnPlanets) * (1 - (totalOwnPlanets / (totalOwnPlanets + totalEnemyPlanets))));
+    int originalMinimumBaseDefenceFleetSize = minimumBaseDefenceFleetSize;
+
+    // Try to keep the defence above the universe average so that our planets
+    // are less attractive to attack than other enemy planets (for further
+    // enemies). However, there is no need to stay significantly above that
+    // average. Remember, if we bind our ships for defence, they cannot attack.
+
+    // If the average enemy defence fleet size is above our minimum defence
+    // fleet size, simply ignore that fact. This happens if there is only one
+    // enemy planet left with very high defence (for example the example AI).
+
+    int minimumCappedBaseDefenceFleetSize = 1.5 * averageNonOwnPlanetDefenceFleetSize;
+
+    // This value is a good indicator, but has one slight problem: If the enemy
+    // really just towers on his initial home planet, we never get a fleet large
+    // enough to conquer it. So limit the own defence fleet further capped on
+    // the damage the enemy can do.
+
+    int enemyAttackPerOwnPlanet = std::ceil((double) totalEnemyDefence / totalOwnPlanets);
+
+    kDebug() << "total own production: " << totalOwnProduction;
+    kDebug() << "total own fleet: " << totalOwnFleet;
+    kDebug() << "total own planets: " << totalOwnPlanets;
+    kDebug() << "total total planets: " << totalTotalPlanets;
+    kDebug() << "total enemy production: " << totalEnemyProduction;
+    kDebug() << "total enemy defence: " << totalEnemyDefence;
+    kDebug() << "total enemy planets: " << totalEnemyPlanets;
+    kDebug() << "minimum base defence fleet size: " << minimumBaseDefenceFleetSize;
+    kDebug() << "minimum capped base defence fleet size: " << minimumCappedBaseDefenceFleetSize;
+    kDebug() << "enemy attack per own planet: " << enemyAttackPerOwnPlanet;
+    kDebug() << "average non-own planet defence fleet size: " << averageNonOwnPlanetDefenceFleetSize;
+    kDebug() << "average own kill percentage: " << averageOwnKillPercentage;
+    kDebug() << "average own production: " << averageOwnProduction;
+
+    if (minimumBaseDefenceFleetSize > minimumCappedBaseDefenceFleetSize) {
+        kDebug() << "Reducing minimum base defence fleet size from " << minimumBaseDefenceFleetSize << " to " << minimumCappedBaseDefenceFleetSize << " (cap).";
+        minimumBaseDefenceFleetSize = minimumCappedBaseDefenceFleetSize;
+    }
+
+    if (minimumBaseDefenceFleetSize > enemyAttackPerOwnPlanet) {
+        kDebug() << "Reducing minimum base defence fleet size from " << minimumBaseDefenceFleetSize << " to " << enemyAttackPerOwnPlanet << " (enemy attack per own planet).";
+        minimumBaseDefenceFleetSize = enemyAttackPerOwnPlanet;
+
+        if (minimumBaseDefenceFleetSize < averageOwnProduction) {
+
+            // We are now in a very end-game situation. The enemy has hardly any
+            // planets anymore so our required defence can be lowered as the
+            // enemy can hardly damage us. Still, if our defence gets reduced
+            // by too much, even a very small enemy fleet can conquer a planet.
+            // So keep up a minimum fleet hopefully large enough to destroy any
+            // (small) incoming enemy fleet.
+
+            kDebug() << "Minimum base defence is too low. Resetting to original minimum base defence fleet size!";
+            minimumBaseDefenceFleetSize = originalMinimumBaseDefenceFleetSize;
+        }
+    }
+
+    if (totalEnemyPlanets == 0) {
+
+        // This is a very special case. The enemy has no planets anymore but is
+        // still alive, so it has fleets attacking us. As we do not know where
+        // they will attack, evenly distribute our fleet among all our planets.
+
+        // The actual problem is that in this situation, the calculated
+        // minimum base defence fleet size is zero.
+
+        kDebug() << "No enemy planets found! Evenly distribute our fleet among our planets.";
+        minimumBaseDefenceFleetSize = totalOwnFleet / totalOwnPlanets;
+    }
+
+    foreach (Planet *home, m_game->planets()) {
+        if (home->player() != this) {
+            continue;
+        }
+
+        int minimumDefenceFleetSize = getMinimumDefenceFleetSize(home, minimumBaseDefenceFleetSize, averageOwnKillPercentage, averageOwnProduction);
+        int surplusFleetSize = home->ships() - minimumDefenceFleetSize; // can be negative
+
+        Planet* closestUpstreamPlanet = home;
+        double closestUpstreamDistance = DBL_MAX;
+
+        Planet* closestSupportPlanet = home;
+        double closestSupportDistance = DBL_MAX;
+
+        QMultiMap<double, TargetPlanet> targetList;
+
+        foreach (Planet *other, m_game->planets()) {
+            if (other->player() == this) {
+                if (other != home) {
+
+                    // We found one of our own planets which is not the current
+                    // planet.
+
+                    double distance = m_game->map()->distance(home, other);
+
+                    if (distance <= closestUpstreamDistance) {
+
+                        // The planet is closer. Check if it has a higher
+                        // kill percentage. We just want to find the closest
+                        // planet with a higher kill percentage.
+
+                        if (other->killPercentage() > home->killPercentage()) {
+                            closestUpstreamPlanet = other;
+                            closestUpstreamDistance = distance;
+                        }
+                    }
+
+                    if (distance <= closestSupportDistance) {
+
+                        // The planet is closer. Check if it needs support.
+
+                        if (other->ships() < getMinimumDefenceFleetSize(other, minimumBaseDefenceFleetSize, averageOwnKillPercentage, averageOwnProduction)) {
+                            closestSupportPlanet = other;
+                            closestSupportDistance = distance;
+                        }
+                    }
+                }
+            }
+            else {
+
+                // We found an enemy planet.
+
+                // Do not send a fleet to the planet if a fleet is already on
+                // its way.
+
+                bool found = false;
+
+                foreach (AttackFleet *fleet, this->attackList() + this->newAttacks()) {
+                    if (fleet->destination == other) {
+                        found = true;
+                        break;
+                    }
+                }
+
+                if (found != true) {
+                    double distance = m_game->map()->distance(home, other);
+
+                    int production;
+                    bool isNeutral;
+
+                    if (other->player()->isNeutral()) {
+                        production = m_game->options().NeutralsProduction;
+                        isNeutral = true;
+                    }
+                    else {
+                        production = other->production();
+                        isNeutral = false;
+                    }
+
+                    double killPercentageOther = other->killPercentage();
+                    double killPercentageHome = home->killPercentage();
+
+                    // Avoid division by zero. Right now, these cases are only
+                    // possible by manually altering game conditions at the
+                    // beginning.
+
+                    if (killPercentageOther < 0.1) {
+                        killPercentageOther = 0.1;
+                    }
+
+                    if (killPercentageHome < 0.1) {
+                        killPercentageOther = 0.1;
+                    }
+
+                    /**
+                     * @todo The production increment in cumultative games is
+                     * not considered here.
+                     */
+
+                    int minimumAttackFleetToConquerPlanet = std::ceil((other->ships() + std::ceil(distance) * production) * killPercentageOther / killPercentageHome);
+
+                    if (minimumAttackFleetToConquerPlanet == 0) {
+
+                        // In case neutral planets have a production of zero,
+                        // we would need a fleet of size zero to conquer them.
+                        // As this does not work we use a minimum attack fleet
+                        // size of one.
+
+                        ++minimumAttackFleetToConquerPlanet;
+                    }
+
+                    // Prefer closer targets to further away targets (distance
+                    // penalty).
+
+                    /**
+                     * @todo Instead of a distance penalty, simply consider the
+                     * production we could get from the closer planet due to
+                     * saved transit time compared to what we save in attack
+                     * ships.
+                     */
+
+                    /**
+                     * @todo Maybe not take the weakest we can get (first on
+                     * list), but the strongest we can get.
+                     */
+
+                    /**
+                     * @todo The universe is flat, so prefer planets at the
+                     * edge. They have less closer neightbours than centrally
+                     * located planets!
+                     */
+
+                    /**
+                     * @todo If we have a choice, attack the strongest player.
+                     * This way our empire can expand and at the end, we do not
+                     * have to face fighting a superpower.
+                     */
+
+                    double planetScore = minimumAttackFleetToConquerPlanet * distance;
+
+                    // Try to prefer higher production and higher kill percentage.
+
+                    planetScore *= 1 / (killPercentageOther / averageOwnKillPercentage);
+                    planetScore *= 1 / (other->production() / averageOwnProduction);
+
+                    // Prefer attacking non-neutral planets.
+                    // Neutrals don't harm us, enemies do.
+
+                    if (isNeutral) {
+
+                        // Penalty for neutral planets!
+                        // Multiply the score with the defence so that closer
+                        // planets are higher ranked (smaller value).
+
+                        targetList.insert(1.1 * planetScore, TargetPlanet(other, minimumAttackFleetToConquerPlanet, distance));
+                    }
+                    else {
+                        targetList.insert(1.0 * planetScore, TargetPlanet(other, minimumAttackFleetToConquerPlanet, distance));
+                    }
+                }
+            }
+        }
+
+        // Process the first few possible targets from the target list. As this
+        // is a priority list, only consider the first few ones and do not
+        // process possible targets with low priority (high score).
+
+        QMapIterator<double, TargetPlanet> targetListIt(targetList);
+
+        int skipCount = 3;
+
+        while (targetListIt.hasNext() && (skipCount > 0)) {
+            targetListIt.next();
+
+            // Always decrement the skip counter. If an attack can be launched
+            // successfully, simply increment it again as this is no skip then.
+
+            --skipCount;
+
+            Planet *attackPlanet = targetListIt.value().planet();
+            int minimumAttackFleetToConquerPlanet = targetListIt.value().minimumAttackFleetToConquerPlanet();
+            double distance = targetListIt.value().distance();
+
+            if (closestUpstreamPlanet != home) {
+
+                // We know that there is an own planet with higher kill
+                // percentage. Do not attack other planet if that one is quite
+                // far away compared to upstream planet.
+
+                if (distance > 2 * closestUpstreamDistance) {
+                    continue;
+                }
+            }
+
+            if (surplusFleetSize > 0) {
+
+                // We actually have more ships than required for defence,
+                // so we can start an attack.
+
+                if (surplusFleetSize > minimumAttackFleetToConquerPlanet) {
+
+                    // We now know that we can conquer the planet with our home
+                    // fleet. Now, figure out how many ships we need and
+                    // actually want to send.
+
+                    // Send as many ships as needed to conquer the planet and to
+                    // build up a "proper" defence fleet. If we do not have that
+                    // many ships, send our whole surplus fleet.
+
+                    /**
+                     * @todo Maybe first check if we can attack all targets, and
+                     * if we still have a surplus, use that one distributed
+                     * across all targets!
+                     */
+
+                    int attackFleetSize = 0;
+                    int minimumActualDefenceFleetSize = getMinimumDefenceFleetSize(attackPlanet, minimumBaseDefenceFleetSize, averageOwnKillPercentage, averageOwnProduction);
+
+                    if (surplusFleetSize > minimumAttackFleetToConquerPlanet + minimumActualDefenceFleetSize) {
+                        attackFleetSize = minimumAttackFleetToConquerPlanet + minimumActualDefenceFleetSize;
+                    }
+                    else
+                    if (surplusFleetSize > minimumAttackFleetToConquerPlanet) {
+
+                        // We actually can conquer the planet. So send as much
+                        // as we have of surplus.
+
+                        attackFleetSize = surplusFleetSize;
+                    }
+
+                    if (attackFleetSize > 0) {
+                        kDebug() << "Attacking " << attackPlanet->name() << " from " << home->name() << " with " << attackFleetSize << ".";
+                        m_game->attack(home, attackPlanet, attackFleetSize);
+
+                        surplusFleetSize -= attackFleetSize;
+                        ++skipCount;
+                    }
+                }
+            }
+            else {
+
+                // Either our surplus fleet size is negative and we actually
+                // need supply, or our surplus fleet is not large enough
+                // to successfully conquer this planet.
+
+            }
+        }
+
+        // If we still have surplus now, this is likely because we are out of
+        // close targets. Either use surplus for support or send it upstream.
+
+        if (closestSupportPlanet != home) {
+            if (closestSupportDistance < 2 * closestUpstreamDistance) {
+
+                // Send ships to support the other planet, but only if that
+                // planet is not much further away than our upstream planet.
+
+                // And only send support if not another planet sent support
+                // already. So for now, just check if there is an incoming
+                // friendly attack fleet.
+
+                bool skip = false;
+
+                foreach (AttackFleet *fleet, this->attackList() + this->newAttacks()) {
+                    if ((fleet->destination == closestSupportPlanet) && (fleet->owner == this)) {
+                        skip = true;
+                        break;
+                    }
+                }
+
+                if (!skip) {
+
+                    // Additionally add as much as one round of production.
+
+                    // Or better not, as that just makes this planet look like
+                    // requiring support from others.
+
+                    // surplusFleetSize += home->production();
+
+                    if (surplusFleetSize > 0) {
+
+                        // Send complete surplus fleet. This could be more than
+                        // the support planet actually needs (or less), but for
+                        // now this is good enough.
+
+                        kDebug() << "Supporting (support) " << closestSupportPlanet->name() << " from " << home->name() << " with " << surplusFleetSize << ".";
+                        m_game->attack(home, closestSupportPlanet, surplusFleetSize);
+
+                        // We do not have any surplus fleet anymore to send
+                        // upsteam.
+
+                        surplusFleetSize = 0;
+                    }
+                }
+            }
+        }
+
+        if (closestUpstreamPlanet != home) {
+
+            // Send ships in larger chunks. Having defence on our planets makes
+            // them less attractive for enemy attacks. And having more than the
+            // minimum defence allows giving support to other planets, if
+            // needed.
+
+            if (surplusFleetSize > 3 * home->production()) { // > 0) {
+                kDebug() << "Supporting (upstream) " << closestUpstreamPlanet->name() << " from " << home->name() << " with " << surplusFleetSize << ".";
+                m_game->attack(home, closestUpstreamPlanet, surplusFleetSize);
+            }
+        }
+    }
+
+    emit(donePlaying());
+}
diff --git a/players/ai/example/example_gui.cpp b/players/ai/becai/becai.h
similarity index 62%
copy from players/ai/example/example_gui.cpp
copy to players/ai/becai/becai.h
index 5e347ca..1a2732c 100644
--- a/players/ai/example/example_gui.cpp
+++ b/players/ai/becai/becai.h
@@ -16,18 +16,32 @@
     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  */
 
-#include "example_gui.h"
-#include "example.h"
+#ifndef AI_BECAI_H
+#define AI_BECAI_H
 
+#include "../../computerplayer.h"
 
-AiExampleGui::AiExampleGui() :
-    PlayerGui(i18n("Example/Passive"))
-{
-}
 
+/**
+ * This is quite a challenging AI.
+ */
 
-Player*
-AiExampleGui::createInstance(Game *game, const QString &newName, const QColor &color) const
+class AiBecai : public ComputerPlayer
 {
-    return new AiExample(game, newName, color);
-}
+    Q_OBJECT
+
+public:
+    explicit AiBecai(Game *game, const QString &newName, const QColor &color);
+
+    virtual void play();
+
+private:
+    int getMinimumDefenceFleetSize(Planet *planet, int minimumBaseDefenceFleetSize, double averageOwnKillPercentage, double averageOwnProduction);
+
+signals:
+
+public slots:
+
+};
+
+#endif // AI_BECAI_H
diff --git a/players/ai/example/example_gui.cpp b/players/ai/becai/becai_gui.cpp
similarity index 73%
copy from players/ai/example/example_gui.cpp
copy to players/ai/becai/becai_gui.cpp
index 5e347ca..d44d15a 100644
--- a/players/ai/example/example_gui.cpp
+++ b/players/ai/becai/becai_gui.cpp
@@ -16,18 +16,18 @@
     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  */
 
-#include "example_gui.h"
-#include "example.h"
+#include "becai_gui.h"
+#include "becai.h"
 
 
-AiExampleGui::AiExampleGui() :
-    PlayerGui(i18n("Example/Passive"))
+AiBecaiGui::AiBecaiGui() :
+    PlayerGui(i18nc("A distinct unique AI playing a balanced strategy", "Becai (Balanced)"))
 {
 }
 
 
 Player*
-AiExampleGui::createInstance(Game *game, const QString &newName, const QColor &color) const
+AiBecaiGui::createInstance(Game *game, const QString &newName, const QColor &color) const
 {
-    return new AiExample(game, newName, color);
+    return new AiBecai(game, newName, color);
 }
diff --git a/players/ai/example/example_gui.cpp b/players/ai/becai/becai_gui.h
similarity index 74%
copy from players/ai/example/example_gui.cpp
copy to players/ai/becai/becai_gui.h
index 5e347ca..d2197ce 100644
--- a/players/ai/example/example_gui.cpp
+++ b/players/ai/becai/becai_gui.h
@@ -16,18 +16,17 @@
     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  */
 
-#include "example_gui.h"
-#include "example.h"
+#ifndef AI_BECAI_GUI_H
+#define AI_BECAI_GUI_H
 
-
-AiExampleGui::AiExampleGui() :
-    PlayerGui(i18n("Example/Passive"))
-{
-}
+#include "../../player_gui.h"
 
 
-Player*
-AiExampleGui::createInstance(Game *game, const QString &newName, const QColor &color) const
+class AiBecaiGui : public PlayerGui
 {
-    return new AiExample(game, newName, color);
-}
+public:
+    explicit AiBecaiGui();
+    virtual Player* createInstance(Game *game, const QString &newName, const QColor &color) const;
+};
+
+#endif // AI_BECAI_GUI_H
diff --git a/players/ai/default/hard_gui.cpp b/players/ai/default/hard_gui.cpp
index e4e1568..8ca9c50 100644
--- a/players/ai/default/hard_gui.cpp
+++ b/players/ai/default/hard_gui.cpp
@@ -21,7 +21,7 @@
 
 
 AiDefaultHardGui::AiDefaultHardGui() :
-    PlayerGui(i18n("Default Hard/Defensive"))
+    PlayerGui(i18n("Default (Hard)"))
 {
 }
 
diff --git a/players/ai/default/normal_gui.cpp b/players/ai/default/normal_gui.cpp
index 41b2d31..d3c085e 100644
--- a/players/ai/default/normal_gui.cpp
+++ b/players/ai/default/normal_gui.cpp
@@ -21,7 +21,7 @@
 
 
 AiDefaultNormalGui::AiDefaultNormalGui() :
-    PlayerGui(i18n("Default Normal/Offensive"))
+    PlayerGui(i18n("Default (Offensive)"))
 {
 }
 
diff --git a/players/ai/default/weak_gui.cpp b/players/ai/default/weak_gui.cpp
index feb76e9..37efe44 100644
--- a/players/ai/default/weak_gui.cpp
+++ b/players/ai/default/weak_gui.cpp
@@ -21,7 +21,7 @@
 
 
 AiDefaultWeakGui::AiDefaultWeakGui() :
-    PlayerGui(i18n("Default Weak"))
+    PlayerGui(i18n("Default (Weak)"))
 {
 }
 
diff --git a/players/ai/example/example_gui.cpp b/players/ai/example/example_gui.cpp
index 5e347ca..ed2faf7 100644
--- a/players/ai/example/example_gui.cpp
+++ b/players/ai/example/example_gui.cpp
@@ -21,7 +21,7 @@
 
 
 AiExampleGui::AiExampleGui() :
-    PlayerGui(i18n("Example/Passive"))
+    PlayerGui(i18n("Example (Passive)"))
 {
 }
 


More information about the kde-doc-english mailing list