1 /***********************************************************************
2 Freeciv - Copyright (C) 1996 - A Kjeldberg, L Gregersen, P Unold
3 This program is free software; you can redistribute it and/or modify
4 it under the terms of the GNU General Public License as published by
5 the Free Software Foundation; either version 2, or (at your option)
6 any later version.
7
8 This program is distributed in the hope that it will be useful,
9 but WITHOUT ANY WARRANTY; without even the implied warranty of
10 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 GNU General Public License for more details.
12 ***********************************************************************/
13
14 #ifdef HAVE_CONFIG_H
15 #include <fc_config.h>
16 #endif
17
18 /* utility */
19 #include "astring.h"
20 #include "bitvector.h"
21 #include "rand.h"
22
23 /* common */
24 #include "player.h"
25
26 /* ai */
27 #include "handicaps.h"
28
29 #include "difficulty.h"
30
31 static bv_handicap handicap_of_skill_level(enum ai_level level);
32 static int fuzzy_of_skill_level(enum ai_level level);
33 static int science_cost_of_skill_level(enum ai_level level);
34 static int expansionism_of_skill_level(enum ai_level level);
35
36 /**************************************************************************
37 Set an AI level and related quantities, with no feedback.
38 **************************************************************************/
set_ai_level_directer(struct player * pplayer,enum ai_level level)39 void set_ai_level_directer(struct player *pplayer, enum ai_level level)
40 {
41 handicaps_set(pplayer, handicap_of_skill_level(level));
42 pplayer->ai_common.fuzzy = fuzzy_of_skill_level(level);
43 pplayer->ai_common.expand = expansionism_of_skill_level(level);
44 pplayer->ai_common.science_cost = science_cost_of_skill_level(level);
45 pplayer->ai_common.skill_level = level;
46 }
47
48 /**************************************************************************
49 Returns handicap bitvector for given AI skill level
50 **************************************************************************/
handicap_of_skill_level(enum ai_level level)51 static bv_handicap handicap_of_skill_level(enum ai_level level)
52 {
53 bv_handicap handicap;
54
55 fc_assert(ai_level_is_valid(level));
56
57 BV_CLR_ALL(handicap);
58
59 switch (level) {
60 case AI_LEVEL_AWAY:
61 BV_SET(handicap, H_AWAY);
62 BV_SET(handicap, H_FOG);
63 BV_SET(handicap, H_MAP);
64 BV_SET(handicap, H_RATES);
65 BV_SET(handicap, H_TARGETS);
66 BV_SET(handicap, H_HUTS);
67 BV_SET(handicap, H_REVOLUTION);
68 break;
69 case AI_LEVEL_NOVICE:
70 case AI_LEVEL_HANDICAPPED:
71 BV_SET(handicap, H_RATES);
72 BV_SET(handicap, H_TARGETS);
73 BV_SET(handicap, H_HUTS);
74 BV_SET(handicap, H_NOPLANES);
75 BV_SET(handicap, H_DIPLOMAT);
76 BV_SET(handicap, H_LIMITEDHUTS);
77 BV_SET(handicap, H_DEFENSIVE);
78 BV_SET(handicap, H_DIPLOMACY);
79 BV_SET(handicap, H_REVOLUTION);
80 BV_SET(handicap, H_EXPANSION);
81 BV_SET(handicap, H_DANGER);
82 BV_SET(handicap, H_CEASEFIRE);
83 break;
84 case AI_LEVEL_EASY:
85 BV_SET(handicap, H_RATES);
86 BV_SET(handicap, H_TARGETS);
87 BV_SET(handicap, H_HUTS);
88 BV_SET(handicap, H_NOPLANES);
89 BV_SET(handicap, H_DIPLOMAT);
90 BV_SET(handicap, H_LIMITEDHUTS);
91 BV_SET(handicap, H_DEFENSIVE);
92 BV_SET(handicap, H_DIPLOMACY);
93 BV_SET(handicap, H_REVOLUTION);
94 BV_SET(handicap, H_EXPANSION);
95 BV_SET(handicap, H_CEASEFIRE);
96 break;
97 case AI_LEVEL_NORMAL:
98 BV_SET(handicap, H_RATES);
99 BV_SET(handicap, H_TARGETS);
100 BV_SET(handicap, H_HUTS);
101 BV_SET(handicap, H_DIPLOMAT);
102 BV_SET(handicap, H_CEASEFIRE);
103 break;
104
105 #ifdef DEBUG
106 case AI_LEVEL_EXPERIMENTAL:
107 BV_SET(handicap, H_EXPERIMENTAL);
108 break;
109 #endif /* DEBUG */
110
111 case AI_LEVEL_CHEATING:
112 BV_SET(handicap, H_RATES);
113 break;
114 case AI_LEVEL_HARD:
115 /* No handicaps */
116 break;
117 case AI_LEVEL_COUNT:
118 fc_assert(level != AI_LEVEL_COUNT);
119 break;
120 }
121
122 return handicap;
123 }
124
125 /**************************************************************************
126 Return the AI fuzziness (0 to 1000) corresponding to a given skill
127 level (1 to 10). See ai_fuzzy()
128 **************************************************************************/
fuzzy_of_skill_level(enum ai_level level)129 static int fuzzy_of_skill_level(enum ai_level level)
130 {
131 fc_assert(ai_level_is_valid(level));
132
133 switch(level) {
134 case AI_LEVEL_AWAY:
135 return 0;
136 case AI_LEVEL_HANDICAPPED:
137 case AI_LEVEL_NOVICE:
138 return 400;
139 case AI_LEVEL_EASY:
140 return 300;
141 case AI_LEVEL_NORMAL:
142 case AI_LEVEL_HARD:
143 case AI_LEVEL_CHEATING:
144 #ifdef DEBUG
145 case AI_LEVEL_EXPERIMENTAL:
146 #endif /* DEBUG */
147 return 0;
148 case AI_LEVEL_COUNT:
149 fc_assert(level != AI_LEVEL_COUNT);
150 return 0;
151 }
152
153 return 0;
154 }
155
156 /**************************************************************************
157 Return the AI's science development cost; a science development cost of 100
158 means that the AI develops science at the same speed as a human; a science
159 development cost of 200 means that the AI develops science at half the speed
160 of a human, and a science development cost of 50 means that the AI develops
161 science twice as fast as the human.
162 **************************************************************************/
science_cost_of_skill_level(enum ai_level level)163 static int science_cost_of_skill_level(enum ai_level level)
164 {
165 fc_assert(ai_level_is_valid(level));
166
167 switch(level) {
168 case AI_LEVEL_AWAY:
169 return 100;
170 case AI_LEVEL_HANDICAPPED:
171 case AI_LEVEL_NOVICE:
172 return 250;
173 case AI_LEVEL_EASY:
174 case AI_LEVEL_NORMAL:
175 case AI_LEVEL_HARD:
176 case AI_LEVEL_CHEATING:
177 #ifdef DEBUG
178 case AI_LEVEL_EXPERIMENTAL:
179 #endif /* DEBUG */
180 return 100;
181 case AI_LEVEL_COUNT:
182 fc_assert(level != AI_LEVEL_COUNT);
183 return 100;
184 }
185
186 return 100;
187 }
188
189 /**************************************************************************
190 Return the AI expansion tendency, a percentage factor to value new cities,
191 compared to defaults. 0 means _never_ build new cities, > 100 means to
192 (over?)value them even more than the default (already expansionistic) AI.
193 **************************************************************************/
expansionism_of_skill_level(enum ai_level level)194 static int expansionism_of_skill_level(enum ai_level level)
195 {
196 fc_assert(ai_level_is_valid(level));
197
198 switch(level) {
199 case AI_LEVEL_AWAY:
200 return 0;
201 case AI_LEVEL_HANDICAPPED:
202 case AI_LEVEL_NOVICE:
203 case AI_LEVEL_EASY:
204 return 10;
205 case AI_LEVEL_NORMAL:
206 case AI_LEVEL_HARD:
207 case AI_LEVEL_CHEATING:
208 #ifdef DEBUG
209 case AI_LEVEL_EXPERIMENTAL:
210 #endif /* DEBUG */
211 return 100;
212 case AI_LEVEL_COUNT:
213 fc_assert(level != AI_LEVEL_COUNT);
214 return 100;
215 }
216
217 return 100;
218 }
219
220 /**************************************************************************
221 Helper function for skill level command help.
222 'cmdname' is a server command name.
223 Caller must free returned string.
224 **************************************************************************/
ai_level_help(const char * cmdname)225 char *ai_level_help(const char *cmdname)
226 {
227 /* Translate cmdname to AI level. */
228 enum ai_level level = ai_level_by_name(cmdname, fc_strcasecmp);
229 struct astring help = ASTRING_INIT, features = ASTRING_INIT;
230 bv_handicap handicaps;
231 enum handicap_type h;
232
233 fc_assert(ai_level_is_valid(level));
234
235 if (level == AI_LEVEL_AWAY) {
236 /* Special case */
237 astr_add_line(&help,
238 _("Toggles 'away' mode for your nation. In away mode, "
239 "the AI will govern your nation but make only minimal "
240 "changes."));
241 } else {
242 /* TRANS: %s is a (translated) skill level ('Novice', 'Hard', etc) */
243 astr_add_line(&help,
244 _("With no arguments, sets all AI players to skill level "
245 "'%s', and sets the default level for any new AI "
246 "players to '%s'. With an argument, sets the skill "
247 "level for the specified player only."),
248 _(ai_level_name(level)), _(ai_level_name(level)));
249 }
250
251 handicaps = handicap_of_skill_level(level);
252 for (h = 0; h < H_LAST; h++) {
253 bool inverted;
254 const char *desc = handicap_desc(h, &inverted);
255
256 if (desc && BV_ISSET(handicaps, h) != inverted) {
257 astr_add_line(&features, "%s", desc);
258 }
259 }
260
261 if (fuzzy_of_skill_level(level) > 0) {
262 astr_add_line(&features, _("Has erratic decision-making."));
263 }
264 {
265 int science = science_cost_of_skill_level(level);
266
267 if (science != 100) {
268 astr_add_line(&features,
269 _("Research takes %d%% as long as usual."), science);
270 }
271 }
272 if (expansionism_of_skill_level(level) < 100) {
273 astr_add_line(&features, _("Has reduced appetite for expansion."));
274 } /* no level currently has >100, so no string yet */
275
276 switch (level) {
277 case AI_LEVEL_HANDICAPPED:
278 /* TRANS: describing an AI skill level */
279 astr_add_line(&help,
280 _("\nThis skill level has the same features as 'Novice', "
281 "but may suffer additional ruleset-defined penalties."));
282 break;
283 case AI_LEVEL_CHEATING:
284 /* TRANS: describing an AI skill level */
285 astr_add_line(&help,
286 _("\nThis skill level has the same features as 'Hard', "
287 "but may enjoy additional ruleset-defined bonuses."));
288 break;
289 default:
290 /* In principle this text should vary, but all current skill levels
291 * have _some_ feature text */
292 fc_assert(!astr_empty(&features));
293 /* TRANS: describing an AI skill level */
294 astr_add_line(&help,
295 _("\nThis skill level's features include the following. "
296 "(Some rulesets may define extra level-specific "
297 "behavior.)"));
298 break;
299 }
300
301 if (!astr_empty(&features)) {
302 astr_add_line(&help, "\n%s", astr_str(&features));
303 }
304
305 astr_free(&features);
306 return astr_to_str(&help);
307 }
308
309 /**************************************************************************
310 Return the value normal_decision (a boolean), except if the AI is fuzzy,
311 then sometimes flip the value. The intention of this is that instead of
312 if (condition) { action }
313 you can use
314 if (ai_fuzzy(pplayer, condition)) { action }
315 to sometimes flip a decision, to simulate an AI with some confusion,
316 indecisiveness, forgetfulness etc. In practice its often safer to use
317 if (condition && ai_fuzzy(pplayer, TRUE)) { action }
318 for an action which only makes sense if condition holds, but which a
319 fuzzy AI can safely "forget". Note that for a non-fuzzy AI, or for a
320 human player being helped by the AI (eg, autosettlers), you can ignore
321 the "ai_fuzzy(pplayer," part, and read the previous example as:
322 if (condition && TRUE) { action }
323 --dwp
324 **************************************************************************/
ai_fuzzy(const struct player * pplayer,bool normal_decision)325 bool ai_fuzzy(const struct player *pplayer, bool normal_decision)
326 {
327 if (!pplayer->ai_controlled || pplayer->ai_common.fuzzy == 0) {
328 return normal_decision;
329 }
330 if (fc_rand(1000) >= pplayer->ai_common.fuzzy) {
331 return normal_decision;
332 }
333 return !normal_decision;
334 }
335