Refactor the wage function for readability.

Add a test for wages at different castle sizes.
This commit is contained in:
Enno Rehling 2020-09-16 18:53:35 +02:00
parent 6868cbb928
commit 8c62bd95cd
10 changed files with 147 additions and 58 deletions

View File

@ -1363,9 +1363,11 @@ static void cr_output_region(FILE * F, report_context * ctx, region * r)
fprintf(F, "%d;Rekruten\n", rpeasants(r) / RECRUITFRACTION); fprintf(F, "%d;Rekruten\n", rpeasants(r) / RECRUITFRACTION);
} }
if (max_production(r)) { if (max_production(r)) {
int p_wage = wage(r, NULL, NULL, turn + 1); /* Im CR steht der Bauernlohn, der bei Trauer nur 10 ist */
bool mourn = is_mourning(r, turn);
int p_wage = peasant_wage(r, mourn);
fprintf(F, "%d;Lohn\n", p_wage); fprintf(F, "%d;Lohn\n", p_wage);
if (is_mourning(r, turn + 1)) { if (mourn) {
fputs("1;mourning\n", F); fputs("1;mourning\n", F);
} }
} }

View File

@ -1993,7 +1993,8 @@ expandwork(region * r, econ_request * work_begin, econ_request * work_end, int m
/* n: verbleibende Einnahmen */ /* n: verbleibende Einnahmen */
/* fishes: maximale Arbeiter */ /* fishes: maximale Arbeiter */
int jobs = maxwork; int jobs = maxwork;
int p_wage = wage(r, NULL, NULL, turn); bool mourn = is_mourning(r, turn);
int p_wage = peasant_wage(r, mourn);
int money = rmoney(r); int money = rmoney(r);
if (total > 0 && !rule_autowork()) { if (total > 0 && !rule_autowork()) {
econ_request *o; econ_request *o;
@ -2017,7 +2018,7 @@ expandwork(region * r, econ_request * work_begin, econ_request * work_end, int m
assert(workers >= 0); assert(workers >= 0);
u->n = workers * wage(u->region, u->faction, u_race(u), turn); u->n = workers * wage(u->region, u_race(u));
jobs -= workers; jobs -= workers;
assert(jobs >= 0); assert(jobs >= 0);
@ -2061,7 +2062,7 @@ static int work_cmd(unit * u, order * ord, econ_request ** io_req)
} }
return 0; return 0;
} }
w = wage(r, u->faction, u_race(u), turn); w = wage(r, u_race(u));
add_request(req++, ECON_WORK, u, ord, w * u->number); add_request(req++, ECON_WORK, u, ord, w * u->number);
*io_req = req; *io_req = req;
return u->number; return u->number;

View File

