widelands-dev team mailing list archive
-
widelands-dev team
-
Mailing list archive
-
Message #03854
[Merge] lp:~widelands-dev/widelands/ai-military-changes into lp:widelands
TiborB has proposed merging lp:~widelands-dev/widelands/ai-military-changes into lp:widelands.
Requested reviews:
Widelands Developers (widelands-dev)
For more details, see:
https://code.launchpad.net/~widelands-dev/widelands/ai-military-changes/+merge/253881
Another bunch of AI changes, this time only files within src/ai/ were changed. Reworked were:
- attacking
- training sites
- mines
- further small things
--
Your team Widelands Developers is requested to review the proposed merge of lp:~widelands-dev/widelands/ai-military-changes into lp:widelands.
=== modified file 'src/ai/ai_help_structs.h'
--- src/ai/ai_help_structs.h 2015-03-05 20:57:07 +0000
+++ src/ai/ai_help_structs.h 2015-03-23 21:37:01 +0000
@@ -40,7 +40,7 @@
class ProductionSite;
class MilitarySite;
-enum class ExtendedBool : uint8_t {kUnset, kTrue, kFalse };
+enum class ExtendedBool : uint8_t {kUnset, kTrue, kFalse};
struct CheckStepRoadAI {
CheckStepRoadAI(Player* const pl, uint8_t const mc, bool const oe)
@@ -110,6 +110,22 @@
}
};
+// Sometimes we need to know how many nodes our allies owns
+struct FindNodeAllyOwned {
+ bool accept(const Map&, const FCoords& fc) const {
+ return (fc.field->nodecaps() & MOVECAPS_WALK) && (fc.field->get_owned_by() != 0) &&
+ (fc.field->get_owned_by() != pn) &&
+ !player_->is_hostile(*game.get_player(fc.field->get_owned_by()));
+ }
+
+ Player* player_;
+ Game& game;
+ PlayerNumber pn;
+
+ FindNodeAllyOwned(Player* p, Game& g, PlayerNumber n) : player_(p), game(g), pn(n) {
+ }
+};
+
// When looking for unowned terrain to acquire, we must
// pay speciall attention to fields where mines can be built.
// Fields should be completely unowned
@@ -148,8 +164,9 @@
bool accept(const Map& /* map */, const FCoords& coord) const {
return (world_.terrain_descr(coord.field->terrain_d()).get_is() &
- TerrainDescription::Type::kWater) ||
- (world_.terrain_descr(coord.field->terrain_r()).get_is() & TerrainDescription::Type::kWater);
+ TerrainDescription::Type::kWater) ||
+ (world_.terrain_descr(coord.field->terrain_r()).get_is() &
+ TerrainDescription::Type::kWater);
}
private:
@@ -213,7 +230,7 @@
struct BuildableField {
Widelands::FCoords coords;
- uint32_t next_update_due_;
+ uint32_t field_info_expiration_;
bool preferred_;
bool enemy_nearby_;
@@ -232,7 +249,7 @@
uint8_t space_consumers_nearby_;
// to manage the military better following variables exists:
// capacity of nearby buildings:
- int16_t military_capacity_;
+ int16_t area_military_capacity_;
// distance to near buldings:
int16_t military_loneliness_;
// count of military buildings in construction
@@ -241,7 +258,7 @@
// are construction sites that will change this once they are built
int16_t military_in_constr_nearby_;
// actual count of soldiers in nearby buldings
- int16_t military_presence_;
+ int16_t area_military_presence_;
// stationed (manned) military buildings nearby
int16_t military_stationed_;
// stationed (manned) military buildings nearby
@@ -256,7 +273,7 @@
BuildableField(const Widelands::FCoords& fc)
: coords(fc),
- next_update_due_(0),
+ field_info_expiration_(20000),
preferred_(false),
enemy_nearby_(0),
unowned_land_nearby_(0),
@@ -277,10 +294,10 @@
critters_nearby_(-1),
ground_water_(1),
space_consumers_nearby_(0),
- military_capacity_(0),
+ area_military_capacity_(0),
military_loneliness_(1000),
military_in_constr_nearby_(0),
- military_presence_(0),
+ area_military_presence_(0),
military_stationed_(0),
military_unstationed_(0),
is_portspace_(false),
@@ -292,7 +309,7 @@
struct MineableField {
Widelands::FCoords coords;
- uint32_t next_update_due_;
+ uint32_t field_info_expiration_;
bool preferred_;
@@ -302,7 +319,7 @@
MineableField(const Widelands::FCoords& fc)
: coords(fc),
- next_update_due_(0),
+ field_info_expiration_(20000),
preferred_(false),
mines_nearby_(0),
same_mine_fields_nearby_(0) {
@@ -346,14 +363,16 @@
bool is_fisher_; // need to identify fishers
bool is_port_;
bool is_shipyard_;
- bool space_consumer_; // farm, vineyard... = true
- bool expansion_type_; // military building used that can be used to control area
- bool fighting_type_; // military building built near enemies
- bool mountain_conqueror_; // military building built near mountains
+ bool space_consumer_; // farm, vineyard... = true
+ bool expansion_type_; // military building used that can be used to control area
+ bool fighting_type_; // military building built near enemies
+ bool mountain_conqueror_; // military building built near mountains
uint32_t prohibited_till_; // do not build before (ms)
uint32_t forced_after_; // do not wait until ware is needed
+ uint8_t ts_type_;
- bool unoccupied_; //
+ bool unoccupied_;
+ uint16_t unconnected_; // to any warehouse (count of such buildings)
int32_t mines_; // type of resource it mines_
uint16_t mines_percent_; // % of res it can mine
@@ -429,4 +448,44 @@
uint8_t preciousness_;
};
+//Computer player does not get notification messages about enemy militarysites
+//and warehouses, so following is collected based on observation
+//It is conventient to have some information preserved, like nearby minefields,
+//when it was attacked, whether it is warehouse and so on
+//Also AI test more such targets when considering attack and calculated score is
+//is stored in the observer
+struct EnemySiteObserver {
+ bool warehouse_;
+ uint8_t attack_soldiers;
+ uint8_t defenders;
+ uint8_t stationed_soldiers;
+ uint32_t last_time_attackable;
+ uint32_t last_tested;
+ int16_t score;
+ bool warehouse;
+ Widelands::ExtendedBool mines_nearby;
+ int16_t no_attack_counter;
+
+ EnemySiteObserver()
+ : warehouse_(false),
+ attack_soldiers(0),
+ stationed_soldiers(0),
+ last_time_attackable(std::numeric_limits<uint32_t>::max()),
+ last_tested(0),
+ score(0),
+ mines_nearby(Widelands::ExtendedBool::kUnset),
+ no_attack_counter(0) {
+ }
+};
+
+// as all mines have 3 levels, AI does not know total count of mines per mined material
+// so this observer will be used for this
+struct MineTypesObserver {
+ uint16_t in_construction;
+ uint16_t finished;
+
+ MineTypesObserver() : in_construction(0), finished(0) {
+ }
+};
+
#endif // end of include guard: WL_AI_AI_HELP_STRUCTS_H
=== modified file 'src/ai/ai_hints.cc'
--- src/ai/ai_hints.cc 2015-03-05 20:57:07 +0000
+++ src/ai/ai_hints.cc 2015-03-23 21:37:01 +0000
@@ -33,7 +33,8 @@
mountain_conqueror_(section ? section->get_bool("mountain_conqueror") : false),
prohibited_till_(section ? section->get_natural("prohibited_till", 0) : 0),
forced_after_(section ? section->get_natural("forced_after", 864000) : 0), // 10 days default
- mines_percent_(section ? section->get_int("mines_percent", 100) : 0)
+ mines_percent_(section ? section->get_int("mines_percent", 100) : 0),
+ ts_type_(section ? section->get_int("ts_type", 0) : 0)
{
if (section) {
if (section->has_val("renews_map_resource"))
=== modified file 'src/ai/ai_hints.h'
--- src/ai/ai_hints.h 2015-03-05 20:57:07 +0000
+++ src/ai/ai_hints.h 2015-03-23 21:37:01 +0000
@@ -94,6 +94,10 @@
return mines_percent_;
}
+ uint8_t get_ts_type() const {
+ return ts_type_;
+ }
+
private:
std::string renews_map_resource_;
std::string mines_;
@@ -109,6 +113,7 @@
int32_t prohibited_till_;
int32_t forced_after_;
uint8_t mines_percent_;
+ uint8_t ts_type_;
DISALLOW_COPY_AND_ASSIGN(BuildingHints);
};
=== modified file 'src/ai/defaultai.cc'
--- src/ai/defaultai.cc 2015-03-06 07:36:40 +0000
+++ src/ai/defaultai.cc 2015-03-23 21:37:01 +0000
@@ -51,12 +51,15 @@
#include "profile/profile.h"
// following is in miliseconds (widelands counts time in ms)
-constexpr int kFieldUpdateInterval = 2000;
-constexpr int kIdleMineUpdateInterval = 22000;
+// constexpr int kFieldUpdateInterval = 2000;
+constexpr int kFieldInfoExpiration = 12 * 1000;
+constexpr int kMineFieldInfoExpiration = 20 * 1000;
+constexpr int kNewMineConstInterval = 19000;
constexpr int kBusyMineUpdateInterval = 2000;
// building of the same building can be started after 25s at earliest
constexpr int kBuildingMinInterval = 25 * 1000;
constexpr int kMinBFCheckInterval = 5 * 1000;
+constexpr int kMinMFCheckInterval = 19 * 1000;
constexpr int kShipCheckInterval = 5 * 1000;
constexpr int kMarineDecisionInterval = 20 * 1000;
constexpr int kTrainingSitesCheckInterval = 5 * 60 * 1000;
@@ -85,6 +88,8 @@
num_milit_constructionsites(0),
num_prod_constructionsites(0),
num_ports(0),
+ last_attacked_player_(std::numeric_limits<uint16_t>::max()),
+ enemysites_check_delay_(60),
next_ai_think_(0),
next_mine_construction_due_(0),
inhibit_road_building_(0),
@@ -92,9 +97,8 @@
enemy_last_seen_(0),
numof_warehouses_(0),
new_buildings_stop_(false),
- resource_necessity_territory_(255),
- resource_necessity_mines_(255),
- resource_necessity_stones_(255),
+ resource_necessity_territory_(100),
+ resource_necessity_mines_(100),
resource_necessity_water_(0),
resource_necessity_water_needed_(false),
unstationed_milit_buildings_(0),
@@ -105,7 +109,13 @@
next_attack_waittime_(10),
seafaring_economy(false),
colony_scan_area_(35),
- spots_(0) {
+ spots_(0),
+ vacant_mil_positions_(0),
+ ts_type1_count_(0),
+ ts_type1_const_count_(0),
+ ts_type2_count_(0),
+ ts_type2_const_count_(0),
+ ts_without_trainers_(0) {
// Subscribe to NoteFieldPossession.
field_possession_subscriber_ =
@@ -166,23 +176,23 @@
break;
case NoteShipMessage::Message::kLost:
- for (std::list<ShipObserver>::iterator i = allships.begin(); i != allships.end(); ++i) {
+ for (std::list<ShipObserver>::iterator i = allships.begin(); i != allships.end(); ++i) {
if (i->ship == note.ship) {
allships.erase(i);
break;
}
}
- break;
+ break;
case NoteShipMessage::Message::kWaitingForCommand:
- for (std::list<ShipObserver>::iterator i = allships.begin(); i != allships.end(); ++i) {
+ for (std::list<ShipObserver>::iterator i = allships.begin(); i != allships.end(); ++i) {
if (i->ship == note.ship) {
i->waiting_for_command_ = true;
break;
}
}
- break;
- default:
+ break;
+ default:
;
}
});
@@ -236,7 +246,11 @@
if (DueTask == ScheduleTasks::kBbuildableFieldsCheck) {
update_all_buildable_fields(gametime);
taskDue[ScheduleTasks::kBbuildableFieldsCheck] = gametime + kMinBFCheckInterval;
- } else if (DueTask == ScheduleTasks::kRoadCheck) {
+ } else if (DueTask == ScheduleTasks::kMineableFieldsCheck) {
+ update_all_mineable_fields(gametime);
+ taskDue[ScheduleTasks::kMineableFieldsCheck] = gametime + kMinMFCheckInterval;
+ }
+ if (DueTask == ScheduleTasks::kRoadCheck) {
if (check_economies()) { // is a must
return;
};
@@ -245,8 +259,6 @@
} else if (DueTask == ScheduleTasks::kUnbuildableFCheck) {
taskDue[ScheduleTasks::kUnbuildableFCheck] = gametime + 4000;
update_all_not_buildable_fields();
- } else if (DueTask == ScheduleTasks::kConsiderAttack) {
- consider_attack(gametime);
} else if (DueTask == ScheduleTasks::kCheckEconomies) {
check_economies();
taskDue[ScheduleTasks::kCheckEconomies] = gametime + 8000;
@@ -280,6 +292,9 @@
check_militarysites(gametime);
} else if (DueTask == ScheduleTasks::kCheckTrainingsites) {
check_trainingsites(gametime);
+ } else if (DueTask == ScheduleTasks::kCountMilitaryVacant) {
+ count_military_vacant_positions();
+ taskDue[ScheduleTasks::kCountMilitaryVacant] = gametime + 90 * 1000;
} else if (DueTask == ScheduleTasks::kWareReview) {
if (check_economies()) { // economies must be consistent
return;
@@ -291,6 +306,9 @@
return;
}
print_stats(gametime);
+ } else if (DueTask == ScheduleTasks::kCheckEnemySites) {
+ check_enemy_sites(gametime);
+ taskDue[ScheduleTasks::kCheckEnemySites] = gametime + 19 * 1000;
}
}
@@ -340,6 +358,7 @@
bo.production_hint_ = -1;
bo.current_stats_ = 0;
bo.unoccupied_ = false;
+ bo.unconnected_ = 0;
bo.is_buildable_ = bld.is_buildable();
bo.need_trees_ = bh.is_logproducer();
bo.need_stones_ = bh.is_stoneproducer();
@@ -353,6 +372,8 @@
bo.prohibited_till_ = bh.get_prohibited_till() * 1000; // value in conf is in seconds
bo.forced_after_ = bh.get_forced_after() * 1000; // value in conf is in seconds
bo.is_port_ = bld.get_isport();
+ bo.ts_type_ = bh.get_ts_type();
+
if (bh.renews_map_resource()) {
bo.production_hint_ = tribe_->safe_ware_index(bh.get_renews_map_resource());
}
@@ -382,6 +403,11 @@
}
bo.mines_percent_ = bh.get_mines_percent();
+
+ // populating mines_per_type map
+ if (mines_per_type.count(bo.mines_) == 0) {
+ mines_per_type[bo.mines_] = MineTypesObserver();
+ }
}
// here we identify hunters
@@ -439,6 +465,7 @@
for (const WareAmount& temp_input : train.inputs()) {
bo.inputs_.push_back(temp_input.first);
}
+ bo.ts_type_ = bh.get_ts_type();
continue;
}
@@ -544,12 +571,14 @@
taskDue[ScheduleTasks::kCheckShips] = 30 * 1000;
taskDue[ScheduleTasks::kCheckEconomies] = 1000;
taskDue[ScheduleTasks::KMarineDecisions] = 30 * 1000;
- taskDue[ScheduleTasks::kConsiderAttack] = 300000;
taskDue[ScheduleTasks::kCheckTrainingsites] = 15 * 60 * 1000;
taskDue[ScheduleTasks::kBbuildableFieldsCheck] = 1000;
+ taskDue[ScheduleTasks::kMineableFieldsCheck] = 1000;
taskDue[ScheduleTasks::kUnbuildableFCheck] = 1000;
taskDue[ScheduleTasks::kWareReview] = 15 * 60 * 1000;
taskDue[ScheduleTasks::kPrintStats] = 30 * 60 * 1000;
+ taskDue[ScheduleTasks::kCountMilitaryVacant] = 10 * 60 * 1000;
+ taskDue[ScheduleTasks::kCheckEnemySites] = 10 * 60 * 1000;
}
/**
@@ -562,7 +591,10 @@
uint16_t i = 0;
- while (!buildable_fields.empty() && buildable_fields.front()->next_update_due_ <= gametime &&
+ // we test 40 fields that were update more than 1 seconds ago
+ while (!buildable_fields.empty() &&
+ (buildable_fields.front()->field_info_expiration_ - kFieldInfoExpiration + 1000) <=
+ gametime &&
i < 40) {
BuildableField& bf = *buildable_fields.front();
@@ -582,7 +614,7 @@
}
update_buildable_field(bf);
- bf.next_update_due_ = gametime + kFieldUpdateInterval;
+ bf.field_info_expiration_ = gametime + kFieldInfoExpiration;
buildable_fields.push_back(&bf);
buildable_fields.pop_front();
@@ -600,8 +632,12 @@
uint16_t i = 0; // counter, used to track # of checked fields
- while (!mineable_fields.empty() && mineable_fields.front()->next_update_due_ <= gametime &&
- i < 40) {
+ // we test 30 fields that were updated more than 1 seconds ago
+ // to avoid re-test of the same field twice
+ while (!mineable_fields.empty() &&
+ (mineable_fields.front()->field_info_expiration_ - kMineFieldInfoExpiration + 1000) <=
+ gametime &&
+ i < 30) {
MineableField* mf = mineable_fields.front();
// check whether we lost ownership of the node
@@ -620,7 +656,7 @@
}
update_mineable_field(*mf);
- mf->next_update_due_ = gametime + kFieldUpdateInterval; // in fact this has very small effect
+ mf->field_info_expiration_ = gametime + kMineFieldInfoExpiration;
mineable_fields.push_back(mf);
mineable_fields.pop_front();
@@ -672,32 +708,38 @@
void DefaultAI::update_buildable_field(BuildableField& field, uint16_t range, bool military) {
// look if there is any unowned land nearby
Map& map = game().map();
+ const int32_t gametime = game().get_gametime();
FindNodeUnowned find_unowned(player_, game());
FindNodeUnownedMineable find_unowned_mines_pots(player_, game());
PlayerNumber const pn = player_->player_number();
const World& world = game().world();
field.unowned_land_nearby_ =
map.find_fields(Area<FCoords>(field.coords, range), nullptr, find_unowned);
+ FindNodeAllyOwned find_ally(player_, game(), player_number());
+ const int32_t AllyOwnedFields =
+ map.find_fields(Area<FCoords>(field.coords, 3), nullptr, find_ally);
field.near_border_ = false;
- if (field.unowned_land_nearby_ > 0) {
+ if (AllyOwnedFields > 0) {
+ field.near_border_ = true;
+ } else if (field.unowned_land_nearby_ > 0) {
if (map.find_fields(Area<FCoords>(field.coords, 4), nullptr, find_unowned) > 0) {
field.near_border_ = true;
}
}
// to save some CPU
- if ((mines_.size() > 8 && game().get_gametime() % 3 > 0) || field.unowned_land_nearby_ == 0) {
+ if ((mines_.size() > 8 && gametime % 3 > 0) || field.unowned_land_nearby_ == 0) {
field.unowned_mines_pots_nearby_ = 0;
} else {
uint32_t close_mines =
map.find_fields(Area<FCoords>(field.coords, 4), nullptr, find_unowned_mines_pots);
uint32_t distant_mines =
- map.find_fields(Area<FCoords>(field.coords, (range + 6 < 12) ? 12 : range + 6),
+ map.find_fields(Area<FCoords>(field.coords, (range + 6 < 14) ? 14 : range + 6),
nullptr,
find_unowned_mines_pots);
distant_mines = distant_mines - close_mines;
- field.unowned_mines_pots_nearby_ = 3 * close_mines + distant_mines / 2;
+ field.unowned_mines_pots_nearby_ = 4 * close_mines + distant_mines / 2;
if (distant_mines > 0) {
field.unowned_mines_pots_nearby_ += 15;
}
@@ -710,7 +752,7 @@
}
}
- // testing for near porspaces
+ // testing for near portspaces
if (field.portspace_nearby_ == Widelands::ExtendedBool::kUnset) {
field.portspace_nearby_ = ExtendedBool::kFalse;
MapRegion<Area<FCoords>> mr(map, Area<FCoords>(field.coords, 2));
@@ -745,9 +787,9 @@
int32_t const tree_attr = MapObjectDescr::get_attribute_id("tree");
field.preferred_ = false;
field.enemy_nearby_ = false;
- field.military_capacity_ = 0;
+ field.area_military_capacity_ = 0;
field.military_loneliness_ = 1000; // instead of floats(v-
- field.military_presence_ = 0;
+ field.area_military_presence_ = 0;
field.military_stationed_ = 0;
field.trees_nearby_ = 0;
field.space_consumers_nearby_ = 0;
@@ -772,8 +814,7 @@
}
// counting fields with fish
- if (field.water_nearby_ > 0 &&
- (field.fish_nearby_ == -1 || game().get_gametime() % 10 == 0)) {
+ if (field.water_nearby_ > 0 && (field.fish_nearby_ == -1 || gametime % 10 == 0)) {
map.find_fields(Area<FCoords>(field.coords, 6),
&resource_list,
FindNodeResource(world.get_resource("fish")));
@@ -782,7 +823,7 @@
// counting fields with critters (game)
// not doing this always, this does not change fast
- if (game().get_gametime() % 10 == 0) {
+ if (gametime % 10 == 0) {
map.find_bobs(Area<FCoords>(field.coords, 6), &critters_list, FindBobCritter());
field.critters_nearby_ = critters_list.size();
}
@@ -808,7 +849,7 @@
if (player_->is_hostile(player_immovable->owner())) {
field.enemy_nearby_ = true;
}
- enemy_last_seen_ = game().get_gametime();
+ enemy_last_seen_ = gametime;
continue;
}
@@ -840,7 +881,7 @@
}
// stones are not renewable, we will count them only if previous state si nonzero
- if (field.stones_nearby_ > 0) {
+ if (field.stones_nearby_ > 0 && gametime % 3 == 0) {
int32_t const stone_attr = MapObjectDescr::get_attribute_id("granite");
field.stones_nearby_ = 0;
@@ -863,13 +904,13 @@
// we get immovables with higher radius
immovables.clear();
- map.find_immovables(Area<FCoords>(field.coords, (range < 10) ? 10 : range), &immovables);
+ map.find_immovables(Area<FCoords>(field.coords, (range < 11) ? 11 : range), &immovables);
field.military_stationed_ = 0;
field.military_unstationed_ = 0;
field.military_in_constr_nearby_ = 0;
- field.military_capacity_ = 0;
+ field.area_military_capacity_ = 0;
field.military_loneliness_ = 1000;
- field.military_presence_ = 0;
+ field.area_military_presence_ = 0;
for (uint32_t i = 0; i < immovables.size(); ++i) {
@@ -890,6 +931,7 @@
}
}
+ // if we are here, immovable is ours
if (upcast(Building const, building, &base_immovable)) {
if (upcast(ConstructionSite const, constructionsite, building)) {
const BuildingDescr& target_descr = constructionsite->building();
@@ -899,7 +941,8 @@
const int32_t radius = target_ms_d->get_conquers() + 4;
if (radius > dist) {
- field.military_capacity_ += target_ms_d->get_max_number_of_soldiers() / 2 + 1;
+ field.area_military_capacity_ +=
+ target_ms_d->get_max_number_of_soldiers() / 2 + 1;
field.military_loneliness_ *= static_cast<double_t>(dist) / radius;
field.military_in_constr_nearby_ += 1;
}
@@ -912,8 +955,8 @@
if (radius > dist) {
- field.military_capacity_ += militarysite->max_soldier_capacity();
- field.military_presence_ += militarysite->stationed_soldiers().size();
+ field.area_military_capacity_ += militarysite->max_soldier_capacity();
+ field.area_military_presence_ += militarysite->stationed_soldiers().size();
if (militarysite->stationed_soldiers().empty()) {
field.military_unstationed_ += 1;
@@ -935,7 +978,7 @@
Map& map = game().map();
map.find_immovables(Area<FCoords>(field.coords, 5), &immovables);
field.preferred_ = false;
- field.mines_nearby_ = 1;
+ field.mines_nearby_ = 0;
FCoords fse;
map.get_brn(field.coords, &fse);
@@ -948,11 +991,20 @@
for (const ImmovableFound& temp_immovable : immovables) {
if (upcast(Building const, bld, temp_immovable.object)) {
+ if (player_number() != bld->owner().player_number()) {
+ continue;
+ }
if (bld->descr().get_ismine()) {
- ++field.mines_nearby_;
+ if (get_building_observer(bld->descr().name().c_str()).mines_ ==
+ field.coords.field->get_resources()) {
+ ++field.mines_nearby_;
+ }
} else if (upcast(ConstructionSite const, cs, bld)) {
if (cs->building().get_ismine()) {
- ++field.mines_nearby_;
+ if (get_building_observer(cs->building().name().c_str()).mines_ ==
+ field.coords.field->get_resources()) {
+ ++field.mines_nearby_;
+ }
}
}
}
@@ -976,23 +1028,34 @@
for (uint32_t i = 0; i < buildings_.size(); ++i) {
buildings_.at(i).current_stats_ = 0;
buildings_.at(i).unoccupied_ = false;
+ buildings_.at(i).unconnected_ = 0;
}
// Check all available productionsites
for (uint32_t i = 0; i < productionsites.size(); ++i) {
assert(productionsites.front().bo->cnt_built_ > 0);
- // Add statistics value
- productionsites.front().bo->current_stats_ +=
- productionsites.front().site->get_crude_statistics();
-
- // counting fishers
- if (productionsites.front().bo->is_fisher_) {
- fishers_count += 1;
+ // is connected
+ const bool connected_to_wh =
+ !productionsites.front().site->get_economy()->warehouses().empty();
+
+ // unconnected buildings are excluded from statistics review
+ if (connected_to_wh) {
+ // Add statistics value
+ productionsites.front().bo->current_stats_ +=
+ productionsites.front().site->get_crude_statistics();
+
+ // counting fishers
+ if (productionsites.front().bo->is_fisher_) {
+ fishers_count += 1;
+ }
+
+ // Check whether this building is completely occupied
+ productionsites.front().bo->unoccupied_ |=
+ !productionsites.front().site->can_start_working();
+ } else {
+ productionsites.front().bo->unconnected_ += 1;
}
- // Check whether this building is completely occupied
- productionsites.front().bo->unoccupied_ |= !productionsites.front().site->can_start_working();
-
// Now reorder the buildings
productionsites.push_back(productionsites.front());
productionsites.pop_front();
@@ -1000,11 +1063,11 @@
if (resource_necessity_water_needed_) {
if (fishers_count == 0) {
- resource_necessity_water_ = 255;
+ resource_necessity_water_ = 100;
} else if (fishers_count == 1) {
- resource_necessity_water_ = 150;
+ resource_necessity_water_ = 50;
} else {
- resource_necessity_water_ = 18;
+ resource_necessity_water_ = 10;
}
}
@@ -1012,10 +1075,20 @@
// Check all available mines
for (uint32_t i = 0; i < mines_.size(); ++i) {
assert(mines_.front().bo->cnt_built_ > 0);
- // Add statistics value
- mines_.front().bo->current_stats_ += mines_.front().site->get_statistics_percent();
- // Check whether this building is completely occupied
- mines_.front().bo->unoccupied_ |= !mines_.front().site->can_start_working();
+
+ const bool connected_to_wh =
+ !productionsites.front().site->get_economy()->warehouses().empty();
+
+ // unconnected mines are excluded from statistics review
+ if (connected_to_wh) {
+ // Add statistics value
+ mines_.front().bo->current_stats_ += mines_.front().site->get_statistics_percent();
+ // Check whether this building is completely occupied
+ mines_.front().bo->unoccupied_ |= !mines_.front().site->can_start_working();
+ } else {
+ buildings_.at(i).unconnected_ += 1;
+ }
+
// Now reorder the buildings
mines_.push_back(mines_.front());
mines_.pop_front();
@@ -1023,8 +1096,9 @@
// Scale statistics down
for (uint32_t i = 0; i < buildings_.size(); ++i) {
- if (buildings_.at(i).cnt_built_ > 0) {
- buildings_.at(i).current_stats_ /= buildings_.at(i).cnt_built_;
+ if ((buildings_.at(i).cnt_built_ - buildings_.at(i).unconnected_) > 0) {
+ buildings_.at(i).current_stats_ /=
+ (buildings_.at(i).cnt_built_ - buildings_.at(i).unconnected_);
}
}
}
@@ -1100,54 +1174,48 @@
// sometimes there is too many military buildings in construction, so we must
// prevent initialization of further buildings start
- const uint32_t treshold = militarysites.size() / 40 + 2;
-
- if (unstationed_milit_buildings_ + num_milit_constructionsites > 3 * treshold) {
+ const int32_t vacant_plus_in_construction_minus_prod =
+ vacant_mil_positions_ + 2 * num_milit_constructionsites - productionsites.size() / 15;
+ if (vacant_plus_in_construction_minus_prod > 20) {
expansion_mode = MilitaryStrategy::kNoNewMilitary;
- } else if (unstationed_milit_buildings_ + num_milit_constructionsites > 2 * treshold) {
+ } else if (vacant_plus_in_construction_minus_prod > 13) {
expansion_mode = MilitaryStrategy::kDefenseOnly;
- } else if (unstationed_milit_buildings_ + num_milit_constructionsites >= treshold - 1) {
+ } else if (vacant_plus_in_construction_minus_prod > 6) {
expansion_mode = MilitaryStrategy::kResourcesOrDefense;
- } else if (unstationed_milit_buildings_ + num_milit_constructionsites >= 1) {
- expansion_mode = MilitaryStrategy::kExpansion;
} else {
- expansion_mode = MilitaryStrategy::kPushExpansion;
+ if (unstationed_milit_buildings_ + num_milit_constructionsites >= 1) {
+ expansion_mode = MilitaryStrategy::kExpansion;
+ } else {
+ expansion_mode = MilitaryStrategy::kPushExpansion;
+ }
}
// we must consider need for mines
// set necessity for mines
// we use 'virtual mines', because also mine spots can be changed
// to mines when AI decides so
- const int32_t virtual_mines = mines_.size() + mineable_fields.size() / 15;
- if (virtual_mines <= 7) {
- resource_necessity_mines_ = std::numeric_limits<uint8_t>::max();
- } else if (virtual_mines > 19) {
- resource_necessity_mines_ = 50;
+ const int32_t virtual_mines =
+ mines_.size() + mineable_fields.size() / 15 - productionsites.size() / 25;
+ resource_necessity_mines_ = 100 * (15 - virtual_mines) / 15;
+ resource_necessity_mines_ = (resource_necessity_mines_ > 100) ? 100 : resource_necessity_mines_;
+ resource_necessity_mines_ = (resource_necessity_mines_ < 20) ? 10 : resource_necessity_mines_;
+
+ // here we calculate how badly we need to expand, result is number (0-100)
+ // like a percent
+ if (spots_ == 0) {
+ resource_necessity_territory_ = 100;
} else {
- const uint32_t tmp = (24 - virtual_mines) * 10;
- resource_necessity_mines_ = tmp;
- }
-
- // here we calculate a need for expansion and reduce necessity for new land
- // the game has two stages:
- // First: virtual mines<=5 - stage of building the economics
- // Second: virtual mines>5 - teritorial expansion
- if (virtual_mines <= 5) {
- if (spots_avail.at(BUILDCAPS_BIG) <= 4) {
- resource_necessity_territory_ = 255;
- } else {
- resource_necessity_territory_ = 0;
+ resource_necessity_territory_ = 100 * 5 * (productionsites.size() + 5) / spots_;
+ resource_necessity_territory_ =
+ (resource_necessity_territory_ > 100) ? 100 : resource_necessity_territory_;
+ resource_necessity_territory_ =
+ (resource_necessity_territory_ < 10) ? 10 : resource_necessity_territory_;
+ // alse we need at lest 4 big spots
+ if (spots_avail.at(BUILDCAPS_BIG) < 2) {
+ resource_necessity_territory_ = 100;
}
- } else { // or we have enough mines and regulate speed of expansion
- if (spots_ == 0) {
- resource_necessity_territory_ = 255;
- } else {
- const uint32_t tmp = 255 * 4 * productionsites.size() / spots_;
- if (tmp > 255) {
- resource_necessity_territory_ = 255;
- } else {
- resource_necessity_territory_ = tmp;
- }
+ if (spots_avail.at(BUILDCAPS_MEDIUM) < 4) {
+ resource_necessity_territory_ = 100;
}
}
@@ -1205,7 +1273,7 @@
++i) {
BuildableField* const bf = *i;
- if (bf->next_update_due_ < gametime - 10000) {
+ if (bf->field_info_expiration_ < gametime) {
continue;
}
@@ -1369,7 +1437,7 @@
continue;
}
- if (bo.total_count() == 0) {
+ if (bo.total_count() - bo.unconnected_ == 0) {
prio += 150;
}
@@ -1394,12 +1462,15 @@
continue;
}
- if (new_buildings_stop_) {
+ if (gametime > 5 * 60 * 1000 &&
+ (bo.total_count() - bo.unconnected_ == 0)) {
+ prio += 10;
+ } else if (new_buildings_stop_) {
continue;
}
prio +=
- (bf->critters_nearby_ * 2) - 8 - 5 * bf->producers_nearby_.at(bo.outputs_.at(0));
+ (bf->critters_nearby_ * 3) - 8 - 5 * bf->producers_nearby_.at(bo.outputs_.at(0));
} else if (bo.is_fisher_) { // fisher
@@ -1490,7 +1561,7 @@
prio -= bf->space_consumers_nearby_ * 5;
} else { // FISH BREEDERS and GAME KEEPERS
- if (new_buildings_stop_ && bo.total_count() > 0) {
+ if (new_buildings_stop_ && (bo.total_count() - bo.unconnected_) > 0) {
continue;
}
@@ -1502,7 +1573,7 @@
prio += bf->water_nearby_ / 5;
}
- if (bo.total_count() > bo.cnt_target_) {
+ if ((bo.total_count() - bo.unconnected_) > bo.cnt_target_) {
continue;
}
@@ -1543,9 +1614,10 @@
continue;
}
- if (bo.forced_after_ < gametime && bo.total_count() == 0) {
+ if (bo.forced_after_ < gametime && (bo.total_count() - bo.unconnected_) == 0) {
prio += 150;
- } else if (bo.cnt_built_ == 1 && game().get_gametime() > 40 * 60 * 1000 &&
+ } else if ((bo.cnt_built_ - bo.unconnected_) == 1 &&
+ game().get_gametime() > 40 * 60 * 1000 &&
bo.desc->enhancement() != INVALID_INDEX && !mines_.empty()) {
prio += 10;
} else if (bo.is_shipyard_) {
@@ -1554,9 +1626,10 @@
}
} else if (!output_is_needed) {
continue;
- } else if (bo.cnt_built_ == 0 && game().get_gametime() > 40 * 60 * 1000) {
+ } else if ((bo.cnt_built_ - bo.unconnected_) == 0 &&
+ game().get_gametime() > 40 * 60 * 1000) {
prio += kDefaultPrioBoost;
- } else if (bo.cnt_built_ > 1 && bo.current_stats_ > 97) {
+ } else if ((bo.cnt_built_ - bo.unconnected_) > 1 && bo.current_stats_ > 97) {
prio -= kDefaultPrioBoost * (new_buildings_stop_);
} else if (new_buildings_stop_)
continue;
@@ -1593,15 +1666,16 @@
else if (bo.is_shipyard_) {
// for now AI builds only one shipyard
- if (bf->water_nearby_ > 3 && bo.total_count() == 0 && seafaring_economy) {
+ if (bf->water_nearby_ > 3 && (bo.total_count() - bo.unconnected_) == 0 &&
+ seafaring_economy) {
prio += kDefaultPrioBoost + productionsites.size() * 5 + bf->water_nearby_;
}
} else if (!bo.inputs_.empty()) {
- if (bo.total_count() == 0) {
+ if ((bo.total_count() == 0 - bo.unconnected_)) {
prio += max_needed_preciousness + kDefaultPrioBoost;
}
- if (bo.cnt_built_ > 0 && bo.current_stats_ > 70) {
+ if ((bo.cnt_built_ - bo.unconnected_) > 0 && bo.current_stats_ > 70) {
prio += max_needed_preciousness + kDefaultPrioBoost - 3 +
(bo.current_stats_ - 70) / 5;
}
@@ -1623,7 +1697,6 @@
}
} // production sites done
else if (bo.type == BuildingObserver::MILITARYSITE) {
-
// we allow 1 exemption from big buildings prohibition
if (bo.build_material_shortage_ &&
(bo.cnt_under_construction_ > 0 || !(bf->enemy_nearby_))) {
@@ -1631,6 +1704,7 @@
}
if (!bf->unowned_land_nearby_) {
+
continue;
}
@@ -1648,8 +1722,7 @@
if (expansion_mode == MilitaryStrategy::kResourcesOrDefense &&
!(bf->unowned_mines_pots_nearby_ || bf->stones_nearby_ || bf->water_nearby_ ||
- (bf->distant_water_ && resource_necessity_water_needed_) ||
- bf->enemy_nearby_)) {
+ (bf->distant_water_ && resource_necessity_water_needed_) || bf->enemy_nearby_)) {
continue;
}
@@ -1667,31 +1740,40 @@
if (bo.desc->get_size() == 3 && gametime % 3 >= 1) {
continue;
};
- }
- else {
+ } else {
continue;
} // the building is not suitable for situation
-
// not to build so many military buildings nearby
if (!bf->enemy_nearby_ &&
(bf->military_in_constr_nearby_ + bf->military_unstationed_) > 0) {
continue;
}
-
// a boost to prevent an expansion halt
int32_t local_boost = 0;
if (expansion_mode == MilitaryStrategy::kPushExpansion) {
local_boost = 200;
}
- prio = ((bf->unowned_land_nearby_ * 2 * resource_necessity_territory_) / 255 +
- (bf->unowned_mines_pots_nearby_ * resource_necessity_mines_) / 255 +
- bf->stones_nearby_ / 2 + bf->military_loneliness_ / 10 - 60 + local_boost +
- (bf->water_nearby_ * resource_necessity_water_) / 255);
+ // priority based on basic resources
+ prio = ((bf->unowned_mines_pots_nearby_ * resource_necessity_mines_) / 100 +
+ bf->stones_nearby_ + bf->military_loneliness_ / 10 - 40 + local_boost +
+ (bf->water_nearby_ * resource_necessity_water_) / 100);
+
+ // Depending on wheter resource only are considered or no
+ if (expansion_mode == MilitaryStrategy::kResourcesOrDefense) {
+ prio *= 2;
+ prio += bf->unowned_land_nearby_ * resource_necessity_territory_ / 100 / 2;
+ } else { // addding score for territory
+ prio += (bf->unowned_land_nearby_ * resource_necessity_territory_) / 100 * 3 / 2;
+ }
+
+ // adding score for distance to other military sites
+ prio += bf->military_loneliness_ / 10 - 40;
// special bonus due to remote water for atlanteans
- if (resource_necessity_water_needed_)
- prio += (bf->distant_water_ * resource_necessity_water_) / 255;
+ if (resource_necessity_water_needed_) {
+ prio += (bf->distant_water_ * resource_necessity_water_) / 100 / 2;
+ }
// special bonus if a portspace is close
if (bf->portspace_nearby_ == ExtendedBool::kTrue) {
@@ -1702,6 +1784,11 @@
}
}
+ //special bonus for bigger buildings in enemy is nearby
+ if (bf->enemy_nearby_) {
+ prio += (bo.desc->get_size() - 1) * 15;
+ }
+
if (bo.desc->get_size() < maxsize) {
prio = prio - 5;
} // penalty
@@ -1713,12 +1800,12 @@
// for expansion)
const int16_t bottom_treshold =
15 - ((virtual_mines <= 4) ? (5 - virtual_mines) * 2 : 0);
- if (bf->enemy_nearby_ && bf->military_capacity_ < bottom_treshold) {
- prio += 50 + (bottom_treshold - bf->military_capacity_) * 20;
+ if (bf->enemy_nearby_ && bf->area_military_capacity_ < bottom_treshold) {
+ prio += 50 + (bottom_treshold - bf->area_military_capacity_) * 20;
}
- if (bf->enemy_nearby_ && bf->military_capacity_ > bottom_treshold + 4) {
- prio -= (bf->military_capacity_ - (bottom_treshold + 4)) * 5;
+ if (bf->enemy_nearby_ && bf->area_military_capacity_ > bottom_treshold + 4) {
+ prio -= (bf->area_military_capacity_ - (bottom_treshold + 4)) * 5;
}
} else if (bo.type == BuildingObserver::WAREHOUSE) {
@@ -1780,7 +1867,7 @@
// take care about and enemies
if (bf->enemy_nearby_) {
- prio /= 2;
+ prio /= 4;
}
if (bf->unowned_land_nearby_ && !bo.is_port_) {
@@ -1789,24 +1876,43 @@
} else if (bo.type == BuildingObserver::TRAININGSITE) {
- if (virtual_mines < 5) {
- continue;
- }
-
// exclude spots on border
if (bf->near_border_) {
continue;
}
- if (virtual_mines < 3) {
- continue;
- }
-
- // build after 20 production sites and then after each 50 production site
- if (static_cast<int32_t>((productionsites.size() + 40) / 60) > bo.total_count() &&
- bo.cnt_under_construction_ == 0) {
- prio = 4 + kDefaultPrioBoost;
- }
+ // it is a bit difficult to get a new trainer.....
+ if (ts_without_trainers_) {
+ continue;
+ }
+
+ // target is only one for both types
+ if ((ts_type1_const_count_ + ts_type2_const_count_) > 0) {
+ continue;
+ }
+
+ // we build one training site for 100 militarysites
+ if (bo.ts_type_ == 1 &&
+ militarysites.size() / 100 < static_cast<int32_t>(ts_type1_count_)) {
+ continue;
+ }
+ // we build one training site for 100 militarysites
+ if (bo.ts_type_ == 2 &&
+ militarysites.size() / 100 < static_cast<int32_t>(ts_type2_count_)) {
+ continue;
+ }
+
+ // for type1 we need 15 productionsties
+ if (bo.ts_type_ == 1 && productionsites.size() < 15) {
+ continue;
+ }
+
+ // for type2 we need 4 mines
+ if (bo.ts_type_ == 2 && virtual_mines < 4) {
+ continue;
+ }
+
+ prio = 4 + kDefaultPrioBoost;
// take care about borders and enemies
if (bf->enemy_nearby_) {
@@ -1854,8 +1960,9 @@
// then try all mines_ - as soon as basic economy is build up.
if (gametime > next_mine_construction_due_) {
- update_all_mineable_fields(gametime);
- next_mine_construction_due_ = gametime + kIdleMineUpdateInterval;
+ // not done here
+ // update_all_mineable_fields(gametime);
+ next_mine_construction_due_ = gametime + kNewMineConstInterval;
if (!mineable_fields.empty()) {
@@ -1885,22 +1992,33 @@
check_ware_necessity(
bo, &output_is_needed, &max_preciousness, &max_needed_preciousness);
- if (!output_is_needed && bo.total_count() > 0) {
+ if (!output_is_needed && (bo.total_count() - bo.unconnected_) > 0) {
continue;
}
// if current one(s) are performing badly
- if (bo.total_count() >= 1 && bo.current_stats_ < 50) {
+ if ((bo.total_count() - bo.unconnected_) >= 1 && bo.current_stats_ < 50) {
continue;
}
// this is penalty if there are existing mines too close
// it is treated as multiplicator for count of near mines
uint32_t nearness_penalty = 0;
- if ((bo.cnt_built_ + bo.cnt_under_construction_) == 0) {
+ if ((mines_per_type[bo.mines_].in_construction + mines_per_type[bo.mines_].finished) ==
+ 0) {
nearness_penalty = 0;
} else {
- nearness_penalty = 10;
+ nearness_penalty = 30;
+ }
+
+ // bonus score to prefer if too few mines
+ uint32_t bonus_score = 0;
+ if ((mines_per_type[bo.mines_].in_construction + mines_per_type[bo.mines_].finished) ==
+ 0) {
+ bonus_score = 15;
+ } else if ((mines_per_type[bo.mines_].in_construction +
+ mines_per_type[bo.mines_].finished) == 1) {
+ bonus_score = 5;
}
// iterating over fields
@@ -1908,26 +2026,46 @@
j != mineable_fields.end();
++j) {
- if ((*j)->coords.field->get_resources() != bo.mines_) {
- continue;
- }
-
- int32_t prio = (*j)->coords.field->get_resources_amount();
+ MineableField* const mf = *j;
+
+ if (mf->field_info_expiration_ <= gametime) {
+ continue;
+ }
+
+ if (mf->coords.field->get_resources() != bo.mines_) {
+ continue;
+ }
+
+ int32_t prio = 0;
+ MapRegion<Area<FCoords>> mr(map, Area<FCoords>(mf->coords, 2));
+ do {
+ if (bo.mines_ == mr.location().field->get_resources()) {
+ prio += mr.location().field->get_resources_amount();
+ }
+ } while (mr.advance(map));
+
+ prio /= 10;
+
+ // Only build mines_ on locations where some material can be mined
+ if (prio < 1) {
+ continue;
+ }
// applying nearnes penalty
- prio = prio - (*j)->mines_nearby_ * nearness_penalty;
-
- // Only build mines_ on locations where some material can be mined
- if (prio < 2) {
- continue;
- }
+ prio -= mf->mines_nearby_ * nearness_penalty;
+
+ // applying bonus score
+ prio += bonus_score;
+
+ // applying max needed
+ prio += max_needed_preciousness * 3;
// prefer mines in the middle of mine fields of the
// same type, so we add a small bonus here
// depending on count of same mines nearby,
// though this does not reflects how many resources
// are (left) in nearby mines
- prio += (*j)->same_mine_fields_nearby_ / 3;
+ prio += mf->same_mine_fields_nearby_;
// Continue if field is blocked at the moment
bool blocked = false;
@@ -1946,13 +2084,12 @@
}
// Prefer road side fields
- prio += (*j)->preferred_ ? 1 : 0;
+ prio += mf->preferred_ ? 1 : 0;
if (prio > proposed_priority) {
- // proposed_building = bo.id;
best_building = &bo;
proposed_priority = prio;
- proposed_coords = (*j)->coords;
+ proposed_coords = mf->coords;
mine = true;
}
} // end of evaluation of field
@@ -2257,11 +2394,11 @@
if (occupied_military_) {
eco->dismantle_grace_time_ =
- (gametime + 20 * 60 * 1000) + (eco->flags.size() * 20 * 1000);
+ (gametime + 90 * 60 * 1000) + (eco->flags.size() * 20 * 1000);
} else { // for other normal buildings
eco->dismantle_grace_time_ =
- gametime + (5 * 60 * 1000) + (eco->flags.size() * 20 * 1000);
+ gametime + (45 * 60 * 1000) + (eco->flags.size() * 20 * 1000);
}
}
@@ -2547,6 +2684,9 @@
site.unoccupied_till_ = game().get_gametime();
}
+ // is it connected to wh at all?
+ const bool connected_to_wh = !site.site->get_economy()->warehouses().empty();
+
// do not dismantle or upgrade the same type of building too soon - to give some time to update
// statistics
if (site.bo->last_dismantle_time_ > game().get_gametime() - 30 * 1000) {
@@ -2571,7 +2711,8 @@
// b) if there are two buildings
// statistics percents are decisive
const BuildingIndex enhancement = site.site->descr().enhancement();
- if (enhancement != INVALID_INDEX && (site.bo->cnt_built_ - site.bo->unoccupied_) > 1) {
+ if (connected_to_wh && enhancement != INVALID_INDEX &&
+ (site.bo->cnt_built_ - site.bo->unoccupied_) > 1) {
BuildingIndex enbld = INVALID_INDEX; // to get rid of this
@@ -2664,7 +2805,11 @@
// so finally we dismantle the lumberjac
site.bo->last_dismantle_time_ = game().get_gametime();
flags_to_be_removed.push_back(site.site->base_flag().get_position());
- game().send_player_dismantle(*site.site);
+ if (connected_to_wh) {
+ game().send_player_dismantle(*site.site);
+ } else {
+ game().send_player_bulldoze(*site.site);
+ }
return true;
}
@@ -2675,7 +2820,11 @@
site.site->get_statistics_percent() == 0) {
site.bo->last_dismantle_time_ = gametime;
flags_to_be_removed.push_back(site.site->base_flag().get_position());
- game().send_player_dismantle(*site.site);
+ if (connected_to_wh) {
+ game().send_player_dismantle(*site.site);
+ } else {
+ game().send_player_bulldoze(*site.site);
+ }
return true;
}
@@ -2695,7 +2844,11 @@
if (site.bo->stocklevel_ > 250 + productionsites.size() * 5) { // dismantle
site.bo->last_dismantle_time_ = game().get_gametime();
flags_to_be_removed.push_back(site.site->base_flag().get_position());
- game().send_player_dismantle(*site.site);
+ if (connected_to_wh) {
+ game().send_player_dismantle(*site.site);
+ } else {
+ game().send_player_bulldoze(*site.site);
+ }
return true;
}
@@ -2714,7 +2867,11 @@
// the destruction of the flag avoids that defaultAI will have too many
// unused roads - if needed the road will be rebuild directly.
flags_to_be_removed.push_back(site.site->base_flag().get_position());
- game().send_player_dismantle(*site.site);
+ if (connected_to_wh) {
+ game().send_player_dismantle(*site.site);
+ } else {
+ game().send_player_bulldoze(*site.site);
+ }
return true;
}
@@ -2723,7 +2880,11 @@
// it is possible that there are stones but quary is not able to mine them
site.bo->last_dismantle_time_ = game().get_gametime();
flags_to_be_removed.push_back(site.site->base_flag().get_position());
- game().send_player_dismantle(*site.site);
+ if (connected_to_wh) {
+ game().send_player_dismantle(*site.site);
+ } else {
+ game().send_player_bulldoze(*site.site);
+ }
return true;
}
@@ -2743,7 +2904,7 @@
site.bo->space_consumer_ && !site.bo->plants_trees_) {
// if we have more buildings then target
- if (site.bo->cnt_built_ > site.bo->cnt_target_) {
+ if ((site.bo->cnt_built_ - site.bo->unconnected_) > site.bo->cnt_target_) {
if (site.bo->stocklevel_time < game().get_gametime() - 5 * 1000) {
site.bo->stocklevel_ = get_stocklevel(*site.bo);
site.bo->stocklevel_time = game().get_gametime();
@@ -2753,7 +2914,11 @@
site.bo->stocklevel_ > 100) { // production stats == 0%
site.bo->last_dismantle_time_ = game().get_gametime();
flags_to_be_removed.push_back(site.site->base_flag().get_position());
- game().send_player_dismantle(*site.site);
+ if (connected_to_wh) {
+ game().send_player_dismantle(*site.site);
+ } else {
+ game().send_player_bulldoze(*site.site);
+ }
return true;
}
}
@@ -2762,7 +2927,11 @@
if (site.site->get_statistics_percent() <= 10 && site.bo->cnt_built_ > 1) {
flags_to_be_removed.push_back(site.site->base_flag().get_position());
- game().send_player_dismantle(*site.site);
+ if (connected_to_wh) {
+ game().send_player_dismantle(*site.site);
+ } else {
+ game().send_player_bulldoze(*site.site);
+ }
return true;
}
@@ -2778,7 +2947,11 @@
site.bo->last_dismantle_time_ = game().get_gametime();
flags_to_be_removed.push_back(site.site->base_flag().get_position());
- game().send_player_dismantle(*site.site);
+ if (connected_to_wh) {
+ game().send_player_dismantle(*site.site);
+ } else {
+ game().send_player_bulldoze(*site.site);
+ }
return true;
}
@@ -2792,7 +2965,11 @@
site.bo->last_dismantle_time_ = game().get_gametime();
flags_to_be_removed.push_back(site.site->base_flag().get_position());
- game().send_player_dismantle(*site.site);
+ if (connected_to_wh) {
+ game().send_player_dismantle(*site.site);
+ } else {
+ game().send_player_bulldoze(*site.site);
+ }
return true;
}
@@ -2813,16 +2990,29 @@
site.bo->last_dismantle_time_ = game().get_gametime();
flags_to_be_removed.push_back(site.site->base_flag().get_position());
- game().send_player_dismantle(*site.site);
+ if (connected_to_wh) {
+ game().send_player_dismantle(*site.site);
+ } else {
+ game().send_player_bulldoze(*site.site);
+ }
return true;
}
if (score > 120 && !site.site->is_stopped()) {
game().send_player_start_stop_building(*site.site);
+ return true;
}
+ const uint32_t trees_in_vicinity =
+ map.find_immovables(Area<FCoords>(map.get_fcoords(site.site->get_position()), 5),
+ nullptr,
+ FindImmovableAttribute(MapObjectDescr::get_attribute_id("tree")));
- if (score < 80 && site.site->is_stopped()) {
+ if (trees_in_vicinity > 25) {
+ if (!site.site->is_stopped()) {
+ game().send_player_start_stop_building(*site.site);
+ }
+ } else if (score < 80 && site.site->is_stopped()) {
game().send_player_start_stop_building(*site.site);
}
@@ -3043,11 +3233,17 @@
// Get link to productionsite that should be checked
ProductionSiteObserver& site = mines_.front();
+ const bool connected_to_wh = !site.site->get_economy()->warehouses().empty();
+
// first get rid of mines that are missing workers for some time (6 minutes),
// released worker (if any) can be usefull elsewhere !
if (site.built_time_ + 6 * 60 * 1000 < gametime && !site.site->can_start_working()) {
flags_to_be_removed.push_back(site.site->base_flag().get_position());
- game().send_player_dismantle(*site.site);
+ if (connected_to_wh) {
+ game().send_player_dismantle(*site.site);
+ } else {
+ game().send_player_bulldoze(*site.site);
+ }
return true;
}
@@ -3059,7 +3255,11 @@
// dismantling when the failed count is too high
if (site.no_resources_count > 12) {
flags_to_be_removed.push_back(site.site->base_flag().get_position());
- game().send_player_dismantle(*site.site);
+ if (connected_to_wh) {
+ game().send_player_dismantle(*site.site);
+ } else {
+ game().send_player_bulldoze(*site.site);
+ }
site.bo->construction_decision_time_ = gametime;
return true;
}
@@ -3084,6 +3284,11 @@
return false;
}
+ if (!connected_to_wh) {
+ // no enhancement possible
+ return false;
+ }
+
bool changed = false;
if (player_->is_building_type_allowed(enhancement)) {
// first exclude possibility there are enhancements in construction or unoccupied_
@@ -3212,6 +3417,18 @@
return count;
}
+// this just counts free positions in military and training sites
+void DefaultAI::count_military_vacant_positions() {
+ // counting vacant positions
+ vacant_mil_positions_ = 0;
+ for (TrainingSiteObserver tso : trainingsites) {
+ vacant_mil_positions_ += tso.site->soldier_capacity() - tso.site->stationed_soldiers().size();
+ }
+ for (MilitarySiteObserver mso : militarysites) {
+ vacant_mil_positions_ += mso.site->soldier_capacity() - mso.site->stationed_soldiers().size();
+ }
+}
+
// this function only manipulates with trainingsites' inputs priority
// decreases it when too many unoccupied military buildings
bool DefaultAI::check_trainingsites(uint32_t gametime) {
@@ -3221,22 +3438,38 @@
if (!trainingsites.empty()) {
taskDue[ScheduleTasks::kCheckTrainingsites] = gametime + kTrainingSitesCheckInterval;
} else {
- taskDue[ScheduleTasks::kCheckTrainingsites] = gametime + 5 * kTrainingSitesCheckInterval;
- }
-
- uint8_t new_priority = DEFAULT_PRIORITY;
- if (unstationed_milit_buildings_ > 2) {
- new_priority = LOW_PRIORITY;
- } else {
- new_priority = DEFAULT_PRIORITY;
- }
+ taskDue[ScheduleTasks::kCheckTrainingsites] = gametime + 2 * kTrainingSitesCheckInterval;
+ return false;
+ }
+
+ TrainingSite* ts = trainingsites.front().site;
+ TrainingSiteObserver& tso = trainingsites.front();
+
+ const BuildingIndex enhancement = ts->descr().enhancement();
+
+ if (enhancement != INVALID_INDEX && ts_without_trainers_ == 0 && mines_.size() > 3 &&
+ (ts_type1_const_count_ + ts_type2_const_count_) == 0 && ts_type2_count_ > 0) {
+
+ if (player_->is_building_type_allowed(enhancement)) {
+ game().send_player_enhance_building(*tso.site, enhancement);
+ }
+ }
+
+ trainingsites.push_back(trainingsites.front());
+ trainingsites.pop_front();
+
+ // changing capacity
+ if (tso.site->soldier_capacity() != 2) {
+ game().send_player_change_soldier_capacity(*ts, 2 - tso.site->soldier_capacity());
+ }
+
+ ts_without_trainers_ = 0; // zeroing
for (std::list<TrainingSiteObserver>::iterator site = trainingsites.begin();
site != trainingsites.end();
++site) {
- for (uint32_t k = 0; k < site->bo->inputs_.size(); ++k) {
- game().send_player_set_ware_priority(
- *site->site, wwWARE, site->bo->inputs_.at(k), new_priority);
+ if (!site->site->can_start_working()) {
+ ts_without_trainers_ += 1;
}
}
return true;
@@ -3310,14 +3543,18 @@
BuildableField bf(f);
update_buildable_field(bf, vision, true);
const int32_t size_penalty = ms->get_size() - 1;
+ FindNodeAllyOwned find_ally(player_, game(), player_number());
+ const int32_t allyOwnedFields =
+ map.find_fields(Area<FCoords>(f, vision), nullptr, find_ally);
int16_t score = 0;
- score += (bf.military_capacity_ > 5);
- score += (bf.military_presence_ > 3);
+ score += (bf.area_military_capacity_ > 6);
+ score += (bf.area_military_capacity_ > 18);
+ score += (bf.area_military_presence_ > 4);
score += (bf.military_loneliness_ < 180);
- score += (bf.military_stationed_ > (2 + size_penalty));
- score -= (ms->soldier_capacity() * 2 > static_cast<uint32_t>(bf.military_capacity_));
- score += (bf.unowned_land_nearby_ < 10);
+ score += (bf.military_stationed_ > 2);
+ score -= size_penalty;
+ score += ((bf.unowned_land_nearby_ + allyOwnedFields) < 10);
if (score >= 4) {
if (ms->get_playercaps() & Widelands::Building::PCap_Dismantle) {
@@ -3340,7 +3577,6 @@
// yes enemy is nearby, but still we must distinguish whether
// he is accessible (over the land)
-
if (other_player_accessible(
vision + 4, &unused1, &unused2, ms->get_position(), WalkSearch::kOtherPlayers)) {
@@ -3502,7 +3738,10 @@
}
}
- throw wexception("Help: I do not know what to do with a %s", name);
+ throw wexception("Help: I (player %d / tribe %s) do not know what to do with a %s",
+ player_number(),
+ tribe_->name().c_str(),
+ name);
}
// this is called whenever we gain ownership of a PlayerImmovable
@@ -3804,6 +4043,7 @@
// this is called whenever we gain a new building
void DefaultAI::gain_building(Building& b) {
+
BuildingObserver& bo = get_building_observer(b.descr().name().c_str());
if (bo.type == BuildingObserver::CONSTRUCTIONSITE) {
@@ -3817,6 +4057,17 @@
if (target_bo.type == BuildingObserver::MILITARYSITE) {
++num_milit_constructionsites;
}
+ if (target_bo.type == BuildingObserver::MINE) {
+ mines_per_type[target_bo.mines_].in_construction += 1;
+ }
+ if (target_bo.type == BuildingObserver::TRAININGSITE) {
+ if (target_bo.ts_type_ == 1) {
+ ts_type1_const_count_ += 1;
+ }
+ if (target_bo.ts_type_ == 2) {
+ ts_type2_const_count_ += 1;
+ }
+ }
// Let defaultAI try to directly connect the constructionsite
taskDue[ScheduleTasks::kRoadCheck] = game().get_gametime();
@@ -3852,6 +4103,9 @@
for (uint32_t i = 0; i < bo.inputs_.size(); ++i)
++wares.at(bo.inputs_.at(i)).consumers_;
+
+ mines_per_type[bo.mines_].finished += 1;
+
} else if (bo.type == BuildingObserver::MILITARYSITE) {
militarysites.push_back(MilitarySiteObserver());
militarysites.back().site = &dynamic_cast<MilitarySite&>(b);
@@ -3860,9 +4114,16 @@
militarysites.back().enemies_nearby_ = true;
} else if (bo.type == BuildingObserver::TRAININGSITE) {
+ ts_without_trainers_ += 1;
trainingsites.push_back(TrainingSiteObserver());
trainingsites.back().site = &dynamic_cast<TrainingSite&>(b);
trainingsites.back().bo = &bo;
+ if (bo.ts_type_ == 1) {
+ ts_type1_count_ += 1;
+ }
+ if (bo.ts_type_ == 2) {
+ ts_type2_count_ += 1;
+ }
} else if (bo.type == BuildingObserver::WAREHOUSE) {
++numof_warehouses_;
@@ -3888,6 +4149,7 @@
// this is called whenever we lose a building
void DefaultAI::lose_building(const Building& b) {
+
BuildingObserver& bo = get_building_observer(b.descr().name().c_str());
if (bo.type == BuildingObserver::CONSTRUCTIONSITE) {
@@ -3901,6 +4163,17 @@
if (target_bo.type == BuildingObserver::MILITARYSITE) {
--num_milit_constructionsites;
}
+ if (target_bo.type == BuildingObserver::MINE) {
+ mines_per_type[target_bo.mines_].in_construction -= 1;
+ }
+ if (target_bo.type == BuildingObserver::TRAININGSITE) {
+ if (target_bo.ts_type_ == 1) {
+ ts_type1_const_count_ -= 1;
+ }
+ if (target_bo.ts_type_ == 2) {
+ ts_type2_const_count_ -= 1;
+ }
+ }
} else {
--bo.cnt_built_;
@@ -3938,6 +4211,9 @@
for (uint32_t i = 0; i < bo.inputs_.size(); ++i) {
--wares.at(bo.inputs_.at(i)).consumers_;
}
+
+ mines_per_type[bo.mines_].finished -= 1;
+
} else if (bo.type == BuildingObserver::MILITARYSITE) {
for (std::list<MilitarySiteObserver>::iterator i = militarysites.begin();
@@ -3955,6 +4231,12 @@
++i) {
if (i->site == &b) {
trainingsites.erase(i);
+ if (bo.ts_type_ == 1) {
+ ts_type1_count_ -= 1;
+ }
+ if (bo.ts_type_ == 2) {
+ ts_type2_count_ -= 1;
+ }
break;
}
}
@@ -3999,49 +4281,21 @@
return supplied == bo.inputs_.size();
}
-/**
- * The defaultAi "considers" via this function whether to attack an
- * enemy, if opposing military buildings are in sight. In case of an attack it
- * sends all available forces.
- *
- * \returns true, if attack was started.
- */
-
-bool DefaultAI::consider_attack(int32_t const gametime) {
-
- // we assume that we are not attacking so we extend waitperiod
- // in case of attack the variable will be decreased below
- // this is intended to save some CPU and add randomness in attacking
- // and also differentiate according to type
- next_attack_waittime_ += gametime % 30;
- if (next_attack_waittime_ > 600 && type_ == DEFENSIVE) {
- next_attack_waittime_ = 20;
- }
- if (next_attack_waittime_ > 450 && type_ == NORMAL) {
- next_attack_waittime_ = 20;
- }
- if (next_attack_waittime_ > 300 && type_ == AGGRESSIVE) {
- next_attack_waittime_ = 20;
- }
-
- // Only useable, if it owns at least one militarysite
- if (militarysites.empty()) {
- taskDue[ScheduleTasks::kConsiderAttack] = next_attack_waittime_ * 1000 + gametime;
- return false;
- }
-
- // First we iterate over all players and define which ones (if any)
- // are attackable (comparing overal strength)
- // counting players in game
- uint32_t plr_in_game = 0;
+bool DefaultAI::check_enemy_sites(uint32_t const gametime) {
+
+ Map& map = game().map();
+
+ // define which players are attackable
std::vector<bool> player_attackable;
- PlayerNumber const nr_players = game().map().get_nrplayers();
+ PlayerNumber const nr_players = map.get_nrplayers();
player_attackable.resize(nr_players);
- bool any_attackable = false;
+ uint32_t plr_in_game = 0;
uint16_t const pn = player_number();
- std::unordered_set<uint32_t> irrelevant_immovables;
-
- std::vector<ImmovableFound> target_buildings;
+
+ iterate_players_existing_novar(p, nr_players, game())++ plr_in_game;
+
+ // receiving games statistics and parsing it (reading latest entry)
+ const Game::GeneralStatsVector& genstats = game().get_general_statistics();
// defining treshold ratio of own_strenght/enemy's_strength
uint32_t treshold_ratio = 100;
@@ -4052,37 +4306,10 @@
treshold_ratio = 120;
}
- iterate_players_existing_novar(p, nr_players, game())++ plr_in_game;
-
- // receiving games statistics and parsing it (reading latest entry)
- const Game::GeneralStatsVector& genstats = game().get_general_statistics();
-
- // first we try to prevent exhaustion of military forces (soldiers)
- // via excessive attacking
- // before building an economy with mines.
- // 'Margin' is an difference between count of actual soldiers and
- // military sites to be manned.
- // If we have no mines yet, we need to preserve some soldiers for further
- // expansion (if enemy allows this)
- // TODO(sirver): this next line is completely unreadable and maybe even wrong given the
- // precedence of ?: and +. Replace through some if/else.
- int32_t needed_margin = (mines_.size() < 6) ?
- ((6 - mines_.size()) * 3) :
- 0 + 2 + ((type_ == NORMAL) ? 4 : 0 + ((type_ == DEFENSIVE) ? 8 : 0));
- const int32_t current_margin =
- genstats[pn - 1].miltary_strength.back() - militarysites.size() - num_milit_constructionsites;
-
- if (current_margin < needed_margin) { // no attacking!
- last_attack_target_.x = std::numeric_limits<uint16_t>::max();
- last_attack_target_.y = std::numeric_limits<uint16_t>::max();
- taskDue[ScheduleTasks::kConsiderAttack] = next_attack_waittime_ * 1000 + gametime;
- return false;
- }
-
// now we test all players which one are 'attackable'
for (uint8_t j = 1; j <= plr_in_game; ++j) {
- if (pn == j) {
- player_attackable.at(j - 1) = false;
+ if (pn == j) { // its me
+ player_attackable[j - 1] = false;
continue;
}
@@ -4095,12 +4322,10 @@
// Avoid division by zero
} else if (genstats.at(j - 1).miltary_strength.back() == 0) {
player_attackable.at(j - 1) = true;
- any_attackable = true;
// Check threshold
} else if ((genstats.at(pn - 1).miltary_strength.back() * 100 /
genstats.at(j - 1).miltary_strength.back()) > treshold_ratio) {
player_attackable.at(j - 1) = true;
- any_attackable = true;
} else {
player_attackable.at(j - 1) = false;
}
@@ -4112,147 +4337,229 @@
}
}
- // if we cannot attack anybody, terminating...
- if (!any_attackable) {
- taskDue[ScheduleTasks::kConsiderAttack] = next_attack_waittime_ * 1000 + gametime;
- last_attack_target_.x = std::numeric_limits<uint16_t>::max();
- last_attack_target_.y = std::numeric_limits<uint16_t>::max();
- return false;
- }
-
- // the logic of attacking is to pick n own military buildings - random ones
- // and test the vicinity for attackable buildings
- // candidates are put into target_buildings vector for later processing
- const uint16_t test_every = 4;
- Map& map = game().map();
- MilitarySite* best_ms_target = nullptr;
- Warehouse* best_wh_target = nullptr;
- int32_t best_ms_score = 0;
- int32_t best_wh_score = 0;
- const int8_t minimal_difference = 2;
-
- for (uint32_t position = gametime % test_every; position < militarysites.size();
- position += test_every) {
-
- std::list<MilitarySiteObserver>::iterator mso = militarysites.begin();
- std::advance(mso, position);
-
- MilitarySite* ms = mso->site;
-
- if (!mso->enemies_nearby_) {
- continue;
- }
-
+ // first we scan vicitnity of couple of militarysites to get new enemy sites
+ // militarysites rotate
+ int32_t i = 0;
+ for (MilitarySiteObserver mso : militarysites) {
+ i += 1;
+ if (i % 4 == 0)
+ continue;
+ if (i > 20)
+ continue;
+
+ MilitarySite* ms = mso.site;
uint32_t const vision = ms->descr().vision_range();
FCoords f = map.get_fcoords(ms->get_position());
// get list of immovable around this our military site
std::vector<ImmovableFound> immovables;
- map.find_immovables(Area<FCoords>(f, vision + 3), &immovables, FindImmovableAttackable());
+ map.find_immovables(Area<FCoords>(f, (vision + 3 < 13) ? 13 : vision + 3),
+ &immovables,
+ FindImmovableAttackable());
for (uint32_t j = 0; j < immovables.size(); ++j) {
-
- // skip if in irrelevant_immovables
- const uint32_t hash = coords_hash(immovables.at(j).coords);
- if (irrelevant_immovables.count(hash) == 0) {
- irrelevant_immovables.insert(hash);
- }
-
- // maybe these are not good candidates to attack
- if (upcast(MilitarySite, bld, immovables.at(j).object)) {
-
- if (!player_attackable[bld->owner().player_number() - 1]) {
- continue;
- }
-
- // in case this is the same building as previously attacked
- if (last_attack_target_ == bld->get_position()) {
- continue;
- }
-
+ if (upcast(MilitarySite const, bld, immovables.at(j).object)) {
+ if (player_->is_hostile(bld->owner())) {
+ if (enemy_sites.count(coords_hash(bld->get_position())) == 0) {
+ enemy_sites[coords_hash(bld->get_position())] = EnemySiteObserver();
+ }
+ }
+ }
+ if (upcast(Warehouse const, wh, immovables.at(j).object)) {
+ if (player_->is_hostile(wh->owner())) {
+ if (enemy_sites.count(coords_hash(wh->get_position())) == 0) {
+ enemy_sites[coords_hash(wh->get_position())] = EnemySiteObserver();
+ }
+ }
+ }
+ }
+ }
+
+ // now we update some of them
+ uint32_t best_target = std::numeric_limits<uint32_t>::max();
+ uint8_t best_score = 0;
+ uint32_t count = 0;
+
+ for (std::map<uint32_t, EnemySiteObserver>::iterator site = enemy_sites.begin();
+ site != enemy_sites.end();
+ ++site) {
+
+ // we test max 12 sites and prefer ones tested more then 1 min ago
+ if (((site->second.last_tested + (enemysites_check_delay_ * 1000)) > gametime && count > 4) ||
+ count > 12) {
+ continue;
+ }
+ count += 1;
+
+ site->second.last_tested = gametime;
+ uint8_t defenders = 0;
+ bool is_warehouse = false;
+ bool is_attackable = false;
+ uint16_t onwer_number = 100;
+
+ // testing if we can attack the building - result is a flag
+ // if we dont get a flag, we remove the building from observers list
+ FCoords f = map.get_fcoords(coords_unhash(site->first));
+ uint32_t site_to_be_removed = std::numeric_limits<uint32_t>::max();
+ Flag* flag = nullptr;
+ if (upcast(MilitarySite, bld, f.field->get_immovable())) {
+ if (player_->is_hostile(bld->owner())) {
+ defenders = bld->present_soldiers().size();
+ flag = &bld->base_flag();
if (bld->can_attack()) {
-
- int32_t attack_soldiers = player_->find_attack_soldiers(bld->base_flag());
- if (attack_soldiers < 1) {
- continue;
- }
-
- const int32_t soldiers_difference =
- player_->find_attack_soldiers(bld->base_flag()) - bld->present_soldiers().size();
-
- if (soldiers_difference < minimal_difference)
- continue;
- if (soldiers_difference <= best_ms_score)
- continue;
-
- best_ms_target = bld;
- best_ms_score = soldiers_difference;
- continue;
- }
- } else if (upcast(Warehouse, wh, immovables.at(j).object)) {
- if (!player_->is_hostile(wh->owner())) {
- continue;
- }
-
- // in case this is the same building as previously attacked
- if (last_attack_target_ == wh->get_position()) {
- continue;
- }
-
- if (wh->can_attack()) {
- int32_t attack_soldiers = player_->find_attack_soldiers(wh->base_flag());
- if (attack_soldiers < 1) {
- continue;
- }
-
- const int32_t soldiers_difference = player_->find_attack_soldiers(wh->base_flag()) -
- wh->present_soldiers().size() +
- 3; //+3 is to boost attack here
-
- if (soldiers_difference < minimal_difference)
- continue;
- if (soldiers_difference <= best_wh_score)
- continue;
-
- best_wh_target = wh;
- best_wh_score = soldiers_difference;
- }
- }
- }
- }
-
- // we always try to attack warehouse first
- if (best_wh_target != nullptr && gametime % 2 == 0) {
- // attacking with all attack-ready soldiers
- int32_t attackers = player_->find_attack_soldiers(best_wh_target->base_flag());
-
- game().send_player_enemyflagaction(best_wh_target->base_flag(), pn, attackers);
- last_attack_target_ = best_wh_target->get_position();
- taskDue[ScheduleTasks::kConsiderAttack] = (gametime % 10 + 10) * 1000 + gametime;
- next_attack_waittime_ = 10;
- return true;
-
- } else if (best_ms_target != nullptr) {
-
- // attacking with defenders + 6 soldiers
- int32_t attackers = player_->find_attack_soldiers(best_ms_target->base_flag());
- const int32_t defenders = best_ms_target->present_soldiers().size();
- if (attackers > defenders + 10) { // we need to leave meaningful count of soldiers
- // for next attack
- attackers = defenders + 6;
- }
-
- game().send_player_enemyflagaction(best_ms_target->base_flag(), pn, attackers);
- last_attack_target_ = best_ms_target->get_position();
- taskDue[ScheduleTasks::kConsiderAttack] = (gametime % 10 + 10) * 1000 + gametime;
- next_attack_waittime_ = 10;
- return true;
+ is_attackable = true;
+ }
+ onwer_number = bld->owner().player_number();
+ }
+ }
+ if (upcast(Warehouse, Wh, f.field->get_immovable())) {
+ if (player_->is_hostile(Wh->owner())) {
+ defenders = Wh->present_soldiers().size();
+ flag = &Wh->base_flag();
+ is_warehouse = true;
+ if (Wh->can_attack()) {
+ is_attackable = true;
+ }
+ onwer_number = Wh->owner().player_number();
+ }
+ }
+
+ // if flag is defined it is a good taget
+ if (flag) {
+ // updating some info
+ // updating info on mines nearby if needed
+ if (site->second.mines_nearby == ExtendedBool::kUnset) {
+ FindNodeMineable find_mines_spots_nearby(game(), f.field->get_resources());
+ const int32_t minescount =
+ map.find_fields(Area<FCoords>(f, 6), nullptr, find_mines_spots_nearby);
+ if (minescount > 0) {
+ site->second.mines_nearby = ExtendedBool::kTrue;
+ } else {
+ site->second.mines_nearby = ExtendedBool::kFalse;
+ }
+ }
+
+ site->second.warehouse = is_warehouse;
+
+ // getting rid of default
+ if (site->second.last_time_attackable == std::numeric_limits<uint32_t>::max()) {
+ site->second.last_time_attackable = gametime;
+ }
+
+ // can we attack:
+ if (is_attackable) {
+ site->second.attack_soldiers = player_->find_attack_soldiers(*flag);
+ } else {
+ site->second.attack_soldiers = 0;
+ }
+
+ site->second.defenders = defenders;
+
+ if (site->second.attack_soldiers > 0) {
+ site->second.score = site->second.attack_soldiers - site->second.defenders / 2;
+
+ if (!is_warehouse)
+ site->second.score -= 1;
+
+ // here is some differentiation based on "character" of a player
+ if (type_ == NORMAL) {
+ site->second.score -= 1;
+ site->second.score -= vacant_mil_positions_ / 10;
+ } else if (type_ == DEFENSIVE) {
+ site->second.score -= 2;
+ site->second.score -= vacant_mil_positions_ / 5;
+ } else { //=AGRESSIVE
+ site->second.score -= vacant_mil_positions_ / 15;
+ }
+ if (site->second.mines_nearby == ExtendedBool::kFalse) {
+ site->second.score -= 1;
+ }
+ // we dont want to attack multiple players at the same time too eagerly
+ if (onwer_number != last_attacked_player_) {
+ site->second.score -= 3;
+ }
+ // if we dont have mines yet
+ if (mines_.size() <= 2) {
+ site->second.score -= 2;
+ }
+ // also we should have at least some training sites
+ if ((ts_type1_count_ + ts_type2_count_) == 0) {
+ site->second.score -= 2;
+ }
+ // treating no attack score
+ if (site->second.no_attack_counter < 0) {
+ site->second.score = 0;
+ site->second.no_attack_counter += 1;
+ }
+ } else {
+ site->second.score = 0;
+ } // or the score will remain 0
+
+ if (site->second.score > 0 && player_attackable[onwer_number - 1]) {
+ if (site->second.score > best_score) {
+ best_score = site->second.score;
+ best_target = site->first;
+ }
+ }
+
+ if (site->second.attack_soldiers > 0) {
+ site->second.last_time_attackable = gametime;
+ }
+ if (site->second.last_time_attackable + 20 * 60 * 1000 < gametime) {
+ site_to_be_removed = site->first;
+ }
+ } else { // we dont have a flag, let remove the site from out observer list
+ site_to_be_removed = site->first;
+ }
+
+ if (site_to_be_removed < std::numeric_limits<uint32_t>::max()) {
+ enemy_sites.erase(site_to_be_removed);
+ continue;
+ }
+ }
+
+ // modifying enemysites_check_delay_,this depends on the count
+ // of enemysites in observer
+ if (count >= 13 && enemysites_check_delay_ < 180) {
+ enemysites_check_delay_ += 3;
+ }
+ if (count < 10 && enemysites_check_delay_ > 45) {
+ enemysites_check_delay_ -= 2;
+ }
+
+ // if coordinates hash is not set
+ if (best_target == std::numeric_limits<uint32_t>::max()) {
+ return false;
+ }
+
+ // attacking
+ FCoords f = map.get_fcoords(coords_unhash(best_target));
+ // setting no attack counter here
+ // this gauranties that it will not be attacked in next 4
+ // turns
+ enemy_sites[best_target].no_attack_counter = -4;
+
+ Flag* flag = nullptr; // flag of a building to be attacked
+ if (upcast(MilitarySite, bld, f.field->get_immovable())) {
+ flag = &bld->base_flag();
+ } else if (upcast(Warehouse, Wh, f.field->get_immovable())) {
+ flag = &Wh->base_flag();
} else {
- taskDue[ScheduleTasks::kConsiderAttack] = next_attack_waittime_ * 1000 + gametime;
- last_attack_target_.x = std::numeric_limits<uint16_t>::max();
- last_attack_target_.y = std::numeric_limits<uint16_t>::max();
+ return false; // this should not happen
+ }
+
+ // how many attack soldiers we can send?
+ uint32_t attackers = player_->find_attack_soldiers(*flag);
+ // Just add some randomness
+ attackers -= gametime % 3;
+ if (attackers <= 0) {
return false;
}
+
+ game().send_player_enemyflagaction(*flag, player_number(), attackers);
+ last_attacked_player_ = flag->owner().player_number();
+
+ return true;
}
// This runs once in 15 minutes, and adjust wares targets based on number of
@@ -4286,7 +4593,7 @@
// run over dueTasks map and returns task with lower duetime
DefaultAI::ScheduleTasks DefaultAI::get_oldest_task(uint32_t const gametime) {
- uint32_t oldestTaskTime = gametime; // we are looking for jobs due before now
+ uint32_t oldestTaskTime = gametime; // we are looking for jobs due before now
ScheduleTasks DueTask = ScheduleTasks::kIdle; // default
taskDue[ScheduleTasks::kIdle] = gametime;
=== modified file 'src/ai/defaultai.h'
--- src/ai/defaultai.h 2015-03-05 20:57:07 +0000
+++ src/ai/defaultai.h 2015-03-23 21:37:01 +0000
@@ -9,6 +9,7 @@
* 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
+ * 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
@@ -82,9 +83,9 @@
enum class NewShip : uint8_t {kBuilt, kFoundOnLoad};
enum class ScheduleTasks : uint8_t {
kBbuildableFieldsCheck,
+ kMineableFieldsCheck,
kRoadCheck,
kUnbuildableFCheck,
- kConsiderAttack,
kCheckEconomies,
kProductionsitesStats,
kConstructBuilding,
@@ -96,7 +97,9 @@
kPrintStats,
kIdle,
kCheckMilitarysites,
- kCheckTrainingsites
+ kCheckTrainingsites,
+ kCountMilitaryVacant,
+ kCheckEnemySites
};
enum class MilitaryStrategy : uint8_t {
kNoNewMilitary,
@@ -161,7 +164,6 @@
int16_t* max_preciousness,
int16_t* max_needed_preciousness);
-
ScheduleTasks get_oldest_task(uint32_t);
bool construct_building(uint32_t);
@@ -195,12 +197,14 @@
bool check_militarysites(uint32_t);
bool marine_main_decisions(uint32_t);
bool check_ships(uint32_t);
+ bool check_enemy_sites(uint32_t);
void print_stats(uint32_t);
uint32_t get_stocklevel_by_hint(size_t);
uint32_t get_stocklevel(BuildingObserver&);
uint32_t get_warehoused_stock(Widelands::WareIndex wt);
uint32_t get_stocklevel(Widelands::WareIndex); // count all direct outputs_
void review_wares_targets(uint32_t);
+ void count_military_vacant_positions();
// sometimes scanning an area in radius gives inappropriate results, so this is to verify that
// other player is accessible
@@ -232,7 +236,7 @@
bool check_supply(const BuildingObserver&);
- bool consider_attack(int32_t);
+ // bool consider_attack(int32_t);
void print_land_stats();
@@ -252,6 +256,10 @@
uint32_t num_prod_constructionsites;
uint32_t num_ports;
+ uint16_t last_attacked_player_;
+ // check ms in this interval - will auto-adjust
+ uint32_t enemysites_check_delay_;
+
std::list<Widelands::FCoords> unusable_fields;
std::list<BuildableField*> buildable_fields;
std::list<BlockedField> blocked_fields;
@@ -268,6 +276,9 @@
std::list<TrainingSiteObserver> trainingsites;
std::list<ShipObserver> allships;
std::map<ScheduleTasks, uint32_t> taskDue;
+ std::map<uint32_t, EnemySiteObserver> enemy_sites;
+ // it will map mined material to observer
+ std::map<int32_t, MineTypesObserver> mines_per_type;
std::vector<WareObserver> wares;
@@ -284,10 +295,10 @@
// when territory is expanded for every candidate field benefits are calculated
// but need for water, space, mines can vary
// so if 255 = resource is needed, 0 = not needed
- uint8_t resource_necessity_territory_;
- uint8_t resource_necessity_mines_;
- uint8_t resource_necessity_stones_;
- uint8_t resource_necessity_water_;
+ int32_t resource_necessity_territory_;
+ int32_t resource_necessity_mines_;
+ int32_t resource_necessity_stones_; // NOCOM
+ int32_t resource_necessity_water_;
bool resource_necessity_water_needed_; // unless atlanteans
uint16_t unstationed_milit_buildings_; // counts empty military buildings (ones where no soldier
@@ -295,14 +306,21 @@
uint16_t military_under_constr_;
uint16_t military_last_dismantle_;
uint32_t military_last_build_; // sometimes expansions just stops, this is time of last military
- // building build
+ // building build
Widelands::Coords
- last_attack_target_; // flag to abuilding (position) that was attacked last time
+ last_attack_target_; // flag to abuilding (position) that was attacked last time
uint32_t next_attack_waittime_; // second till the next attack consideration
- bool seafaring_economy; // false by default, until first port space is found
+ bool seafaring_economy; // false by default, until first port space is found
uint32_t colony_scan_area_; // distance from a possible port that is scanned for owned territory
// it decreases with failed scans
int32_t spots_; // sum of buildable fields
+ int32_t vacant_mil_positions_; // sum of vacant positions in militarysites and training sites
+ //statistics for training sites per type
+ uint8_t ts_type1_count_;
+ uint8_t ts_type1_const_count_;
+ uint8_t ts_type2_count_;
+ uint8_t ts_type2_const_count_;
+ uint8_t ts_without_trainers_;
enum {kReprioritize, kStopShipyard, kStapShipyard};
=== modified file 'tribes/atlanteans/dungeon/conf'
--- tribes/atlanteans/dungeon/conf 2014-03-17 17:23:26 +0000
+++ tribes/atlanteans/dungeon/conf 2015-03-23 21:37:01 +0000
@@ -77,3 +77,6 @@
[idle]
pics=dungeon_i_??.png # ???
hotspot=47 48
+
+[aihints]
+ts_type=2
=== modified file 'tribes/atlanteans/labyrinth/conf'
--- tribes/atlanteans/labyrinth/conf 2014-10-07 20:06:46 +0000
+++ tribes/atlanteans/labyrinth/conf 2015-03-23 21:37:01 +0000
@@ -99,4 +99,4 @@
hotspot=80 88
[aihints]
-prohibited_till=2700
+ts_type=1
=== modified file 'tribes/barbarians/battlearena/conf'
--- tribes/barbarians/battlearena/conf 2014-07-29 09:27:08 +0000
+++ tribes/barbarians/battlearena/conf 2015-03-23 21:37:01 +0000
@@ -74,3 +74,6 @@
pics=battlearena_w_??.png # ???
hotspot=110 72
fps=10
+
+[aihints]
+ts_type=1
=== modified file 'tribes/barbarians/trainingcamp/conf'
--- tribes/barbarians/trainingcamp/conf 2014-09-24 21:06:00 +0000
+++ tribes/barbarians/trainingcamp/conf 2015-03-23 21:37:01 +0000
@@ -36,7 +36,7 @@
max_level=4
[aihints]
-prohibited_till=2700
+ts_type=2
[soldier hp]
min_level=0
=== modified file 'tribes/empire/arena/conf'
--- tribes/empire/arena/conf 2014-09-30 08:05:35 +0000
+++ tribes/empire/arena/conf 2015-03-23 21:37:01 +0000
@@ -54,3 +54,6 @@
[build]
pics=arena_b_??.png # ???
hotspot=82 83
+
+[aihints]
+ts_type=1
=== modified file 'tribes/empire/colosseum/conf'
--- tribes/empire/colosseum/conf 2014-09-30 08:05:35 +0000
+++ tribes/empire/colosseum/conf 2015-03-23 21:37:01 +0000
@@ -60,3 +60,6 @@
[idle]
pics=colosseum_i_??.png # ???
hotspot=81 106
+
+[aihints]
+ts_type=1
=== modified file 'tribes/empire/piggery/conf'
--- tribes/empire/piggery/conf 2014-08-02 19:55:23 +0000
+++ tribes/empire/piggery/conf 2015-03-23 21:37:01 +0000
@@ -2,6 +2,7 @@
output=meat
[aihints]
+forced_after=9000
[buildcost]
log=2
=== modified file 'tribes/empire/trainingcamp/conf'
--- tribes/empire/trainingcamp/conf 2014-08-02 19:55:23 +0000
+++ tribes/empire/trainingcamp/conf 2015-03-23 21:37:01 +0000
@@ -127,4 +127,4 @@
hotspot=82 105
[aihints]
-prohibited_till=2700
+ts_type=2
Follow ups