[Freeciv-Dev] (PR#12734) quick&dirty CM v5
[Top] [All Lists]
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index] [Thread Index]
Subject: |
[Freeciv-Dev] (PR#12734) quick&dirty CM v5 |
From: |
"Per I. Mathisen" <per@xxxxxxxxxxx> |
Date: |
Mon, 11 Apr 2005 14:34:20 -0700 |
Reply-to: |
bugs@xxxxxxxxxxx |
<URL: http://bugs.freeciv.org/Ticket/Display.html?id=12734 >
This patch is still quite raw. Look in server/cityturn.c to know what it
is doing before you mess around with it.
CHANGES:
- Generalized the priorities in which it will soft fail. Added API for
this to CM parameter.
- Luxury need calculation did not take into account citizens made happy
by effects. Now it does.
- The ugly distribute() calls are gone, replaced by a reverse lookup
table for distribute(), so that we can query what amount of trade we
need to achieve for a given minimum of a secondary output.
- We can now use trade instead of a secondary output rather accurately.
- We take specialists from unhappy citizens when this is possible.
- Refactored code a little bit.
- Addressed a number of points that Benoit made.
- It now does much less stupid things. For smallish cities (less than 8
in size), it almost always does the same as CM does, given the same CM
parameter. For larger cities, they diverge rapidly, with quick&dirty
favouring food and other high-minimum outputs much more highly.
TODO:
- Encapsulate the reverse lookup table so that we can handle queries over
the limit (breaking it down into multiple queries).
- Even more testing.
- Try to do away with the need to run a full city refresh first, for
server. This actually takes about 4-9 times as long as the quick&dirty
CM itself.
- Make code less ugly...
- Move it somewhere sane.
- Per
Index: common/city.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/city.c,v
retrieving revision 1.325
diff -u -r1.325 city.c
--- common/city.c 10 Apr 2005 23:55:24 -0000 1.325
+++ common/city.c 11 Apr 2005 21:19:53 -0000
@@ -2641,3 +2643,419 @@
unit_list_free(pcity->units_supported);
free(pcity);
}
+
+/**************************************************************************
+ Calculate necessary luxury to achieve goals for this city.
+
+ We ignore here the fact that specialists may be taken from unhappy
+ citizens when we no longer have any content ones left.
+**************************************************************************/
+int city_luxury_need(struct city *pcity, bool order, bool celebrate)
+{
+ struct player *pplayer = city_owner(pcity);
+ int result = 0;
+ int happy, content, unhappy, angry;
+
+ if (order || celebrate) {
+ /* We need to figure out how much luxuries we need. */
+ citizen_base_mood(pplayer, 0, &happy, &content, &unhappy, &angry,
+ pcity->size);
+ citizen_content_buildings(pcity, &content, &unhappy, &angry);
+ citizen_happy_units(pcity, &happy, &content, &unhappy, &angry);
+ citizen_happy_wonders(pcity, &happy, &content, &unhappy, &angry);
+ }
+ if (order || celebrate) {
+ /* Find out how much luxuries we need to make everyone content. */
+ result = MAX((angry * 2 + unhappy - happy) * HAPPY_COST, 0);
+ }
+ if (celebrate) {
+ /* Find out how much extra luxuries we will need to make enough
+ * citizens happy that we can celebrate. */
+ result += MAX(((pcity->size - happy + 1) / 2) * HAPPY_COST, 0);
+ }
+
+ return result;
+}
+
+/****************************** Fast CM **********************************/
+
+#define REVERSE_LIMIT 100
+#define GCALOG(...) if (pcity->debug) { freelog(LOG_NORMAL, __VA_ARGS__); }
+
+static const int SCIENCE = 0, TAX = 1, LUXURY = 2;
+static int reverse_calc[3][REVERSE_LIMIT];
+
+BV_DEFINE(bv_fail, O_MAX);
+
+struct best_result {
+ int value, x, y, sp;
+ int output[O_MAX];
+};
+
+/**************************************************************************
+ Check if our requirements hold up. If so, copy temp to best. We allow
+ either of trade or the secondary outputs meet our criteria.
+**************************************************************************/
+static void greedy_copy_if_better(struct player *pplayer,
+ bv_fail fail_reqs, struct best_result *temp,
+ struct best_result *best)
+{
+ /* There is enough trade, so it does not matter if we fail the
+ * secondary outputs directly. These are already counted as part of
+ * trade if tax rates are higher than zero for this output. */
+ if (!BV_ISSET(fail_reqs, O_TRADE)
+ && pplayer->economic.luxury > 0) {
+ BV_CLR(fail_reqs, O_LUXURY);
+ }
+ if (!BV_ISSET(fail_reqs, O_TRADE)
+ && pplayer->economic.science > 0) {
+ BV_CLR(fail_reqs, O_SCIENCE);
+ }
+ if (!BV_ISSET(fail_reqs, O_TRADE)
+ && pplayer->economic.gold > 0) {
+ BV_CLR(fail_reqs, O_GOLD);
+ }
+
+ /* If we can get the secondary output minimums anyhow, we do not
+ * need trade, since trade is just a means to secondary output. */
+ if (BV_ISSET(fail_reqs, O_TRADE)
+ && !BV_ISSET(fail_reqs, O_LUXURY)
+ && !BV_ISSET(fail_reqs, O_GOLD)
+ && !BV_ISSET(fail_reqs, O_SCIENCE)) {
+ BV_CLR(fail_reqs, O_TRADE);
+ }
+
+ /* If we fail none of our minimums, copy if higher. */
+ if (!BV_ISSET_ANY(fail_reqs) && temp->value > best->value) {
+ *best = *temp;
+ }
+}
+
+/**************************************************************************
+ Calculate reverse tax lookups. This is needed for the fast CM.
+**************************************************************************/
+static void city_precompute_taxes(struct player *pplayer)
+{
+ int i;
+ static int reverse_rates[3] = { -1, -1, -1 };
+
+ if (reverse_rates[SCIENCE] == pplayer->economic.science
+ && reverse_rates[LUXURY] == pplayer->economic.luxury) {
+ return; /* has not changed */
+ }
+
+ reverse_rates[SCIENCE] = pplayer->economic.science;
+ reverse_rates[LUXURY] = pplayer->economic.luxury;
+ reverse_rates[TAX] = 100 - reverse_rates[SCIENCE] - reverse_rates[LUXURY];
+ memset(reverse_calc, 0, sizeof(reverse_calc));
+
+ /* We run it in reverse to get the smallest possible amount of
+ * trade that gives us what we want, since we overwrite previous
+ * results. */
+ for (i = REVERSE_LIMIT - 1; i > 0 ; i--) {
+ int result[3];
+
+ distribute(i, 3, reverse_rates, result);
+ assert(result[SCIENCE] <= i);
+ assert(result[LUXURY] <= i);
+ assert(result[TAX] <= i);
+ reverse_calc[SCIENCE][result[SCIENCE]] = i;
+ reverse_calc[LUXURY][result[LUXURY]] = i;
+ reverse_calc[TAX][result[TAX]] = i;
+ }
+}
+
+/**************************************************************************
+ This function implements a greedy citizen allocation algorithm. It
+ is meant to be very fast, but not optimal.
+
+ The basic algorithm is as follows: Figure out (through pcity->usage[]
+ and some luxury magic) what our needs are, and place one tile at a
+ time, each aiming to produce to its share of what is necessary for
+ the whole city's needs.
+
+ We employ a number of tricks to make this work. First we try to delay
+ the necessary picks, so that perhaps picks by cmp->factor that are done
+ first may solve our needs by accident. Second, if we cannot find all
+ the requirements for a placement, we try to swap out requirements
+ for this placement with that of the next (push_retry). This solves
+ a number of 'deadlock'-like situations. Third, we drop requirements
+ as we detect situations we cannot handle, starting with gold, then
+ either food or luxuries, and lastly just dropping all requirements.
+
+ It is crucial to note that we do _not_ attempt to find a solution
+ where we end up with negative shields. The human user is expected
+ to solve these himself, and usually we end up throwing the city into
+ disorder. Through disorder this problem is made patently obvious.
+ Similarly for the AI, its emergency management code is meant to solve
+ situations like this, which can try to solve it by rehoming units,
+ for example.
+
+ We treat trade as a special case: We reverse the distribution of
+ secondary output from trade to get how much trade we need in order
+ to satisfy our secondary output minimums. We can either satisfy
+ our trade demand or our secondary minimum demand, given that we
+ have tax settings that allow us to do such conversions.
+
+ If we can take specialists from unhappy citizens (number of
+ base content is citizens is zero), we do so instead of grabbing
+ luxuries.
+
+ There are some problems with this approach, however. First, we treat
+ the secondary outputs (gold, science, luxuries) as a linear
+ accumulation, which they are not. Second, our calculation of city
+ effects on outputs is done on the gradual accumulation of outputs,
+ which is also not precise. Third, some pathological cases,
+ especially where we can only avoid disorder by _not_ picking the
+ most desirable tiles first, get us in trouble we cannot solve.
+
+ Not that we never give up - this function always returns a valid
+ result.
+
+ cmr->surplus does not return the correct result for secondary
+ outputs due to the special way we handle them here.
+
+ To debug, use "/debug city x y" (server-side usage only).
+
+ All cities passed to this function must have the following data
+ (and only the following data) up to date refreshed and valid:
+ * pcity->usage[]
+ * pcity->tile_output[][][]
+ * pcity->surplus[]
+ * pcity->city_map[][]
+ * pcity->size
+ * pcity->bonus
+
+ TODO:
+ - arrange only one or some citizens method (treat existing as
+ free tiles? and use no. new instead of city size in for loop?)
+ - for future tiles, increase food condition by FOOD_COST for each
+**************************************************************************/
+void greedy_citizen_arrange(struct city *pcity, const struct cm_parameter *cmp,
+ struct cm_result *cmr)
+{
+ struct player *pplayer = city_owner(pcity);
+ int minimums_priority = 0; /* minimums we will satisfy */
+ int minimum_stats[O_COUNT]; /* total requirements */
+ int requirements[O_COUNT][pcity->size]; /* requirements by citizen */
+ int sums[O_COUNT]; /* accumulated outputs */
+ bool taken[CITY_MAP_SIZE][CITY_MAP_SIZE]; /* taken worker positions */
+ int push_retry = 0; /* push requirements forward? */
+ double bonus[O_COUNT]; /* multipliers from effects */
+ int i, specialists = 0;
+ int content = content_citizens(pplayer);
+ int factor[O_COUNT];
+
+ memcpy(factor, cmp->factor, sizeof(factor));
+
+ /* Trade cannot be set to zero here. */
+ factor[O_TRADE] = MAX(factor[O_TRADE],
+ MAX(factor[O_LUXURY],
+ MAX(factor[O_GOLD],
+ factor[O_SCIENCE])));
+
+ /* Check secondary output reverse cache */
+ city_precompute_taxes(pplayer);
+
+ for (i = 0; i < O_COUNT && cmp->minimums_priority[i] != O_LAST; i++) {
+ minimums_priority++;
+ }
+
+ /*** Find minimums. ***/
+
+ output_type_iterate(o) {
+ minimum_stats[o] = pcity->usage[o];
+ bonus[o] = pcity->bonus[o] / 100;
+ } output_type_iterate_end;
+ minimum_stats[O_LUXURY] +=
+ city_luxury_need(pcity, !cmp->allow_disorder, cmp->require_happy);
+ minimum_stats[O_TRADE] += reverse_calc[SCIENCE][minimum_stats[O_SCIENCE]];
+ minimum_stats[O_TRADE] += reverse_calc[LUXURY][minimum_stats[O_LUXURY]];
+ minimum_stats[O_TRADE] += reverse_calc[TAX][minimum_stats[O_GOLD]];
+
+ /* Distribute workload among citizens. We try to put off most of the
+ * work as late as possible (for last citizens considered) in the hope
+ * that earlier labour will give us what we want anyway. */
+ memset(requirements, 0, sizeof(requirements));
+ output_type_iterate(o) {
+ int load = minimum_stats[o];
+
+ while (load > 0) {
+ for (i = pcity->size - 1; i >= 0 && load > 0; i--) {
+ requirements[o][i]++;
+ load--;
+ }
+ }
+ } output_type_iterate_end;
+
+ /*** Try to satisfy found minimums. ***/
+
+ /* Clear vars */
+ memset(sums, 0, sizeof(sums));
+ memset(cmr, 0, sizeof(*cmr));
+ city_map_iterate(x, y) {
+ taken[x][y] = FALSE;
+ if (!is_free_worked_tile(x, y)) {
+ cmr->worker_positions_used[x][y] = FALSE;
+ } else {
+ cmr->worker_positions_used[x][y] = TRUE;
+ }
+ } city_map_iterate_end;
+
+ /* Add city center (and other free tiles) */
+ city_map_iterate(x, y) {
+ /* Query: Can a free tile ever be unavailable in the future? */
+ /* Assumption: That is not very likely. In any case, checking for
+ * this may cause more problems than it is worth. */
+ if (is_free_worked_tile(x, y)) {
+ output_type_iterate(o) {
+ sums[o] += pcity->tile_output[x][y][o];
+ taken[x][y] = TRUE;
+ } output_type_iterate_end;
+ }
+ } city_map_iterate_end;
+
+ for (i = 0; i < pcity->size; i++) {
+ struct best_result best;
+
+ best.value = -1;
+ do {
+ /* Find best city tile */
+ city_map_iterate(x, y) {
+ bv_fail fail_reqs; /* do we satisfy current requirements? */
+ struct best_result temp;
+
+ if (taken[x][y] || pcity->city_map[x][y] == C_TILE_UNAVAILABLE) {
+ continue;
+ }
+ BV_CLR_ALL(fail_reqs);
+ output_type_iterate(o) {
+ temp.output[o] = pcity->tile_output[x][y][o];
+ } output_type_iterate_end;
+ temp.value = 0;
+ output_type_iterate(o) {
+ if (temp.output[o] * bonus[o] < requirements[o][i]
+ && sums[o] * bonus[o] < minimum_stats[o]) {
+ GCALOG("%s: pick %d, tile(%d, %d) not satisfy %s (want %d, got %d;"
+ " sum got %g, sum want %d)",
+ pcity->name, i, x, y, get_output_name(o),
requirements[o][i],
+ temp.output[o], sums[o] * bonus[o], minimum_stats[o]);
+ BV_SET(fail_reqs, o);
+ }
+ temp.value += temp.output[o] * factor[o];
+ } output_type_iterate_end;
+ temp.x = x;
+ temp.y = y;
+ temp.sp = -1;
+ greedy_copy_if_better(pplayer, fail_reqs, &temp, &best);
+ } city_map_iterate_end;
+
+ /* Find better specialist, if any */
+ specialist_type_iterate(sp) {
+ if (city_can_use_specialist(pcity, sp)) {
+ bv_fail fail_reqs; /* do we satisfy current requirements? */
+ struct best_result temp;
+
+ BV_CLR_ALL(fail_reqs);
+ output_type_iterate(o) {
+ temp.output[o] = game.rgame.specialists[sp].bonus[o];
+ } output_type_iterate_end;
+ if (specialists + 1 > content) {
+ /* We have an opportunity to take a specialist out of the
+ * ranks of the unhappy! This counts as if we had lux here. */
+ temp.output[O_LUXURY] += HAPPY_COST;
+ }
+ temp.value = 0;
+ output_type_iterate(o) {
+ if (temp.output[o] * bonus[o] < requirements[o][i]
+ && sums[o] * bonus[o] < minimum_stats[o]) {
+ BV_SET(fail_reqs, o);
+ }
+ temp.value += temp.output[o] * factor[o];
+ } output_type_iterate_end;
+ temp.x = -1;
+ temp.y = -1;
+ temp.sp = sp;
+ greedy_copy_if_better(pplayer, fail_reqs, &temp, &best);
+ }
+ } specialist_type_iterate_end;
+
+ /* Now we may or may not have a best candidate. If we don't, see
+ * if we can relax the requirements a bit and try again. Do not
+ * push twice in a row, though. */
+ if (best.value == -1) {
+ if (push_retry == 0 && i < pcity->size - 1) {
+ /* Try to push all other requirements ahead of us if we can,
+ * this solves some deadlock situations. We push our req from
+ * the next requirements level back, though, to avoid it next
+ * time. */
+ enum output_type chosen_req = O_LAST;
+ output_type_iterate(o) {
+ if (requirements[o][i] > 0
+ && sums[o] * bonus[o] < minimum_stats[o]
+ && chosen_req == O_LAST) {
+ chosen_req = o;
+ requirements[o][i] += requirements[o][i + 1];
+ requirements[o][i + 1] = 0;
+ } else if (requirements[o][i] > 0) {
+ requirements[o][i + 1] += requirements[o][i];
+ requirements[o][i] = 0;
+ }
+ } output_type_iterate_end;
+ push_retry += 2; /* now don't try this next citizen */
+ GCALOG("%s: pushed other reqs than %s ahead at %d", pcity->name,
+ get_output_name(chosen_req), i);
+ } else if (minimums_priority > 0) {
+ /* Relax least prioritized minimum */
+ minimum_stats[cmp->minimums_priority[minimums_priority - 1]] =
-FC_INFINITY;
+ GCALOG("%s: relaxed %s req at %d (priority %d)", pcity->name,
+ get_output_name(cmp->minimums_priority[minimums_priority -
1]),
+ i, minimums_priority);
+ minimums_priority--;
+ } else {
+ /* Rulesets should always have a specialist as a fallback. */
+ die("Neither usable tile nor specialist found for %s. Error in "
+ "ruleset?", pcity->name);
+ }
+ }
+ } while (best.value == -1);
+
+ /* Fill cmr with best result and move on to next citizen */
+ if (best.sp >= 0) {
+ cmr->specialists[best.sp]++;
+ specialists++;
+ } else {
+ cmr->worker_positions_used[best.x][best.y] = TRUE;
+ taken[best.x][best.y] = TRUE;
+ }
+ output_type_iterate(o) {
+ sums[o] += best.output[o];
+ } output_type_iterate_end;
+ output_type_iterate(o) {
+ GCALOG("%s pick %d (%d,%d|%d), here %d/%d, total %d/%d, of %s (p%d)",
+ pcity->name, i, best.x, best.y, best.sp,
+ best.output[o], requirements[o][i], sums[o], minimum_stats[o],
+ get_output_name(o), minimums_priority);
+ } output_type_iterate_end;
+ if (push_retry > 0) {
+ push_retry--;
+ }
+ }
+
+ output_type_iterate(o) {
+ cmr->surplus[o] = (sums[o] * bonus[o]) - pcity->usage[o];
+ } output_type_iterate_end;
+ /* Make an educated guess - ignoring external constraints */
+ if (minimum_stats[O_LUXURY] <= sums[O_LUXURY] && cmp->require_happy) {
+ cmr->happy = TRUE;
+ } else {
+ cmr->happy = FALSE;
+ }
+ if (minimum_stats[O_LUXURY] <= sums[O_LUXURY]
+ && (!cmp->allow_disorder || cmp->require_happy)) {
+ cmr->disorder = FALSE;
+ } else {
+ cmr->happy = TRUE;
+ }
+ cmr->found_a_valid = TRUE; /* Always */
+}
Index: common/city.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/city.h,v
retrieving revision 1.204
diff -u -r1.204 city.h
--- common/city.h 10 Apr 2005 21:53:30 -0000 1.204
+++ common/city.h 11 Apr 2005 21:19:53 -0000
@@ -525,6 +526,11 @@
int city_pollution_types(const struct city *pcity, int shield_total,
int *pollu_prod, int *pollu_pop, int *pollu_mod);
int city_pollution(const struct city *pcity, int shield_total);
+int city_luxury_need(struct city *pcity, bool order, bool celebrate);
+
+/* city management */
+void greedy_citizen_arrange(struct city *pcity, const struct cm_parameter *cmp,
+ struct cm_result *cmr);
/*
* Iterates over all improvements which are built in the given city.
Index: common/fc_types.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/fc_types.h,v
retrieving revision 1.17
diff -u -r1.17 fc_types.h
--- common/fc_types.h 29 Mar 2005 12:28:26 -0000 1.17
+++ common/fc_types.h 11 Apr 2005 21:19:53 -0000
@@ -54,6 +54,9 @@
struct tile;
struct unit;
+struct cm_parameter;
+struct cm_result;
+
#define SP_MAX 20
#define MAX_NUM_REQS 2
Index: common/aicore/cm.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/aicore/cm.c,v
retrieving revision 1.62
diff -u -r1.62 cm.c
--- common/aicore/cm.c 7 Apr 2005 04:36:02 -0000 1.62
+++ common/aicore/cm.c 11 Apr 2005 21:19:54 -0000
@@ -1859,6 +1859,7 @@
output_type_iterate(stat) {
dest->minimal_surplus[stat] = 0;
dest->factor[stat] = 1;
+ dest->minimums_priority[stat] = O_LAST;
} output_type_iterate_end;
dest->happy_factor = 1;
@@ -1876,6 +1877,7 @@
output_type_iterate(stat) {
dest->minimal_surplus[stat] = -FC_INFINITY;
dest->factor[stat] = 1;
+ dest->minimums_priority[stat] = O_LAST;
} output_type_iterate_end;
dest->happy_factor = 1;
Index: common/aicore/cm.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/common/aicore/cm.h,v
retrieving revision 1.13
diff -u -r1.13 cm.h
--- common/aicore/cm.h 3 Dec 2004 09:39:41 -0000 1.13
+++ common/aicore/cm.h 11 Apr 2005 21:19:54 -0000
@@ -26,6 +26,7 @@
* the weighted sum over the surplus of each type.
*/
+#include "fc_types.h"
#include "city.h" /* CITY_MAP_SIZE */
#include "shared.h" /* bool type */
@@ -38,6 +39,9 @@
int factor[O_MAX];
int happy_factor;
+
+ /* The priority in which we will attempt to achieve surplus minimums. */
+ Output_type_id minimums_priority[O_MAX];
};
/* A result which can examined. */
Index: server/cityturn.c
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/cityturn.c,v
retrieving revision 1.300
diff -u -r1.300 cityturn.c
--- server/cityturn.c 18 Mar 2005 11:26:24 -0000 1.300
+++ server/cityturn.c 11 Apr 2005 21:19:54 -0000
@@ -34,6 +34,7 @@
#include "support.h"
#include "tech.h"
#include "unit.h"
+#include "timing.h"
#include "citytools.h"
#include "gamelog.h"
@@ -145,7 +146,7 @@
Rearrange workers according to a cm_result struct. The caller must make
sure that the result is valid.
**************************************************************************/
-void apply_cmresult_to_city(struct city *pcity, struct cm_result *cmr)
+void apply_cmresult_to_city(struct city *pcity, const struct cm_result *cmr)
{
/* The caller had better check this! */
if (!cmr->found_a_valid) {
@@ -173,6 +174,7 @@
} specialist_type_iterate_end;
}
+#if 1
/**************************************************************************
You need to call sync_cities so that the affected cities are synced with
the client.
@@ -181,6 +183,14 @@
{
struct cm_parameter cmp;
struct cm_result cmr;
+ struct cm_result cmr2;
+ static struct timer *t1 = NULL;
+ static struct timer *t2 = NULL;
+
+ if (t1 == NULL) {
+ t1 = new_timer(TIMER_CPU, TIMER_ACTIVE);
+ t2 = new_timer(TIMER_CPU, TIMER_ACTIVE);
+ }
/* See comment in freeze_workers(): we can't rearrange while
* workers are frozen (i.e. multiple updates need to be done). */
@@ -210,6 +220,7 @@
cmp.require_happy = FALSE;
cmp.allow_disorder = FALSE;
cmp.allow_specialists = TRUE;
+ cmp.search_type = SEARCH_FIRST_SUFFICIENT_SOLUTION;
/* We used to look at pplayer->ai.xxx_priority to determine the values
* to be used here. However that doesn't work at all because those values
@@ -226,12 +237,17 @@
cmp.factor[O_FOOD] = 20;
}
cmp.factor[O_SHIELD] = 5;
- cmp.factor[O_TRADE] = 0; /* Trade only provides gold/science. */
+ cmp.factor[O_TRADE] = 0;
cmp.factor[O_GOLD] = 2;
- cmp.factor[O_LUXURY] = 0; /* Luxury only influences happiness. */
+ cmp.factor[O_LUXURY] = 0;
cmp.factor[O_SCIENCE] = 2;
cmp.happy_factor = 0;
+ cmp.minimums_priority[0] = O_LUXURY;
+ cmp.minimums_priority[1] = O_SHIELD;
+ cmp.minimums_priority[2] = O_FOOD;
+ cmp.minimums_priority[3] = O_GOLD;
+
cmp.minimal_surplus[O_FOOD] = 1;
cmp.minimal_surplus[O_SHIELD] = 1;
cmp.minimal_surplus[O_TRADE] = 0;
@@ -239,8 +255,14 @@
cmp.minimal_surplus[O_LUXURY] = 0;
cmp.minimal_surplus[O_SCIENCE] = 0;
- cm_query_result(pcity, &cmp, &cmr);
+ /* TEST */
+ start_timer(t2);
+ city_refresh(pcity);
+ greedy_citizen_arrange(pcity, &cmp, &cmr2);
+ stop_timer(t2);
+ start_timer(t1);
+ cm_query_result(pcity, &cmp, &cmr);
if (!cmr.found_a_valid) {
/* Drop surpluses and try again. */
cmp.minimal_surplus[O_FOOD] = 0;
@@ -266,14 +288,114 @@
cm_init_emergency_parameter(&cmp);
cm_query_result(pcity, &cmp, &cmr);
}
+ stop_timer(t1);
assert(cmr.found_a_valid);
+ assert(cmr2.found_a_valid);
- apply_cmresult_to_city(pcity, &cmr);
+ {
+ static int sum1 = 0, sum2 = 0;
+ struct ai_data *ai = ai_data_get(city_owner(pcity));
+ int total1, total2 = 0;
+
+ /* Compare */
+ apply_cmresult_to_city(pcity, &cmr);
+ city_refresh(pcity);
+ sanity_check_city(pcity);
+ total1 = ai_eval_calc_city(pcity, ai);
+
+ apply_cmresult_to_city(pcity, &cmr2);
+ city_refresh(pcity);
+ sanity_check_city(pcity);
+ total2 = ai_eval_calc_city(pcity, ai);
+
+ sum1 += total1 / 40;
+ sum2 += total2 / 40;
+ if (total1 != total2) {
+ freelog(LOG_NORMAL, "%s: CM=%d (%g)[%d], greed=%d (%g)[%d]", pcity->name,
+ total1, read_timer_seconds(t1), sum1, total2,
read_timer_seconds(t2),
+ sum2);
+// cm_print_result(pcity, &cmr);
+// cm_print_result(pcity, &cmr2);
+ }
+ }
+}
+#else
+/**************************************************************************
+ You need to call sync_cities so that the affected cities are synced with
+ the client.
+**************************************************************************/
+void auto_arrange_workers(struct city *pcity)
+{
+ struct cm_parameter cmp;
+ struct cm_result cmr;
+
+ /* See comment in freeze_workers(): we can't rearrange while
+ * workers are frozen (i.e. multiple updates need to be done). */
+ if (pcity->server.workers_frozen > 0) {
+ pcity->server.needs_arrange = TRUE;
+ return;
+ }
+
+ /* Freeze the workers and make sure all the tiles around the city
+ * are up to date. Then thaw, but hackishly make sure that thaw
+ * doesn't call us recursively, which would waste time. */
+ city_freeze_workers(pcity);
+ pcity->server.needs_arrange = FALSE;
+ map_city_radius_iterate(pcity->tile, ptile) {
+ update_city_tile_status_map(pcity, ptile);
+ } map_city_radius_iterate_end;
+
+ pcity->server.needs_arrange = FALSE;
+ city_thaw_workers(pcity);
+
+ /* Now start actually rearranging. */
sanity_check_city(pcity);
+ cm_init_parameter(&cmp);
+ cmp.require_happy = FALSE;
+ cmp.allow_disorder = FALSE;
+ cmp.minimums_priority[0] = O_LUXURY;
+ cmp.minimums_priority[1] = O_FOOD;
+ cmp.minimums_priority[2] = O_SHIELD;
+ cmp.minimums_priority[3] = O_GOLD;
+
+ /* We used to look at pplayer->ai.xxx_priority to determine the values
+ * to be used here. However that doesn't work at all because those values
+ * are on a different scale. Later the ai may wish to adjust its
+ * priorities - this should be done via a separate set of variables. */
+ if (pcity->size > 1) {
+ if (pcity->size <= game.notradesize) {
+ cmp.factor[O_FOOD] = 15;
+ } else {
+ cmp.factor[O_FOOD] = 10;
+ }
+ } else {
+ /* Growing to size 2 is the highest priority. */
+ cmp.factor[O_FOOD] = 20;
+ }
+ cmp.factor[O_SHIELD] = 5;
+ cmp.factor[O_TRADE] = 0;
+ cmp.factor[O_GOLD] = 2;
+ cmp.factor[O_LUXURY] = 0;
+ cmp.factor[O_SCIENCE] = 2;
+ cmp.happy_factor = 0;
+
city_refresh(pcity);
+
+ greedy_citizen_arrange(pcity, &cmp, &cmr);
+ apply_cmresult_to_city(pcity, &cmr);
+ city_refresh(pcity);
+ output_type_iterate(o) {
+ /* Compare actual results to calculated results */
+ if (pcity->surplus[o] != cmr.surplus[o]) {
+ freelog(LOG_NORMAL, "%s::%s imagined %d, actual %d", pcity->name,
+ get_output_name(o), cmr.surplus[o], pcity->surplus[o]);
+ }
+ } output_type_iterate_end;
+ sanity_check_city(pcity);
}
+#endif
/**************************************************************************
Notices about cities that should be sent to all players.
Index: server/cityturn.h
===================================================================
RCS file: /home/freeciv/CVS/freeciv/server/cityturn.h,v
retrieving revision 1.36
diff -u -r1.36 cityturn.h
--- server/cityturn.h 3 Sep 2004 04:22:37 -0000 1.36
+++ server/cityturn.h 11 Apr 2005 21:19:54 -0000
@@ -25,7 +25,7 @@
void global_city_refresh(struct player *pplayer); /* tax/govt changed */
void auto_arrange_workers(struct city *pcity); /* will arrange the workers */
-void apply_cmresult_to_city(struct city *pcity, struct cm_result *cmr);
+void apply_cmresult_to_city(struct city *pcity, const struct cm_result *cmr);
bool city_reduce_size(struct city *pcity, int pop_loss);
void send_global_city_turn_notifications(struct conn_list *dest);
- [Freeciv-Dev] Re: (PR#12734) A new, fast and greedy CM implementation, Jason Short, 2005/04/06
- [Freeciv-Dev] Re: (PR#12734) A new, fast and greedy CM implementation, Vasco Alexandre da Silva Costa, 2005/04/06
- [Freeciv-Dev] Re: (PR#12734) A new, fast and greedy CM implementation, Benoit Hudson, 2005/04/06
- [Freeciv-Dev] Re: (PR#12734) A new, fast and greedy CM implementation, Benoit Hudson, 2005/04/06
- [Freeciv-Dev] Re: (PR#12734) A new, fast and greedy CM implementation, Jason Short, 2005/04/06
- [Freeciv-Dev] Re: (PR#12734) A new, fast and greedy CM implementation, Per I. Mathisen, 2005/04/07
- [Freeciv-Dev] Re: (PR#12734) A new, fast and greedy CM implementation, Per I. Mathisen, 2005/04/07
- [Freeciv-Dev] Re: (PR#12734) A new, fast and greedy CM implementation, Per I. Mathisen, 2005/04/10
- [Freeciv-Dev] (PR#12734) calculate city's lux need, Per I. Mathisen, 2005/04/11
- [Freeciv-Dev] (PR#12734) calculate city's lux need, Per I. Mathisen, 2005/04/11
- [Freeciv-Dev] (PR#12734) quick&dirty CM v5,
Per I. Mathisen <=
- [Freeciv-Dev] Re: (PR#12734) calculate city's lux need, Benoit Hudson, 2005/04/12
- [Freeciv-Dev] Re: (PR#12734) calculate city's lux need, Jason Short, 2005/04/12
- [Freeciv-Dev] Re: (PR#12734) calculate city's lux need, Per I. Mathisen, 2005/04/13
|
|