From c8ebde3990b50537c8148f8dd02a2213efeb6ae0 Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Mon, 9 Jul 2018 03:31:13 +0200 Subject: [PATCH] autostudy continued (WIP) --- src/academy.c | 8 ++-- src/academy.h | 2 +- src/automate.c | 101 +++++++++++++++++++++++++++++++------------- src/automate.h | 7 +-- src/automate.test.c | 64 ++++++++++++++++++++-------- src/study.c | 86 ++++++++++++++++++------------------- 6 files changed, 170 insertions(+), 98 deletions(-) diff --git a/src/academy.c b/src/academy.c index 5df13efa4..1298e9b57 100644 --- a/src/academy.c +++ b/src/academy.c @@ -33,13 +33,13 @@ void academy_teaching_bonus(struct unit *u, skill_t sk, int students) { } } -bool academy_can_teach(unit *teacher, unit *student, skill_t sk) { +bool academy_can_teach(unit *teacher, unit *scholar, skill_t sk) { const struct building_type *btype = bt_find("academy"); - if (active_building(teacher, btype) && active_building(student, btype)) { - int j = study_cost(student, sk) * 2; + if (active_building(teacher, btype) && active_building(scholar, btype)) { + int j = study_cost(scholar, sk) * 2; if (j < 50) j = 50; /* kann Einheit das zahlen? */ - return get_pooled(student, get_resourcetype(R_SILVER), GET_DEFAULT, j) >= j; + return get_pooled(scholar, get_resourcetype(R_SILVER), GET_DEFAULT, j) >= j; /* sonst nehmen sie nicht am Unterricht teil */ } return false; diff --git a/src/academy.h b/src/academy.h index f6af93748..3c496d8fa 100644 --- a/src/academy.h +++ b/src/academy.h @@ -9,7 +9,7 @@ extern "C" { struct unit; void academy_teaching_bonus(struct unit *u, skill_t sk, int academy); - bool academy_can_teach(struct unit *teacher, struct unit *student, skill_t sk); + bool academy_can_teach(struct unit *teacher, struct unit *scholar, skill_t sk); #ifdef __cplusplus } #endif diff --git a/src/automate.c b/src/automate.c index f195bb6db..997f67bd8 100644 --- a/src/automate.c +++ b/src/automate.c @@ -13,9 +13,10 @@ #include -int cmp_students(const void *lhs, const void *rhs) { - const student *a = (const student *)lhs; - const student *b = (const student *)rhs; +int cmp_scholars(const void *lhs, const void *rhs) +{ + const scholar *a = (const scholar *)lhs; + const scholar *b = (const scholar *)rhs; if (a->sk == b->sk) { /* sort by level, descending: */ return b->level - a->level; @@ -24,17 +25,17 @@ int cmp_students(const void *lhs, const void *rhs) { return (int)a->sk - (int)b->sk; } -int autostudy_init(student students[], int max_students, region *r) +int autostudy_init(scholar scholars[], int max_scholars, region *r) { unit *u; - int nstudents = 0; + int nscholars = 0; for (u = r->units; u; u = u->next) { keyword_t kwd = getkeyword(u->thisorder); if (kwd == K_AUTOSTUDY) { - student * st = students + nstudents; - if (++nstudents == max_students) { - log_fatal("you must increase MAXSTUDENTS"); + scholar * st = scholars + nscholars; + if (++nscholars == max_scholars) { + log_fatal("you must increase MAXSCHOLARS"); } st->u = u; init_order(u->thisorder, u->faction->locale); @@ -43,31 +44,73 @@ int autostudy_init(student students[], int max_students, region *r) st->learn = 0; } } - if (nstudents > 0) { - qsort(students, nstudents, sizeof(student), cmp_students); + if (nscholars > 0) { + qsort(scholars, nscholars, sizeof(scholar), cmp_scholars); } - return nstudents; + return nscholars; } -#define MAXSTUDENTS 128 - -void do_autostudy(region *r) { - student students[MAXSTUDENTS]; - int nstudents = autostudy_init(students, MAXSTUDENTS, r); - - if (nstudents > 0) { - int i; - skill_t sk = NOSKILL; - - for (i = 0; i != nstudents; ++i) { - if (students[i].u) { - if (sk == NOSKILL) { - sk = students[i].sk; +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 */ + } + else { + scholars[s].learn += i; + /* go to next suitable teacher */ + do { + ++t; } - else if (sk != students[i].sk) { - continue; - } - students[i].u = NULL; + while (scholars[t].level - TEACHDIFFERENCE < scholars[s].level); + } + } + } +} + +#define MAXSCHOLARS 128 + +void do_autostudy(region *r) +{ + scholar scholars[MAXSCHOLARS]; + int nscholars = autostudy_init(scholars, MAXSCHOLARS, r); + + if (nscholars > 0) { + int i; + skill_t sk = NOSKILL; + + for (i = 0; i != nscholars; ++i) { + if (scholars[i].u) { + if (sk == NOSKILL) { + sk = scholars[i].sk; + } + else if (sk != scholars[i].sk) { + continue; + } + scholars[i].u = NULL; } } } diff --git a/src/automate.h b/src/automate.h index 49b052f56..285fc6638 100644 --- a/src/automate.h +++ b/src/automate.h @@ -26,15 +26,16 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. struct region; struct unit; -typedef struct student { +typedef struct scholar { struct unit *u; skill_t sk; int level; int learn; -} student; +} scholar; void do_autostudy(struct region *r); -int autostudy_init(student students[], int max_students, struct region *r); +int autostudy_init(scholar scholars[], int max_scholars, struct region *r); +void autostudy_run(scholar scholars[], int nscholars); #endif diff --git a/src/automate.test.c b/src/automate.test.c index 3ea640cdf..7404a2d9c 100644 --- a/src/automate.test.c +++ b/src/automate.test.c @@ -13,8 +13,8 @@ #include -static void test_autostudy(CuTest *tc) { - student students[4]; +static void test_autostudy_init(CuTest *tc) { + scholar scholars[4]; unit *u1, *u2, *u3; faction *f; region *r; @@ -30,27 +30,55 @@ static void test_autostudy(CuTest *tc) { set_level(u2, SK_ENTERTAINMENT, 2); u3 = test_create_unit(f, r); u3->thisorder = create_order(K_AUTOSTUDY, f->locale, skillnames[SK_PERCEPTION]); - students[3].u = NULL; - CuAssertIntEquals(tc, 3, autostudy_init(students, 4, r)); - CuAssertPtrEquals(tc, u2, students[0].u); - CuAssertIntEquals(tc, 2, students[0].level); - CuAssertIntEquals(tc, 0, students[0].learn); - CuAssertIntEquals(tc, SK_ENTERTAINMENT, students[0].sk); - CuAssertPtrEquals(tc, u1, students[1].u); - CuAssertIntEquals(tc, 0, students[1].level); - CuAssertIntEquals(tc, 0, students[1].learn); - CuAssertIntEquals(tc, SK_ENTERTAINMENT, students[1].sk); - CuAssertPtrEquals(tc, u3, students[2].u); - CuAssertIntEquals(tc, 0, students[2].level); - CuAssertIntEquals(tc, 0, students[2].learn); - CuAssertIntEquals(tc, SK_PERCEPTION, students[2].sk); - CuAssertPtrEquals(tc, NULL, students[3].u); + scholars[3].u = NULL; + CuAssertIntEquals(tc, 3, autostudy_init(scholars, 4, r)); + CuAssertPtrEquals(tc, u2, scholars[0].u); + CuAssertIntEquals(tc, 2, scholars[0].level); + CuAssertIntEquals(tc, 0, scholars[0].learn); + CuAssertIntEquals(tc, SK_ENTERTAINMENT, scholars[0].sk); + CuAssertPtrEquals(tc, u1, scholars[1].u); + CuAssertIntEquals(tc, 0, scholars[1].level); + CuAssertIntEquals(tc, 0, scholars[1].learn); + CuAssertIntEquals(tc, SK_ENTERTAINMENT, scholars[1].sk); + CuAssertPtrEquals(tc, u3, scholars[2].u); + CuAssertIntEquals(tc, 0, scholars[2].level); + CuAssertIntEquals(tc, 0, scholars[2].learn); + CuAssertIntEquals(tc, SK_PERCEPTION, scholars[2].sk); + CuAssertPtrEquals(tc, NULL, scholars[3].u); test_teardown(); } +static void test_autostudy_run(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_ENTERTAINMENT]); + set_number(u1, 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); + 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); +} + CuSuite *get_automate_suite(void) { CuSuite *suite = CuSuiteNew(); - SUITE_ADD_TEST(suite, test_autostudy); + SUITE_ADD_TEST(suite, test_autostudy_init); + SUITE_ADD_TEST(suite, test_autostudy_run); return suite; } diff --git a/src/study.c b/src/study.c index 217fe9505..0dc4386bc 100644 --- a/src/study.c +++ b/src/study.c @@ -195,37 +195,37 @@ const attrib_type at_learning = { #define EXPERIENCEDAYS 10 -static int study_days(unit * student, skill_t sk) +static int study_days(unit * scholar, skill_t sk) { int speed = STUDYDAYS; - if (u_race(student)->study_speed) { - speed += u_race(student)->study_speed[sk]; + if (u_race(scholar)->study_speed) { + speed += u_race(scholar)->study_speed[sk]; if (speed < STUDYDAYS) { - skill *sv = unit_skill(student, sk); + skill *sv = unit_skill(scholar, sk); if (sv == 0) { speed = STUDYDAYS; } } } - return student->number * speed; + return scholar->number * speed; } static int -teach_unit(unit * teacher, unit * student, int nteaching, skill_t sk, +teach_unit(unit * teacher, unit * scholar, int nteaching, skill_t sk, bool report, int *academy_students) { teaching_info *teach = NULL; attrib *a; int students; - if (magic_lowskill(student)) { + if (magic_lowskill(scholar)) { cmistake(teacher, teacher->thisorder, 292, MSG_EVENT); return 0; } - students = student->number; + students = scholar->number; /* subtract already taught students */ - a = a_find(student->attribs, &at_learning); + a = a_find(scholar->attribs, &at_learning); if (a != NULL) { teach = (teaching_info *)a->data.v; students -= teach->students; @@ -235,18 +235,18 @@ teach_unit(unit * teacher, unit * student, int nteaching, skill_t sk, if (students > 0) { if (teach == NULL) { - a = a_add(&student->attribs, a_new(&at_learning)); + a = a_add(&scholar->attribs, a_new(&at_learning)); teach = (teaching_info *)a->data.v; } selist_push(&teach->teachers, teacher); teach->days += students * STUDYDAYS; teach->students += students; - if (student->building && teacher->building == student->building) { + if (scholar->building && teacher->building == scholar->building) { /* Solange Akademien groessenbeschraenkt sind, sollte Lehrer und * Student auch in unterschiedlichen Gebaeuden stehen duerfen */ /* FIXME comment contradicts implementation */ - if (academy_can_teach(teacher, student, sk)) { + if (academy_can_teach(teacher, scholar, sk)) { /* Jeder Schueler zusaetzlich +10 Tage wenn in Uni. */ teach->days += students * EXPERIENCEDAYS; /* learning erhoehen */ /* Lehrer zusaetzlich +1 Tag pro Schueler. */ @@ -304,7 +304,7 @@ int teach_cmd(unit * teacher, struct order *ord) #if TEACH_ALL if (getparam(teacher->faction->locale) == P_ANY) { skill_t sk; - unit *student; + unit *scholar; skill_t teachskill[MAXSKILLS]; int t = 0; @@ -313,15 +313,15 @@ int teach_cmd(unit * teacher, struct order *ord) teachskill[t] = getskill(teacher->faction->locale); } while (sk != NOSKILL); - for (student = r->units; teaching > 0 && student; student = student->next) { - if (LongHunger(student)) { + for (scholar = r->units; teaching > 0 && scholar; scholar = scholar->next) { + if (LongHunger(scholar)) { continue; } - else if (student->faction == teacher->faction) { - if (getkeyword(student->thisorder) == K_STUDY) { + else if (scholar->faction == teacher->faction) { + if (getkeyword(scholar->thisorder) == K_STUDY) { /* Input ist nun von student->thisorder !! */ - init_order(student->thisorder, student->faction->locale); - sk = getskill(student->faction->locale); + init_order(scholar->thisorder, scholar->faction->locale); + sk = getskill(scholar->faction->locale); if (sk != NOSKILL && teachskill[0] != NOSKILL) { for (t = 0; teachskill[t] != NOSKILL; ++t) { if (sk == teachskill[t]) { @@ -331,20 +331,20 @@ int teach_cmd(unit * teacher, struct order *ord) sk = teachskill[t]; } if (sk != NOSKILL - && effskill_study(teacher, sk) - TEACHDIFFERENCE > effskill_study(student, sk)) { - teaching -= teach_unit(teacher, student, teaching, sk, true, &academy_students); + && effskill_study(teacher, sk) - TEACHDIFFERENCE > effskill_study(scholar, sk)) { + teaching -= teach_unit(teacher, scholar, teaching, sk, true, &academy_students); } } } #ifdef TEACH_FRIENDS - else if (alliedunit(teacher, student->faction, HELP_GUARD)) { - if (getkeyword(student->thisorder) == K_STUDY) { + else if (alliedunit(teacher, scholar->faction, HELP_GUARD)) { + if (getkeyword(scholar->thisorder) == K_STUDY) { /* Input ist nun von student->thisorder !! */ - init_order(student->thisorder, student->faction->locale); - sk = getskill(student->faction->locale); + init_order(scholar->thisorder, scholar->faction->locale); + sk = getskill(scholar->faction->locale); if (sk != NOSKILL - && effskill_study(teacher, sk) - TEACHDIFFERENCE >= effskill(student, sk, NULL)) { - teaching -= teach_unit(teacher, student, teaching, sk, true, &academy_students); + && effskill_study(teacher, sk) - TEACHDIFFERENCE >= effskill(scholar, sk, NULL)) { + teaching -= teach_unit(teacher, scholar, teaching, sk, true, &academy_students); } } } @@ -363,15 +363,15 @@ int teach_cmd(unit * teacher, struct order *ord) while (!parser_end()) { skill_t sk; - unit *student; + unit *scholar; bool feedback; - getunit(r, teacher->faction, &student); + getunit(r, teacher->faction, &scholar); ++count; /* Falls die Unit nicht gefunden wird, Fehler melden */ - if (!student) { + if (!scholar) { char tbuf[20]; const char *uid; const char *token; @@ -403,8 +403,8 @@ int teach_cmd(unit * teacher, struct order *ord) continue; } - feedback = teacher->faction == student->faction - || alliedunit(student, teacher->faction, HELP_GUARD); + feedback = teacher->faction == scholar->faction + || alliedunit(scholar, teacher->faction, HELP_GUARD); /* Neuen Befehl zusammenbauen. TEMP-Einheiten werden automatisch in * ihre neuen Nummern uebersetzt. */ @@ -412,31 +412,31 @@ int teach_cmd(unit * teacher, struct order *ord) strncat(zOrder, " ", sz - 1); --sz; } - sz -= str_strlcpy(zOrder + 4096 - sz, itoa36(student->no), sz); + sz -= str_strlcpy(zOrder + 4096 - sz, itoa36(scholar->no), sz); - if (getkeyword(student->thisorder) != K_STUDY) { + if (getkeyword(scholar->thisorder) != K_STUDY) { ADDMSG(&teacher->faction->msgs, - msg_feedback(teacher, ord, "teach_nolearn", "student", student)); + msg_feedback(teacher, ord, "teach_nolearn", "student", scholar)); continue; } /* Input ist nun von student->thisorder !! */ parser_pushstate(); - init_order(student->thisorder, student->faction->locale); - sk = getskill(student->faction->locale); + init_order(scholar->thisorder, scholar->faction->locale); + sk = getskill(scholar->faction->locale); parser_popstate(); if (sk == NOSKILL) { ADDMSG(&teacher->faction->msgs, - msg_feedback(teacher, ord, "teach_nolearn", "student", student)); + msg_feedback(teacher, ord, "teach_nolearn", "student", scholar)); continue; } - if (effskill_study(student, sk) > effskill_study(teacher, sk) + if (effskill_study(scholar, sk) > effskill_study(teacher, sk) - TEACHDIFFERENCE) { if (feedback) { ADDMSG(&teacher->faction->msgs, msg_feedback(teacher, ord, "teach_asgood", - "student", student)); + "student", scholar)); } continue; } @@ -444,18 +444,18 @@ int teach_cmd(unit * teacher, struct order *ord) /* ist der Magier schon spezialisiert, so versteht er nur noch * Lehrer seines Gebietes */ sc_mage *mage1 = get_mage_depr(teacher); - sc_mage *mage2 = get_mage_depr(student); + sc_mage *mage2 = get_mage_depr(scholar); if (mage2 && mage1 && mage2->magietyp != M_GRAY && mage1->magietyp != mage2->magietyp) { if (feedback) { ADDMSG(&teacher->faction->msgs, msg_feedback(teacher, ord, - "error_different_magic", "target", student)); + "error_different_magic", "target", scholar)); } continue; } } sk_academy = sk; - teaching -= teach_unit(teacher, student, teaching, sk, false, &academy_students); + teaching -= teach_unit(teacher, scholar, teaching, sk, false, &academy_students); } new_order = create_order(K_TEACH, teacher->faction->locale, "%s", zOrder); replace_order(&teacher->orders, ord, new_order);