widelands-dev team mailing list archive
-
widelands-dev team
-
Mailing list archive
-
Message #06206
[Merge] lp:~widelands-dev/widelands/ai_small_tweaks into lp:widelands
TiborB has proposed merging lp:~widelands-dev/widelands/ai_small_tweaks into lp:widelands.
Requested reviews:
Widelands Developers (widelands-dev)
For more details, see:
https://code.launchpad.net/~widelands-dev/widelands/ai_small_tweaks/+merge/286407
Another bunch of changes to AI, partially code cleaning and partially functional improvements, areas affected
- blocked fields management
- player strength processing
- attacking reworked a bit
- road management modified
- other nits
--
Your team Widelands Developers is requested to review the proposed merge of lp:~widelands-dev/widelands/ai_small_tweaks into lp:widelands.
=== modified file 'src/ai/ai_help_structs.h'
--- src/ai/ai_help_structs.h 2016-01-20 20:12:00 +0000
+++ src/ai/ai_help_structs.h 2016-02-17 21:18:41 +0000
@@ -168,6 +168,20 @@
}
};
+// Unowned but walkable fields nearby
+struct FindNodeUnownedWalkable {
+ bool accept(const Map&, const FCoords& fc) const {
+
+ return (fc.field->nodecaps() & MOVECAPS_WALK) && (fc.field->get_owned_by() == 0);
+ }
+
+ Player* player_;
+ Game& game;
+
+ FindNodeUnownedWalkable(Player* p, Game& g) : player_(p), game(g) {
+ }
+};
+
// Looking only for mines-capable fields nearby
// of specific type
struct FindNodeMineable {
@@ -245,14 +259,6 @@
};
}
-struct BlockedField {
- Widelands::FCoords coords;
- uint32_t blocked_until_;
-
- BlockedField(Widelands::FCoords c, int32_t until) : coords(c), blocked_until_(until) {
- }
-};
-
struct BuildableField {
Widelands::FCoords coords;
@@ -453,6 +459,7 @@
uint32_t stocklevel_time; // time when stocklevel_ was last time recalculated
uint32_t last_dismantle_time_;
uint32_t construction_decision_time_;
+ uint32_t last_building_built_;
uint32_t unoccupied_count_;
@@ -593,4 +600,123 @@
};
+// List of blocked fields with block time, with some accompanying functions
+struct BlockedFields {
+ // <hash of field coordinates, time till blocked>
+ // of course hash of an blocked field is unique
+ std::map<uint32_t, uint32_t> BlockedFields;
+
+ void add(uint32_t hash, uint32_t till){
+ if (BlockedFields.count(hash) == 0) {
+ BlockedFields.insert(std::pair<uint32_t, uint32_t>(hash, till));
+ } else if (BlockedFields[hash] < till) {
+ BlockedFields[hash] = till;
+ }
+ //third possibility is that a field has been already blocked for longer time than 'till'
+ }
+
+ uint32_t count(){
+ return BlockedFields.size();
+ }
+
+ void remove_expired(uint32_t gametime) {
+ std::vector<uint32_t> fields_to_remove;
+ for (auto field: BlockedFields) {
+ if (field.second<gametime) {
+ fields_to_remove.push_back(field.first);
+ }
+ }
+ while (!fields_to_remove.empty()) {
+ BlockedFields.erase(fields_to_remove.back());
+ fields_to_remove.pop_back();
+ }
+ }
+
+ bool is_blocked(uint32_t hash){
+ if (BlockedFields.count(hash) == 0) {
+ return false;
+ } else {
+ return true;
+ }
+ }
+};
+
+// This is an struct that stores strength of players, info on teams and provides some outputs from these data
+struct PlayersStrengths {
+ struct PlayerStat {
+ uint8_t tn_;
+ uint32_t players_power_;
+
+ PlayerStat() {};
+ PlayerStat(uint8_t tc, uint32_t pp) : tn_(tc), players_power_(pp) {}
+ };
+
+ // This is core part of this struct
+ std::map<uint16_t, PlayerStat> all_stats;
+
+ // Number of team, sum of players' strength
+ std::map<uint8_t, uint32_t> team_powers;
+
+ // Inserting/updating data
+ void add(uint16_t pn, uint8_t tn, uint32_t pp){
+ if (all_stats.count(pn) == 0) {
+ all_stats.insert(std::pair<uint16_t, PlayerStat>(pn, PlayerStat(tn, pp)));
+ } else {
+ all_stats[pn].players_power_ = pp;
+ }
+ }
+
+ void recalculate_team_power() {
+ team_powers.clear();
+ for (auto& item: all_stats){
+ if (item.second.tn_ > 0) { //is a member of a team
+ if (team_powers.count(item.second.tn_) > 0){
+ team_powers[item.second.tn_] += item.second.players_power_;
+ } else {
+ team_powers[item.second.tn_] = item.second.players_power_;
+ }
+ }
+ }
+ }
+
+ // This is strength of player plus third of strength of other members of his team
+ uint32_t get_modified_player_power(uint16_t pn){
+ uint32_t result = 0;
+ uint8_t team = 0;
+ if (all_stats.count(pn) > 0) {
+ result = all_stats[pn].players_power_;
+ team = all_stats[pn].tn_;
+ };
+ if (team > 0 && team_powers.count(team) > 0) {
+ result = result + (team_powers[team] - result) / 3;
+ };
+ return result;
+ }
+
+ bool players_in_same_team(uint16_t pl1, uint16_t pl2){
+ if (all_stats.count(pl1) > 0 && all_stats.count(pl2) > 0 && pl1 != pl2) {
+ // team number 0 = no team
+ return all_stats[pl1].tn_ > 0 && all_stats[pl1].tn_ == all_stats[pl2].tn_;
+ } else {
+ return false;
+ }
+ }
+
+ bool strong_enough(uint16_t pl) {
+ if (all_stats.count(pl) == 0) {
+ return false;
+ }
+ uint32_t my_strength = all_stats[pl].players_power_;
+ uint32_t strongest_oponent_strength=0;
+ for (auto item : all_stats) {
+ if (!players_in_same_team(item.first, pl) && pl != item.first) {
+ if (get_modified_player_power(item.first) > strongest_oponent_strength) {
+ strongest_oponent_strength = get_modified_player_power(item.first);
+ }
+ }
+ }
+ return my_strength > strongest_oponent_strength + 50;
+ }
+};
+
#endif // end of include guard: WL_AI_AI_HELP_STRUCTS_H
=== modified file 'src/ai/defaultai.cc'
--- src/ai/defaultai.cc 2016-02-13 12:15:29 +0000
+++ src/ai/defaultai.cc 2016-02-17 21:18:41 +0000
@@ -75,6 +75,10 @@
constexpr uint32_t kNever = std::numeric_limits<uint32_t>::max();
constexpr uint32_t kNoExpedition = 0;
+// following two are used for roads management, for creating shortcuts and dismantling dispensable roads
+constexpr int32_t kSpotsTooLittle = 15;
+constexpr int32_t kSpotsEnough = 25;
+
// this is intended for map developers, by default should be off
constexpr bool kPrintStats = false;
@@ -510,6 +514,7 @@
// this is set to negative number, otherwise the AI would wait 25 sec
// after game start not building anything
bo.construction_decision_time_ = -60 * 60 * 1000;
+ bo.last_building_built_ = kNever;
bo.build_material_shortage_ = false;
bo.production_hint_ = kUncalculated;
bo.current_stats_ = 0;
@@ -831,9 +836,7 @@
MapRegion<Area<FCoords>> mr(
map, Area<FCoords>(map.get_fcoords(ps_obs.site->get_position()), 4));
do {
- BlockedField blocked2(
- map.get_fcoords(*(mr.location().field)), game().get_gametime() + 20 * 60 * 1000);
- blocked_fields.push_back(blocked2);
+ blocked_fields.add(coords_hash(mr.location()), game().get_gametime() + 20 * 60 * 1000);
} while (mr.advance(map));
}
}
@@ -1003,12 +1006,12 @@
// look if there is any unowned land nearby
Map& map = game().map();
const uint32_t gametime = game().get_gametime();
- FindNodeUnowned find_unowned(player_, game());
+ FindNodeUnownedWalkable find_unowned_walkable(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);
+ map.find_fields(Area<FCoords>(field.coords, range), nullptr, find_unowned_walkable);
FindNodeAllyOwned find_ally(player_, game(), player_number());
const int32_t AllyOwnedFields =
map.find_fields(Area<FCoords>(field.coords, 3), nullptr, find_ally);
@@ -1017,7 +1020,7 @@
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) {
+ if (map.find_fields(Area<FCoords>(field.coords, 4), nullptr, find_unowned_walkable) > 0) {
field.near_border_ = true;
}
}
@@ -1059,7 +1062,7 @@
// 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));
+ MapRegion<Area<FCoords>> mr(map, Area<FCoords>(field.coords, 4));
do {
if (port_reserved_coords.count(coords_hash(mr.location())) > 0) {
field.portspace_nearby_ = ExtendedBool::kTrue;
@@ -1442,7 +1445,6 @@
}
// Just used for easy checking whether a mine or something else was built.
bool mine = false;
- bool field_blocked = false;
uint32_t consumers_nearby_count = 0;
std::vector<int32_t> spots_avail;
spots_avail.resize(4);
@@ -1502,7 +1504,8 @@
bool needs_boost_economy = false;
if (highest_nonmil_prio_ > 10
&& has_enough_space
- && virtual_mines >= 5){
+ && virtual_mines >= 5 &&
+ !player_statistics.strong_enough(player_number())) {
needs_boost_economy = true;
}
@@ -1635,12 +1638,7 @@
Coords proposed_coords;
// Remove outdated fields from blocker list
- for (std::list<BlockedField>::iterator i = blocked_fields.begin(); i != blocked_fields.end();)
- if (i->blocked_until_ < gametime) {
- i = blocked_fields.erase(i);
- } else {
- ++i;
- }
+ blocked_fields.remove_expired(gametime);
// testing big military buildings, whether critical construction
// material is available (at least in amount of
@@ -1779,17 +1777,7 @@
}
// Continue if field is blocked at the moment
- field_blocked = false;
-
- for (std::list<BlockedField>::iterator j = blocked_fields.begin(); j != blocked_fields.end();
- ++j) {
- if (j->coords == bf->coords) {
- field_blocked = true;
- }
- }
-
- // continue;
- if (field_blocked) {
+ if (blocked_fields.is_blocked(coords_hash(bf->coords))) {
continue;
}
@@ -2129,7 +2117,7 @@
} // production sites done
else if (bo.type == BuildingObserver::MILITARYSITE) {
- if (!bf->unowned_land_nearby_) {
+ if (!(bf->unowned_land_nearby_ || bf->enemy_nearby_)) {
continue;
}
@@ -2183,7 +2171,20 @@
prio += bf->distant_water_ * resource_necessity_water_needed_ / 100;
prio += bf->military_loneliness_ / 10;
prio += bf->trees_nearby_ / 3;
- if (bf->portspace_nearby_ == ExtendedBool::kTrue) prio += 25;
+ if (bf->portspace_nearby_ == ExtendedBool::kTrue) {
+ if (num_ports == 0) {
+ prio += 100;
+ } else {
+ prio += 25;
+ }
+ }
+ //sometimes expansion is stalled and this is to help boost it
+ if (msites_in_constr() == 0 && vacant_mil_positions_ <= 2) {
+ prio += 10;
+ if (bf->enemy_nearby_){
+ prio += 20;
+ }
+ }
// additional score for bigger buildings
int32_t prio_for_size = bo.desc->get_size() - 1;
@@ -2443,18 +2444,7 @@
prio += mf->same_mine_fields_nearby_;
// Continue if field is blocked at the moment
- bool blocked = false;
-
- for (std::list<BlockedField>::iterator k = blocked_fields.begin();
- k != blocked_fields.end();
- ++k)
- if ((*j)->coords == k->coords) {
- blocked = true;
- break;
- }
-
- if (blocked) {
-
+ if (blocked_fields.is_blocked(coords_hash(mf->coords))) {
continue;
}
@@ -2490,9 +2480,7 @@
// send the command to construct a new building
game().send_player_build(player_number(), proposed_coords, best_building->id);
- BlockedField blocked(
- game().map().get_fcoords(proposed_coords), game().get_gametime() + 120000); // two minutes
- blocked_fields.push_back(blocked);
+ blocked_fields.add(coords_hash(proposed_coords), game().get_gametime() + 2 * 60 * 1000);
// resetting new_building_overdue_
best_building->new_building_overdue_ = 0;
@@ -2505,7 +2493,11 @@
uint32_t block_time = 0;
uint32_t block_area = 0;
if (best_building->space_consumer_) {
- block_time = 45 * 60 * 1000;
+ if (spots_ > kSpotsEnough) {
+ block_time = 45 * 60 * 1000;
+ } else {
+ block_time = 10 * 60 * 1000;
+ }
block_area = 3;
} else { // militray buildings for a very short time
block_time = 25 * 1000;
@@ -2514,9 +2506,7 @@
MapRegion<Area<FCoords>> mr(map, Area<FCoords>(map.get_fcoords(proposed_coords), block_area));
do {
- BlockedField blocked2(
- map.get_fcoords(*(mr.location().field)), game().get_gametime() + block_time);
- blocked_fields.push_back(blocked2);
+ blocked_fields.add(coords_hash(mr.location()), game().get_gametime() + block_time);
} while (mr.advance(map));
}
@@ -2538,12 +2528,11 @@
// improves current road system
bool DefaultAI::improve_roads(uint32_t gametime) {
- // first force a split on roads that are longer than 3 parts
- // with exemption when there is too few building spots
- if (spots_ > 20 && !roads.empty()) {
+ if (!roads.empty()) {
const Path& path = roads.front()->get_path();
- if (path.get_nsteps() > 3) {
+ // first force a split on roads that are longer than 3 parts
+ if (path.get_nsteps() > 3 && spots_ > kSpotsEnough) {
const Map& map = game().map();
CoordPath cp(map, path);
// try to split after two steps
@@ -2570,13 +2559,15 @@
// Unable to set a flag - perhaps the road was build stupid
game().send_player_bulldoze(*const_cast<Road*>(roads.front()));
+ return true;
}
roads.push_back(roads.front());
roads.pop_front();
// occasionaly we test if the road can be dismounted
- if (gametime % 5 == 0) {
+ // if there is shortage of spots we do it allways
+ if (gametime % 5 == 0 || spots_ < kSpotsTooLittle) {
const Road& road = *roads.front();
if (dispensable_road_test(*const_cast<Road*>(&road))) {
game().send_player_bulldoze(*const_cast<Road*>(&road));
@@ -2627,16 +2618,33 @@
}
}
- // if this is end flag (or sole building) or just randomly
- if (flag.nr_of_roads() <= 1 || gametime % 10 == 0) {
+ // is connected to a warehouse?
+ const bool needs_warehouse = flag.get_economy()->warehouses().empty();
+
+ // needs to be connected
+ if (flag.nr_of_roads() == 0 || needs_warehouse) {
create_shortcut_road(flag, 13, 22, gametime);
inhibit_road_building_ = gametime + 800;
+ } else if (flag.nr_of_roads() == 1 || gametime % 10 == 0) {
+ if (spots_ > kSpotsEnough) {
+ // This is normal situation
+ create_shortcut_road(flag, 13, 22, gametime);
+ inhibit_road_building_ = gametime + 800;
+ } else if (spots_ > kSpotsTooLittle) {
+ // We are shoft of spots so shortening must be significant
+ create_shortcut_road(flag, 13, 35, gametime);
+ inhibit_road_building_ = gametime + 800;
+ } else {
+ // We are very shoft of spots so shortening must be even bigger
+ create_shortcut_road(flag, 13, 50, gametime);
+ inhibit_road_building_ = gametime + 800;
+ }
// a warehouse with 3 or less roads
} else if (is_warehouse && flag.nr_of_roads() <= 3) {
create_shortcut_road(flag, 9, -1, gametime);
inhibit_road_building_ = gametime + 400;
// and when a flag is full with wares
- } else if (flag.current_wares() > 5) {
+ } else if (spots_ > kSpotsEnough && flag.current_wares() > 5) {
create_shortcut_road(flag, 9, -2, gametime);
inhibit_road_building_ = gametime + 400;
} else {
@@ -2655,7 +2663,9 @@
Flag& roadstartflag = road.get_flag(Road::FlagStart);
Flag& roadendflag = road.get_flag(Road::FlagEnd);
- if (roadstartflag.current_wares() > 0 || roadendflag.current_wares() > 0) {
+ // We do not dismantle (even consider it) if the road is busy (some wares on flags), unless there
+ // is shortage of build spots
+ if (spots_ > kSpotsTooLittle && roadstartflag.current_wares() + roadendflag.current_wares() > 0) {
return false;
}
@@ -2664,7 +2674,14 @@
std::vector<NearFlag> reachableflags;
queue.push(NearFlag(roadstartflag, 0, 0));
uint8_t pathcounts = 0;
- uint8_t checkradius = 8;
+ uint8_t checkradius = 0;
+ if (spots_ > kSpotsEnough) {
+ checkradius = 8;
+ } else if (spots_ > kSpotsTooLittle) {
+ checkradius = 12;
+ } else {
+ checkradius = 16;
+ }
Map& map = game().map();
// algorithm to walk on roads
@@ -3000,11 +3017,12 @@
// if all possible roads skipped
if (last_attempt_) {
Building* bld = flag.get_building();
- // first we block the field for 15 minutes, probably it is not good place to build a
- // building on
- BlockedField blocked(
- game().map().get_fcoords(bld->get_position()), game().get_gametime() + 15 * 60 * 1000);
- blocked_fields.push_back(blocked);
+ // first we block the field and vicinity for 15 minutes, probably it is not good place to build on
+ MapRegion<Area<FCoords>> mr(
+ game().map(), Area<FCoords>(map.get_fcoords(bld->get_position()), 2));
+ do {
+ blocked_fields.add(coords_hash(mr.location()), game().get_gametime() + 15 * 60 * 1000);
+ } while (mr.advance(map));
eco->flags.remove(&flag);
game().send_player_bulldoze(*const_cast<Flag*>(&flag));
return true;
@@ -3169,6 +3187,11 @@
// Lumberjack / Woodcutter handling
if (site.bo->need_trees_) {
+ //do not dismantle immediatelly
+ if ((game().get_gametime() - site.built_time_) < 4 * 60 * 1000) {
+ return false;
+ }
+
const uint32_t remaining_trees =
map.find_immovables(Area<FCoords>(map.get_fcoords(site.site->get_position()), radius),
nullptr,
@@ -3188,8 +3211,6 @@
return false;
}
-
-
// do not dismantle if there are some trees remaining
if (remaining_trees > 5) {
return false;
@@ -4118,6 +4139,10 @@
if (bo.current_stats_ < 40) {
return BuildingNecessity::kForbidden;
}
+ assert (bo.last_building_built_ != kNever);
+ if (gametime < bo.last_building_built_ + 3 * 60 * 1000) {
+ return BuildingNecessity::kForbidden;
+ }
return needed_type;
} if (bo.max_needed_preciousness_ > 0) {
if (bo.cnt_under_construction_ + bo.unoccupied_count_ > 0) {
@@ -4134,7 +4159,14 @@
} else if (bo.total_count() == 0) {
return needed_type;
} else if (bo.current_stats_ > 10 + 70 / bo.outputs_.size()) {
- return needed_type;
+ assert (bo.last_building_built_ != kNever);
+ if (gametime < bo.last_building_built_ + 10 * 60 * 1000) {
+ // Previous building built less then 10 minutes ago
+ // Wait a bit, perhaps average utilization will drop down in the meantime
+ return BuildingNecessity::kNeededPending;
+ } else {
+ return needed_type;
+ }
} else if (needs_second_for_upgrade) {
return needed_type;
} else {
@@ -4559,6 +4591,9 @@
// and if close (up to 2 fields away) from border
if (bf.near_border_) {
prio -= 10;
+ if (spots_ < kSpotsEnough){
+ prio += 3 * (spots_ - kSpotsEnough);
+ }
}
return prio;
@@ -4898,7 +4933,7 @@
// If we have portspace following options are avaiable:
// 1. Build a port there
- if (!so.ship->exp_port_spaces().empty()) { // making sure we have possible portspaces
+ if (so.ship->exp_port_spaces().size() > 0) { // making sure we have possible portspaces
// we score the place
const uint8_t spot_score = spot_scoring(so.ship->exp_port_spaces().front());
@@ -4914,9 +4949,7 @@
MapRegion<Area<FCoords>> mr(
game().map(), Area<FCoords>(map.get_fcoords(so.ship->exp_port_spaces().front()), 8));
do {
- BlockedField blocked2(
- map.get_fcoords(*(mr.location().field)), gametime + 5 * 60 * 1000);
- blocked_fields.push_back(blocked2);
+ blocked_fields.add(coords_hash(mr.location()), game().get_gametime() + 5 * 60 * 1000);
} while (mr.advance(map));
return;
@@ -5006,14 +5039,16 @@
} else {
++bo.cnt_built_;
+ const uint32_t gametime = game().get_gametime();
+ bo.last_building_built_ = gametime;
if (bo.type == BuildingObserver::PRODUCTIONSITE) {
productionsites.push_back(ProductionSiteObserver());
productionsites.back().site = &dynamic_cast<ProductionSite&>(b);
productionsites.back().bo = &bo;
productionsites.back().bo->new_building_overdue_ = 0;
- productionsites.back().built_time_ = game().get_gametime();
- productionsites.back().unoccupied_till_ = game().get_gametime();
+ productionsites.back().built_time_ = gametime;
+ productionsites.back().unoccupied_till_ = gametime;
productionsites.back().stats_zero_ = 0;
productionsites.back().no_resources_since_ = kNever;
productionsites.back().bo->unoccupied_count_ += 1;
@@ -5031,7 +5066,7 @@
mines_.push_back(ProductionSiteObserver());
mines_.back().site = &dynamic_cast<ProductionSite&>(b);
mines_.back().bo = &bo;
- mines_.back().built_time_ = game().get_gametime();
+ mines_.back().built_time_ =gametime;
mines_.back().no_resources_since_ = kNever;
mines_.back().bo->unoccupied_count_ += 1;
@@ -5301,7 +5336,7 @@
Map& map = game().map();
// define which players are attackable
- std::vector<bool> player_attackable;
+ std::vector<Attackable> player_attackable;
PlayerNumber const nr_players = map.get_nrplayers();
player_attackable.resize(nr_players);
uint32_t plr_in_game = 0;
@@ -5312,28 +5347,22 @@
// receiving games statistics and parsing it (reading latest entry)
const Game::GeneralStatsVector& genstats = game().get_general_statistics();
- // summing team power, creating team_power std::map of team_number:strength
- std::map<TeamNumber, uint32_t> team_power;
+ //Collecting statistics and saving them in player_statistics object
for (uint8_t j = 1; j <= plr_in_game; ++j) {
- const Player* other = game().get_player(j);
- const TeamNumber tm = other ? other->team_number() : 0;
- if (tm == 0) {
- continue;
- }
- // for case this is new team
- if (team_power.count(tm) == 0) {
- // adding this team (number) to vector
- team_power[tm] = 0;
- }
- try {
- team_power[tm] += genstats.at(j - 1).miltary_strength.back();
- } catch (const std::out_of_range&) {
- log("ComputerPlayer(%d): genstats entry missing - size :%d\n",
- player_number(),
- static_cast<unsigned int>(genstats.size()));
+ const Player* this_player = game().get_player(j);
+ if (this_player) {
+ try {
+ player_statistics.add(j, this_player->team_number(), genstats.at(j - 1).miltary_strength.back());
+ } catch (const std::out_of_range&) {
+ log("ComputerPlayer(%d): genstats entry missing - size :%d\n",
+ player_number(),
+ static_cast<unsigned int>(genstats.size()));
+ }
}
}
+ player_statistics.recalculate_team_power();
+
// defining treshold ratio of own_strength/enemy's_strength
uint32_t treshold_ratio = 100;
if (type_ == DefaultAI::Type::kNormal) {
@@ -5358,65 +5387,37 @@
treshold_ratio += persistent_data->ai_personality_attack_margin;
}
- uint32_t my_power = 0;
- try {
- my_power = genstats.at(pn - 1).miltary_strength.back();
- } catch (const std::out_of_range&) {
- log("ComputerPlayer(%d): genstats entry missing - size :%d\n",
- player_number(),
- static_cast<unsigned int>(genstats.size()));
- }
- // adding power of team (minus my power) divided by 2
- // (if I am a part of a team of course)
- const TeamNumber team_number = player_->team_number();
- if (team_number > 0) {
- my_power += (team_power[team_number] - my_power) / 2;
- }
+ const uint32_t my_power = player_statistics.get_modified_player_power(pn);
+
// now we test all players to identify 'attackable' ones
for (uint8_t j = 1; j <= plr_in_game; ++j) {
- // if it's me
- if (pn == j) {
- player_attackable[j - 1] = false;
- continue;
- }
- // if we are the same team
- const Player* other = game().get_player(j);
- const TeamNumber tm = other ? other->team_number() : 0;
- if (team_number > 0 && team_number == tm) {
- player_attackable[j - 1] = false;
+ // if we are the same team, or just it is me
+ if (player_statistics.players_in_same_team(pn, j) || pn == j) {
+ player_attackable[j - 1] = Attackable::kNotAttackable;
continue;
}
// now we compare strength
- try {
- // strength of the other player
- uint32_t players_power = 0;
- if (!genstats.at(j - 1).miltary_strength.empty()) {
- players_power += genstats.at(j - 1).miltary_strength.back();
- }
- // +power of team (if member of a team)
- if (tm > 0) {
- players_power += (team_power[tm] - players_power) / 2;
- }
+ // strength of the other player (considering his team)
+ uint32_t players_power = player_statistics.get_modified_player_power(j);;
- if (players_power == 0) {
- player_attackable.at(j - 1) = true;
- } else if (my_power * 100 / players_power > treshold_ratio) {
- player_attackable.at(j - 1) = true;
- } else {
- player_attackable.at(j - 1) = false;
- }
- } catch (const std::out_of_range&) {
- log("ComputerPlayer(%d): genstats entry missing - size :%d\n",
- player_number(),
- static_cast<unsigned int>(genstats.size()));
- player_attackable.at(j - 1) = false;
+ if (players_power == 0) {
+ player_attackable.at(j - 1) = Attackable::kAttackable;
+ } else if (my_power * 100 / players_power > treshold_ratio * 8) {
+ player_attackable.at(j - 1) = Attackable::kAttackableVeryWeak;
+ } else if (my_power * 100 / players_power > treshold_ratio * 4) {
+ player_attackable.at(j - 1) = Attackable::kAttackableAndWeak;
+ } else if (my_power * 100 / players_power > treshold_ratio) {
+ player_attackable.at(j - 1) = Attackable::kAttackable;
+ } else {
+ player_attackable.at(j - 1) = Attackable::kNotAttackable;
}
+
}
// first we scan vicitnity of couple of militarysites to get new enemy sites
- // militarysites rotate
+ // Militarysites rotate (see check_militarysites())
int32_t i = 0;
for (MilitarySiteObserver mso : militarysites) {
i += 1;
@@ -5506,6 +5507,8 @@
;
}
+ const bool strong_enough = player_statistics.strong_enough(pn);
+
for (std::map<uint32_t, EnemySiteObserver>::iterator site = enemy_sites.begin();
site != enemy_sites.end();
++site) {
@@ -5598,7 +5601,9 @@
if (site->second.attack_soldiers_strength > 0
&&
- player_attackable[owner_number - 1]) {
+ (player_attackable[owner_number - 1] == Attackable::kAttackable ||
+ player_attackable[owner_number - 1] == Attackable::kAttackableAndWeak ||
+ player_attackable[owner_number - 1] == Attackable::kAttackableVeryWeak)) {
site->second.score = site->second.attack_soldiers_strength - site->second.defenders_strength / 2;
if (is_warehouse) {
@@ -5626,6 +5631,19 @@
// Applying (decreasing score) if trainingsites are not working
site->second.score += training_score;
+ // We have an advantage over stongest opponent
+ if (strong_enough) {
+ site->second.score += 3;
+ }
+
+ // Enemy is too weak, be more aggressive attacking him
+ if (player_attackable[owner_number - 1] == Attackable::kAttackableAndWeak) {
+ site->second.score += 4;
+ }
+ if (player_attackable[owner_number - 1] == Attackable::kAttackableVeryWeak) {
+ site->second.score += 8;
+ }
+
// treating no attack score
if (site->second.no_attack_counter < 0) {
// we cannot attack yet
=== modified file 'src/ai/defaultai.h'
--- src/ai/defaultai.h 2016-02-09 20:22:50 +0000
+++ src/ai/defaultai.h 2016-02-17 21:18:41 +0000
@@ -85,6 +85,7 @@
enum class WoodPolicy : uint8_t {kDismantleRangers, kStopRangers, kAllowRangers};
enum class NewShip : uint8_t {kBuilt, kFoundOnLoad};
enum class PerfEvaluation : uint8_t {kForConstruction, kForDismantle};
+ enum class Attackable : uint8_t {kNotAttackable, kAttackable, kAttackableAndWeak, kAttackableVeryWeak};
enum class Tribes : uint8_t {
kNone,
@@ -93,7 +94,6 @@
kEmpire
};
-
/// Implementation for Strong
struct NormalImpl : public ComputerPlayer::Implementation {
NormalImpl() {
@@ -263,7 +263,8 @@
std::list<Widelands::FCoords> unusable_fields;
std::list<BuildableField*> buildable_fields;
- std::list<BlockedField> blocked_fields;
+ BlockedFields blocked_fields;
+ PlayersStrengths player_statistics;
std::unordered_set<uint32_t> port_reserved_coords;
std::list<MineableField*> mineable_fields;
std::list<Widelands::Flag const*> new_flags;
Follow ups
-
[Merge] lp:~widelands-dev/widelands/ai_small_tweaks into lp:widelands
From: noreply, 2016-02-22
-
Re: [Merge] lp:~widelands-dev/widelands/ai_small_tweaks into lp:widelands
From: GunChleoc, 2016-02-22
-
[Merge] lp:~widelands-dev/widelands/ai_small_tweaks into lp:widelands
From: bunnybot, 2016-02-22
-
[Merge] lp:~widelands-dev/widelands/ai_small_tweaks into lp:widelands
From: bunnybot, 2016-02-22
-
[Merge] lp:~widelands-dev/widelands/ai_small_tweaks into lp:widelands
From: bunnybot, 2016-02-22
-
[Merge] lp:~widelands-dev/widelands/ai_small_tweaks into lp:widelands
From: bunnybot, 2016-02-21
-
Re: [Merge] lp:~widelands-dev/widelands/ai_small_tweaks into lp:widelands
From: TiborB, 2016-02-21
-
[Merge] lp:~widelands-dev/widelands/ai_small_tweaks into lp:widelands
From: bunnybot, 2016-02-19
-
Re: [Merge] lp:~widelands-dev/widelands/ai_small_tweaks into lp:widelands
From: GunChleoc, 2016-02-18
-
Re: [Merge] lp:~widelands-dev/widelands/ai_small_tweaks into lp:widelands
From: TiborB, 2016-02-18
-
Re: [Merge] lp:~widelands-dev/widelands/ai_small_tweaks into lp:widelands
From: GunChleoc, 2016-02-18
-
Re: [Merge] lp:~widelands-dev/widelands/ai_small_tweaks into lp:widelands
From: TiborB, 2016-02-18
-
Re: [Merge] lp:~widelands-dev/widelands/ai_small_tweaks into lp:widelands
From: GunChleoc, 2016-02-18