@ -495,20 +495,22 @@ extern struct attrib_type at_icastle;
/** returns the building's build stage (NOT size in people). /** returns the building's build stage (NOT size in people).
* only makes sense for castles or similar buildings with multiple * only makes sense for castles or similar buildings with multiple
* stages */ * stages */
int buildingeffsize(const building * b, int img) int buildingeffsize(const building * b, bool imaginary)
{ {
const struct building_type *btype = NULL; const struct building_type *btype = NULL;
if (b == NULL) if (b == NULL)
return 0; return 0;
btype = b->type; if (imaginary) {
if (img) {
const attrib *a = a_find(b->attribs, &at_icastle); const attrib *a = a_find(b->attribs, &at_icastle);
if (a) { if (a) {
btype = (const struct building_type *)a->data.v; btype = (const struct building_type *)a->data.v;
} }
} }
else {
btype = b->type;
}
return bt_effsize(btype, b, b->size); return bt_effsize(btype, b, b->size);
} }
@ -518,7 +520,7 @@ int bt_effsize(const building_type * btype, const building * b, int bsize)
bsize = adjust_size(b, bsize); bsize = adjust_size(b, bsize);
} }
if (btype->stages) { if (btype && btype->stages) {
int n = 0; int n = 0;
const building_stage *stage = btype->stages; const building_stage *stage = btype->stages;
do { do {
@ -728,7 +730,7 @@ static const int wagetable[7][3] = {
}; };
static int static int
default_wage(const region * r, const faction * f, const race * rc, int in_turn) default_wage(const region * r, const race * rc)
{ {
building *b = largestbuilding(r, cmp_wage, false); building *b = largestbuilding(r, cmp_wage, false);
int esize = 0; int esize = 0;
@ -739,27 +741,21 @@ default_wage(const region * r, const faction * f, const race * rc, int in_turn)
esize = buildingeffsize(b, false); esize = buildingeffsize(b, false);
} }
if (f != NULL) { if (rc != NULL) {
static const struct race *rc_orc, *rc_snotling;
static int rc_cache;
int index = 0; int index = 0;
if (rc == get_race(RC_ORC) || rc == get_race(RC_SNOTLING)) { if (rc_changed(&rc_cache)) {
rc_orc = get_race(RC_ORC);
rc_snotling = get_race(RC_SNOTLING);
}
if (rc == rc_orc || rc == rc_snotling) {
index = 1; index = 1;
} }
wage = wagetable[esize][index]; wage = wagetable[esize][index];
} }
else { else {
if (is_mourning(r, in_turn)) { wage = wagetable[esize][2];
wage = 10;
}
else if (fval(r->terrain, SEA_REGION)) {
wage = 11;
}
else {
wage = wagetable[esize][2];
}
if (r->attribs && rule_blessed_harvest() == HARVEST_WORK) {
/* E1 rules */
wage += harvest_effect(r);
}
} }
if (r->attribs) { if (r->attribs) {
@ -767,6 +763,10 @@ default_wage(const region * r, const faction * f, const race * rc, int in_turn)
curse *c; curse *c;
variant vm; variant vm;
if (rule_blessed_harvest() & HARVEST_WORK) {
/* In E3 */
wage += harvest_effect(r);
}
/* Godcurse: Income -10 */ /* Godcurse: Income -10 */
vm = frac_make(wage, 1); vm = frac_make(wage, 1);
@ -786,31 +786,37 @@ default_wage(const region * r, const faction * f, const race * rc, int in_turn)
} }
static int static int
minimum_wage(const region * r, const faction * f, const race * rc, int in_turn) minimum_wage(const region * r, const race * rc)
{ {
if (f && rc) { if (rc) {
return rc->maintenance; return rc->maintenance;
} }
return default_wage(r, f, rc, in_turn); return default_wage(r, rc);
} }
/** /**
* Gibt Arbeitslohn fuer entsprechende Rasse zurueck, oder fuer * Gibt Arbeitslohn fuer entsprechende Rasse zurueck, oder fuer
* die Bauern wenn f == NULL. */ * die Bauern wenn rc == NULL. */
int wage(const region * r, const faction * f, const race * rc, int in_turn) int wage(const region * r, const race * rc)
{ {
static int config; static int config;
static int rule_wage; static int rule_wage;
if (config_changed(&config)) { if (config_changed(&config)) {
rule_wage = config_get_int("rules.wage.function", 1); rule_wage = config_get_int("rules.wage.function", 1);
} }
if (rule_wage==0) { if (rule_wage == 0) {
return 0; return 0;
} }
if (rule_wage==1) {
return default_wage(r, f, rc, in_turn); if (rule_wage == 1) {
return default_wage(r, rc);
} }
return minimum_wage(r, f, rc, in_turn); return minimum_wage(r, rc);
}
int peasant_wage(const struct region *r, bool mourn)
{
return mourn ? 10 : wage(r, NULL);
} }
int cmp_wage(const struct building *b, const building * a) int cmp_wage(const struct building *b, const building * a)

View File

@ -116,8 +116,8 @@ extern "C" {
int id, int size, struct order *ord); int id, int size, struct order *ord);
bool building_finished(const struct building *b); bool building_finished(const struct building *b);
int wage(const struct region *r, const struct faction *f, int wage(const struct region *r, const struct race *rc);
const struct race *rc, int in_turn); int peasant_wage(const struct region *r, bool mourn);
typedef int(*cmp_building_cb) (const struct building * b, typedef int(*cmp_building_cb) (const struct building * b,
const struct building * a); const struct building * a);
@ -130,7 +130,7 @@ extern "C" {
int building_taxes(const building *b); int building_taxes(const building *b);
/* old functions, still in build.c: */ /* old functions, still in build.c: */
int buildingeffsize(const building * b, int imaginary); int buildingeffsize(const building * b, bool imaginary);
void bhash(struct building *b); void bhash(struct building *b);
void bunhash(struct building *b); void bunhash(struct building *b);
int buildingcapacity(const struct building *b); int buildingcapacity(const struct building *b);

View File

@ -434,6 +434,80 @@ static void test_cmp_castle_size(CuTest *tc) {
test_teardown(); test_teardown();
} }
static void test_wage(CuTest *tc) {
region *r;
building *b;
building_type *btype;
struct building_stage *stage;
race *rc_orc, *rc_elf;
test_setup();
rc_orc = test_create_race("orc");
rc_elf = test_create_race("elf");
rc_elf->maintenance = 13;
btype = test_create_buildingtype("castle");
stage = btype->stages;
stage->construction->maxsize = 2; /* site */
stage = stage->next = calloc(1, sizeof(struct building_stage));
stage->construction = calloc(1, sizeof(struct construction));
stage->construction->maxsize = 8; /* tradepost */
stage = stage->next = calloc(1, sizeof(struct building_stage));
stage->construction = calloc(1, sizeof(struct construction));
stage->construction->maxsize = 40; /* fortification */
stage = stage->next = calloc(1, sizeof(struct building_stage));
stage->construction = calloc(1, sizeof(struct construction));
stage->construction->maxsize = 200; /* fortification */
r = test_create_plain(0, 0);
CuAssertIntEquals(tc, 10, wage(r, rc_elf));
CuAssertIntEquals(tc, 10, wage(r, rc_orc));
CuAssertIntEquals(tc, 11, peasant_wage(r, false));
CuAssertIntEquals(tc, 10, peasant_wage(r, true));
b = test_create_building(r, btype);
b->size = 1;
CuAssertIntEquals(tc, 0, buildingeffsize(b, false));
CuAssertIntEquals(tc, 10, wage(r, rc_elf));
CuAssertIntEquals(tc, 10, wage(r, rc_orc));
CuAssertIntEquals(tc, 11, peasant_wage(r, false));
CuAssertIntEquals(tc, 10, peasant_wage(r, true));
b->size = 2;
CuAssertIntEquals(tc, 1, buildingeffsize(b, false));
b->size = 9;
CuAssertIntEquals(tc, 1, buildingeffsize(b, false));
CuAssertIntEquals(tc, 10, wage(r, rc_elf));
CuAssertIntEquals(tc, 10, wage(r, rc_orc));
CuAssertIntEquals(tc, 11, peasant_wage(r, false));
CuAssertIntEquals(tc, 10, peasant_wage(r, true));
b->size = 10;
CuAssertIntEquals(tc, 2, buildingeffsize(b, false));
b->size = 49;
CuAssertIntEquals(tc, 2, buildingeffsize(b, false));
CuAssertIntEquals(tc, 11, wage(r, rc_elf));
CuAssertIntEquals(tc, 11, wage(r, rc_orc));
CuAssertIntEquals(tc, 12, peasant_wage(r, false));
CuAssertIntEquals(tc, 10, peasant_wage(r, true));
b->size = 50;
CuAssertIntEquals(tc, 3, buildingeffsize(b, false));
b->size = 249;
CuAssertIntEquals(tc, 3, buildingeffsize(b, false));
CuAssertIntEquals(tc, 12, wage(r, rc_elf));
CuAssertIntEquals(tc, 11, wage(r, rc_orc));
CuAssertIntEquals(tc, 13, peasant_wage(r, false));
CuAssertIntEquals(tc, 10, peasant_wage(r, true));
b->size = 250;
CuAssertIntEquals(tc, 4, buildingeffsize(b, false));
CuAssertIntEquals(tc, 13, wage(r, rc_elf));
CuAssertIntEquals(tc, 12, wage(r, rc_orc));
CuAssertIntEquals(tc, 14, peasant_wage(r, false));
CuAssertIntEquals(tc, 10, peasant_wage(r, true));
config_set_int("rules.wage.function", 1);
CuAssertIntEquals(tc, 13, wage(r, rc_elf));
config_set_int("rules.wage.function", 0);
CuAssertIntEquals(tc, 0, wage(r, rc_elf));
config_set_int("rules.wage.function", 2);
CuAssertIntEquals(tc, rc_elf->maintenance, wage(r, rc_elf));
test_teardown();
}
static void test_cmp_wage(CuTest *tc) { static void test_cmp_wage(CuTest *tc) {
region *r; region *r;
building *b1, *b2; building *b1, *b2;
@ -619,6 +693,7 @@ CuSuite *get_building_suite(void)
SUITE_ADD_TEST(suite, test_cmp_castle_size); SUITE_ADD_TEST(suite, test_cmp_castle_size);
SUITE_ADD_TEST(suite, test_cmp_taxes); SUITE_ADD_TEST(suite, test_cmp_taxes);
SUITE_ADD_TEST(suite, test_cmp_wage); SUITE_ADD_TEST(suite, test_cmp_wage);
SUITE_ADD_TEST(suite, test_wage);
SUITE_ADD_TEST(suite, test_cmp_current_owner); SUITE_ADD_TEST(suite, test_cmp_current_owner);
SUITE_ADD_TEST(suite, test_register_building); SUITE_ADD_TEST(suite, test_register_building);
SUITE_ADD_TEST(suite, test_btype_defaults); SUITE_ADD_TEST(suite, test_btype_defaults);

View File

@ -42,7 +42,7 @@ extern "C" {
bool rule_stealth_anon(void); /* units can anonymize their faction, TARNE PARTEI [NICHT] */ bool rule_stealth_anon(void); /* units can anonymize their faction, TARNE PARTEI [NICHT] */
int rule_alliance_limit(void); int rule_alliance_limit(void);
int rule_faction_limit(void); int rule_faction_limit(void);
#define HARVEST_WORK 0x00 #define HARVEST_WORK 0x02
#define HARVEST_TAXES 0x01 #define HARVEST_TAXES 0x01
int rule_blessed_harvest(void); int rule_blessed_harvest(void);
#define GIVE_SELF 1 #define GIVE_SELF 1

View File

@ -602,7 +602,7 @@ int rpeasants(const region * r)
return value; return value;
} }
void rsetpeasants(region * r, int value) int rsetpeasants(region * r, int value)
{ {
assert(r->land || value==0); assert(r->land || value==0);
assert(value >= 0); assert(value >= 0);
@ -612,7 +612,9 @@ void rsetpeasants(region * r, int value)
value = USHRT_MAX; value = USHRT_MAX;
} }
r->land->peasants = (unsigned short)value; r->land->peasants = (unsigned short)value;
return r->land->peasants;
} }
return 0;
} }
int rmoney(const region * r) int rmoney(const region * r)
@ -746,17 +748,16 @@ int rsettrees(const region * r, int ageclass, int value)
{ {
if (!r->land) { if (!r->land) {
assert(value == 0); assert(value == 0);
return 0;
}
assert(value >= 0);
if (value < MAXTREES) {
r->land->trees[ageclass] = value;
} }
else { else {
assert(value >= 0); r->land->trees[ageclass] = MAXTREES;
if (value <= MAXTREES) {
return r->land->trees[ageclass] = value;
}
else {
r->land->trees[ageclass] = MAXTREES;
}
} }
return 0; return r->land->trees[ageclass];
} }
region *region_create(int uid) region *region_create(int uid)
@ -1095,11 +1096,10 @@ void init_region(region *r)
if (!fval(r, RF_CHAOTIC)) { if (!fval(r, RF_CHAOTIC)) {
int peasants; int peasants;
int p_wage = 1 + peasant_wage(r, false) + rng_int() % 5;
peasants = (region_maxworkers(r) * (20 + dice(6, 10))) / 100; peasants = (region_maxworkers(r) * (20 + dice(6, 10))) / 100;
if (peasants < 100) peasants = 100; if (peasants < 100) peasants = 100;
rsetpeasants(r, peasants); rsetmoney(r, rsetpeasants(r, peasants) * p_wage);
rsetmoney(r, rpeasants(r) * ((wage(r, NULL, NULL,
INT_MAX) + 1) + rng_int() % 5));
} }
} }

View File

@ -176,7 +176,7 @@ extern "C" {
int rsettrees(const struct region *r, int ageclass, int value); int rsettrees(const struct region *r, int ageclass, int value);
int rpeasants(const struct region *r); int rpeasants(const struct region *r);
void rsetpeasants(struct region *r, int value); int rsetpeasants(struct region *r, int value);
int rmoney(const struct region *r); int rmoney(const struct region *r);
void rsetmoney(struct region *r, int value); void rsetmoney(struct region *r, int value);
int rhorses(const struct region *r); int rhorses(const struct region *r);

View File

@ -743,7 +743,8 @@ void immigration(void)
/* if less than 50 are in the region and there is space and no monster or demon units in the region */ /* if less than 50 are in the region and there is space and no monster or demon units in the region */
if (repopulate) { if (repopulate) {
int peasants = rpeasants(r); int peasants = rpeasants(r);
int income = wage(r, NULL, NULL, turn) - maintenance_cost(NULL) + 1; bool mourn = is_mourning(r, turn);
int income = peasant_wage(r, mourn) - maintenance_cost(NULL) + 1;
if (income >= 0 && r->land && (peasants < repopulate) && region_maxworkers(r) >(peasants + 30) * 2) { if (income >= 0 && r->land && (peasants < repopulate) && region_maxworkers(r) >(peasants + 30) * 2) {
int badunit = 0; int badunit = 0;
unit *u; unit *u;
@ -754,7 +755,7 @@ void immigration(void)
} }
} }
if (badunit == 0) { if (badunit == 0) {
peasants += (int)(rng_double()*income); peasants += (int)(rng_double() * income);
rsetpeasants(r, peasants); rsetpeasants(r, peasants);
} }
} }
@ -836,8 +837,10 @@ void demographics(void)
if (r->age > 20) { if (r->age > 20) {
double mwp = fmax(region_maxworkers(r), 1); double mwp = fmax(region_maxworkers(r), 1);
bool mourn = is_mourning(r, turn);
int p_wage = peasant_wage(r, mourn);
double prob = double prob =
pow(rpeasants(r) / (mwp * wage(r, NULL, NULL, turn) * 0.13), 4.0) pow(rpeasants(r) / (mwp * p_wage * 0.13), 4.0)
* PLAGUE_CHANCE; * PLAGUE_CHANCE;
if (rng_double() < prob) { if (rng_double() < prob) {

View File

@ -1182,11 +1182,13 @@ static void report_statistics(struct stream *out, const region * r, const factio
if (max_production(r) && (!fval(r->terrain, SEA_REGION) if (max_production(r) && (!fval(r->terrain, SEA_REGION)
|| f->race == get_race(RC_AQUARIAN))) { || f->race == get_race(RC_AQUARIAN))) {
if (markets_module()) { /* hack */ if (markets_module()) { /* hack */
bool mourn = is_mourning(r, turn);
int p_wage = peasant_wage(r, mourn);
m = m =
msg_message("nr_stat_salary_new", "max", wage(r, NULL, NULL, turn + 1)); msg_message("nr_stat_salary_new", "max", p_wage);
} }
else { else {
m = msg_message("nr_stat_salary", "max", wage(r, f, f->race, turn + 1)); m = msg_message("nr_stat_salary", "max", wage(r, f->race));
} }
nr_render(m, f->locale, buf, sizeof(buf), f); nr_render(m, f->locale, buf, sizeof(buf), f);
paragraph(out, buf, 2, 2, 0); paragraph(out, buf, 2, 2, 0);
@ -1337,7 +1339,7 @@ report_template(const char *filename, report_context * ctx, const char *bom)
} }
rps_nowrap(out, buf); rps_nowrap(out, buf);
newline(out); newline(out);
sprintf(buf, "; ECheck Lohn %d", wage(r, f, f->race, turn + 1)); sprintf(buf, "; ECheck Lohn %d", wage(r, f->race));
rps_nowrap(out, buf); rps_nowrap(out, buf);
newline(out); newline(out);
newline(out); newline(out);