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 "log.h"
20 
21 /* common */
22 #include "city.h"
23 #include "game.h"
24 #include "map.h"
25 #include "movement.h"
26 #include "tile.h"
27 #include "unit.h"
28 #include "workertask.h"
29 
30 /* server */
31 #include "citytools.h"
32 
33 /* server/advisors */
34 #include "autosettlers.h"
35 #include "infracache.h"
36 
37 /* ai/threaded */
38 #include "taimsg.h"
39 
40 #include "taicity.h"
41 
42 /* Task Want Multiplier */
43 #define TWMP 100
44 
45 struct tai_worker_task_req
46 {
47   int city_id;
48   struct worker_task task;
49 };
50 
51 enum tai_worker_task_limitation {
52   TWTL_CURRENT_UNITS
53 };
54 
55 static bool tai_city_worker_task_select(struct player *pplayer, struct city *pcity,
56                                         struct worker_task *task,
57                                         enum tai_worker_task_limitation limit);
58 
59 /**************************************************************************
60   Create worker request for the city. Only tasks that existing units can
61   do are created.
62 **************************************************************************/
tai_city_worker_requests_create(struct player * pplayer,struct city * pcity)63 void tai_city_worker_requests_create(struct player *pplayer, struct city *pcity)
64 {
65   struct worker_task task;
66 
67   if (tai_city_worker_task_select(pplayer, pcity, &task, TWTL_CURRENT_UNITS)) {
68     struct tai_worker_task_req *data = fc_malloc(sizeof(*data));
69 
70     data->city_id = pcity->id;
71     data->task.ptile = task.ptile;
72     data->task.act = task.act;
73     data->task.tgt = task.tgt;
74     data->task.want = task.want;
75 
76     tai_send_req(TAI_REQ_WORKER_TASK, pplayer, data);
77   }
78 }
79 
80 struct tai_tile_state
81 {
82   int uw_max_base; /* Value for the city working the tile */
83   int uw_max;      /* With road connectivity bonus */
84   int worst_worked;
85   int orig_worst_worked;
86   int old_worst_worked;
87 };
88 
89 /**************************************************************************
90   Select worker task suitable for the tile.
91 **************************************************************************/
tai_tile_worker_task_select(struct player * pplayer,struct city * pcity,struct tile * ptile,int cindex,struct unit_list * units,struct worker_task * worked,struct worker_task * unworked,struct tai_tile_state * state)92 static void tai_tile_worker_task_select(struct player *pplayer,
93                                         struct city *pcity, struct tile *ptile,
94                                         int cindex, struct unit_list *units,
95                                         struct worker_task *worked,
96                                         struct worker_task *unworked,
97                                         struct tai_tile_state *state)
98 {
99   int orig_value;
100   bool potential_worst_worked = FALSE;
101 
102   if (!city_can_work_tile(pcity, ptile)) {
103     return;
104   }
105 
106   orig_value = city_tile_value(pcity, ptile, 0, 0);
107 
108   if (tile_worked(ptile) == pcity
109       && orig_value < state->worst_worked) {
110     state->worst_worked = orig_value;
111     state->orig_worst_worked = orig_value;
112     potential_worst_worked = TRUE;
113   }
114 
115   as_transform_activity_iterate(act) {
116     bool consider = TRUE;
117     bool possible = FALSE;
118     enum extra_cause cause;
119     enum extra_rmcause rmcause;
120 
121     /* Do not request activities that already are under way. */
122     unit_list_iterate(ptile->units, punit) {
123       if (unit_owner(punit) == pplayer
124           && unit_has_type_flag(punit, UTYF_SETTLERS)
125           && punit->activity == act) {
126         consider = FALSE;
127         break;
128       }
129     } unit_list_iterate_end;
130 
131     if (!consider) {
132       continue;
133     }
134 
135     cause = activity_to_extra_cause(act);
136     rmcause = activity_to_extra_rmcause(act);
137 
138     unit_list_iterate(units, punit) {
139       struct extra_type *tgt = NULL;
140 
141       if (cause != EC_NONE) {
142         tgt = next_extra_for_tile(ptile, cause, pplayer, punit);
143       } else if (rmcause != ERM_NONE) {
144         tgt = prev_extra_in_tile(ptile, rmcause, pplayer, punit);
145       }
146 
147       if (can_unit_do_activity_targeted_at(punit, act, tgt, ptile)) {
148         possible = TRUE;
149         break;
150       }
151     } unit_list_iterate_end;
152 
153     if (possible) {
154       int value = adv_city_worker_act_get(pcity, cindex, act);
155 
156       if (tile_worked(ptile) == pcity) {
157         if ((value - orig_value) * TWMP > worked->want) {
158           worked->want  = TWMP * (value - orig_value);
159           worked->ptile = ptile;
160           worked->act   = act;
161           worked->tgt   = NULL;
162         }
163         if (value > state->old_worst_worked) {
164           /* After improvement it would not be the worst */
165           potential_worst_worked = FALSE;
166         } else {
167           state->worst_worked = value;
168         }
169       } else {
170         if (value > orig_value && value > state->uw_max) {
171           state->uw_max = value;
172           state->uw_max_base = value;
173           unworked->want  = TWMP * (value - orig_value);
174           unworked->ptile = ptile;
175           unworked->act   = act;
176           unworked->tgt   = NULL;
177         }
178       }
179     }
180   } as_transform_activity_iterate_end;
181 
182   extra_type_iterate(tgt) {
183     enum unit_activity act = ACTIVITY_LAST;
184     bool removing = tile_has_extra(ptile, tgt);
185 
186     unit_list_iterate(units, punit) {
187       if (removing) {
188         as_rmextra_activity_iterate(try_act) {
189           if (is_extra_removed_by_action(tgt, try_act)
190               && can_unit_do_activity_targeted_at(punit, try_act, tgt, ptile)) {
191             act = try_act;
192             break;
193           }
194         } as_rmextra_activity_iterate_end;
195       } else {
196         as_extra_activity_iterate(try_act) {
197           if (is_extra_caused_by_action(tgt, try_act)
198               && can_unit_do_activity_targeted_at(punit, try_act, tgt, ptile)) {
199             act = try_act;
200             break;
201           }
202         } as_extra_activity_iterate_end;
203       }
204     } unit_list_iterate_end;
205 
206     if (act != ACTIVITY_LAST) {
207       int base_value;
208       int value;
209       int extra;
210       bool consider = TRUE;
211       struct road_type *proad;
212 
213       /* Do not request activities that already are under way. */
214       unit_list_iterate(ptile->units, punit) {
215         if (unit_owner(punit) == pplayer
216             && unit_has_type_flag(punit, UTYF_SETTLERS)
217             && punit->activity == act) {
218           consider = FALSE;
219           break;
220         }
221       } unit_list_iterate_end;
222 
223       if (!consider) {
224         continue;
225       }
226 
227       proad = extra_road_get(tgt);
228 
229       if (removing) {
230         base_value = adv_city_worker_rmextra_get(pcity, cindex, tgt);
231       } else {
232         base_value = adv_city_worker_extra_get(pcity, cindex, tgt);
233       }
234 
235       if (proad != NULL && road_provides_move_bonus(proad)) {
236         int old_move_cost;
237         int mc_multiplier = 1;
238         int mc_divisor = 1;
239 
240         /* Here 'old' means actually 'without the evaluated': In case of
241          * removal activity it's the value after the removal. */
242         old_move_cost = tile_terrain(ptile)->movement_cost * SINGLE_MOVE;
243 
244         extra_type_by_cause_iterate(EC_ROAD, poe) {
245           struct road_type *pold = extra_road_get(poe);
246 
247           if (tile_has_extra(ptile, poe) && poe != tgt) {
248             if (road_provides_move_bonus(pold)
249                 && pold->move_cost < old_move_cost) {
250               old_move_cost = pold->move_cost;
251             }
252           }
253         } extra_type_by_cause_iterate_end;
254 
255         if (proad->move_cost < old_move_cost) {
256           if (proad->move_cost >= terrain_control.move_fragments) {
257             mc_divisor = proad->move_cost / terrain_control.move_fragments;
258           } else {
259             if (proad->move_cost == 0) {
260               mc_multiplier = 2;
261             } else {
262               mc_multiplier = 1 - proad->move_cost;
263             }
264             mc_multiplier += old_move_cost;
265           }
266         }
267 
268         extra = adv_settlers_road_bonus(ptile, proad) * mc_multiplier / mc_divisor;
269 
270         if (removing) {
271           extra = -extra;
272         }
273       } else {
274         extra = 0;
275       }
276 
277       value = base_value + extra;
278 
279       if (tile_worked(ptile) == pcity) {
280         if ((value - orig_value) * TWMP > worked->want) {
281           worked->want  = TWMP * (value - orig_value);
282           worked->ptile = ptile;
283           worked->act   = act;
284           worked->tgt   = tgt;
285         }
286         if (value > state->old_worst_worked) {
287           /* After improvement it would not be the worst */
288           potential_worst_worked = FALSE;
289         } else {
290           state->worst_worked = value;
291         }
292       } else {
293         if (value > orig_value && value > state->uw_max) {
294           state->uw_max = value;
295           state->uw_max_base = base_value;
296           unworked->want  = TWMP * (value - orig_value);
297           unworked->ptile = ptile;
298           unworked->act   = act;
299           unworked->tgt   = tgt;
300         }
301       }
302     }
303   } extra_type_iterate_end;
304 
305   if (potential_worst_worked) {
306     /* Would still be worst worked even if we improved *it*. */
307     state->old_worst_worked = state->worst_worked;
308   }
309 }
310 
311 /**************************************************************************
312   Select worker task suitable for the city.
313 **************************************************************************/
tai_city_worker_task_select(struct player * pplayer,struct city * pcity,struct worker_task * task,enum tai_worker_task_limitation limit)314 static bool tai_city_worker_task_select(struct player *pplayer, struct city *pcity,
315                                         struct worker_task *task,
316                                         enum tai_worker_task_limitation limit)
317 {
318   struct worker_task *selected;
319   struct worker_task worked = { .ptile = NULL, .want = 0, .act = ACTIVITY_IDLE, .tgt = NULL };
320   struct worker_task unworked = { .ptile = NULL, .want = 0, .act = ACTIVITY_IDLE, .tgt = NULL };
321   struct tai_tile_state state = { .uw_max = 0, .uw_max_base = 0, .worst_worked = FC_INFINITY,
322                                   .orig_worst_worked = 0, .old_worst_worked = FC_INFINITY };
323   struct unit_list *units = NULL;
324 
325   switch (limit) {
326   case TWTL_CURRENT_UNITS:
327     units = pplayer->units;
328     break;
329   }
330 
331   city_tile_iterate_index(city_map_radius_sq_get(pcity), city_tile(pcity),
332                           ptile, cindex) {
333     tai_tile_worker_task_select(pplayer, pcity, ptile, cindex, units, &worked, &unworked,
334                                 &state);
335   } city_tile_iterate_end;
336 
337   if (worked.ptile == NULL
338       || (state.old_worst_worked < state.uw_max
339           && (state.uw_max - state.orig_worst_worked) * TWMP > worked.want)
340       || (state.uw_max - state.uw_max_base * TWMP > worked.want)) {
341     /* It's better to improve best yet unworked tile and take it to use after that,
342        than to improve already worked tile. OR it's more important to
343        improve road connectivity outside worked tiles than improve worked tiles */
344     selected = &unworked;
345   } else {
346     selected = &worked;
347   }
348 
349   if (selected->ptile != NULL) {
350     struct extra_type *target = NULL;
351 
352     if (selected->tgt == NULL) {
353       enum extra_cause cause = activity_to_extra_cause(selected->act);
354 
355       if (cause != EC_NONE) {
356         target = next_extra_for_tile(selected->ptile, cause, pplayer, NULL);
357       } else {
358         enum extra_rmcause rmcause = activity_to_extra_rmcause(selected->act);
359 
360         if (rmcause != ERM_NONE) {
361           target = prev_extra_in_tile(selected->ptile, rmcause, pplayer, NULL);
362         }
363       }
364     } else {
365       target = selected->tgt;
366     }
367 
368     task->ptile = selected->ptile;
369     task->act = selected->act;
370     task->tgt = target;
371     task->want = selected->want;
372 
373     return TRUE;
374   }
375 
376   return FALSE;
377 }
378 
379 /**************************************************************************
380   Receive message from thread to main thread.
381 **************************************************************************/
tai_req_worker_task_rcv(struct tai_req * req)382 void tai_req_worker_task_rcv(struct tai_req *req)
383 {
384   struct tai_worker_task_req *data = (struct tai_worker_task_req *)req->data;
385   struct city *pcity;
386 
387   pcity = game_city_by_number(data->city_id);
388 
389   if (pcity != NULL && city_owner(pcity) == req->plr) {
390     /* City has not been lost meanwhile */
391     struct worker_task *ptask = worker_task_list_get(pcity->task_reqs, 0);
392 
393     if (ptask == NULL) {
394       ptask = fc_malloc(sizeof(struct worker_task));
395       worker_task_init(ptask);
396       worker_task_list_append(pcity->task_reqs, ptask);
397     }
398 
399     log_debug("%s storing req for act %d at (%d,%d)",
400               pcity->name, data->task.act, TILE_XY(data->task.ptile));
401     ptask->ptile = data->task.ptile;
402     ptask->act   = data->task.act;
403     ptask->tgt   = data->task.tgt;
404     ptask->want  = data->task.want;
405 
406     /* Send info to observers */
407     package_and_send_worker_tasks(pcity);
408   }
409 
410   free(data);
411 }
412