1 /*
2 * Copyright (C) 2004-2020 by the Widelands Development Team
3 *
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 *
18 */
19
20 #include "ai/ai_hints.h"
21
22 #include <memory>
23
24 /* RST
25 AI Hints and Restrictions
26 =========================
27
28 Every :doc:`building <autogen_toc_lua_tribes_buildings>`'s ``init.lua`` file has an ``aihints``
29 table in its ``new_<building_type>_type{table}`` function. This ``aihints`` table can contain any
30 number of entries, which will help the AI decide when and where to build or dismantle a building of
31 that type and/or how to treat it.
32
33 All entries in ``aihints`` are optional.
34
35 - :ref:`ai_hints_introduction`
36 - :ref:`ai_hints_common`
37 - :ref:`ai_hints_military`
38 - :ref:`ai_hints_production`
39
40 .. _ai_hints_introduction:
41
42 Introduction
43 ------------
44
45 The AI has separate code paths for various building categories. Many of them are
46 categories that contain one unique building (ranger, well, port, fish breeder,
47 barracks and so on), and these cannot be combined with other types of buildings
48 and are mostly mandatory.
49
50 The main categories where you can freely modify and add buildings are:
51
52 - Military sites
53 - Training sites
54 - Pure production sites (they have outputs, and optionally inputs, but no production hints)
55 - Pure supporters (they have production hints, but neither inputs nor outputs)
56 - A combination of supporter and production site is possible, but suboptimal
57
58 You can create as many building types as you want in these main categories, but
59 make sure that you don't combine any incompatible features (for example,
60 ``shipyard`` and ``mines`` don't combine).
61
62 With the exception of the barracks and the building that produces carrier2
63 (see: :ref:`lua_tribes_<tribename>.lua`), production of workers in production sites
64 is not supported at this time.
65
66 .. _ai_hints_common:
67
68 Common Building Hints
69 ---------------------
70
71 **basic_amount**
72 The amount of this building to be built while establishing a basic economy, e.g.::
73
74 basic_amount = 1,
75
76 **forced_after**
77 If a building of this type hasn't been built already, force that a building
78 of this type will be constructed after the given time (in seconds) has elapsed,
79 e.g.::
80
81 forced_after = 890,
82
83 **Note:** ``forced after`` can interfere with setting up the basic economy
84 defined by ``basic_amount``: if we don't want the building to be part of the
85 basic economy but it is forced before all the buildings for the basic economy
86 have been built, this can lead to unobvious behavior. Part of this ambiguity
87 is due to the genetic algorithm.
88
89 **prohibited_till**
90 Do not build this building before the given time (in seconds) has elapsed, e.g.::
91
92 prohibited_till = 1500,
93
94 This takes precedence over ``basic_amount``, so it means it can delay achieving
95 the basic economy.
96
97 **very_weak_ai_limit**
98 The maximum number of this building type that the very weak AI is allowed to build, e.g.::
99
100 very_weak_ai_limit = 1,
101
102 **weak_ai_limit_**
103 The maximum number of this building type that the weak AI is allowed to build, e.g.::
104
105 weak_ai_limit_ = 2,
106
107 .. _ai_hints_military:
108
109 Military Sites
110 --------------
111
112 **expansion**
113 The building will be used by the AI for expanding the territory, e.g.::
114
115 expansion = true,
116
117 **Note:** It is recommended to have at least one building has this feature.
118
119 **fighting**
120 The building is suitable for military conflicts, e.g.::
121
122 fighting = true,
123
124 **Note:** It is recommended to have at least one building has this feature.
125
126 **mountain_conqueror**
127 Prefer this type of military site near mountains, e.g.::
128
129 mountain_conqueror = true,
130
131 **Note:** It is recommended to have at least one building has this feature.
132
133 .. _ai_hints_production:
134
135 Production Sites
136 ----------------
137
138 **collects_ware_from_map**
139 The building will generate this ware from the map, e.g. a well mining the ``water`` ware,
140 or the hunter returning from the hunt with the ``meat`` ware. The same ware needs also to be
141 listed as the first one of the building's outputs, e.g.::
142
143 aihints = {
144 collects_ware_from_map = "meat"
145 },
146
147 outputs = {
148 "meat",
149 "fur"
150 },
151
152 **Note:** The AI expects exactly one such building type for each of the following wares:
153 ``fish`` (fisher), ``granite`` (quarry), ``log`` (lumberjack/woodcutter), ``meat`` (hunter),
154 ``water`` (well).
155
156 **mines**
157 The building will mine to obtain the given ware, e.g.::
158
159 mines = "gold",
160
161 **mines_percent**
162 The percentage that a mine will mine of its resource before it needs enhancing, e.g.::
163
164 mines_percent = 60,
165
166 **needs_water**
167 The building needs to be placed near a body of water, e.g.::
168
169 needs_water = true,
170
171 **Note:** The AI expects exactly one such building type.
172
173 **recruitment**
174 The building will recruit the tribe's carrier2, for example oxen or horses, e.g.::
175
176 recruitment = true,
177
178 **shipyard**
179 This building will construct ships, e.g.::
180
181 shipyard = true,
182
183 **Note:** The AI expects exactly one such building type.
184
185 **supports_seafaring**
186 This building is needed for expedition and seafaring, e.g.::
187
188 supports_seafaring = true,
189
190 **space_consumer**
191 The building needs a lot of space around it, for example a farm needs space for
192 its fields, e.g.::
193
194 space_consumer = true,
195
196 **supports_production_of**
197 This building will support the production of the given wares without producing
198 it directly, e.g.::
199
200 supports_production_of = { "fish" },
201
202 For example, if a building supports the production of fish, it should be placed
203 near a building that has fish in its output.
204
205 **Note:** The AI expects that supporters will have no inputs and no outputs. Although
206 the AI can tolerate such buildings, they will be primarily treated as normal
207 production sites when deciding on the building's location.
208
209 **requires_supporters**
210 This building will be built only if a supporter is nearby::
211
212 requires_supporters = true,
213
214 For example if set for lumberjack, it will be built only if a renger is nearby.
215
216 **trainingsites_max_percent**
217 The maximum percengate this training site will have among all training sites, e.g.::
218
219 trainingsites_max_percent = 20,
220
221 In this example, if an AI has built 5 training sites, it will have a maximum of
222 1 site of this type. If it has built 10 training sites, the maximum is 2.
223 For training sites that don't have this value set, their percentage will be
224 distributed evenly.
225
226 */
227
BuildingHints(std::unique_ptr<LuaTable> table)228 BuildingHints::BuildingHints(std::unique_ptr<LuaTable> table)
229 : mines_(table->has_key("mines") ? table->get_string("mines") : ""),
230 needs_water_(table->has_key("needs_water") ? table->get_bool("needs_water") : false),
231 space_consumer_(table->has_key("space_consumer") ? table->get_bool("space_consumer") : false),
232 expansion_(table->has_key("expansion") ? table->get_bool("expansion") : false),
233 fighting_(table->has_key("fighting") ? table->get_bool("fighting") : false),
234 mountain_conqueror_(
235 table->has_key("mountain_conqueror") ? table->get_bool("mountain_conqueror") : false),
236 shipyard_(table->has_key("shipyard") ? table->get_bool("shipyard") : false),
237 supports_seafaring_(
238 table->has_key("supports_seafaring") ? table->get_bool("supports_seafaring") : false),
239 collects_ware_from_map_(table->has_key("collects_ware_from_map") ?
240 table->get_string("collects_ware_from_map") :
241 ""),
242 prohibited_till_(table->has_key("prohibited_till") ? table->get_int("prohibited_till") : 0),
243 basic_amount_(table->has_key("basic_amount") ? table->get_int("basic_amount") : 0),
244 // 10 days default
245 forced_after_(table->has_key("forced_after") ? table->get_int("forced_after") : 864000),
246 mines_percent_(table->has_key("mines_percent") ? table->get_int("mines_percent") : 100),
247 very_weak_ai_limit_(
248 table->has_key("very_weak_ai_limit") ? table->get_int("very_weak_ai_limit") : -1),
249 weak_ai_limit_(table->has_key("weak_ai_limit") ? table->get_int("weak_ai_limit") : -1),
250 normal_ai_limit_(table->has_key("normal_ai_limit") ? table->get_int("normal_ai_limit") : -1),
251 requires_supporters_(
252 table->has_key("requires_supporters") ? table->get_bool("requires_supporters") : false),
253 trainingsites_max_percent_(table->has_key("trainingsites_max_percent") ?
254 table->get_int("trainingsites_max_percent") :
255 0) {
256 if (table->has_key("supports_production_of")) {
257 for (const std::string& ware_name :
258 table->get_table("supports_production_of")->array_entries<std::string>()) {
259 supported_production_.insert(ware_name);
260 }
261 }
262 }
263
set_trainingsites_max_percent(int percent)264 void BuildingHints::set_trainingsites_max_percent(int percent) {
265 trainingsites_max_percent_ = percent;
266 }
267
trainingsites_max_percent() const268 uint8_t BuildingHints::trainingsites_max_percent() const {
269 return trainingsites_max_percent_;
270 }
271
get_ai_limit(const Widelands::AiType ai_type) const272 int16_t BuildingHints::get_ai_limit(const Widelands::AiType ai_type) const {
273 switch (ai_type) {
274 case Widelands::AiType::kVeryWeak:
275 return very_weak_ai_limit_;
276 case Widelands::AiType::kWeak:
277 return weak_ai_limit_;
278 case Widelands::AiType::kNormal:
279 return normal_ai_limit_;
280 }
281 NEVER_HERE();
282 }
283
284 // TODO(GunChleoc): WareDescr has a bare "preciousness" table that should be moved below a new
285 // "aihints" table.
read_preciousness(const std::string & name,const LuaTable & table)286 void WareWorkerHints::read_preciousness(const std::string& name, const LuaTable& table) {
287 constexpr int kMaxRecommendedPreciousness = 50;
288 for (const std::string& key : table.keys<std::string>()) {
289 const int value = table.get_int(key);
290 if (value > 200) {
291 throw wexception("Preciousness of %d is far too high for ware/worker '%s' and tribe '%s'. "
292 "We recommend not going over %d.",
293 value, name.c_str(), key.c_str(), kMaxRecommendedPreciousness);
294 } else if (value > kMaxRecommendedPreciousness) {
295 log("WARNING: Preciousness of %d is a bit high for ware/worker '%s' and tribe '%s'. We "
296 "recommend not going over %d.\n",
297 value, name.c_str(), key.c_str(), kMaxRecommendedPreciousness);
298 }
299
300 preciousnesses_.insert(std::make_pair(key, value));
301 }
302 }
303
304 /// Returns the preciousness of the ware, or kInvalidWare if the tribe doesn't use the ware.
preciousness(const std::string & tribename) const305 int WareWorkerHints::preciousness(const std::string& tribename) const {
306 if (preciousnesses_.count(tribename) > 0) {
307 return preciousnesses_.at(tribename);
308 }
309 return Widelands::kInvalidWare;
310 }
311
WareHints(const std::string & ware_name,const LuaTable & table)312 WareHints::WareHints(const std::string& ware_name, const LuaTable& table) : WareWorkerHints() {
313 read_preciousness(ware_name, table);
314 }
315
WorkerHints(const std::string & worker_name,const LuaTable & table)316 WorkerHints::WorkerHints(const std::string& worker_name, const LuaTable& table)
317 : WareWorkerHints() {
318 read_preciousness(worker_name, *table.get_table("preciousness"));
319 }
320