From 26e06804d677fe77e94ede1174d61652dc48fe13 Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Thu, 5 Jul 2018 20:06:32 +0200 Subject: [PATCH 01/19] autostudy framework --- src/CMakeLists.txt | 1 + src/automate.c | 71 +++++++++++++++++++++++++++++++++++++++++++++ src/automate.h | 28 ++++++++++++++++++ src/kernel/config.c | 3 +- src/kernel/order.c | 19 +++++++++--- src/kernel/types.h | 1 + src/kernel/unit.c | 5 ++-- src/kernel/unit.h | 2 +- src/keyword.c | 1 + src/keyword.h | 1 + src/laws.c | 2 ++ src/study.c | 8 ++--- src/study.h | 1 + 13 files changed, 130 insertions(+), 13 deletions(-) create mode 100644 src/automate.c create mode 100644 src/automate.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 5b267c0d5..0ff92bf2d 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -86,6 +86,7 @@ ENDIF() set (ERESSEA_SRC vortex.c + automate.c move.c piracy.c spells.c diff --git a/src/automate.c b/src/automate.c new file mode 100644 index 000000000..0855ff0e3 --- /dev/null +++ b/src/automate.c @@ -0,0 +1,71 @@ +#include + +#include "kernel/faction.h" +#include "kernel/order.h" +#include "kernel/region.h" +#include "kernel/unit.h" + +#include "util/log.h" + +#include "automate.h" +#include "keyword.h" +#include "study.h" + +#include + +typedef struct student { + unit *u; + skill_t sk; + int level; +} student; + +#define MAXSTUDENTS 128 + +int cmp_students(const void *lhs, const void *rhs) { + const student *a = (const student *)lhs; + const student *b = (const student *)rhs; + if (a->sk == b->sk) { + /* sort by level, descending: */ + return b->level - a->level; + } + /* order by skill */ + return (int)a->sk - (int)b->sk; +} + +void do_autostudy(region *r) { + unit *u; + int nstudents = 0; + student students[MAXSTUDENTS]; + + for (u = r->units; u; u = u->next) { + keyword_t kwd = getkeyword(u->thisorder); + if (kwd == K_AUTOSTUDY) { + student * st = students + nstudents; + if (++nstudents == MAXSTUDENTS) { + log_fatal("you must increase MAXSTUDENTS"); + } + st->u = u; + init_order(u->thisorder, u->faction->locale); + st->sk = getskill(u->faction->locale); + st->level = effskill_study(u, st->sk); + } + } + if (nstudents > 0) { + int i, taught = 0; + skill_t sk = NOSKILL; + student *teacher = NULL, *student = NULL; + + qsort(students, nstudents, sizeof(student), cmp_students); + for (i = 0; i != nstudents; ++i) { + if (students[i].u) { + if (sk == NOSKILL) { + sk = students[i].sk; + } + else if (sk != students[i].sk) { + continue; + } + students[i].u = NULL; + } + } + } +} diff --git a/src/automate.h b/src/automate.h new file mode 100644 index 000000000..ded8f938b --- /dev/null +++ b/src/automate.h @@ -0,0 +1,28 @@ +/* +Copyright (c) 1998-2018, Enno Rehling +Katja Zedel + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +**/ + +#pragma once + +#ifndef H_GC_AUTOMATE +#define H_GC_AUTOMATE + +struct region; + +void do_autostudy(struct region *r); + +#endif diff --git a/src/kernel/config.c b/src/kernel/config.c index ec45ddd0d..750d406f6 100644 --- a/src/kernel/config.c +++ b/src/kernel/config.c @@ -135,7 +135,8 @@ const char *parameters[MAXPARAMS] = { "GRUPPE", "PARTEITARNUNG", "BAEUME", - "ALLIANZ" + "ALLIANZ", + "AUTO" }; int findoption(const char *s, const struct locale *lang) diff --git a/src/kernel/order.c b/src/kernel/order.c index e660edf81..ac2573758 100644 --- a/src/kernel/order.c +++ b/src/kernel/order.c @@ -214,12 +214,12 @@ static int create_data(keyword_t kwd, const char *s, order_data *data; int id; - assert(kwd!=NOKEYWORD); + assert(kwd != NOKEYWORD); if (!s || *s == 0) { return 0; } - if (kwd==K_STUDY) { + if (kwd == K_STUDY || kwd == K_AUTOSTUDY) { const char * sptr = s; skill_t sk = get_skill(parse_token_depr(&sptr), lang); if (sk != SK_MAGIC && sk != NOSKILL) { @@ -332,6 +332,14 @@ order *parse_order(const char *s, const struct locale * lang) sptr = sp; } } + else if (kwd == K_STUDY) { + const char *sp = sptr; + p = parse_token_depr(&sp); + if (p && isparam(p, lang, P_AUTO)) { + kwd = K_AUTOSTUDY; + sptr = sp; + } + } if (kwd != NOKEYWORD) { order *ord = (order *)malloc(sizeof(order)); create_order_i(ord, kwd, sptr, persistent, noerror, lang); @@ -366,6 +374,7 @@ bool is_repeated(keyword_t kwd) case K_STEAL: case K_SABOTAGE: case K_STUDY: + case K_AUTOSTUDY: case K_TEACH: case K_GROW: case K_PLANT: @@ -406,6 +415,7 @@ bool is_exclusive(const order * ord) case K_STEAL: case K_SABOTAGE: case K_STUDY: + case K_AUTOSTUDY: case K_TEACH: case K_GROW: case K_PLANT: @@ -447,6 +457,7 @@ bool is_long(keyword_t kwd) case K_STEAL: case K_SABOTAGE: case K_STUDY: + case K_AUTOSTUDY: case K_TEACH: case K_GROW: case K_PLANT: @@ -541,7 +552,7 @@ keyword_t init_order(const struct order *ord, const struct locale *lang) assert(sk < MAXSKILLS); assert(lang); - assert(kwd == K_STUDY); + assert(kwd == K_STUDY || kwd == K_AUTOSTUDY); str = skillname(sk, lang); if (strchr(str, ' ') == NULL) { init_tokens_str(str); @@ -575,7 +586,7 @@ keyword_t init_order_depr(const struct order *ord) { if (ord) { keyword_t kwd = ORD_KEYWORD(ord); - assert(kwd != K_STUDY); + assert(kwd != K_STUDY && kwd != K_AUTOSTUDY); } return init_order(ord, NULL); } diff --git a/src/kernel/types.h b/src/kernel/types.h index 61f1268cc..48a4326fa 100644 --- a/src/kernel/types.h +++ b/src/kernel/types.h @@ -130,6 +130,7 @@ typedef enum { P_FACTIONSTEALTH, P_TREES, P_ALLIANCE, + P_AUTO, MAXPARAMS, NOPARAM } param_t; diff --git a/src/kernel/unit.c b/src/kernel/unit.c index 07c6c0856..770723bea 100644 --- a/src/kernel/unit.c +++ b/src/kernel/unit.c @@ -1310,13 +1310,12 @@ int eff_skill(const unit * u, const skill *sv, const region *r) return 0; } -int effskill_study(const unit * u, skill_t sk, const region * r) +int effskill_study(const unit * u, skill_t sk) { skill *sv = unit_skill(u, sk); if (sv && sv->level > 0) { int mlevel = sv->level; - if (!r) r = u->region; - mlevel += get_modifier(u, sv->id, sv->level, r, true); + mlevel += get_modifier(u, sv->id, sv->level, u->region, true); if (mlevel > 0) return mlevel; } diff --git a/src/kernel/unit.h b/src/kernel/unit.h index ac61affde..a6135715c 100644 --- a/src/kernel/unit.h +++ b/src/kernel/unit.h @@ -163,7 +163,7 @@ extern "C" { void clone_men(const struct unit *src, struct unit *dst, int n); /* like transfer, but do not subtract from src */ int eff_skill(const struct unit *u, const struct skill *sv, const struct region *r); - int effskill_study(const struct unit *u, skill_t sk, const struct region *r); + int effskill_study(const struct unit *u, skill_t sk); int get_modifier(const struct unit *u, skill_t sk, int level, const struct region *r, bool noitem); diff --git a/src/keyword.c b/src/keyword.c index 7519b6449..48726bcbc 100644 --- a/src/keyword.c +++ b/src/keyword.c @@ -149,5 +149,6 @@ const char *keywords[MAXKEYWORDS] = { "promote", "pay", "loot", + "autostudy", }; diff --git a/src/keyword.h b/src/keyword.h index a9273c3f5..6e4832708 100644 --- a/src/keyword.h +++ b/src/keyword.h @@ -72,6 +72,7 @@ extern "C" K_PROMOTION, K_PAY, K_LOOT, + K_AUTOSTUDY, MAXKEYWORDS, NOKEYWORD } keyword_t; diff --git a/src/laws.c b/src/laws.c index aa6e237e2..3902e9a8e 100644 --- a/src/laws.c +++ b/src/laws.c @@ -26,6 +26,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include #include "alchemy.h" +#include "automate.h" #include "battle.h" #include "economy.h" #include "keyword.h" @@ -4011,6 +4012,7 @@ void init_processor(void) } p += 10; + add_proc_region(p, do_autostudy, "study automation"); add_proc_order(p, K_TEACH, teach_cmd, PROC_THISORDER | PROC_LONGORDER, "Lehren"); p += 10; diff --git a/src/study.c b/src/study.c index 46da90b74..6df5b5eaf 100644 --- a/src/study.c +++ b/src/study.c @@ -69,7 +69,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #define TEACH_ALL 1 #define TEACH_FRIENDS -static skill_t getskill(const struct locale *lang) +skill_t getskill(const struct locale *lang) { char token[128]; const char * s = gettoken(token, sizeof(token)); @@ -331,7 +331,7 @@ int teach_cmd(unit * teacher, struct order *ord) sk = teachskill[t]; } if (sk != NOSKILL - && effskill_study(teacher, sk, 0) - TEACHDIFFERENCE > effskill_study(student, sk, 0)) { + && effskill_study(teacher, sk) - TEACHDIFFERENCE > effskill_study(student, sk)) { teaching -= teach_unit(teacher, student, teaching, sk, true, &academy_students); } } @@ -343,7 +343,7 @@ int teach_cmd(unit * teacher, struct order *ord) init_order(student->thisorder, student->faction->locale); sk = getskill(student->faction->locale); if (sk != NOSKILL - && effskill_study(teacher, sk, 0) - TEACHDIFFERENCE >= effskill(student, sk, 0)) { + && effskill_study(teacher, sk) - TEACHDIFFERENCE >= effskill(student, sk)) { teaching -= teach_unit(teacher, student, teaching, sk, true, &academy_students); } } @@ -432,7 +432,7 @@ int teach_cmd(unit * teacher, struct order *ord) continue; } - if (effskill_study(student, sk, 0) > effskill_study(teacher, sk, 0) + if (effskill_study(student, sk, NULL) > effskill_study(teacher, sk) - TEACHDIFFERENCE) { if (feedback) { ADDMSG(&teacher->faction->msgs, msg_feedback(teacher, ord, "teach_asgood", diff --git a/src/study.h b/src/study.h index b4b76bb8c..e99fb4808 100644 --- a/src/study.h +++ b/src/study.h @@ -33,6 +33,7 @@ extern "C" { int study_cmd(struct unit *u, struct order *ord); magic_t getmagicskill(const struct locale *lang); + skill_t getskill(const struct locale *lang); bool is_migrant(struct unit *u); int study_cost(struct unit *u, skill_t talent); From a93d8fd56b08787376ace7df8904ad36b3f295ab Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Sat, 7 Jul 2018 17:29:22 +0200 Subject: [PATCH 02/19] finish parser for K_AUTOSTUDY and P_AUTO. --- src/CMakeLists.txt | 1 + src/automate.c | 26 +++++++++++++------------- src/automate.h | 12 ++++++++++++ src/automate.test.c | 38 ++++++++++++++++++++++++++++++++++++++ src/kernel/order.c | 2 +- src/kernel/order.test.c | 31 ++++++++++++++++++++++++++++--- src/study.c | 4 ++-- src/test_eressea.c | 3 ++- 8 files changed, 97 insertions(+), 20 deletions(-) create mode 100644 src/automate.test.c diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 0ff92bf2d..cf96cfbc2 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -188,6 +188,7 @@ set(TESTS_SRC tests.c academy.test.c alchemy.test.c + automate.test.c battle.test.c creport.test.c direction.test.c diff --git a/src/automate.c b/src/automate.c index 0855ff0e3..c1d4b384b 100644 --- a/src/automate.c +++ b/src/automate.c @@ -13,14 +13,6 @@ #include -typedef struct student { - unit *u; - skill_t sk; - int level; -} student; - -#define MAXSTUDENTS 128 - int cmp_students(const void *lhs, const void *rhs) { const student *a = (const student *)lhs; const student *b = (const student *)rhs; @@ -32,16 +24,16 @@ int cmp_students(const void *lhs, const void *rhs) { return (int)a->sk - (int)b->sk; } -void do_autostudy(region *r) { +int autostudy_init(student students[], int max_students, region *r) +{ unit *u; int nstudents = 0; - student students[MAXSTUDENTS]; for (u = r->units; u; u = u->next) { keyword_t kwd = getkeyword(u->thisorder); if (kwd == K_AUTOSTUDY) { student * st = students + nstudents; - if (++nstudents == MAXSTUDENTS) { + if (++nstudents == max_students) { log_fatal("you must increase MAXSTUDENTS"); } st->u = u; @@ -50,10 +42,18 @@ void do_autostudy(region *r) { st->level = effskill_study(u, st->sk); } } + return nstudents; +} + +#define MAXSTUDENTS 128 + +void do_autostudy(region *r) { + student students[MAXSTUDENTS]; + int nstudents = autostudy_init(students, MAXSTUDENTS, r); + if (nstudents > 0) { - int i, taught = 0; + int i; skill_t sk = NOSKILL; - student *teacher = NULL, *student = NULL; qsort(students, nstudents, sizeof(student), cmp_students); for (i = 0; i != nstudents; ++i) { diff --git a/src/automate.h b/src/automate.h index ded8f938b..49b052f56 100644 --- a/src/automate.h +++ b/src/automate.h @@ -21,8 +21,20 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #ifndef H_GC_AUTOMATE #define H_GC_AUTOMATE +#include "skill.h" + struct region; +struct unit; + +typedef struct student { + struct unit *u; + skill_t sk; + int level; + int learn; +} student; void do_autostudy(struct region *r); +int autostudy_init(student students[], int max_students, struct region *r); + #endif diff --git a/src/automate.test.c b/src/automate.test.c new file mode 100644 index 000000000..d8dc8ed74 --- /dev/null +++ b/src/automate.test.c @@ -0,0 +1,38 @@ +#ifdef _MSC_VER +#include +#endif + +#include "automate.h" + +#include "kernel/faction.h" +#include "kernel/order.h" +#include "kernel/region.h" +#include "kernel/unit.h" + +#include "tests.h" + +#include + +static void test_autostudy(CuTest *tc) { + student students[4]; + unit *u1, *u2; + 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]); + u2 = test_create_unit(f, r); + u2->thisorder = create_order(K_AUTOSTUDY, f->locale, skillnames[SK_ENTERTAINMENT]); + CuAssertIntEquals(tc, 2, autostudy_init(students, 4, r)); + test_teardown(); +} + +CuSuite *get_automate_suite(void) +{ + CuSuite *suite = CuSuiteNew(); + SUITE_ADD_TEST(suite, test_autostudy); + return suite; +} diff --git a/src/kernel/order.c b/src/kernel/order.c index ac2573758..342f45577 100644 --- a/src/kernel/order.c +++ b/src/kernel/order.c @@ -84,7 +84,7 @@ char* get_command(const order *ord, const struct locale *lang, char *sbuffer, si sbs_strcat(&sbs, str); if (ord->id < 0) { skill_t sk = (skill_t)(100+ord->id); - assert(kwd == K_STUDY && sk != SK_MAGIC && sk < MAXSKILLS); + assert((kwd == K_STUDY || kwd == K_AUTOSTUDY) && sk != SK_MAGIC && sk < MAXSKILLS); str = skillname(sk, lang); if (str) { if (strchr(str, ' ') == NULL) { diff --git a/src/kernel/order.test.c b/src/kernel/order.test.c index 3a2f44c56..d1f8b4176 100644 --- a/src/kernel/order.test.c +++ b/src/kernel/order.test.c @@ -113,6 +113,30 @@ static void test_parse_make(CuTest *tc) { test_teardown(); } +static void test_parse_autostudy(CuTest *tc) { + char cmd[32]; + order *ord; + struct locale * lang; + + test_setup(); + lang = get_or_create_locale("en"); + locale_setstring(lang, mkname("skill", skillnames[SK_ENTERTAINMENT]), "Entertainment"); + locale_setstring(lang, keyword(K_STUDY), "STUDY"); + locale_setstring(lang, keyword(K_AUTOSTUDY), "AUTOSTUDY"); + locale_setstring(lang, parameters[P_AUTO], "AUTO"); + init_locale(lang); + + ord = parse_order("STUDY AUTO Entertainment", lang); + CuAssertPtrNotNull(tc, ord); + CuAssertIntEquals(tc, K_AUTOSTUDY, getkeyword(ord)); + CuAssertStrEquals(tc, "AUTOSTUDY Entertainment", get_command(ord, lang, cmd, sizeof(cmd))); + + CuAssertIntEquals(tc, K_AUTOSTUDY, init_order(ord, lang)); + CuAssertStrEquals(tc, "Entertainment", getstrtoken()); + free_order(ord); + test_teardown(); +} + static void test_parse_make_temp(CuTest *tc) { char cmd[32]; order *ord; @@ -130,7 +154,7 @@ static void test_parse_make_temp(CuTest *tc) { CuAssertIntEquals(tc, K_MAKETEMP, getkeyword(ord)); CuAssertStrEquals(tc, "MAKETEMP herp", get_command(ord, lang, cmd, sizeof(cmd))); - CuAssertIntEquals(tc, K_MAKETEMP, init_order_depr(ord)); + CuAssertIntEquals(tc, K_MAKETEMP, init_order(ord, lang)); CuAssertStrEquals(tc, "herp", getstrtoken()); free_order(ord); test_teardown(); @@ -153,7 +177,7 @@ static void test_parse_maketemp(CuTest *tc) { CuAssertPtrNotNull(tc, ord); CuAssertStrEquals(tc, "MAKETEMP herp", get_command(ord, lang, cmd, sizeof(cmd))); CuAssertIntEquals(tc, K_MAKETEMP, getkeyword(ord)); - CuAssertIntEquals(tc, K_MAKETEMP, init_order_depr(ord)); + CuAssertIntEquals(tc, K_MAKETEMP, init_order(ord, lang)); CuAssertStrEquals(tc, "herp", getstrtoken()); free_order(ord); test_teardown(); @@ -167,7 +191,7 @@ static void test_init_order(CuTest *tc) { lang = get_or_create_locale("en"); ord = create_order(K_MAKETEMP, lang, "hurr durr"); - CuAssertIntEquals(tc, K_MAKETEMP, init_order_depr(ord)); + CuAssertIntEquals(tc, K_MAKETEMP, init_order(ord, lang)); CuAssertStrEquals(tc, "hurr", getstrtoken()); CuAssertStrEquals(tc, "durr", getstrtoken()); free_order(ord); @@ -548,6 +572,7 @@ CuSuite *get_order_suite(void) SUITE_ADD_TEST(suite, test_study_order_quoted); SUITE_ADD_TEST(suite, test_parse_order); SUITE_ADD_TEST(suite, test_parse_make); + SUITE_ADD_TEST(suite, test_parse_autostudy); SUITE_ADD_TEST(suite, test_parse_make_temp); SUITE_ADD_TEST(suite, test_parse_maketemp); SUITE_ADD_TEST(suite, test_init_order); diff --git a/src/study.c b/src/study.c index 6df5b5eaf..217fe9505 100644 --- a/src/study.c +++ b/src/study.c @@ -343,7 +343,7 @@ int teach_cmd(unit * teacher, struct order *ord) init_order(student->thisorder, student->faction->locale); sk = getskill(student->faction->locale); if (sk != NOSKILL - && effskill_study(teacher, sk) - TEACHDIFFERENCE >= effskill(student, sk)) { + && effskill_study(teacher, sk) - TEACHDIFFERENCE >= effskill(student, sk, NULL)) { teaching -= teach_unit(teacher, student, teaching, sk, true, &academy_students); } } @@ -432,7 +432,7 @@ int teach_cmd(unit * teacher, struct order *ord) continue; } - if (effskill_study(student, sk, NULL) > effskill_study(teacher, sk) + if (effskill_study(student, sk) > effskill_study(teacher, sk) - TEACHDIFFERENCE) { if (feedback) { ADDMSG(&teacher->faction->msgs, msg_feedback(teacher, ord, "teach_asgood", diff --git a/src/test_eressea.c b/src/test_eressea.c index c299fc09c..5684a8ad4 100644 --- a/src/test_eressea.c +++ b/src/test_eressea.c @@ -93,7 +93,6 @@ int RunAllTests(int argc, char *argv[]) ADD_SUITE(xerewards); /* kernel */ ADD_SUITE(academy); - ADD_SUITE(alchemy); ADD_SUITE(alliance); ADD_SUITE(ally); ADD_SUITE(building); @@ -121,6 +120,8 @@ int RunAllTests(int argc, char *argv[]) ADD_SUITE(spells); ADD_SUITE(unit); /* gamecode */ + ADD_SUITE(alchemy); + ADD_SUITE(automate); ADD_SUITE(battle); ADD_SUITE(calendar); ADD_SUITE(creport); From 9bd439e2e192fbeb97611eaa35a68abece3990b1 Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Sat, 7 Jul 2018 20:56:35 +0200 Subject: [PATCH 03/19] Test, sorting units by skill and level --- src/automate.c | 5 ++++- src/automate.test.c | 22 ++++++++++++++++++++-- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/src/automate.c b/src/automate.c index c1d4b384b..f195bb6db 100644 --- a/src/automate.c +++ b/src/automate.c @@ -40,8 +40,12 @@ int autostudy_init(student students[], int max_students, region *r) init_order(u->thisorder, u->faction->locale); st->sk = getskill(u->faction->locale); st->level = effskill_study(u, st->sk); + st->learn = 0; } } + if (nstudents > 0) { + qsort(students, nstudents, sizeof(student), cmp_students); + } return nstudents; } @@ -55,7 +59,6 @@ void do_autostudy(region *r) { int i; skill_t sk = NOSKILL; - qsort(students, nstudents, sizeof(student), cmp_students); for (i = 0; i != nstudents; ++i) { if (students[i].u) { if (sk == NOSKILL) { diff --git a/src/automate.test.c b/src/automate.test.c index d8dc8ed74..3ea640cdf 100644 --- a/src/automate.test.c +++ b/src/automate.test.c @@ -15,7 +15,7 @@ static void test_autostudy(CuTest *tc) { student students[4]; - unit *u1, *u2; + unit *u1, *u2, *u3; faction *f; region *r; @@ -24,9 +24,27 @@ static void test_autostudy(CuTest *tc) { f = test_create_faction(NULL); u1 = test_create_unit(f, r); u1->thisorder = create_order(K_AUTOSTUDY, f->locale, skillnames[SK_ENTERTAINMENT]); + test_create_unit(f, r); u2 = test_create_unit(f, r); u2->thisorder = create_order(K_AUTOSTUDY, f->locale, skillnames[SK_ENTERTAINMENT]); - CuAssertIntEquals(tc, 2, autostudy_init(students, 4, r)); + 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); test_teardown(); } From 7affbd6c74a9ebebf8d949a2b881e607a423a4ea Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Mon, 9 Jul 2018 03:31:13 +0200 Subject: [PATCH 04/19] 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); From 51740476baa501fcc17803bfe15f7350ce522571 Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Wed, 11 Jul 2018 21:03:00 +0200 Subject: [PATCH 05/19] finished autostudy --- src/automate.c | 110 +++++++++++++++++++++++++++++++------------- src/automate.h | 2 + src/automate.test.c | 34 ++++++++++++-- 3 files changed, 111 insertions(+), 35 deletions(-) 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; } From c4424f9899376c0b6a4d3e844ff830cc0fc48a54 Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Wed, 11 Jul 2018 21:07:31 +0200 Subject: [PATCH 06/19] actually do the learning. --- src/automate.c | 22 +++++----------------- src/study.c | 6 +++--- 2 files changed, 8 insertions(+), 20 deletions(-) diff --git a/src/automate.c b/src/automate.c index bf130acf3..e08d7a625 100644 --- a/src/automate.c +++ b/src/automate.c @@ -142,22 +142,10 @@ void autostudy_run(scholar scholars[], int nscholars) 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; - } - } + int i, nscholars = autostudy_init(scholars, MAXSCHOLARS, r); + autostudy_run(scholars, nscholars); + for (i = 0; i != nscholars; ++i) { + int days = STUDYDAYS * scholars[i].learn; + learn_skill(scholars[i].u, scholars[i].sk, days); } } diff --git a/src/study.c b/src/study.c index 0dc4386bc..bd29fc2a8 100644 --- a/src/study.c +++ b/src/study.c @@ -766,9 +766,6 @@ int study_cmd(unit * u, order * ord) days *= 2; } - if (fval(u, UFL_HUNGER)) - days /= 2; - learn_skill(u, sk, days); if (a != NULL) { if (teach->teachers) { @@ -832,6 +829,9 @@ void learn_skill(unit *u, skill_t sk, int days) { int leveldays = STUDYDAYS * u->number; int weeks = 0; + if (fval(u, UFL_HUNGER)) { + days /= 2; + } assert(sk >= 0 && sk < MAXSKILLS); if (inject_learn_fun) { inject_learn_fun(u, sk, days); From af0a359acf03250653bdfe5f4d321e3b68e3aedf Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Wed, 11 Jul 2018 21:39:33 +0200 Subject: [PATCH 07/19] make local comparison function static --- clibs | 2 +- src/automate.c | 2 +- storage | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/clibs b/clibs index 9b6e34959..f9842e07a 160000 --- a/clibs +++ b/clibs @@ -1 +1 @@ -Subproject commit 9b6e34959f77d7ca3a4ce3826cb487487f557441 +Subproject commit f9842e07a442c5453c270badf25ab72633b4edf5 diff --git a/src/automate.c b/src/automate.c index e08d7a625..2f188f352 100644 --- a/src/automate.c +++ b/src/automate.c @@ -14,7 +14,7 @@ #include #include -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; diff --git a/storage b/storage index 6eea76952..5623ee652 160000 --- a/storage +++ b/storage @@ -1 +1 @@ -Subproject commit 6eea7695285f9c9f1d25421897ca55f466ef7566 +Subproject commit 5623ee6527e97af20c7d8efc03800b6fe1579744 From 25d93f3a182ca592445cb29da1386216237694e1 Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Thu, 12 Jul 2018 20:52:09 +0200 Subject: [PATCH 08/19] update storage library with needed bugfix --- storage | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/storage b/storage index 5623ee652..0ef4b39e3 160000 --- a/storage +++ b/storage @@ -1 +1 @@ -Subproject commit 5623ee6527e97af20c7d8efc03800b6fe1579744 +Subproject commit 0ef4b39e39d8146d6ca323500a285c53843db988 From 902ce9bf69ff1d8751a243738fb1876788cdfd9b Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Sat, 14 Jul 2018 15:56:44 +0200 Subject: [PATCH 09/19] extract long_order_allowed for use in autostudy. --- clibs | 2 +- src/automate.c | 26 ++++++++++++++++++-------- src/automate.test.c | 2 ++ src/laws.c | 31 ++++++++++++++++++++----------- src/laws.h | 1 + src/laws.test.c | 30 ++++++++++++++++++++++++++++++ src/study.test.c | 20 ++++++++++---------- 7 files changed, 82 insertions(+), 30 deletions(-) diff --git a/clibs b/clibs index f9842e07a..9b6e34959 160000 --- a/clibs +++ b/clibs @@ -1 +1 @@ -Subproject commit f9842e07a442c5453c270badf25ab72633b4edf5 +Subproject commit 9b6e34959f77d7ca3a4ce3826cb487487f557441 diff --git a/src/automate.c b/src/automate.c index 2f188f352..ec4ea83d8 100644 --- a/src/automate.c +++ b/src/automate.c @@ -1,6 +1,7 @@ #include #include "kernel/faction.h" +#include "kernel/messages.h" #include "kernel/order.h" #include "kernel/region.h" #include "kernel/unit.h" @@ -9,6 +10,7 @@ #include "automate.h" #include "keyword.h" +#include "laws.h" #include "study.h" #include @@ -34,15 +36,21 @@ int autostudy_init(scholar scholars[], int max_scholars, region *r) for (u = r->units; u; u = u->next) { keyword_t kwd = getkeyword(u->thisorder); if (kwd == K_AUTOSTUDY) { - scholar * st = scholars + nscholars; - if (++nscholars == max_scholars) { - log_fatal("you must increase MAXSCHOLARS"); + if (long_order_allowed(u) && unit_can_study(u)) { + scholar * st = scholars + nscholars; + if (++nscholars == max_scholars) { + log_fatal("you must increase MAXSCHOLARS"); + } + st->u = u; + init_order(u->thisorder, u->faction->locale); + st->sk = getskill(u->faction->locale); + st->level = effskill_study(u, st->sk); + st->learn = 0; + } + else { + ADDMSG(&u->faction->msgs, msg_feedback(u, u->thisorder, "error_race_nolearn", "race", + u_race(u))); } - st->u = u; - init_order(u->thisorder, u->faction->locale); - st->sk = getskill(u->faction->locale); - st->level = effskill_study(u, st->sk); - st->learn = 0; } } if (nscholars > 0) { @@ -54,11 +62,13 @@ int autostudy_init(scholar scholars[], int max_scholars, region *r) static void teaching(scholar *s, int n) { assert(n <= s->u->number); s->learn += n; + fset(s->u, UFL_LONGACTION); } static void learning(scholar *s, int n) { assert(n <= s->u->number); s->learn += n; + fset(s->u, UFL_LONGACTION); } void autostudy_run(scholar scholars[], int nscholars) diff --git a/src/automate.test.c b/src/automate.test.c index 999cb25d4..a77376668 100644 --- a/src/automate.test.c +++ b/src/automate.test.c @@ -73,6 +73,7 @@ static void test_autostudy_run(CuTest *tc) { CuAssertIntEquals(tc, 0, scholars[0].learn); CuAssertIntEquals(tc, 20, scholars[1].learn); CuAssertIntEquals(tc, 15, scholars[2].learn); + test_teardown(); } static void test_autostudy_run_noteachers(CuTest *tc) { @@ -100,6 +101,7 @@ static void test_autostudy_run_noteachers(CuTest *tc) { CuAssertIntEquals(tc, 2, scholars[0].learn); CuAssertIntEquals(tc, 10, scholars[1].learn); CuAssertIntEquals(tc, 15, scholars[2].learn); + test_teardown(); } CuSuite *get_automate_suite(void) diff --git a/src/laws.c b/src/laws.c index 3902e9a8e..3280d91bf 100644 --- a/src/laws.c +++ b/src/laws.c @@ -3647,6 +3647,24 @@ void add_proc_unit(int priority, void(*process) (unit *), const char *name) } } +bool long_order_allowed(const unit *u) +{ + const region *r = u->region; + if (fval(u, UFL_LONGACTION)) { + /* this message was already given in laws.update_long_order + cmistake(u, ord, 52, MSG_PRODUCE); + */ + return false; + } + else if (fval(r->terrain, SEA_REGION) + && u_race(u) != get_race(RC_AQUARIAN) + && !(u_race(u)->flags & RCF_SWIM)) { + /* error message disabled by popular demand */ + return false; + } + return true; +} + /* per priority, execute processors in order from PR_GLOBAL down to PR_ORDER */ void process(void) { @@ -3716,16 +3734,7 @@ void process(void) cmistake(u, ord, 224, MSG_MAGIC); ord = NULL; } - else if (fval(u, UFL_LONGACTION)) { - /* this message was already given in laws.update_long_order - cmistake(u, ord, 52, MSG_PRODUCE); - */ - ord = NULL; - } - else if (fval(r->terrain, SEA_REGION) - && u_race(u) != get_race(RC_AQUARIAN) - && !(u_race(u)->flags & RCF_SWIM)) { - /* error message disabled by popular demand */ + else if (!long_order_allowed(u)) { ord = NULL; } } @@ -4167,7 +4176,7 @@ void update_subscriptions(void) /** determine if unit can be seen by faction * @param f -- the observiong faction * @param u -- the unit that is observed - * @param r -- the region that u is obesrved in (see below) + * @param r -- the region that u is obesrved from (see below) * @param m -- terrain modifier to stealth * * r kann != u->region sein, wenn es um Durchreisen geht, diff --git a/src/laws.h b/src/laws.h index ae2c712ca..0b5db17c7 100755 --- a/src/laws.h +++ b/src/laws.h @@ -66,6 +66,7 @@ extern "C" { void update_long_order(struct unit *u); void sinkships(struct region * r); void do_enter(struct region *r, bool is_final_attempt); + bool long_order_allowed(const struct unit *u); int password_cmd(struct unit *u, struct order *ord); int banner_cmd(struct unit *u, struct order *ord); diff --git a/src/laws.test.c b/src/laws.test.c index 8d13b7c81..c7c7a6682 100644 --- a/src/laws.test.c +++ b/src/laws.test.c @@ -1759,6 +1759,34 @@ static void test_nmr_timeout(CuTest *tc) { test_teardown(); } +static void test_long_orders(CuTest *tc) { + unit *u; + + test_setup(); + u = test_create_unit(test_create_faction(NULL), test_create_plain(0, 0)); + CuAssertTrue(tc, long_order_allowed(u)); + u->flags |= UFL_LONGACTION; + CuAssertTrue(tc, !long_order_allowed(u)); + test_teardown(); +} + +static void test_long_order_on_ocean(CuTest *tc) { + unit *u; + race * rc; + + test_setup(); + rc = test_create_race("pikachu"); + u = test_create_unit(test_create_faction(rc), test_create_ocean(0, 0)); + CuAssertTrue(tc, !long_order_allowed(u)); + rc->flags |= RCF_SWIM; + CuAssertTrue(tc, long_order_allowed(u)); + + rc = test_create_race("aquarian"); + u = test_create_unit(test_create_faction(rc), u->region); + CuAssertTrue(tc, long_order_allowed(u)); + test_teardown(); +} + CuSuite *get_laws_suite(void) { CuSuite *suite = CuSuiteNew(); @@ -1831,6 +1859,8 @@ CuSuite *get_laws_suite(void) SUITE_ADD_TEST(suite, test_cansee_ring); SUITE_ADD_TEST(suite, test_cansee_sphere); SUITE_ADD_TEST(suite, test_nmr_timeout); + SUITE_ADD_TEST(suite, test_long_orders); + SUITE_ADD_TEST(suite, test_long_order_on_ocean); return suite; } diff --git a/src/study.test.c b/src/study.test.c index c1d0b0841..a63be7cf1 100644 --- a/src/study.test.c +++ b/src/study.test.c @@ -89,15 +89,15 @@ static void setup_teacher(study_fixture *fix, skill_t sk) { setup_locale(lang); fix->u = test_create_unit(f, r); assert(fix->u); - fix->u->thisorder = create_order(K_STUDY, f->locale, "%s", skillnames[sk]); + fix->u->thisorder = create_order(K_STUDY, f->locale, skillnames[sk]); fix->teachers[0] = test_create_unit(f, r); assert(fix->teachers[0]); - fix->teachers[0]->thisorder = create_order(K_TEACH, f->locale, "%s", itoa36(fix->u->no)); + fix->teachers[0]->thisorder = create_order(K_TEACH, f->locale, itoa36(fix->u->no)); fix->teachers[1] = test_create_unit(f, r); assert(fix->teachers[1]); - fix->teachers[1]->thisorder = create_order(K_TEACH, f->locale, "%s", itoa36(fix->u->no)); + fix->teachers[1]->thisorder = create_order(K_TEACH, f->locale, itoa36(fix->u->no)); test_clear_messages(f); } @@ -110,7 +110,7 @@ static void test_study_no_teacher(CuTest *tc) { CuAssertPtrNotNull(tc, sv = unit_skill(fix.u, SK_CROSSBOW)); CuAssertIntEquals(tc, 1, sv->level); CuAssertIntEquals(tc, 2, sv->weeks); - CuAssertPtrEquals(tc, 0, test_get_last_message(fix.u->faction->msgs)); + CuAssertPtrEquals(tc, NULL, test_get_last_message(fix.u->faction->msgs)); test_teardown(); } @@ -121,7 +121,7 @@ static void test_study_with_teacher(CuTest *tc) { setup_teacher(&fix, SK_CROSSBOW); set_level(fix.teachers[0], SK_CROSSBOW, TEACHDIFFERENCE); teach_cmd(fix.teachers[0], fix.teachers[0]->thisorder); - CuAssertPtrEquals(tc, 0, test_get_last_message(fix.u->faction->msgs)); + CuAssertPtrEquals(tc, NULL, test_get_last_message(fix.u->faction->msgs)); study_cmd(fix.u, fix.u->thisorder); CuAssertPtrNotNull(tc, sv = unit_skill(fix.u, SK_CROSSBOW)); CuAssertIntEquals(tc, 1, sv->level); @@ -288,7 +288,7 @@ static void test_academy_bonus(CuTest *tc) { u1 = test_create_unit(u->faction, u->region); u3 = test_create_unit(u->faction, u->region); u0->thisorder = create_order(K_TEACH, loc, "%s %s", itoa36(u3->no), itoa36(u1->no)); - u->thisorder = create_order(K_TEACH, loc, "%s", itoa36(u1->no)); + u->thisorder = create_order(K_TEACH, loc, itoa36(u1->no)); u1->thisorder = create_order(K_STUDY, loc, skillnames[SK_CROSSBOW]); u3->thisorder = create_order(K_STUDY, loc, skillnames[SK_CROSSBOW]); @@ -405,7 +405,7 @@ static void test_study_magic(CuTest *tc) { f = test_create_faction(NULL); lang = f->locale; u = test_create_unit(f, test_create_region(0, 0, NULL)); - u->thisorder = create_order(K_STUDY, lang, "%s", skillnames[SK_MAGIC]); + u->thisorder = create_order(K_STUDY, lang, skillnames[SK_MAGIC]); itype = test_create_silver(); CuAssertIntEquals(tc, -1, study_cmd(u, u->thisorder)); @@ -423,7 +423,7 @@ static void test_study_magic(CuTest *tc) { CuAssertIntEquals(tc, M_GWYRRD, f->magiegebiet); CuAssertIntEquals(tc, 0, i_get(u->items, itype)); CuAssertPtrNotNull(tc, get_mage_depr(u)); - CuAssertPtrEquals(tc, 0, test_find_messagetype(f->msgs, "error65")); + CuAssertPtrEquals(tc, NULL, test_find_messagetype(f->msgs, "error65")); CuAssertIntEquals(tc, M_GWYRRD, get_mage_depr(u)->magietyp); test_teardown(); @@ -491,12 +491,12 @@ static void test_teach_magic(CuTest *tc) { f = test_create_faction(NULL); f->magiegebiet = M_GWYRRD; u = test_create_unit(f, test_create_region(0, 0, NULL)); - u->thisorder = create_order(K_STUDY, f->locale, "%s", skillnames[SK_MAGIC]); + u->thisorder = create_order(K_STUDY, f->locale, skillnames[SK_MAGIC]); i_change(&u->items, itype, study_cost(u, SK_MAGIC)); ut = test_create_unit(f, u->region); set_level(ut, SK_MAGIC, TEACHDIFFERENCE); create_mage(ut, M_GWYRRD); - ut->thisorder = create_order(K_TEACH, f->locale, "%s", itoa36(u->no)); + ut->thisorder = create_order(K_TEACH, f->locale, itoa36(u->no)); learn_inject(); teach_cmd(ut, ut->thisorder); study_cmd(u, u->thisorder); From 8a503445f61f5c568b7c4bfa0cb1a59c5892b849 Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Mon, 16 Jul 2018 10:53:12 +0200 Subject: [PATCH 10/19] missing include, do not use fset --- src/.DS_Store | Bin 0 -> 8196 bytes src/automate.c | 4 ++-- src/util/.DS_Store | Bin 0 -> 6148 bytes 3 files changed, 2 insertions(+), 2 deletions(-) create mode 100644 src/.DS_Store create mode 100644 src/util/.DS_Store diff --git a/src/.DS_Store b/src/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..f331cdebb3c73099a3bcd9d817441024e2973339 GIT binary patch literal 8196 zcmeHMTWl0n82-QQg&8{1=~`i1ob1-67O{kuAOumny>V+v8`{z>8p`g@2&2=TvNO9y zBxW_yXbe%`Pz?Iui>a3v1$|UsjQU_?UudEx>We<83BHgR<9}vmNlWR2i7`axB&!R^U%O8r^O&Jsl`%c4U= z$Uw+I$Uw+I$Uw-z&A zFY&c1E*KL5rUESc$R1Acn=<-M5gak#H)neyh)V@n_R()n2o4_zMn-T#fj2t$pYXy7 zDIdc!WFTZ`8_*pnU34lwSY)XZC(8;NnYKc+@|dx8a|+mOsmn7 zY0Qnxx~83X4jBc9aawJ3j@xtla&~^SU8zGbfTVaG1G{a$C&s#Um@T`5y7NuKO9eMn5Gp~CVZJV-PQgwO4%47}8H|j@*7+UN@;S=EV0h@4Dy1qO*U+DZd7Oh>6ePtqAWOY`&rU7(Bf8GS_+ zTA-ikH~O8f&>wUa5lD!l6-l(?Zgijv526Pt^kP2_VG?O%kcAEdkHLY966SCk&*C{e zj~DO~&f-;^!|Qkp@8UhYkB{&PzQ#AWgbKdJclaJZ;}=}PpOL!A>PYZAHWF+RG+Mn4 zA0y3-i?u!*e5*QlW39(`@7cRg8M=A3etC`1((TQ6tX-FA-?U}h&WHN0$>6%DzgUBw z=zLXqYOAWrd)P~4T=7?e5}#mE8Yfp;5b7$ic%;humeyGOuJ{ItwWbEuj`%$iuLd>Z z^>uG&Tv2OS__9~+>Q>@v9cx_js`qVI6t$KYf$zP47klej?!LE2V=u8!45M_!8)WI< zlI|CDiGHA8S+rN_FVv#}F|@H2JAaIA)%d01VJ&3fA21;Heois*-W*){`@a_y79j&61OHD3P&+U-(8u?-(yk)T z+6m4_Ig2FX%07BcxLEQbfa}-(Fy!k5hl)0-0LwnIh0}lkAz=AW*Kq&$9z|H(LJa&3 DfC<08 literal 0 HcmV?d00001 diff --git a/src/automate.c b/src/automate.c index ec4ea83d8..a74cf770b 100644 --- a/src/automate.c +++ b/src/automate.c @@ -62,13 +62,13 @@ int autostudy_init(scholar scholars[], int max_scholars, region *r) static void teaching(scholar *s, int n) { assert(n <= s->u->number); s->learn += n; - fset(s->u, UFL_LONGACTION); + s->u->flags |= UFL_LONGACTION; } static void learning(scholar *s, int n) { assert(n <= s->u->number); s->learn += n; - fset(s->u, UFL_LONGACTION); + s->u->flags |= UFL_LONGACTION; } void autostudy_run(scholar scholars[], int nscholars) diff --git a/src/util/.DS_Store b/src/util/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..5008ddfcf53c02e82d7eee2e57c38e5672ef89f6 GIT binary patch literal 6148 zcmeH~Jr2S!425mzP>H1@V-^m;4Wg<&0T*E43hX&L&p$$qDprKhvt+--jT7}7np#A3 zem<@ulZcFPQ@L2!n>{z**++&mCkOWA81W14cNZlEfg7;MkzE(HCqgga^y>{tEnwC%0;vJ&^%eQ zLs35+`xjp>T0 Date: Thu, 5 Jul 2018 20:06:32 +0200 Subject: [PATCH 11/19] autostudy framework --- src/CMakeLists.txt | 1 + src/automate.c | 71 +++++++++++++++++++++++++++++++++++++++++++++ src/automate.h | 28 ++++++++++++++++++ src/kernel/config.c | 3 +- src/kernel/order.c | 19 +++++++++--- src/kernel/types.h | 1 + src/kernel/unit.c | 5 ++-- src/kernel/unit.h | 2 +- src/keyword.c | 1 + src/keyword.h | 1 + src/laws.c | 2 ++ src/study.c | 8 ++--- src/study.h | 1 + 13 files changed, 130 insertions(+), 13 deletions(-) create mode 100644 src/automate.c create mode 100644 src/automate.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 5b267c0d5..0ff92bf2d 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -86,6 +86,7 @@ ENDIF() set (ERESSEA_SRC vortex.c + automate.c move.c piracy.c spells.c diff --git a/src/automate.c b/src/automate.c new file mode 100644 index 000000000..0855ff0e3 --- /dev/null +++ b/src/automate.c @@ -0,0 +1,71 @@ +#include + +#include "kernel/faction.h" +#include "kernel/order.h" +#include "kernel/region.h" +#include "kernel/unit.h" + +#include "util/log.h" + +#include "automate.h" +#include "keyword.h" +#include "study.h" + +#include + +typedef struct student { + unit *u; + skill_t sk; + int level; +} student; + +#define MAXSTUDENTS 128 + +int cmp_students(const void *lhs, const void *rhs) { + const student *a = (const student *)lhs; + const student *b = (const student *)rhs; + if (a->sk == b->sk) { + /* sort by level, descending: */ + return b->level - a->level; + } + /* order by skill */ + return (int)a->sk - (int)b->sk; +} + +void do_autostudy(region *r) { + unit *u; + int nstudents = 0; + student students[MAXSTUDENTS]; + + for (u = r->units; u; u = u->next) { + keyword_t kwd = getkeyword(u->thisorder); + if (kwd == K_AUTOSTUDY) { + student * st = students + nstudents; + if (++nstudents == MAXSTUDENTS) { + log_fatal("you must increase MAXSTUDENTS"); + } + st->u = u; + init_order(u->thisorder, u->faction->locale); + st->sk = getskill(u->faction->locale); + st->level = effskill_study(u, st->sk); + } + } + if (nstudents > 0) { + int i, taught = 0; + skill_t sk = NOSKILL; + student *teacher = NULL, *student = NULL; + + qsort(students, nstudents, sizeof(student), cmp_students); + for (i = 0; i != nstudents; ++i) { + if (students[i].u) { + if (sk == NOSKILL) { + sk = students[i].sk; + } + else if (sk != students[i].sk) { + continue; + } + students[i].u = NULL; + } + } + } +} diff --git a/src/automate.h b/src/automate.h new file mode 100644 index 000000000..ded8f938b --- /dev/null +++ b/src/automate.h @@ -0,0 +1,28 @@ +/* +Copyright (c) 1998-2018, Enno Rehling +Katja Zedel + +Permission to use, copy, modify, and/or distribute this software for any +purpose with or without fee is hereby granted, provided that the above +copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES +WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR +ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES +WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN +ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF +OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +**/ + +#pragma once + +#ifndef H_GC_AUTOMATE +#define H_GC_AUTOMATE + +struct region; + +void do_autostudy(struct region *r); + +#endif diff --git a/src/kernel/config.c b/src/kernel/config.c index ec45ddd0d..750d406f6 100644 --- a/src/kernel/config.c +++ b/src/kernel/config.c @@ -135,7 +135,8 @@ const char *parameters[MAXPARAMS] = { "GRUPPE", "PARTEITARNUNG", "BAEUME", - "ALLIANZ" + "ALLIANZ", + "AUTO" }; int findoption(const char *s, const struct locale *lang) diff --git a/src/kernel/order.c b/src/kernel/order.c index e660edf81..ac2573758 100644 --- a/src/kernel/order.c +++ b/src/kernel/order.c @@ -214,12 +214,12 @@ static int create_data(keyword_t kwd, const char *s, order_data *data; int id; - assert(kwd!=NOKEYWORD); + assert(kwd != NOKEYWORD); if (!s || *s == 0) { return 0; } - if (kwd==K_STUDY) { + if (kwd == K_STUDY || kwd == K_AUTOSTUDY) { const char * sptr = s; skill_t sk = get_skill(parse_token_depr(&sptr), lang); if (sk != SK_MAGIC && sk != NOSKILL) { @@ -332,6 +332,14 @@ order *parse_order(const char *s, const struct locale * lang) sptr = sp; } } + else if (kwd == K_STUDY) { + const char *sp = sptr; + p = parse_token_depr(&sp); + if (p && isparam(p, lang, P_AUTO)) { + kwd = K_AUTOSTUDY; + sptr = sp; + } + } if (kwd != NOKEYWORD) { order *ord = (order *)malloc(sizeof(order)); create_order_i(ord, kwd, sptr, persistent, noerror, lang); @@ -366,6 +374,7 @@ bool is_repeated(keyword_t kwd) case K_STEAL: case K_SABOTAGE: case K_STUDY: + case K_AUTOSTUDY: case K_TEACH: case K_GROW: case K_PLANT: @@ -406,6 +415,7 @@ bool is_exclusive(const order * ord) case K_STEAL: case K_SABOTAGE: case K_STUDY: + case K_AUTOSTUDY: case K_TEACH: case K_GROW: case K_PLANT: @@ -447,6 +457,7 @@ bool is_long(keyword_t kwd) case K_STEAL: case K_SABOTAGE: case K_STUDY: + case K_AUTOSTUDY: case K_TEACH: case K_GROW: case K_PLANT: @@ -541,7 +552,7 @@ keyword_t init_order(const struct order *ord, const struct locale *lang) assert(sk < MAXSKILLS); assert(lang); - assert(kwd == K_STUDY); + assert(kwd == K_STUDY || kwd == K_AUTOSTUDY); str = skillname(sk, lang); if (strchr(str, ' ') == NULL) { init_tokens_str(str); @@ -575,7 +586,7 @@ keyword_t init_order_depr(const struct order *ord) { if (ord) { keyword_t kwd = ORD_KEYWORD(ord); - assert(kwd != K_STUDY); + assert(kwd != K_STUDY && kwd != K_AUTOSTUDY); } return init_order(ord, NULL); } diff --git a/src/kernel/types.h b/src/kernel/types.h index 61f1268cc..48a4326fa 100644 --- a/src/kernel/types.h +++ b/src/kernel/types.h @@ -130,6 +130,7 @@ typedef enum { P_FACTIONSTEALTH, P_TREES, P_ALLIANCE, + P_AUTO, MAXPARAMS, NOPARAM } param_t; diff --git a/src/kernel/unit.c b/src/kernel/unit.c index 07c6c0856..770723bea 100644 --- a/src/kernel/unit.c +++ b/src/kernel/unit.c @@ -1310,13 +1310,12 @@ int eff_skill(const unit * u, const skill *sv, const region *r) return 0; } -int effskill_study(const unit * u, skill_t sk, const region * r) +int effskill_study(const unit * u, skill_t sk) { skill *sv = unit_skill(u, sk); if (sv && sv->level > 0) { int mlevel = sv->level; - if (!r) r = u->region; - mlevel += get_modifier(u, sv->id, sv->level, r, true); + mlevel += get_modifier(u, sv->id, sv->level, u->region, true); if (mlevel > 0) return mlevel; } diff --git a/src/kernel/unit.h b/src/kernel/unit.h index ac61affde..a6135715c 100644 --- a/src/kernel/unit.h +++ b/src/kernel/unit.h @@ -163,7 +163,7 @@ extern "C" { void clone_men(const struct unit *src, struct unit *dst, int n); /* like transfer, but do not subtract from src */ int eff_skill(const struct unit *u, const struct skill *sv, const struct region *r); - int effskill_study(const struct unit *u, skill_t sk, const struct region *r); + int effskill_study(const struct unit *u, skill_t sk); int get_modifier(const struct unit *u, skill_t sk, int level, const struct region *r, bool noitem); diff --git a/src/keyword.c b/src/keyword.c index 7519b6449..48726bcbc 100644 --- a/src/keyword.c +++ b/src/keyword.c @@ -149,5 +149,6 @@ const char *keywords[MAXKEYWORDS] = { "promote", "pay", "loot", + "autostudy", }; diff --git a/src/keyword.h b/src/keyword.h index a9273c3f5..6e4832708 100644 --- a/src/keyword.h +++ b/src/keyword.h @@ -72,6 +72,7 @@ extern "C" K_PROMOTION, K_PAY, K_LOOT, + K_AUTOSTUDY, MAXKEYWORDS, NOKEYWORD } keyword_t; diff --git a/src/laws.c b/src/laws.c index 8d75b0897..80c730c8f 100644 --- a/src/laws.c +++ b/src/laws.c @@ -26,6 +26,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include #include "alchemy.h" +#include "automate.h" #include "battle.h" #include "economy.h" #include "keyword.h" @@ -4001,6 +4002,7 @@ void init_processor(void) } p += 10; + add_proc_region(p, do_autostudy, "study automation"); add_proc_order(p, K_TEACH, teach_cmd, PROC_THISORDER | PROC_LONGORDER, "Lehren"); p += 10; diff --git a/src/study.c b/src/study.c index 46da90b74..6df5b5eaf 100644 --- a/src/study.c +++ b/src/study.c @@ -69,7 +69,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #define TEACH_ALL 1 #define TEACH_FRIENDS -static skill_t getskill(const struct locale *lang) +skill_t getskill(const struct locale *lang) { char token[128]; const char * s = gettoken(token, sizeof(token)); @@ -331,7 +331,7 @@ int teach_cmd(unit * teacher, struct order *ord) sk = teachskill[t]; } if (sk != NOSKILL - && effskill_study(teacher, sk, 0) - TEACHDIFFERENCE > effskill_study(student, sk, 0)) { + && effskill_study(teacher, sk) - TEACHDIFFERENCE > effskill_study(student, sk)) { teaching -= teach_unit(teacher, student, teaching, sk, true, &academy_students); } } @@ -343,7 +343,7 @@ int teach_cmd(unit * teacher, struct order *ord) init_order(student->thisorder, student->faction->locale); sk = getskill(student->faction->locale); if (sk != NOSKILL - && effskill_study(teacher, sk, 0) - TEACHDIFFERENCE >= effskill(student, sk, 0)) { + && effskill_study(teacher, sk) - TEACHDIFFERENCE >= effskill(student, sk)) { teaching -= teach_unit(teacher, student, teaching, sk, true, &academy_students); } } @@ -432,7 +432,7 @@ int teach_cmd(unit * teacher, struct order *ord) continue; } - if (effskill_study(student, sk, 0) > effskill_study(teacher, sk, 0) + if (effskill_study(student, sk, NULL) > effskill_study(teacher, sk) - TEACHDIFFERENCE) { if (feedback) { ADDMSG(&teacher->faction->msgs, msg_feedback(teacher, ord, "teach_asgood", diff --git a/src/study.h b/src/study.h index b4b76bb8c..e99fb4808 100644 --- a/src/study.h +++ b/src/study.h @@ -33,6 +33,7 @@ extern "C" { int study_cmd(struct unit *u, struct order *ord); magic_t getmagicskill(const struct locale *lang); + skill_t getskill(const struct locale *lang); bool is_migrant(struct unit *u); int study_cost(struct unit *u, skill_t talent); From f6b6904ced6fdf3460138ceef59a0f7db320d74e Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Sat, 7 Jul 2018 17:29:22 +0200 Subject: [PATCH 12/19] finish parser for K_AUTOSTUDY and P_AUTO. --- src/CMakeLists.txt | 1 + src/automate.c | 26 +++++++++++++------------- src/automate.h | 12 ++++++++++++ src/automate.test.c | 38 ++++++++++++++++++++++++++++++++++++++ src/kernel/order.c | 2 +- src/kernel/order.test.c | 31 ++++++++++++++++++++++++++++--- src/study.c | 4 ++-- src/test_eressea.c | 3 ++- 8 files changed, 97 insertions(+), 20 deletions(-) create mode 100644 src/automate.test.c diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 0ff92bf2d..cf96cfbc2 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -188,6 +188,7 @@ set(TESTS_SRC tests.c academy.test.c alchemy.test.c + automate.test.c battle.test.c creport.test.c direction.test.c diff --git a/src/automate.c b/src/automate.c index 0855ff0e3..c1d4b384b 100644 --- a/src/automate.c +++ b/src/automate.c @@ -13,14 +13,6 @@ #include -typedef struct student { - unit *u; - skill_t sk; - int level; -} student; - -#define MAXSTUDENTS 128 - int cmp_students(const void *lhs, const void *rhs) { const student *a = (const student *)lhs; const student *b = (const student *)rhs; @@ -32,16 +24,16 @@ int cmp_students(const void *lhs, const void *rhs) { return (int)a->sk - (int)b->sk; } -void do_autostudy(region *r) { +int autostudy_init(student students[], int max_students, region *r) +{ unit *u; int nstudents = 0; - student students[MAXSTUDENTS]; for (u = r->units; u; u = u->next) { keyword_t kwd = getkeyword(u->thisorder); if (kwd == K_AUTOSTUDY) { student * st = students + nstudents; - if (++nstudents == MAXSTUDENTS) { + if (++nstudents == max_students) { log_fatal("you must increase MAXSTUDENTS"); } st->u = u; @@ -50,10 +42,18 @@ void do_autostudy(region *r) { st->level = effskill_study(u, st->sk); } } + return nstudents; +} + +#define MAXSTUDENTS 128 + +void do_autostudy(region *r) { + student students[MAXSTUDENTS]; + int nstudents = autostudy_init(students, MAXSTUDENTS, r); + if (nstudents > 0) { - int i, taught = 0; + int i; skill_t sk = NOSKILL; - student *teacher = NULL, *student = NULL; qsort(students, nstudents, sizeof(student), cmp_students); for (i = 0; i != nstudents; ++i) { diff --git a/src/automate.h b/src/automate.h index ded8f938b..49b052f56 100644 --- a/src/automate.h +++ b/src/automate.h @@ -21,8 +21,20 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #ifndef H_GC_AUTOMATE #define H_GC_AUTOMATE +#include "skill.h" + struct region; +struct unit; + +typedef struct student { + struct unit *u; + skill_t sk; + int level; + int learn; +} student; void do_autostudy(struct region *r); +int autostudy_init(student students[], int max_students, struct region *r); + #endif diff --git a/src/automate.test.c b/src/automate.test.c new file mode 100644 index 000000000..d8dc8ed74 --- /dev/null +++ b/src/automate.test.c @@ -0,0 +1,38 @@ +#ifdef _MSC_VER +#include +#endif + +#include "automate.h" + +#include "kernel/faction.h" +#include "kernel/order.h" +#include "kernel/region.h" +#include "kernel/unit.h" + +#include "tests.h" + +#include + +static void test_autostudy(CuTest *tc) { + student students[4]; + unit *u1, *u2; + 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]); + u2 = test_create_unit(f, r); + u2->thisorder = create_order(K_AUTOSTUDY, f->locale, skillnames[SK_ENTERTAINMENT]); + CuAssertIntEquals(tc, 2, autostudy_init(students, 4, r)); + test_teardown(); +} + +CuSuite *get_automate_suite(void) +{ + CuSuite *suite = CuSuiteNew(); + SUITE_ADD_TEST(suite, test_autostudy); + return suite; +} diff --git a/src/kernel/order.c b/src/kernel/order.c index ac2573758..342f45577 100644 --- a/src/kernel/order.c +++ b/src/kernel/order.c @@ -84,7 +84,7 @@ char* get_command(const order *ord, const struct locale *lang, char *sbuffer, si sbs_strcat(&sbs, str); if (ord->id < 0) { skill_t sk = (skill_t)(100+ord->id); - assert(kwd == K_STUDY && sk != SK_MAGIC && sk < MAXSKILLS); + assert((kwd == K_STUDY || kwd == K_AUTOSTUDY) && sk != SK_MAGIC && sk < MAXSKILLS); str = skillname(sk, lang); if (str) { if (strchr(str, ' ') == NULL) { diff --git a/src/kernel/order.test.c b/src/kernel/order.test.c index 3a2f44c56..d1f8b4176 100644 --- a/src/kernel/order.test.c +++ b/src/kernel/order.test.c @@ -113,6 +113,30 @@ static void test_parse_make(CuTest *tc) { test_teardown(); } +static void test_parse_autostudy(CuTest *tc) { + char cmd[32]; + order *ord; + struct locale * lang; + + test_setup(); + lang = get_or_create_locale("en"); + locale_setstring(lang, mkname("skill", skillnames[SK_ENTERTAINMENT]), "Entertainment"); + locale_setstring(lang, keyword(K_STUDY), "STUDY"); + locale_setstring(lang, keyword(K_AUTOSTUDY), "AUTOSTUDY"); + locale_setstring(lang, parameters[P_AUTO], "AUTO"); + init_locale(lang); + + ord = parse_order("STUDY AUTO Entertainment", lang); + CuAssertPtrNotNull(tc, ord); + CuAssertIntEquals(tc, K_AUTOSTUDY, getkeyword(ord)); + CuAssertStrEquals(tc, "AUTOSTUDY Entertainment", get_command(ord, lang, cmd, sizeof(cmd))); + + CuAssertIntEquals(tc, K_AUTOSTUDY, init_order(ord, lang)); + CuAssertStrEquals(tc, "Entertainment", getstrtoken()); + free_order(ord); + test_teardown(); +} + static void test_parse_make_temp(CuTest *tc) { char cmd[32]; order *ord; @@ -130,7 +154,7 @@ static void test_parse_make_temp(CuTest *tc) { CuAssertIntEquals(tc, K_MAKETEMP, getkeyword(ord)); CuAssertStrEquals(tc, "MAKETEMP herp", get_command(ord, lang, cmd, sizeof(cmd))); - CuAssertIntEquals(tc, K_MAKETEMP, init_order_depr(ord)); + CuAssertIntEquals(tc, K_MAKETEMP, init_order(ord, lang)); CuAssertStrEquals(tc, "herp", getstrtoken()); free_order(ord); test_teardown(); @@ -153,7 +177,7 @@ static void test_parse_maketemp(CuTest *tc) { CuAssertPtrNotNull(tc, ord); CuAssertStrEquals(tc, "MAKETEMP herp", get_command(ord, lang, cmd, sizeof(cmd))); CuAssertIntEquals(tc, K_MAKETEMP, getkeyword(ord)); - CuAssertIntEquals(tc, K_MAKETEMP, init_order_depr(ord)); + CuAssertIntEquals(tc, K_MAKETEMP, init_order(ord, lang)); CuAssertStrEquals(tc, "herp", getstrtoken()); free_order(ord); test_teardown(); @@ -167,7 +191,7 @@ static void test_init_order(CuTest *tc) { lang = get_or_create_locale("en"); ord = create_order(K_MAKETEMP, lang, "hurr durr"); - CuAssertIntEquals(tc, K_MAKETEMP, init_order_depr(ord)); + CuAssertIntEquals(tc, K_MAKETEMP, init_order(ord, lang)); CuAssertStrEquals(tc, "hurr", getstrtoken()); CuAssertStrEquals(tc, "durr", getstrtoken()); free_order(ord); @@ -548,6 +572,7 @@ CuSuite *get_order_suite(void) SUITE_ADD_TEST(suite, test_study_order_quoted); SUITE_ADD_TEST(suite, test_parse_order); SUITE_ADD_TEST(suite, test_parse_make); + SUITE_ADD_TEST(suite, test_parse_autostudy); SUITE_ADD_TEST(suite, test_parse_make_temp); SUITE_ADD_TEST(suite, test_parse_maketemp); SUITE_ADD_TEST(suite, test_init_order); diff --git a/src/study.c b/src/study.c index 6df5b5eaf..217fe9505 100644 --- a/src/study.c +++ b/src/study.c @@ -343,7 +343,7 @@ int teach_cmd(unit * teacher, struct order *ord) init_order(student->thisorder, student->faction->locale); sk = getskill(student->faction->locale); if (sk != NOSKILL - && effskill_study(teacher, sk) - TEACHDIFFERENCE >= effskill(student, sk)) { + && effskill_study(teacher, sk) - TEACHDIFFERENCE >= effskill(student, sk, NULL)) { teaching -= teach_unit(teacher, student, teaching, sk, true, &academy_students); } } @@ -432,7 +432,7 @@ int teach_cmd(unit * teacher, struct order *ord) continue; } - if (effskill_study(student, sk, NULL) > effskill_study(teacher, sk) + if (effskill_study(student, sk) > effskill_study(teacher, sk) - TEACHDIFFERENCE) { if (feedback) { ADDMSG(&teacher->faction->msgs, msg_feedback(teacher, ord, "teach_asgood", diff --git a/src/test_eressea.c b/src/test_eressea.c index c299fc09c..5684a8ad4 100644 --- a/src/test_eressea.c +++ b/src/test_eressea.c @@ -93,7 +93,6 @@ int RunAllTests(int argc, char *argv[]) ADD_SUITE(xerewards); /* kernel */ ADD_SUITE(academy); - ADD_SUITE(alchemy); ADD_SUITE(alliance); ADD_SUITE(ally); ADD_SUITE(building); @@ -121,6 +120,8 @@ int RunAllTests(int argc, char *argv[]) ADD_SUITE(spells); ADD_SUITE(unit); /* gamecode */ + ADD_SUITE(alchemy); + ADD_SUITE(automate); ADD_SUITE(battle); ADD_SUITE(calendar); ADD_SUITE(creport); From 0e754a31ac7e6ee19e801dd248445a274166f574 Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Sat, 7 Jul 2018 20:56:35 +0200 Subject: [PATCH 13/19] Test, sorting units by skill and level --- src/automate.c | 5 ++++- src/automate.test.c | 22 ++++++++++++++++++++-- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/src/automate.c b/src/automate.c index c1d4b384b..f195bb6db 100644 --- a/src/automate.c +++ b/src/automate.c @@ -40,8 +40,12 @@ int autostudy_init(student students[], int max_students, region *r) init_order(u->thisorder, u->faction->locale); st->sk = getskill(u->faction->locale); st->level = effskill_study(u, st->sk); + st->learn = 0; } } + if (nstudents > 0) { + qsort(students, nstudents, sizeof(student), cmp_students); + } return nstudents; } @@ -55,7 +59,6 @@ void do_autostudy(region *r) { int i; skill_t sk = NOSKILL; - qsort(students, nstudents, sizeof(student), cmp_students); for (i = 0; i != nstudents; ++i) { if (students[i].u) { if (sk == NOSKILL) { diff --git a/src/automate.test.c b/src/automate.test.c index d8dc8ed74..3ea640cdf 100644 --- a/src/automate.test.c +++ b/src/automate.test.c @@ -15,7 +15,7 @@ static void test_autostudy(CuTest *tc) { student students[4]; - unit *u1, *u2; + unit *u1, *u2, *u3; faction *f; region *r; @@ -24,9 +24,27 @@ static void test_autostudy(CuTest *tc) { f = test_create_faction(NULL); u1 = test_create_unit(f, r); u1->thisorder = create_order(K_AUTOSTUDY, f->locale, skillnames[SK_ENTERTAINMENT]); + test_create_unit(f, r); u2 = test_create_unit(f, r); u2->thisorder = create_order(K_AUTOSTUDY, f->locale, skillnames[SK_ENTERTAINMENT]); - CuAssertIntEquals(tc, 2, autostudy_init(students, 4, r)); + 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); test_teardown(); } From c8ebde3990b50537c8148f8dd02a2213efeb6ae0 Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Mon, 9 Jul 2018 03:31:13 +0200 Subject: [PATCH 14/19] 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); From b4cb1dfe8d48ed3a60586350017f15ac4280b2a6 Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Wed, 11 Jul 2018 21:03:00 +0200 Subject: [PATCH 15/19] finished autostudy --- src/automate.c | 110 +++++++++++++++++++++++++++++++------------- src/automate.h | 2 + src/automate.test.c | 34 ++++++++++++-- 3 files changed, 111 insertions(+), 35 deletions(-) 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; } From b0fb1e503224eb55968360c02b60bb3f63ae80cb Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Wed, 11 Jul 2018 21:07:31 +0200 Subject: [PATCH 16/19] actually do the learning. --- src/automate.c | 22 +++++----------------- src/study.c | 6 +++--- 2 files changed, 8 insertions(+), 20 deletions(-) diff --git a/src/automate.c b/src/automate.c index bf130acf3..e08d7a625 100644 --- a/src/automate.c +++ b/src/automate.c @@ -142,22 +142,10 @@ void autostudy_run(scholar scholars[], int nscholars) 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; - } - } + int i, nscholars = autostudy_init(scholars, MAXSCHOLARS, r); + autostudy_run(scholars, nscholars); + for (i = 0; i != nscholars; ++i) { + int days = STUDYDAYS * scholars[i].learn; + learn_skill(scholars[i].u, scholars[i].sk, days); } } diff --git a/src/study.c b/src/study.c index 0dc4386bc..bd29fc2a8 100644 --- a/src/study.c +++ b/src/study.c @@ -766,9 +766,6 @@ int study_cmd(unit * u, order * ord) days *= 2; } - if (fval(u, UFL_HUNGER)) - days /= 2; - learn_skill(u, sk, days); if (a != NULL) { if (teach->teachers) { @@ -832,6 +829,9 @@ void learn_skill(unit *u, skill_t sk, int days) { int leveldays = STUDYDAYS * u->number; int weeks = 0; + if (fval(u, UFL_HUNGER)) { + days /= 2; + } assert(sk >= 0 && sk < MAXSKILLS); if (inject_learn_fun) { inject_learn_fun(u, sk, days); From 2bb3e7601d96ae4703e53816332f6e99f199c9b1 Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Sun, 5 Aug 2018 06:42:31 +0200 Subject: [PATCH 17/19] merge conflict --- clibs | 2 +- src/automate.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/clibs b/clibs index d86c85254..66a891b38 160000 --- a/clibs +++ b/clibs @@ -1 +1 @@ -Subproject commit d86c8525489d7f11b7ba13e101bb59ecf160b871 +Subproject commit 66a891b383f1a51bb0d4e5cf002530f7f70bf7f4 diff --git a/src/automate.c b/src/automate.c index e08d7a625..2f188f352 100644 --- a/src/automate.c +++ b/src/automate.c @@ -14,7 +14,7 @@ #include #include -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; From 36b7104ce37b92aa6e455e7ee6ebda667590661e Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Sat, 14 Jul 2018 15:56:44 +0200 Subject: [PATCH 18/19] extract long_order_allowed for use in autostudy. --- src/automate.c | 26 ++++++++++++++++++-------- src/automate.test.c | 2 ++ src/laws.c | 31 ++++++++++++++++++++----------- src/laws.h | 1 + src/laws.test.c | 30 ++++++++++++++++++++++++++++++ src/study.test.c | 20 ++++++++++---------- 6 files changed, 81 insertions(+), 29 deletions(-) diff --git a/src/automate.c b/src/automate.c index 2f188f352..ec4ea83d8 100644 --- a/src/automate.c +++ b/src/automate.c @@ -1,6 +1,7 @@ #include #include "kernel/faction.h" +#include "kernel/messages.h" #include "kernel/order.h" #include "kernel/region.h" #include "kernel/unit.h" @@ -9,6 +10,7 @@ #include "automate.h" #include "keyword.h" +#include "laws.h" #include "study.h" #include @@ -34,15 +36,21 @@ int autostudy_init(scholar scholars[], int max_scholars, region *r) for (u = r->units; u; u = u->next) { keyword_t kwd = getkeyword(u->thisorder); if (kwd == K_AUTOSTUDY) { - scholar * st = scholars + nscholars; - if (++nscholars == max_scholars) { - log_fatal("you must increase MAXSCHOLARS"); + if (long_order_allowed(u) && unit_can_study(u)) { + scholar * st = scholars + nscholars; + if (++nscholars == max_scholars) { + log_fatal("you must increase MAXSCHOLARS"); + } + st->u = u; + init_order(u->thisorder, u->faction->locale); + st->sk = getskill(u->faction->locale); + st->level = effskill_study(u, st->sk); + st->learn = 0; + } + else { + ADDMSG(&u->faction->msgs, msg_feedback(u, u->thisorder, "error_race_nolearn", "race", + u_race(u))); } - st->u = u; - init_order(u->thisorder, u->faction->locale); - st->sk = getskill(u->faction->locale); - st->level = effskill_study(u, st->sk); - st->learn = 0; } } if (nscholars > 0) { @@ -54,11 +62,13 @@ int autostudy_init(scholar scholars[], int max_scholars, region *r) static void teaching(scholar *s, int n) { assert(n <= s->u->number); s->learn += n; + fset(s->u, UFL_LONGACTION); } static void learning(scholar *s, int n) { assert(n <= s->u->number); s->learn += n; + fset(s->u, UFL_LONGACTION); } void autostudy_run(scholar scholars[], int nscholars) diff --git a/src/automate.test.c b/src/automate.test.c index 999cb25d4..a77376668 100644 --- a/src/automate.test.c +++ b/src/automate.test.c @@ -73,6 +73,7 @@ static void test_autostudy_run(CuTest *tc) { CuAssertIntEquals(tc, 0, scholars[0].learn); CuAssertIntEquals(tc, 20, scholars[1].learn); CuAssertIntEquals(tc, 15, scholars[2].learn); + test_teardown(); } static void test_autostudy_run_noteachers(CuTest *tc) { @@ -100,6 +101,7 @@ static void test_autostudy_run_noteachers(CuTest *tc) { CuAssertIntEquals(tc, 2, scholars[0].learn); CuAssertIntEquals(tc, 10, scholars[1].learn); CuAssertIntEquals(tc, 15, scholars[2].learn); + test_teardown(); } CuSuite *get_automate_suite(void) diff --git a/src/laws.c b/src/laws.c index 80c730c8f..68f2894b3 100644 --- a/src/laws.c +++ b/src/laws.c @@ -3637,6 +3637,24 @@ void add_proc_unit(int priority, void(*process) (unit *), const char *name) } } +bool long_order_allowed(const unit *u) +{ + const region *r = u->region; + if (fval(u, UFL_LONGACTION)) { + /* this message was already given in laws.update_long_order + cmistake(u, ord, 52, MSG_PRODUCE); + */ + return false; + } + else if (fval(r->terrain, SEA_REGION) + && u_race(u) != get_race(RC_AQUARIAN) + && !(u_race(u)->flags & RCF_SWIM)) { + /* error message disabled by popular demand */ + return false; + } + return true; +} + /* per priority, execute processors in order from PR_GLOBAL down to PR_ORDER */ void process(void) { @@ -3706,16 +3724,7 @@ void process(void) cmistake(u, ord, 224, MSG_MAGIC); ord = NULL; } - else if (fval(u, UFL_LONGACTION)) { - /* this message was already given in laws.update_long_order - cmistake(u, ord, 52, MSG_PRODUCE); - */ - ord = NULL; - } - else if (fval(r->terrain, SEA_REGION) - && u_race(u) != get_race(RC_AQUARIAN) - && !(u_race(u)->flags & RCF_SWIM)) { - /* error message disabled by popular demand */ + else if (!long_order_allowed(u)) { ord = NULL; } } @@ -4157,7 +4166,7 @@ void update_subscriptions(void) /** determine if unit can be seen by faction * @param f -- the observiong faction * @param u -- the unit that is observed - * @param r -- the region that u is obesrved in (see below) + * @param r -- the region that u is obesrved from (see below) * @param m -- terrain modifier to stealth * * r kann != u->region sein, wenn es um Durchreisen geht, diff --git a/src/laws.h b/src/laws.h index ae2c712ca..0b5db17c7 100755 --- a/src/laws.h +++ b/src/laws.h @@ -66,6 +66,7 @@ extern "C" { void update_long_order(struct unit *u); void sinkships(struct region * r); void do_enter(struct region *r, bool is_final_attempt); + bool long_order_allowed(const struct unit *u); int password_cmd(struct unit *u, struct order *ord); int banner_cmd(struct unit *u, struct order *ord); diff --git a/src/laws.test.c b/src/laws.test.c index 8d13b7c81..c7c7a6682 100644 --- a/src/laws.test.c +++ b/src/laws.test.c @@ -1759,6 +1759,34 @@ static void test_nmr_timeout(CuTest *tc) { test_teardown(); } +static void test_long_orders(CuTest *tc) { + unit *u; + + test_setup(); + u = test_create_unit(test_create_faction(NULL), test_create_plain(0, 0)); + CuAssertTrue(tc, long_order_allowed(u)); + u->flags |= UFL_LONGACTION; + CuAssertTrue(tc, !long_order_allowed(u)); + test_teardown(); +} + +static void test_long_order_on_ocean(CuTest *tc) { + unit *u; + race * rc; + + test_setup(); + rc = test_create_race("pikachu"); + u = test_create_unit(test_create_faction(rc), test_create_ocean(0, 0)); + CuAssertTrue(tc, !long_order_allowed(u)); + rc->flags |= RCF_SWIM; + CuAssertTrue(tc, long_order_allowed(u)); + + rc = test_create_race("aquarian"); + u = test_create_unit(test_create_faction(rc), u->region); + CuAssertTrue(tc, long_order_allowed(u)); + test_teardown(); +} + CuSuite *get_laws_suite(void) { CuSuite *suite = CuSuiteNew(); @@ -1831,6 +1859,8 @@ CuSuite *get_laws_suite(void) SUITE_ADD_TEST(suite, test_cansee_ring); SUITE_ADD_TEST(suite, test_cansee_sphere); SUITE_ADD_TEST(suite, test_nmr_timeout); + SUITE_ADD_TEST(suite, test_long_orders); + SUITE_ADD_TEST(suite, test_long_order_on_ocean); return suite; } diff --git a/src/study.test.c b/src/study.test.c index c1d0b0841..a63be7cf1 100644 --- a/src/study.test.c +++ b/src/study.test.c @@ -89,15 +89,15 @@ static void setup_teacher(study_fixture *fix, skill_t sk) { setup_locale(lang); fix->u = test_create_unit(f, r); assert(fix->u); - fix->u->thisorder = create_order(K_STUDY, f->locale, "%s", skillnames[sk]); + fix->u->thisorder = create_order(K_STUDY, f->locale, skillnames[sk]); fix->teachers[0] = test_create_unit(f, r); assert(fix->teachers[0]); - fix->teachers[0]->thisorder = create_order(K_TEACH, f->locale, "%s", itoa36(fix->u->no)); + fix->teachers[0]->thisorder = create_order(K_TEACH, f->locale, itoa36(fix->u->no)); fix->teachers[1] = test_create_unit(f, r); assert(fix->teachers[1]); - fix->teachers[1]->thisorder = create_order(K_TEACH, f->locale, "%s", itoa36(fix->u->no)); + fix->teachers[1]->thisorder = create_order(K_TEACH, f->locale, itoa36(fix->u->no)); test_clear_messages(f); } @@ -110,7 +110,7 @@ static void test_study_no_teacher(CuTest *tc) { CuAssertPtrNotNull(tc, sv = unit_skill(fix.u, SK_CROSSBOW)); CuAssertIntEquals(tc, 1, sv->level); CuAssertIntEquals(tc, 2, sv->weeks); - CuAssertPtrEquals(tc, 0, test_get_last_message(fix.u->faction->msgs)); + CuAssertPtrEquals(tc, NULL, test_get_last_message(fix.u->faction->msgs)); test_teardown(); } @@ -121,7 +121,7 @@ static void test_study_with_teacher(CuTest *tc) { setup_teacher(&fix, SK_CROSSBOW); set_level(fix.teachers[0], SK_CROSSBOW, TEACHDIFFERENCE); teach_cmd(fix.teachers[0], fix.teachers[0]->thisorder); - CuAssertPtrEquals(tc, 0, test_get_last_message(fix.u->faction->msgs)); + CuAssertPtrEquals(tc, NULL, test_get_last_message(fix.u->faction->msgs)); study_cmd(fix.u, fix.u->thisorder); CuAssertPtrNotNull(tc, sv = unit_skill(fix.u, SK_CROSSBOW)); CuAssertIntEquals(tc, 1, sv->level); @@ -288,7 +288,7 @@ static void test_academy_bonus(CuTest *tc) { u1 = test_create_unit(u->faction, u->region); u3 = test_create_unit(u->faction, u->region); u0->thisorder = create_order(K_TEACH, loc, "%s %s", itoa36(u3->no), itoa36(u1->no)); - u->thisorder = create_order(K_TEACH, loc, "%s", itoa36(u1->no)); + u->thisorder = create_order(K_TEACH, loc, itoa36(u1->no)); u1->thisorder = create_order(K_STUDY, loc, skillnames[SK_CROSSBOW]); u3->thisorder = create_order(K_STUDY, loc, skillnames[SK_CROSSBOW]); @@ -405,7 +405,7 @@ static void test_study_magic(CuTest *tc) { f = test_create_faction(NULL); lang = f->locale; u = test_create_unit(f, test_create_region(0, 0, NULL)); - u->thisorder = create_order(K_STUDY, lang, "%s", skillnames[SK_MAGIC]); + u->thisorder = create_order(K_STUDY, lang, skillnames[SK_MAGIC]); itype = test_create_silver(); CuAssertIntEquals(tc, -1, study_cmd(u, u->thisorder)); @@ -423,7 +423,7 @@ static void test_study_magic(CuTest *tc) { CuAssertIntEquals(tc, M_GWYRRD, f->magiegebiet); CuAssertIntEquals(tc, 0, i_get(u->items, itype)); CuAssertPtrNotNull(tc, get_mage_depr(u)); - CuAssertPtrEquals(tc, 0, test_find_messagetype(f->msgs, "error65")); + CuAssertPtrEquals(tc, NULL, test_find_messagetype(f->msgs, "error65")); CuAssertIntEquals(tc, M_GWYRRD, get_mage_depr(u)->magietyp); test_teardown(); @@ -491,12 +491,12 @@ static void test_teach_magic(CuTest *tc) { f = test_create_faction(NULL); f->magiegebiet = M_GWYRRD; u = test_create_unit(f, test_create_region(0, 0, NULL)); - u->thisorder = create_order(K_STUDY, f->locale, "%s", skillnames[SK_MAGIC]); + u->thisorder = create_order(K_STUDY, f->locale, skillnames[SK_MAGIC]); i_change(&u->items, itype, study_cost(u, SK_MAGIC)); ut = test_create_unit(f, u->region); set_level(ut, SK_MAGIC, TEACHDIFFERENCE); create_mage(ut, M_GWYRRD); - ut->thisorder = create_order(K_TEACH, f->locale, "%s", itoa36(u->no)); + ut->thisorder = create_order(K_TEACH, f->locale, itoa36(u->no)); learn_inject(); teach_cmd(ut, ut->thisorder); study_cmd(u, u->thisorder); From 22d0fe5693bd943aa5d8c911dba328f3a8fb75a1 Mon Sep 17 00:00:00 2001 From: Enno Rehling Date: Mon, 16 Jul 2018 10:53:12 +0200 Subject: [PATCH 19/19] missing include, do not use fset --- src/.DS_Store | Bin 0 -> 8196 bytes src/automate.c | 4 ++-- src/util/.DS_Store | Bin 0 -> 6148 bytes 3 files changed, 2 insertions(+), 2 deletions(-) create mode 100644 src/.DS_Store create mode 100644 src/util/.DS_Store diff --git a/src/.DS_Store b/src/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..f331cdebb3c73099a3bcd9d817441024e2973339 GIT binary patch literal 8196 zcmeHMTWl0n82-QQg&8{1=~`i1ob1-67O{kuAOumny>V+v8`{z>8p`g@2&2=TvNO9y zBxW_yXbe%`Pz?Iui>a3v1$|UsjQU_?UudEx>We<83BHgR<9}vmNlWR2i7`axB&!R^U%O8r^O&Jsl`%c4U= z$Uw+I$Uw+I$Uw-z&A zFY&c1E*KL5rUESc$R1Acn=<-M5gak#H)neyh)V@n_R()n2o4_zMn-T#fj2t$pYXy7 zDIdc!WFTZ`8_*pnU34lwSY)XZC(8;NnYKc+@|dx8a|+mOsmn7 zY0Qnxx~83X4jBc9aawJ3j@xtla&~^SU8zGbfTVaG1G{a$C&s#Um@T`5y7NuKO9eMn5Gp~CVZJV-PQgwO4%47}8H|j@*7+UN@;S=EV0h@4Dy1qO*U+DZd7Oh>6ePtqAWOY`&rU7(Bf8GS_+ zTA-ikH~O8f&>wUa5lD!l6-l(?Zgijv526Pt^kP2_VG?O%kcAEdkHLY966SCk&*C{e zj~DO~&f-;^!|Qkp@8UhYkB{&PzQ#AWgbKdJclaJZ;}=}PpOL!A>PYZAHWF+RG+Mn4 zA0y3-i?u!*e5*QlW39(`@7cRg8M=A3etC`1((TQ6tX-FA-?U}h&WHN0$>6%DzgUBw z=zLXqYOAWrd)P~4T=7?e5}#mE8Yfp;5b7$ic%;humeyGOuJ{ItwWbEuj`%$iuLd>Z z^>uG&Tv2OS__9~+>Q>@v9cx_js`qVI6t$KYf$zP47klej?!LE2V=u8!45M_!8)WI< zlI|CDiGHA8S+rN_FVv#}F|@H2JAaIA)%d01VJ&3fA21;Heois*-W*){`@a_y79j&61OHD3P&+U-(8u?-(yk)T z+6m4_Ig2FX%07BcxLEQbfa}-(Fy!k5hl)0-0LwnIh0}lkAz=AW*Kq&$9z|H(LJa&3 DfC<08 literal 0 HcmV?d00001 diff --git a/src/automate.c b/src/automate.c index ec4ea83d8..a74cf770b 100644 --- a/src/automate.c +++ b/src/automate.c @@ -62,13 +62,13 @@ int autostudy_init(scholar scholars[], int max_scholars, region *r) static void teaching(scholar *s, int n) { assert(n <= s->u->number); s->learn += n; - fset(s->u, UFL_LONGACTION); + s->u->flags |= UFL_LONGACTION; } static void learning(scholar *s, int n) { assert(n <= s->u->number); s->learn += n; - fset(s->u, UFL_LONGACTION); + s->u->flags |= UFL_LONGACTION; } void autostudy_run(scholar scholars[], int nscholars) diff --git a/src/util/.DS_Store b/src/util/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..5008ddfcf53c02e82d7eee2e57c38e5672ef89f6 GIT binary patch literal 6148 zcmeH~Jr2S!425mzP>H1@V-^m;4Wg<&0T*E43hX&L&p$$qDprKhvt+--jT7}7np#A3 zem<@ulZcFPQ@L2!n>{z**++&mCkOWA81W14cNZlEfg7;MkzE(HCqgga^y>{tEnwC%0;vJ&^%eQ zLs35+`xjp>T0