Merge lp:~s3734770/widelands/carli-ai into lp:widelands

Proposed by Carli
Status: Rejected
Rejected by: SirVer
Proposed branch: lp:~s3734770/widelands/carli-ai
Merge into: lp:widelands
Diff against target: 487 lines (+125/-52)
4 files modified
compile.sh (+2/-2)
src/ai/ai_help_structs.h (+2/-0)
src/ai/defaultai.cc (+120/-50)
src/ui_basic/table.cc (+1/-0)
To merge this branch: bzr merge lp:~s3734770/widelands/carli-ai
Reviewer Review Type Date Requested Status
SirVer Needs Fixing
Review via email: mp+82061@code.launchpad.net

Description of the change

Hi,

I improved the AI a bit and fixed some bugs.
The AI now takes in account that it needs material for building and for creating soldiers.
The barbarians axefactory upgrade works now.

What I further will do:
 - Treat upgradable buildings like normal buildings and create construction time plans
 - find an automatic balance for the consumer-producer problem

To post a comment you must log in.
Revision history for this message
SirVer (sirver) wrote :

Cool that you work on that. However your changes also contain changes to compile.sh; and also I saw that you replaced bulldoze via dismantle. This let's the flag standing and let to problems in the past. Have you seen this as well?

review: Needs Fixing
Revision history for this message
Carli (s3734770) wrote :

Shouldnt the road optimizer detect the flag problems? I did not notice any problems with that.

About the compile.sh.... the make parameters should not be versioned.

So what should I fix? The make parameter....
But not the dismantle. It works for me. Which version of widelands do you have?

Revision history for this message
SirVer (sirver) wrote :

> Shouldnt the road optimizer detect the flag problems? I did not notice any
> problems with that.
It should, but notice the comment in 256. I guess it is there for a reason.

> About the compile.sh.... the make parameters should not be versioned.
Yep, it shouldn't. You have to be more careful what you commit. You can unmerge your changes or rely on me reverting it when this will be merged.

> But not the dismantle. It works for me. Which version of widelands do you
> have?
Current trunk obviously :). I am really reluctant to merge this for it is not clear what you did. Your branch contains a lot of lines where only whitespace changes happened (something which you should avoid. Use bzr diff or qdiff or similar to check that you do not do that). Some stuff is juggled around and in some places you changed one magic constant into another. What makes you believe that your AI is indeed an improvement?

Revision history for this message
Carli (s3734770) wrote :

I checked the source files with bzr diff, but i did not want to revert it because i _want_ to build with -j. (ok but i could have only committed the ai related files)

How should I comment the constant changes? In the commit message? I think a person who reads the source is not interested in the history of the constants.

Revision history for this message
SirVer (sirver) wrote :

At the very least you should comment in the source code. And you should fix up whitespace issues and repropose for merging.

I am sorry if I appear to be strict. I am sure your work makes widelands better. Nevertheless, we need to keep some standards int the quality of the code.

review: Needs Fixing
Revision history for this message
Nicolai Hähnle (nha) wrote :

Slightly off-topic for the discussion of this particular merge request, but this just highlights our need for some framework to properly judge the competitiveness of the AI, e.g. by letting different versions of it automatically play against each other.

Revision history for this message
Carli (s3734770) wrote :

This is also off-topic, but should i design the AI more learnable?

Revision history for this message
SirVer (sirver) wrote :

learnable is quite vague. Afaik most AI in games no longer use neural networks or similar because heuristics just perform so much better. Nevertheless, there is a bookshelve of literature on "good" game AI, some of them employ something like bayesian parameter tuning for "learning". That could be interesting of course.

Revision history for this message
Carli (s3734770) wrote :

With "learnable" i ment parameter tuning. My question was related to the fact that I changed some constants in the AI code. They shouldnt be constants. (Or be defined in a extra header)

Revision history for this message
Nicolai Hähnle (nha) wrote :

Yes, putting those parameters into a separate header or even a loadable file seems like a good idea. Even more so if it goes along with at least basic documentation of what the numbers actually mean.

Revision history for this message
SirVer (sirver) wrote :

