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