From 33d3ce8e6a30b4dc7071ce36687bce8b2ac69c0a Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Wed, 26 Jun 2019 21:31:47 +0200 Subject: [PATCH] more intelligent batching of automated students. --- src/automate.c | 107 +++++++++++++++++++++---------------- src/automate.h | 3 +- src/automate.test.c | 126 +++++++++++++++++++++++++++++++------------- src/tests.c | 14 ++--- 4 files changed, 156 insertions(+), 94 deletions(-) diff --git a/src/automate.c b/src/automate.c index 0764381f2..2c9a94ad0 100644 --- a/src/automate.c +++ b/src/automate.c @@ -17,50 +17,57 @@ #include #include -static int cmp_scholars(const void *lhs, const void *rhs) -{ +static int cmp_scholars(const void *lhs, const void *rhs) { const scholar *a = (const scholar *)lhs; const scholar *b = (const scholar *)rhs; - if (a->skill == b->skill) { - /* sort by level, descending: */ - return b->level - a->level; - } - /* order by skill */ - return a->skill - b->skill; + return b->level - a->level; } -int autostudy_init(scholar scholars[], int max_scholars, unit **units) +int autostudy_init(scholar scholars[], int max_scholars, unit **units, skill_t *o_skill) { unit *unext = NULL, *u = *units; faction *f = u->faction; int nscholars = 0; - + skill_t skill = NOSKILL; while (u) { - keyword_t kwd = init_order(u->thisorder, u->faction->locale); - if (kwd == K_AUTOSTUDY) { - if (long_order_allowed(u)) { + if (!fval(u, UFL_MARK)) { + keyword_t kwd = init_order(u->thisorder, u->faction->locale); + if (kwd == K_AUTOSTUDY) { if (f == u->faction) { - scholar * st = scholars + nscholars; - skill_t sk = getskill(u->faction->locale); - if (check_student(u, u->thisorder, sk)) { - st->skill = (short)sk; - st->level = (short)effskill_study(u, sk); - st->learn = 0; - st->u = u; - if (++nscholars >= max_scholars) { - log_warning("you must increase MAXSCHOLARS"); - *units = u->next; - return max_scholars; + unext = u->next; + if (long_order_allowed(u)) { + scholar * st = scholars + nscholars; + skill_t sk = getskill(u->faction->locale); + if (skill == NOSKILL && sk != NOSKILL) { + skill = sk; + if (o_skill) { + *o_skill = skill; + } + } + if (check_student(u, u->thisorder, sk)) { + if (sk == skill) { + fset(u, UFL_MARK); + st->level = (short)effskill_study(u, sk); + st->learn = 0; + st->u = u; + if (++nscholars >= max_scholars) { + log_warning("you must increase MAXSCHOLARS"); + break; + } + } + } + else { + fset(u, UFL_MARK); } } } - else if (!unext) { - unext = u; - } } } u = u->next; } + while (unext && unext->faction != f) { + unext = unext->next; + } *units = unext; if (nscholars > 0) { qsort(scholars, nscholars, sizeof(scholar), cmp_scholars); @@ -84,26 +91,25 @@ void autostudy_run(scholar scholars[], int nscholars) { int ti = 0; while (ti != nscholars) { - int skill = scholars[ti].skill; int t, se, ts = 0, tt = 0, si = ti; - for (se = ti; se != nscholars && scholars[se].skill == skill; ++se) { + for (se = ti; se != nscholars; ++se) { int mint; ts += scholars[se].u->number; /* count total scholars */ mint = (ts + 10) / 11; /* need a minimum of ceil(ts/11) teachers */ - for (; mint > tt && si != nscholars && scholars[si].skill == skill; ++si) { + for (; mint > tt && si != nscholars; ++si) { tt += scholars[si].u->number; } } /* now si splits the teachers and students 1:10 */ /* first student must be 2 levels below first teacher: */ - for (; si != se && scholars[si].skill == skill; ++si) { + for (; si != se; ++si) { if (scholars[si].level + TEACHDIFFERENCE <= scholars[ti].level) { break; } tt += scholars[si].u->number; } /* now si is the first unit we can teach, if we can teach any */ - if (si == se || scholars[si].skill != skill) { + if (si == se) { /* there are no students, so standard learning for everyone */ for (t = ti; t != se; ++t) { learning(scholars + t, scholars[t].u->number); @@ -166,25 +172,36 @@ void autostudy_run(scholar scholars[], int nscholars) void do_autostudy(region *r) { - static int max_scholars; - unit *units = r->units; - scholar scholars[MAXSCHOLARS]; static int config; static int batchsize = MAXSCHOLARS; + static int max_scholars; + scholar scholars[MAXSCHOLARS]; + unit *u; + if (config_changed(&config)) { batchsize = config_get_int("automate.batchsize", MAXSCHOLARS); assert(batchsize <= MAXSCHOLARS); } - while (units) { - int i, nscholars = autostudy_init(scholars, batchsize, &units); - if (nscholars > max_scholars) { - stats_count("automate.max_scholars", nscholars - max_scholars); - max_scholars = nscholars; - } - autostudy_run(scholars, nscholars); - for (i = 0; i != nscholars; ++i) { - int days = STUDYDAYS * scholars[i].learn; - learn_skill(scholars[i].u, (skill_t)scholars[i].skill, days); + for (u = r->units; u; u = u->next) { + if (!fval(u, UFL_MARK)) { + unit *ulist = u; + int sum_scholars = 0; + while (ulist) { + skill_t skill = NOSKILL; + int i, nscholars = autostudy_init(scholars, batchsize, &ulist, &skill); + assert(ulist == NULL || ulist->faction == u->faction); + sum_scholars += nscholars; + if (sum_scholars > max_scholars) { + stats_count("automate.max_scholars", sum_scholars - max_scholars); + max_scholars = sum_scholars; + } + autostudy_run(scholars, nscholars); + for (i = 0; i != nscholars; ++i) { + int days = STUDYDAYS * scholars[i].learn; + learn_skill(scholars[i].u, skill, days); + } + } } + freset(u, UFL_MARK); } } diff --git a/src/automate.h b/src/automate.h index 1a7738363..7c71259b2 100644 --- a/src/automate.h +++ b/src/automate.h @@ -29,7 +29,6 @@ struct unit; typedef struct scholar { struct unit *u; int learn; - short skill; short level; } scholar; @@ -38,7 +37,7 @@ typedef struct scholar { void do_autostudy(struct region *r); -int autostudy_init(scholar scholars[], int max_scholars, struct unit **units); +int autostudy_init(scholar scholars[], int max_scholars, struct unit **units, skill_t *o_skill); void autostudy_run(scholar scholars[], int nscholars); #endif diff --git a/src/automate.test.c b/src/automate.test.c index d4e5650e6..5508cce88 100644 --- a/src/automate.test.c +++ b/src/automate.test.c @@ -21,6 +21,8 @@ static void test_autostudy_init(CuTest *tc) { unit *u1, *u2, *u3, *u4, *u5, *ulist; faction *f; region *r; + message *msg; + skill_t skill = NOSKILL; test_setup(); mt_create_error(77); @@ -34,36 +36,36 @@ static void test_autostudy_init(CuTest *tc) { u2 = test_create_unit(f, r); u2->thisorder = create_order(K_AUTOSTUDY, f->locale, skillnames[SK_ENTERTAINMENT]); set_level(u2, SK_ENTERTAINMENT, 2); - u3 = test_create_unit(f, r); - u3->thisorder = create_order(K_AUTOSTUDY, f->locale, skillnames[SK_PERCEPTION]); u4 = test_create_unit(f, r); u4->thisorder = create_order(K_AUTOSTUDY, f->locale, "Dudelidu"); + u3 = test_create_unit(f, r); + u3->thisorder = create_order(K_AUTOSTUDY, f->locale, skillnames[SK_PERCEPTION]); u5 = test_create_unit(test_create_faction(NULL), r); u5->thisorder = create_order(K_AUTOSTUDY, f->locale, skillnames[SK_PERCEPTION]); - scholars[3].u = NULL; + scholars[2].u = NULL; + ulist = r->units; - CuAssertIntEquals(tc, 3, autostudy_init(scholars, 4, &ulist)); - CuAssertPtrNotNull(tc, test_find_messagetype(u4->faction->msgs, "error77")); + CuAssertIntEquals(tc, 2, autostudy_init(scholars, 4, &ulist, &skill)); + CuAssertIntEquals(tc, SK_ENTERTAINMENT, skill); CuAssertPtrEquals(tc, u2, scholars[0].u); CuAssertIntEquals(tc, 2, scholars[0].level); CuAssertIntEquals(tc, 0, scholars[0].learn); - CuAssertIntEquals(tc, SK_ENTERTAINMENT, scholars[0].skill); CuAssertPtrEquals(tc, u1, scholars[1].u); CuAssertIntEquals(tc, 0, scholars[1].level); CuAssertIntEquals(tc, 0, scholars[1].learn); - CuAssertIntEquals(tc, SK_ENTERTAINMENT, scholars[1].skill); - CuAssertPtrEquals(tc, u3, scholars[2].u); - CuAssertIntEquals(tc, 0, scholars[2].level); - CuAssertIntEquals(tc, 0, scholars[2].learn); - CuAssertIntEquals(tc, SK_PERCEPTION, scholars[2].skill); - CuAssertPtrEquals(tc, NULL, scholars[3].u); - CuAssertPtrEquals(tc, u5, ulist); - CuAssertIntEquals(tc, 1, autostudy_init(scholars, 4, &ulist)); - CuAssertPtrEquals(tc, u5, scholars[0].u); + CuAssertPtrEquals(tc, NULL, scholars[2].u); + CuAssertPtrEquals(tc, NULL, ulist); + + ulist = u3; + CuAssertIntEquals(tc, 1, autostudy_init(scholars, 4, &ulist, &skill)); + CuAssertIntEquals(tc, SK_PERCEPTION, skill); + CuAssertPtrEquals(tc, u3, scholars[0].u); CuAssertIntEquals(tc, 0, scholars[0].level); CuAssertIntEquals(tc, 0, scholars[0].learn); - CuAssertIntEquals(tc, SK_PERCEPTION, scholars[0].skill); CuAssertPtrEquals(tc, NULL, ulist); + + CuAssertPtrNotNull(tc, msg = test_find_messagetype(f->msgs, "error77")); + CuAssertPtrEquals(tc, NULL, test_find_messagetype_ex(f->msgs, "error77", msg)); test_teardown(); } @@ -76,6 +78,7 @@ static void test_autostudy_run_twoteachers(CuTest *tc) { unit *u1, *u2, *u3, *u4, *ulist; faction *f; region *r; + skill_t skill; test_setup(); r = test_create_plain(0, 0); @@ -95,9 +98,10 @@ static void test_autostudy_run_twoteachers(CuTest *tc) { set_number(u4, 12); ulist = r->units; - CuAssertIntEquals(tc, 4, nscholars = autostudy_init(scholars, 4, &ulist)); + CuAssertIntEquals(tc, 4, nscholars = autostudy_init(scholars, 4, &ulist, &skill)); CuAssertPtrEquals(tc, NULL, ulist); autostudy_run(scholars, nscholars); + CuAssertIntEquals(tc, SK_ENTERTAINMENT, skill); CuAssertIntEquals(tc, 0, scholars[0].learn); CuAssertIntEquals(tc, 0, scholars[1].learn); CuAssertIntEquals(tc, scholars[2].u->number * 2, scholars[2].learn); @@ -126,21 +130,34 @@ static void test_autostudy_run(CuTest *tc) { u3 = test_create_unit(f, r); u3->thisorder = create_order(K_AUTOSTUDY, f->locale, skillnames[SK_PERCEPTION]); set_number(u3, 15); - scholars[3].u = NULL; + + scholars[2].u = NULL; ulist = r->units; - CuAssertIntEquals(tc, 3, nscholars = autostudy_init(scholars, 4, &ulist)); + CuAssertIntEquals(tc, 2, nscholars = autostudy_init(scholars, 4, &ulist, NULL)); + CuAssertIntEquals(tc, UFL_MARK, u1->flags & UFL_MARK); + CuAssertIntEquals(tc, UFL_MARK, u2->flags & UFL_MARK); + CuAssertIntEquals(tc, 0, u3->flags & UFL_MARK); CuAssertPtrEquals(tc, NULL, ulist); autostudy_run(scholars, nscholars); CuAssertIntEquals(tc, 1, scholars[0].learn); CuAssertIntEquals(tc, 20, scholars[1].learn); - CuAssertIntEquals(tc, 15, scholars[2].learn); + CuAssertPtrEquals(tc, NULL, scholars[2].u); + + scholars[1].u = NULL; + ulist = u3; + CuAssertIntEquals(tc, 1, nscholars = autostudy_init(scholars, 4, &ulist, NULL)); + CuAssertPtrEquals(tc, NULL, ulist); + autostudy_run(scholars, nscholars); + CuAssertIntEquals(tc, 15, scholars[0].learn); + CuAssertPtrEquals(tc, NULL, scholars[1].u); + test_teardown(); } static void test_autostudy_run_noteachers(CuTest *tc) { scholar scholars[4]; int nscholars; - unit *u1, *u2, *u3, *ulist; + unit *u1, *u2, *ulist; faction *f; region *r; @@ -148,23 +165,24 @@ static void test_autostudy_run_noteachers(CuTest *tc) { r = test_create_plain(0, 0); f = test_create_faction(NULL); u1 = test_create_unit(f, r); - u1->thisorder = create_order(K_AUTOSTUDY, f->locale, skillnames[SK_LUMBERJACK]); - set_number(u1, 2); + u1->thisorder = create_order(K_AUTOSTUDY, f->locale, skillnames[SK_ENTERTAINMENT]); + set_number(u1, 5); set_level(u1, SK_ENTERTAINMENT, 2); + u2 = test_create_unit(f, r); u2->thisorder = create_order(K_AUTOSTUDY, f->locale, skillnames[SK_ENTERTAINMENT]); - set_number(u2, 10); - u3 = test_create_unit(f, r); - u3->thisorder = create_order(K_AUTOSTUDY, f->locale, skillnames[SK_PERCEPTION]); - set_number(u3, 15); - scholars[3].u = NULL; + set_number(u2, 7); + set_level(u2, SK_ENTERTAINMENT, 2); + + scholars[2].u = NULL; ulist = r->units; - CuAssertIntEquals(tc, 3, nscholars = autostudy_init(scholars, 4, &ulist)); + CuAssertIntEquals(tc, 2, nscholars = autostudy_init(scholars, 4, &ulist, NULL)); CuAssertPtrEquals(tc, NULL, ulist); autostudy_run(scholars, nscholars); - CuAssertIntEquals(tc, 2, scholars[0].learn); - CuAssertIntEquals(tc, 10, scholars[1].learn); - CuAssertIntEquals(tc, 15, scholars[2].learn); + /* stupid qsort is unstable: */ + CuAssertIntEquals(tc, 12, scholars[0].learn + scholars[1].learn); + CuAssertIntEquals(tc, 35, scholars[0].learn * scholars[1].learn); + CuAssertPtrEquals(tc, NULL, scholars[2].u); test_teardown(); } @@ -189,7 +207,7 @@ static void test_autostudy_run_teachers_learn(CuTest *tc) { u2->thisorder = create_order(K_AUTOSTUDY, f->locale, skillnames[SK_ENTERTAINMENT]); set_number(u2, 10); ulist = r->units; - CuAssertIntEquals(tc, 2, nscholars = autostudy_init(scholars, 4, &ulist)); + CuAssertIntEquals(tc, 2, nscholars = autostudy_init(scholars, 4, &ulist, NULL)); CuAssertPtrEquals(tc, NULL, ulist); autostudy_run(scholars, nscholars); CuAssertIntEquals(tc, 1, scholars[0].learn); @@ -223,7 +241,7 @@ static void test_autostudy_run_skilldiff(CuTest *tc) { set_number(u3, 10); scholars[3].u = NULL; ulist = r->units; - CuAssertIntEquals(tc, 3, nscholars = autostudy_init(scholars, 4, &ulist)); + CuAssertIntEquals(tc, 3, nscholars = autostudy_init(scholars, 4, &ulist, NULL)); CuAssertPtrEquals(tc, NULL, ulist); autostudy_run(scholars, nscholars); CuAssertIntEquals(tc, 0, scholars[0].learn); @@ -232,7 +250,7 @@ static void test_autostudy_run_skilldiff(CuTest *tc) { test_teardown(); } -static void test_do_autostudy(CuTest *tc) { +static void test_autostudy_batches(CuTest *tc) { scholar scholars[2]; int nscholars; unit *u1, *u2, *u3, *ulist; @@ -255,23 +273,55 @@ static void test_do_autostudy(CuTest *tc) { scholars[1].u = NULL; ulist = r->units; config_set("automate.batchsize", "2"); - CuAssertIntEquals(tc, 2, nscholars = autostudy_init(scholars, 2, &ulist)); + CuAssertIntEquals(tc, 2, nscholars = autostudy_init(scholars, 2, &ulist, NULL)); CuAssertPtrEquals(tc, u3, ulist); autostudy_run(scholars, nscholars); CuAssertIntEquals(tc, 0, scholars[0].learn); CuAssertIntEquals(tc, 20, scholars[1].learn); - CuAssertIntEquals(tc, 1, nscholars = autostudy_init(scholars, 2, &ulist)); + CuAssertIntEquals(tc, 1, nscholars = autostudy_init(scholars, 2, &ulist, NULL)); autostudy_run(scholars, nscholars); CuAssertIntEquals(tc, 10, scholars[0].learn); test_teardown(); } +static void test_do_autostudy(CuTest *tc) { + unit *u1, *u2, *u3, *u4; + faction *f; + region *r; + + test_setup(); + r = test_create_plain(0, 0); + f = test_create_faction(NULL); + u1 = test_create_unit(f, r); + u1->thisorder = create_order(K_AUTOSTUDY, f->locale, skillnames[SK_PERCEPTION]); + set_number(u1, 1); + set_level(u1, SK_PERCEPTION, 2); + u2 = test_create_unit(f, r); + u2->thisorder = create_order(K_AUTOSTUDY, f->locale, skillnames[SK_PERCEPTION]); + set_number(u2, 10); + u3 = test_create_unit(f, r); + u3->thisorder = create_order(K_AUTOSTUDY, f->locale, skillnames[SK_ENTERTAINMENT]); + u4 = test_create_unit(test_create_faction(NULL), r); + u4->thisorder = create_order(K_AUTOSTUDY, f->locale, skillnames[SK_ENTERTAINMENT]); + do_autostudy(r); + CuAssertIntEquals(tc, 2, get_level(u1, SK_PERCEPTION)); + /* impossible to say if u2 is T1 or T2 now */ + CuAssertIntEquals(tc, 1, get_level(u3, SK_ENTERTAINMENT)); + CuAssertIntEquals(tc, 1, get_level(u4, SK_ENTERTAINMENT)); + CuAssertIntEquals(tc, 0, u1->flags & UFL_MARK); + CuAssertIntEquals(tc, 0, u2->flags & UFL_MARK); + CuAssertIntEquals(tc, 0, u3->flags & UFL_MARK); + CuAssertIntEquals(tc, 0, u4->flags & UFL_MARK); + test_teardown(); +} + CuSuite *get_automate_suite(void) { CuSuite *suite = CuSuiteNew(); - SUITE_ADD_TEST(suite, test_do_autostudy); SUITE_ADD_TEST(suite, test_autostudy_init); SUITE_ADD_TEST(suite, test_autostudy_run); + SUITE_ADD_TEST(suite, test_do_autostudy); + SUITE_ADD_TEST(suite, test_autostudy_batches); SUITE_ADD_TEST(suite, test_autostudy_run_noteachers); SUITE_ADD_TEST(suite, test_autostudy_run_teachers_learn); SUITE_ADD_TEST(suite, test_autostudy_run_twoteachers); diff --git a/src/tests.c b/src/tests.c index a34e6c743..5a13c97da 100644 --- a/src/tests.c +++ b/src/tests.c @@ -576,15 +576,11 @@ struct message * test_find_messagetype_ex(struct message_list *msgs, const char struct mlist *ml; if (!msgs) return 0; for (ml = msgs->begin; ml; ml = ml->next) { - if (strcmp(name, test_get_messagetype(ml->msg)) == 0) { - if (prev) { - if (ml->msg == prev) { - prev = NULL; - } - } - else { - return ml->msg; - } + if (prev && ml->msg == prev) { + prev = NULL; + } + else if (strcmp(name, test_get_messagetype(ml->msg)) == 0) { + return ml->msg; } } return 0;