Is this pull request still up for discussion? I.e. are you still planing to work on it carli?

Revision history for this message
Carli (s3734770) wrote :

> Is this pull request still up for discussion? I.e. are you still planing to
> work on it carli?

Currently not.
If someone would fix the C++ things with const vs & and such, it could be merged but otherwise I don't care.

Revision history for this message
SirVer (sirver) wrote :

Thanks for the feedback. Okay, I am setting this to rejected than to get it out of the review queue. It is unlikely that someone wants to clean up other peoples code :).

Unmerged revisions

6100. By Carli

Improved AI (is now able to defeat other AI enemies)

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'compile.sh'
2--- compile.sh 2010-11-15 21:23:02 +0000
3+++ compile.sh 2011-11-13 00:10:30 +0000
4@@ -136,7 +136,7 @@
5
6 echo " "
7 cmake -DWL_PORTABLE=true .. -DCMAKE_EXE_CXX_FLAGS="${CFLAGS}" -DCMAKE_BUILD_TYPE="${var_build_type}"
8- make ${MAKEOPTS}
9+ make ${MAKEOPTS} -j
10 return 0
11 }
12
13@@ -182,7 +182,7 @@
14 echo " "
15 echo "bzr pull"
16 echo "cd build"
17- echo "make"
18+ echo "make -j"
19 if [ $var_build_lang -eq 1 ] ; then
20 echo "make lang"
21 fi
22
23=== modified file 'src/ai/ai_help_structs.h'
24--- src/ai/ai_help_structs.h 2010-11-01 22:23:29 +0000
25+++ src/ai/ai_help_structs.h 2011-11-13 00:10:30 +0000
26@@ -268,6 +268,8 @@
27 uint8_t producers;
28 uint8_t consumers;
29 uint8_t preciousness;
30+ uint32_t build_prio;
31+ uint32_t fight_prio;
32 };
33
34 #endif
35
36=== modified file 'src/ai/defaultai.cc'
37--- src/ai/defaultai.cc 2011-10-12 21:05:21 +0000
38+++ src/ai/defaultai.cc 2011-11-13 00:10:30 +0000
39@@ -232,6 +232,30 @@
40 wares.at(i).producers = 0;
41 wares.at(i).consumers = 0;
42 wares.at(i).preciousness = tribe->get_ware_descr(i)->preciousness();
43+ wares.at(i).build_prio = 0;
44+ wares.at(i).fight_prio = 0;
45+ }
46+
47+ Ware_Index const nr_workers = tribe->get_nrworkers();
48+ for (Ware_Index i = Ware_Index::First(); i < nr_workers; ++i) {
49+ // Find all build costs for soldiers
50+ // is it a soldier?
51+ if (tribe->get_worker_descr(i)->get_worker_type() == Worker_Descr::SOLDIER) {
52+ Worker_Descr::Buildcost::const_iterator end = tribe->get_worker_descr(i)->buildcost().end();
53+ for
54+ (Worker_Descr::Buildcost::const_iterator it = tribe->get_worker_descr(i)->buildcost().begin();
55+ it != end; ++it)
56+ {
57+ if (tribe->ware_index(it->first))
58+ wares.at(tribe->ware_index(it->first)).fight_prio += it->second; // Add soldier-need priority
59+ }
60+ }
61+ }
62+
63+ for (Ware_Index i = Ware_Index::First(); i < nr_wares; ++i) {
64+ // We have three building sites
65+ wares.at(i).consumers += wares.at(i).build_prio > 0 ?
66+ (wares.at(i).build_prio > 30 ? 2 : 1) : 0;
67 }
68
69 // collect information about the different buildings our tribe can construct
70@@ -255,6 +279,12 @@
71 bo.current_stats = 100;
72 bo.unoccupied = false;
73
74+ // Find all build costs
75+ Buildcost::const_iterator end = bld.buildcost().end();
76+ for (Buildcost::const_iterator it = bld.buildcost().begin(); it != end; ++it) {
77+ wares.at(it->first).build_prio += it->second; // Add build-need priority
78+ }
79+
80 bo.is_basic = false;
81
82 bo.is_buildable = bld.is_buildable();
83@@ -278,7 +308,8 @@
84 BuildingObserver::PRODUCTIONSITE;
85
86 container_iterate_const(Ware_Types, prod.inputs(), j)
87- bo.inputs.push_back(j.current->first.value());
88+ for (uint32_t i = 0; i < j.current->second; i++) // consider input count
89+ bo.inputs.push_back(j.current->first.value());
90
91 container_iterate_const
92 (ProductionSite_Descr::Output, prod.output_ware_types(), j)
93@@ -318,6 +349,17 @@
94 }
95 }
96
97+ for (Ware_Index i = Ware_Index::First(); i < nr_wares; ++i) {
98+ if (wares.at(i).build_prio)
99+ printf
100+ ("Ware %s ist construction material with priority %d (ToDo: use this information)\n",
101+ tribe->get_ware_descr(i)->name().data(), wares.at(i).build_prio);
102+ if (wares.at(i).fight_prio)
103+ printf
104+ ("Ware %s ist fight material with priority %d (ToDo: use this information)\n",
105+ tribe->get_ware_descr(i)->name().data(), wares.at(i).fight_prio);
106+ }
107+
108 total_constructionsites = 0;
109 next_construction_due = 0;
110 next_road_due = 1000;
111@@ -767,7 +809,7 @@
112 spots += spots_avail.at(BUILDCAPS_MEDIUM);
113 spots += spots_avail.at(BUILDCAPS_BIG);
114 if (type == AGGRESSIVE)
115- spots -= militarysites.size() / 20;
116+ spots -= militarysites.size() / 30;
117 if (spots < 16)
118 expand_factor *= 2;
119 if ((type == AGGRESSIVE) && spots < 32)
120@@ -791,7 +833,7 @@
121 //if (TODO) expand_factor = 0;
122
123 // Defensive AIs also attack sometimes (when they want to expand)
124- if (type == DEFENSIVE && expand_factor > 1)
125+ if (expand_factor > 1)
126 if (next_attack_consideration_due <= game().get_gametime())
127 consider_attack(game().get_gametime());
128
129@@ -863,8 +905,8 @@
130 continue;
131 if (bo.need_trees) {
132 // Priority of woodcutters depend on the number of near trees
133- prio += bf->trees_nearby * 3;
134- prio /= 3 * (1 + bf->producers_nearby.at(bo.outputs.at(0)));
135+ prio += bf->trees_nearby * 4;
136+ prio /= 2 * (1 + bf->producers_nearby.at(bo.outputs.at(0)));
137
138 // TODO improve this - it's still useless to place lumberjack huts randomly
139 /*if (prio <= 0) // no, sometimes we need wood without having a forest
140@@ -898,6 +940,7 @@
141 // production hint (f.e. associate forester with trunks)
142
143 // Calculate the need for this building
144+
145 int16_t inout = wares.at(bo.production_hint).consumers;
146 if
147 (tribe->safe_ware_index("trunk").value()
148@@ -907,6 +950,8 @@
149 inout -= wares.at(bo.production_hint).producers;
150 if (inout < 1)
151 inout = 1;
152+ if (inout > 3)
153+ inout = 2;
154 // the ware they're refreshing
155 Ware_Index wt(static_cast<size_t>(bo.production_hint));
156 container_iterate(std::list<EconomyObserver *>, economies, l) {
157@@ -948,12 +993,23 @@
158 prio = recalc_with_border_range(*bf, prio);
159 } else { // "normal" productionsites
160
161- // ToDo: prefer soldier producing things
162 // Ware_Index const soldier_index = tribe().worker_index("soldier");
163
164 if (bo.is_basic && (bo.total_count() == 0))
165 prio += 100; // for very important buildings
166
167+
168+
169+ // we can enhance this building. build more
170+ // maybe the enhancement can produce needed ware
171+ if (bo.desc->enhancements().size() > 0) {
172+ // this code builds more metalworks
173+ if (bo.total_count() == 0)
174+ prio += 10;
175+ if (bo.total_count() == 1)
176+ prio += 2;
177+ }
178+
179 // Check if the produced wares are needed
180 container_iterate(std::list<EconomyObserver *>, economies, l) {
181 // Don't check if the economy has no warehouse.
182@@ -968,6 +1024,11 @@
183 (*l.current)->economy.ware_target_quantity(wt).permanent)
184 prio -= 20;
185
186+ // if we have none of them, but they are construction material, build!
187+ if (bo.total_count() == 0 && wares.at(bo.outputs.at(m)).build_prio) {
188+ prio += 100;
189+ }
190+
191 // if the economy needs this ware
192 if ((*l.current)->economy.needs_ware(wt)) {
193 prio += 1 + wares.at(bo.outputs.at(m)).preciousness;
194@@ -975,16 +1036,6 @@
195 // big bonus, this site might be elemental
196 prio += 3 * wares.at(bo.outputs.at(m)).preciousness;
197 }
198-
199- // we can enhance this building. build more
200- // maybe the enhancement can produce needed ware
201- if (bo.desc->enhancements().size() > 0) {
202- // this code builds more metalworks
203- if (bo.total_count() == 0)
204- prio += 2;
205- if (bo.total_count() == 1)
206- prio += 8;
207- }
208 }
209 for (uint32_t m = 0; m < bo.inputs.size(); ++m) {
210 Ware_Index wt(static_cast<size_t>(bo.inputs.at(m)));
211@@ -1018,14 +1069,14 @@
212
213 // do not construct more than one building,
214 // if supply line is already broken.
215- if (!check_supply(bo) && bo.total_count() > 0)
216+ if (bo.total_count() > 0 && !check_supply(bo))
217 prio -= 12;
218
219 }
220 } else if (bo.type == BuildingObserver::MILITARYSITE) {
221 if (!bf->unowned_land_nearby)
222 continue;
223- prio = bf->unowned_land_nearby * (1 + type);
224+ prio = bf->unowned_land_nearby * (2 + type);
225 prio -= bf->military_influence * (5 - type);
226 // set to at least 1
227 prio = prio > 0 ? prio : 1;
228@@ -1033,14 +1084,14 @@
229 prio /= 2;
230
231 if (bf->enemy_nearby)
232- prio *= 2;
233+ prio = (prio * 5) / 4;
234 else
235 prio -= bf->military_influence * 2;
236
237 if (bf->avoid_military)
238 prio /= 5;
239
240- prio -= militarysites.size() - productionsites.size() / (3 - type);
241+ prio -= militarysites.size() - productionsites.size() / (4 - type);
242
243 } else if (bo.type == BuildingObserver::WAREHOUSE) {
244 // Build one warehouse for ~every 35 productionsites and mines.
245@@ -1209,7 +1260,7 @@
246 // multiply with current statistics of all other buildings of this
247 // type to avoid constructing buildings where already some are running
248 // on low resources.
249- prio *= 5 + bo.current_stats;
250+ prio *= 40 + bo.current_stats;
251 prio /= 100;
252
253 if (onlymissing) // mines aren't *that* important
254@@ -1613,7 +1664,7 @@
255 // destruct the building and it's flag (via flag destruction)
256 // the destruction of the flag avoids that defaultAI will have too many
257 // unused roads - if needed the road will be rebuild directly.
258- game().send_player_bulldoze(site.site->base_flag());
259+ game().send_player_dismantle(*(site.site));
260 return true;
261 }
262 }
263@@ -1632,7 +1683,7 @@
264 // destruct the building and it's flag (via flag destruction)
265 // the destruction of the flag avoids that defaultAI will have too many
266 // unused roads - if needed the road will be rebuild directly.
267- game().send_player_bulldoze(site.site->base_flag());
268+ game().send_player_dismantle(*(site.site));
269 return true;
270 }
271
272@@ -1662,7 +1713,7 @@
273 //
274 // Add a bonus if one building of this type is still unoccupied
275 if (((game().get_gametime() % 4) + site.bo->unoccupied) > 2) {
276- game().send_player_bulldoze(site.site->base_flag());
277+ game().send_player_dismantle(*(site.site));
278 return true;
279 }
280 else
281@@ -1675,7 +1726,7 @@
282
283 // Do not have too many constructionsites
284 uint32_t producers = mines.size() + productionsites.size();
285- if (total_constructionsites >= (5 + (producers / 10)))
286+ if (total_constructionsites >= (5 + (producers / 5)))
287 return false;
288
289 // Check whether building is enhanceable and if wares of the enhanced
290@@ -1709,7 +1760,7 @@
291 Ware_Index wt(static_cast<size_t>(current_outputs.at(j)));
292 if (site.site->economy().needs_ware(wt))
293 prio -=
294- (2 + wares.at(current_outputs.at(j)).preciousness) / 2;
295+ (2 + wares.at(current_outputs.at(j)).preciousness) / 4;
296 continue;
297 }
298 new_outputs.push_back(static_cast<int16_t>(i));
299@@ -1718,28 +1769,30 @@
300 // Check if the new wares are needed in economy of the building
301 for (uint32_t i = 0; i < new_outputs.size(); ++i) {
302 Ware_Index wt(static_cast<size_t>(new_outputs.at(i)));
303- if (site.site->economy().needs_ware(wt))
304+ if (site.site->economy().needs_ware(wt) || wares.at(wt).fight_prio)
305 prio += 2 + wares.at(new_outputs.at(i)).preciousness;
306 }
307
308 // Compare the number of buildings of current type with the number
309 // of buildings of enhanced type
310- prio += (site.bo->total_count() - en_bo.total_count()) * 2;
311+ prio += (2 * site.bo->total_count() - en_bo.total_count());
312
313 // If the new wares are needed
314 if (prio > 0) {
315 prio = calculate_need_for_ps(en_bo, prio);
316 if (prio > maxprio) {
317+ // we always enhance if it is the first building of this type
318+ if (en_bo.total_count() == 0) prio += 500;
319 maxprio = prio;
320 enbld = (*x.current);
321 }
322- }
323+ };
324 }
325 }
326
327 // Enhance if enhanced building is useful
328 // additional: we dont want to lose the old building
329- if (maxprio > 0 && site.bo->total_count() > 1) {
330+ if (maxprio > 0 && (maxprio > 500 || site.bo->total_count() > 1)) {
331 game().send_player_enhance_building(*site.site, enbld);
332 changed = true;
333 }
334@@ -1778,7 +1831,7 @@
335 // destruct the building and it's flag (via flag destruction)
336 // the destruction of the flag avoids that defaultAI will have too many
337 // unused roads - if needed the road will be rebuild directly.
338- game().send_player_bulldoze(site.site->base_flag());
339+ game().send_player_dismantle(*(site.site));
340 return true;
341 }
342
343@@ -1880,7 +1933,7 @@
344 (static_cast<int32_t>(ms->maxSoldierCapacity() * 4)
345 <
346 bf.military_influence)
347- game().send_player_bulldoze(ms->base_flag());
348+ game().send_player_dismantle(*ms);
349
350 // Else consider enhancing the building (if possible)
351 else {
352@@ -1995,7 +2048,7 @@
353 WareObserver & wo = wares.at(bo.outputs.at(k));
354 if (wo.consumers > 0) {
355 output_prio += wo.preciousness;
356- output_prio += wo.consumers * 2;
357+ output_prio += wo.consumers * 3;
358 output_prio -= wo.producers * 2;
359 if (bo.total_count() == 0)
360 output_prio += 10; // add a big bonus
361@@ -2006,12 +2059,18 @@
362 (ceil(output_prio / sqrt(static_cast<double>(bo.outputs.size()))));
363 prio += 2 * output_prio;
364
365+ if (!bo.inputs.empty() && !bo.outputs.empty() && bo.current_stats >= 80) {
366+ // extra bonus for high productivity
367+ // this should compensate various output count of a production step
368+ prio += 10;
369+ }
370+
371 // If building consumes some wares, multiply with current statistics of all
372 // other buildings of this type to avoid constructing buildings where already
373 // some are running on low resources.
374 // Else at least add a part of the stats t the calculation.
375 if (!bo.inputs.empty()) {
376- prio *= bo.current_stats;
377+ prio = (prio + 50) * bo.current_stats;
378 prio /= 100;
379 } else
380 prio = ((prio * bo.current_stats) / 100) + (prio / 2);
381@@ -2142,8 +2201,14 @@
382 militarysites.back().site = &ref_cast<MilitarySite, Building>(b);
383 militarysites.back().bo = &bo;
384 militarysites.back().checks = bo.desc->get_size();
385- } else if (bo.type == BuildingObserver::WAREHOUSE)
386+ } else if (bo.type == BuildingObserver::WAREHOUSE) {
387 ++numof_warehouses;
388+ Ware_Index const nr_wares = tribe->get_nrwares();
389+ for (Ware_Index i = Ware_Index::First(); i < nr_wares; ++i) {
390+ // Warehouses are consumers of weapons
391+ wares.at(i).consumers += 2 * wares.at(i).fight_prio;
392+ }
393+ }
394 }
395 }
396
397@@ -2207,6 +2272,11 @@
398 } else if (bo.type == BuildingObserver::WAREHOUSE) {
399 assert(numof_warehouses > 0);
400 --numof_warehouses;
401+ Ware_Index const nr_wares = tribe->get_nrwares();
402+ for (Ware_Index i = Ware_Index::First(); i < nr_wares; ++i) {
403+ // Warehouses are consumers of weapons
404+ wares.at(i).consumers -= 2 * wares.at(i).fight_prio;
405+ }
406 }
407 }
408 m_buildable_changed = true;
409@@ -2220,23 +2290,23 @@
410 // TODO: It needs profiling and optimization.
411 bool DefaultAI::check_supply(BuildingObserver const & bo)
412 {
413- size_t supplied = 0;
414 container_iterate_const(std::vector<int16_t>, bo.inputs, i)
415 container_iterate_const(std::vector<BuildingObserver>, buildings, j)
416 if
417 (j.current->cnt_built &&
418- std::find
419- (j.current->outputs.begin(), j.current->outputs.end(),
420- *i.current)
421- !=
422- j.current->outputs.end()
423- &&
424- check_supply(*j.current))
425+ std::find
426+ (j.current->outputs.begin(), j.current->outputs.end(),
427+ *i.current)
428+ !=
429+ j.current->outputs.end()
430+ &&
431+ check_supply(*j.current))
432 {
433- ++supplied;
434 break;
435+ } else {
436+ return false;
437 }
438- return supplied == bo.inputs.size();
439+ return true;
440 }
441
442
443@@ -2277,12 +2347,12 @@
444 continue;
445 if (bld->canAttack()) {
446 int32_t ta = player->findAttackSoldiers(bld->base_flag());
447- if (type == NORMAL)
448- ta = ta * 2 / 3;
449+ if (type != AGGRESSIVE)
450+ ta = (ta * 2) / 3;
451 if (ta < 1)
452 continue;
453
454- int32_t const tc = ta - bld->presentSoldiers().size();
455+ int32_t const tc = ta * 2 - bld->presentSoldiers().size() * 3;
456 if (tc > chance) {
457 target = bld;
458 chance = tc;
459@@ -2313,7 +2383,7 @@
460
461 // Return if chance to win is too low
462 if (chance < 3) {
463- next_attack_consideration_due = gametime % 7 * 1000 + gametime;
464+ next_attack_consideration_due = (2 + gametime % 7) * 1000 + gametime;
465 return false;
466 }
467
468@@ -2326,6 +2396,6 @@
469 (target->base_flag(), pn, attackers, retreat);
470
471 // Do not attack again too soon - returning soldiers must get healed first.
472- next_attack_consideration_due = (gametime % 51 + 10) * 1000 + gametime;
473+ next_attack_consideration_due = (gametime % 51 + 20) * 1000 + gametime;
474 return true;
475 }
476
477=== modified file 'src/ui_basic/table.cc'
478--- src/ui_basic/table.cc 2011-11-06 14:23:36 +0000
479+++ src/ui_basic/table.cc 2011-11-13 00:10:30 +0000
480@@ -429,6 +429,7 @@
481 return result;
482 }
483
484+
485 /**
486 * Scroll to the given position, in pixels.
487 */

Subscribers

People subscribed via source and target branches

to status/vote changes: