diff --git a/res/core/messages.xml b/res/core/messages.xml index d983fe455..171c99b47 100644 --- a/res/core/messages.xml +++ b/res/core/messages.xml @@ -3658,6 +3658,13 @@ "$unit($unit): '$order($command)' - The unit already uses $resource($using,0)." "$unit($unit): '$order($command)' - The unit already uses $resource($using,0)." + + + + + "$if($eq($births,1),"Einen Bauern","$int($births) Bauern") besucht unverhofft der Storch." + "The stork paid an unexpected visit to $if($eq($births,1),"a peasant","$int($births) peasants")." + diff --git a/src/laws.c b/src/laws.c index d09f16c81..753ce0b8f 100755 --- a/src/laws.c +++ b/src/laws.c @@ -258,7 +258,58 @@ static void calculate_emigration(region * r) } } -/** Bauern vermehren sich */ + +static float peasant_growth_factor(void) +{ + return get_param_flt(global.parameters, "rules.peasants.growth.factor", 0.0001F * PEASANTGROWTH); +} + +#ifdef SLOWLUCK +int peasant_luck_effect(int peasants, int luck, int maxp, float variance) { + int n, births=0; + float factor = peasant_growth_factor(); + for (n = peasants; n && luck; --n) { + int chances = 0; + + if (luck > 0) { + --luck; + chances += PEASANTLUCK; + } + + while (chances--) { + if (rng_double() < factor) { + /* Only raise with 75% chance if peasants have + * reached 90% of maxpopulation */ + if (peasants / (float)maxp < 0.9 || chance(PEASANTFORCE)) { + ++births; + } + } + } + } + return births; +} +#else +static float peasant_luck_factor(void) +{ + return get_param_flt(global.parameters, "rules.peasants.peasantluck.factor", PEASANTLUCK); +} + +int peasant_luck_effect(int peasants, int luck, int maxp, float variance) +{ + int births = 0; + double mean = _min(luck, peasants) * peasant_luck_factor() + * peasant_growth_factor() * ((peasants / (float)maxp < .9) ? 1 : + PEASANTFORCE); + + births = RAND_ROUND(normalvariate(mean, variance * mean)); + if (births <= 0) + births = 1; + if (births > peasants / 2) + births = peasants / 2 + 1; + return births; +} + +#endif static void peasants(region * r) { @@ -268,42 +319,20 @@ static void peasants(region * r) int n, satiated; int dead = 0; - /* Bis zu 1000 Bauern können Zwillinge bekommen oder 1000 Bauern - * wollen nicht! */ - if (peasants > 0 && get_param_int(global.parameters, "rules.peasants.growth", 1)) { - int glueck = 0; - double fraction = peasants * 0.0001F * PEASANTGROWTH; - int births = (int)fraction; + int luck = 0; + double fraction = peasants * peasant_growth_factor(); + int births = RAND_ROUND(fraction); attrib *a = a_find(r->attribs, &at_peasantluck); - if (rng_double() < (fraction - births)) { - /* because we don't want regions that never grow pga. rounding. */ - ++births; - } if (a != NULL) { - glueck = a->data.i * 1000; + luck = a->data.i * 1000; } - for (n = peasants; n; --n) { - int chances = 0; + luck = peasant_luck_effect(peasants, luck, maxp, .5); + ADDMSG(&r->msgs, msg_message("peasantluck_success", "births", luck)); - if (glueck > 0) { - --glueck; - chances += PEASANTLUCK; - } - - while (chances--) { - if (rng_int() % 10000 < PEASANTGROWTH) { - /* Only raise with 75% chance if peasants have - * reached 90% of maxpopulation */ - if (peasants / (float)maxp < 0.9 || chance(PEASANTFORCE)) { - ++births; - } - } - } - } - peasants += births; + peasants += births + luck; } /* Alle werden satt, oder halt soviele für die es auch Geld gibt */ diff --git a/src/laws.h b/src/laws.h index f1862a61a..3e9c44013 100755 --- a/src/laws.h +++ b/src/laws.h @@ -106,7 +106,7 @@ extern "C" { const struct unit *u, int modifier); int armedmen(const struct unit *u, bool siege_weapons); void force_leave(struct region *r); - + int peasant_luck_effect(int peasants, int luck, int maxp, float variance); #ifdef __cplusplus } diff --git a/src/laws.test.c b/src/laws.test.c index 2b2f0bb9f..7524e6267 100644 --- a/src/laws.test.c +++ b/src/laws.test.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include @@ -691,6 +692,32 @@ static void test_reserve_self(CuTest *tc) { test_cleanup(); } +static void statistic_test(CuTest *tc, int peasants, int luck, int maxp, + float variance, int min_value, int max_value) +{ + int effect, i; + for (i = 0; i < 1000; ++i) { + effect = peasant_luck_effect(peasants, luck, maxp, variance); + CuAssertTrue(tc, min_value <= effect); + CuAssertTrue(tc, max_value >= effect); + } +} + +static void test_peasant_luck_effect(CuTest *tc) +{ + + set_param(&global.parameters, "rules.peasants.peasantluck.factor", "10"); + set_param(&global.parameters, "rules.peasants.growth.factor", "0.001"); + + statistic_test(tc, 100, 2, 1000, 0, 1, 1); + statistic_test(tc, 1000, 400, 1000, 0, (int)(400 * 10 * 0.001 * .75), + (int)(400 * 10 * 0.001 * .75)); + statistic_test(tc, 1000, 1000, 2000, .5f, 1, 501); + + set_param(&global.parameters, "rules.peasants.growth.factor", "1"); + statistic_test(tc, 1000, 1000, 1000, 0, 501, 501); +} + CuSuite *get_laws_suite(void) { CuSuite *suite = CuSuiteNew(); @@ -721,6 +748,7 @@ CuSuite *get_laws_suite(void) SUITE_ADD_TEST(suite, test_force_leave_buildings); SUITE_ADD_TEST(suite, test_force_leave_ships); SUITE_ADD_TEST(suite, test_force_leave_ships_on_ocean); + SUITE_ADD_TEST(suite, test_peasant_luck_effect); return suite; } diff --git a/src/settings.h b/src/settings.h index e211293b8..81f9f7074 100644 --- a/src/settings.h +++ b/src/settings.h @@ -8,7 +8,7 @@ This program may not be used, modified or distributed without prior permission by the authors of Eressea. -*/ + */ /* * Contains defines for the "free" game (Eressea) . @@ -25,9 +25,9 @@ /* Vermehrungsrate Bauern in 1/10000. * TODO: Evt. Berechnungsfehler, reale Vermehrungsraten scheinen höher. */ -#define PEASANTGROWTH 10 +#define PEASANTGROWTH 10 #define BATTLE_KILLS_PEASANTS 20 -#define PEASANTLUCK 10 +#define PEASANTLUCK 10 #define ROW_FACTOR 3 /* factor for combat row advancement rule */ diff --git a/src/test_eressea.c b/src/test_eressea.c index ca6ebf604..ccbac8463 100644 --- a/src/test_eressea.c +++ b/src/test_eressea.c @@ -53,6 +53,7 @@ int RunAllTests(void) RUN_TESTS(suite, umlaut); RUN_TESTS(suite, unicode); RUN_TESTS(suite, strings); + RUN_TESTS(suite, rng); /* kernel */ RUN_TESTS(suite, alliance); RUN_TESTS(suite, unit); diff --git a/src/util/CMakeLists.txt b/src/util/CMakeLists.txt index a08046ae2..249b1c61c 100644 --- a/src/util/CMakeLists.txt +++ b/src/util/CMakeLists.txt @@ -9,6 +9,7 @@ bsdstring.test.c functions.test.c umlaut.test.c unicode.test.c +rng.test.c ) SET(_FILES diff --git a/src/util/rng.h b/src/util/rng.h index 32dbcaeab..2e8e5affb 100644 --- a/src/util/rng.h +++ b/src/util/rng.h @@ -39,6 +39,7 @@ extern "C" { # define rng_double ((rand()%RAND_MAX)/(double)RAND_MAX) # define RNG_RAND_MAX RAND_MAX #endif +#define RAND_ROUND(fractional) ((rng_double() < fractional-(int)fractional)?((int)fractional+1):((int)fractional)) #ifdef __cplusplus } #endif diff --git a/src/util/rng.test.c b/src/util/rng.test.c new file mode 100644 index 000000000..1740368fb --- /dev/null +++ b/src/util/rng.test.c @@ -0,0 +1,26 @@ +#include +#include + +#include "rng.h" + +static void test_rng_round(CuTest * tc) +{ + double f; + int i, r; + for (i = 0; i < 1000; ++i) { + f = rng_double(); + r = RAND_ROUND(f); + CuAssertTrue(tc, f >= 0); + CuAssertTrue(tc, r <= (int ) f + 1); + CuAssertTrue(tc, r >= (int ) f); + CuAssertTrue(tc, r == (int ) r); + CuAssertTrue(tc, r == RAND_ROUND(r)); + } +} + +CuSuite *get_rng_suite(void) +{ + CuSuite *suite = CuSuiteNew(); + SUITE_ADD_TEST(suite, test_rng_round); + return suite; +}