diff --git a/src/automate.c b/src/automate.c index 997f67bd8..bf130acf3 100644 --- a/src/automate.c +++ b/src/automate.c @@ -12,6 +12,7 @@ #include "study.h" #include +#include int cmp_scholars(const void *lhs, const void *rhs) { @@ -50,44 +51,89 @@ int autostudy_init(scholar scholars[], int max_scholars, region *r) return nscholars; } +static void teaching(scholar *s, int n) { + assert(n <= s->u->number); + s->learn += n; +} + +static void learning(scholar *s, int n) { + assert(n <= s->u->number); + s->learn += n; +} + void autostudy_run(scholar scholars[], int nscholars) { - int i, t, s, ti = 0, si = 0, ts = 0, tt = 0; - skill_t sk = scholars[0].sk; - for (i = ti; i != nscholars && scholars[i].sk == sk; ++i) { - int mint; - ts += scholars[i].u->number; /* count total scholars */ - mint = (ts + 10) / 11; /* need a minimum of ceil(ts/11) teachers */ - while (mint>tt) { - tt += scholars[si++].u->number; - } - } - /* now si splits the teachers and students 1:10 */ - /* first student must be 2 levels below first teacher: */ - while (scholars[ti].level - TEACHDIFFERENCE > scholars[si].level) { - tt += scholars[si++].u->number; - } - /* invariant: unit ti can still teach i students */ - i = scholars[ti].u->number * 10; - for (t = ti, s = si; t != si && s != nscholars; ++t) { - /* TODO: is there no constant for students per teacher? */ - while (s != nscholars) { - int n = scholars[s].u->number; - scholars[s].learn += n; - if (i >= n) { - i -= n; - scholars[s].learn += n; - /* next student */ + int ti = 0; + while (ti != nscholars) { + skill_t sk = scholars[ti].sk; + int t, s, se, ts = 0, tt = 0, si = ti; + for (se = ti; se != nscholars && scholars[se].sk == sk; ++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; ++si) { + tt += scholars[si].u->number; } - else { - scholars[s].learn += i; - /* go to next suitable teacher */ - do { - ++t; + } + /* now si splits the teachers and students 1:10 */ + /* first student must be 2 levels below first teacher: */ + for (; si != se && scholars[ti].level - TEACHDIFFERENCE > scholars[si].level; ++si) { + tt += scholars[si].u->number; + } + if (si == se) { + /* there are no students, so standard learning only */ + for (t = ti; t != se; ++t) { + learning(scholars + t, scholars[t].u->number); + } + } + else { + /* invariant: unit ti can still teach i students */ + int i = scholars[ti].u->number * STUDENTS_PER_TEACHER; + /* invariant: unit si has n students that can still be taught */ + int n = scholars[si].u->number; + for (t = ti, s = si; t != si && s != se; ) { + if (i > n) { + /* t has more than enough teaching capacity for s */ + i -= n; + teaching(scholars + s, n); + learning(scholars + s, scholars[s].u->number); + /* next student, please: */ + if (++s == se) { + continue; + } + n = scholars[s].u->number; + } + else { + /* s gets partial credit and we need a new teacher */ + teaching(scholars + s, i); + + /* we are done with this teacher. any remaining people are regular learners: */ + if (scholars[t].u->number > 1) { + /* remain = number - ceil(taught/10); */ + int remain = (STUDENTS_PER_TEACHER * scholars[t].u->number - i + STUDENTS_PER_TEACHER - 1) / STUDENTS_PER_TEACHER; + learning(scholars + t, remain); + } + + /* we want a new teacher for s. if any exists, it's next in the sequence. */ + if (++t == si) { + continue; + } + if (scholars[t].level - TEACHDIFFERENCE < scholars[s].level) { + /* next teacher cannot teach, we must skip students. */ + do { + learning(scholars + s, (n - i)); + i = 0; + if (++s == se) { + continue; + } + n = scholars[s].u->number; + } while (scholars[t].level - TEACHDIFFERENCE < scholars[s].level); + } + i = scholars[t].u->number * STUDENTS_PER_TEACHER; } - while (scholars[t].level - TEACHDIFFERENCE < scholars[s].level); } } + ti = se; } } diff --git a/src/automate.h b/src/automate.h index 285fc6638..61fed6866 100644 --- a/src/automate.h +++ b/src/automate.h @@ -33,6 +33,8 @@ typedef struct scholar { int learn; } scholar; +#define STUDENTS_PER_TEACHER 10 + void do_autostudy(struct region *r); int autostudy_init(scholar scholars[], int max_scholars, struct region *r); diff --git a/src/automate.test.c b/src/automate.test.c index 7404a2d9c..999cb25d4 100644 --- a/src/automate.test.c +++ b/src/automate.test.c @@ -60,19 +60,46 @@ static void test_autostudy_run(CuTest *tc) { u1 = test_create_unit(f, r); u1->thisorder = create_order(K_AUTOSTUDY, f->locale, skillnames[SK_ENTERTAINMENT]); set_number(u1, 2); + 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); - set_level(u2, SK_ENTERTAINMENT, 2); u3 = test_create_unit(f, r); u3->thisorder = create_order(K_AUTOSTUDY, f->locale, skillnames[SK_PERCEPTION]); - set_number(u3, 20); + set_number(u3, 15); scholars[3].u = NULL; CuAssertIntEquals(tc, 3, autostudy_init(scholars, 4, r)); autostudy_run(scholars, 3); CuAssertIntEquals(tc, 0, scholars[0].learn); CuAssertIntEquals(tc, 20, scholars[1].learn); - CuAssertIntEquals(tc, 30, scholars[2].learn); + CuAssertIntEquals(tc, 15, scholars[2].learn); +} + +static void test_autostudy_run_noteachers(CuTest *tc) { + scholar scholars[4]; + unit *u1, *u2, *u3; + 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_LUMBERJACK]); + set_number(u1, 2); + 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; + CuAssertIntEquals(tc, 3, autostudy_init(scholars, 4, r)); + autostudy_run(scholars, 3); + CuAssertIntEquals(tc, 2, scholars[0].learn); + CuAssertIntEquals(tc, 10, scholars[1].learn); + CuAssertIntEquals(tc, 15, scholars[2].learn); } CuSuite *get_automate_suite(void) @@ -80,5 +107,6 @@ CuSuite *get_automate_suite(void) CuSuite *suite = CuSuiteNew(); SUITE_ADD_TEST(suite, test_autostudy_init); SUITE_ADD_TEST(suite, test_autostudy_run); + SUITE_ADD_TEST(suite, test_autostudy_run_noteachers); return suite; }