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