simple tests for build().

cleaning up the code to give it a common setup function.
change the golem code to use flag, not hard-coded race.
unify the golems, make code more readable, kill a block of unused code.
This commit is contained in:
Enno Rehling 2014-08-24 17:09:32 +02:00
parent 9db6826662
commit 7d17cafdd6
5 changed files with 267 additions and 235 deletions

View File

@ -79,8 +79,7 @@ extern "C" {
void sunhash(struct ship *sh); void sunhash(struct ship *sh);
extern int roqf_factor(void); extern int roqf_factor(void);
extern int build(struct unit *u, const construction * ctype, int completed, int build(struct unit *u, const construction * ctype, int completed, int want);
int want);
extern int maxbuild(const struct unit *u, const construction * cons); extern int maxbuild(const struct unit *u, const construction * cons);
extern struct message *msg_materials_required(struct unit *u, extern struct message *msg_materials_required(struct unit *u,
struct order *ord, const struct construction *ctype, int multi); struct order *ord, const struct construction *ctype, int multi);

View File

@ -13,86 +13,110 @@
#include <CuTest.h> #include <CuTest.h>
#include <tests.h> #include <tests.h>
#include <stdlib.h>
#include <assert.h> #include <assert.h>
static void test_build_building_no_materials(CuTest *tc) { typedef struct build_fixture {
faction *f;
unit *u; unit *u;
region *r; region *r;
faction *f;
race *rc; race *rc;
const building_type *btype; } build_fixture;
static unit * setup_build(build_fixture *bf) {
test_cleanup(); test_cleanup();
test_create_world(); test_create_world();
bf->rc = test_create_race("human");
bf->r = findregion(0, 0);
bf->f = test_create_faction(bf->rc);
assert(bf->rc && bf->f && bf->r);
bf->u = test_create_unit(bf->f, bf->r);
assert(bf->u);
return bf->u;
}
rc = test_create_race("human"); static void test_build(CuTest *tc) {
r = findregion(0, 0); build_fixture bf;
f = test_create_faction(rc); unit *u;
assert(rc && f && r); construction cons = { 0 };
u = test_create_unit(f, r); const struct resource_type *rtype;
u = setup_build(&bf);
rtype = get_resourcetype(R_SILVER);
assert(rtype);
cons.materials = calloc(2, sizeof(requirement));
cons.materials[0].number = 1;
cons.materials[0].rtype = rtype;
cons.skill = SK_ARMORER;
cons.minskill = 2;
cons.reqsize = 1;
CuAssertIntEquals(tc, ENEEDSKILL, build(u, &cons, 1, 1));
set_level(u, SK_ARMORER, 1);
CuAssertIntEquals(tc, ELOWSKILL, build(u, &cons, 1, 1));
set_level(u, SK_ARMORER, 2);
CuAssertIntEquals(tc, ENOMATERIALS, build(u, &cons, 1, 1));
i_change(&u->items, rtype->itype, 1);
CuAssertIntEquals(tc, 1, build(u, &cons, 1, 1));
CuAssertIntEquals(tc, 0, i_get(u->items, rtype->itype));
scale_number(u, 2);
i_change(&u->items, rtype->itype, 2);
CuAssertIntEquals(tc, 2, build(u, &cons, 2, 2));
CuAssertIntEquals(tc, 0, i_get(u->items, rtype->itype));
test_cleanup();
}
static void test_build_building_no_materials(CuTest *tc) {
const building_type *btype;
build_fixture bf = { 0 };
unit *u;
u = setup_build(&bf);
btype = bt_find("castle"); btype = bt_find("castle");
assert(u && btype); assert(btype);
set_level(u, SK_BUILDING, 1); set_level(bf.u, SK_BUILDING, 1);
CuAssertIntEquals(tc, ENOMATERIALS, build_building(u, btype, 0, 4, 0)); CuAssertIntEquals(tc, ENOMATERIALS, build_building(bf.u, btype, 0, 4, 0));
CuAssertPtrEquals(tc, 0, r->buildings); CuAssertPtrEquals(tc, 0, bf.r->buildings);
CuAssertPtrEquals(tc, 0, u->building); CuAssertPtrEquals(tc, 0, bf.u->building);
} }
static void test_build_building_with_golem(CuTest *tc) { static void test_build_building_with_golem(CuTest *tc) {
unit *u; unit *u;
region *r; build_fixture bf;
faction *f;
race *rc;
const building_type *btype; const building_type *btype;
test_cleanup(); u = setup_build(&bf);
test_create_world(); bf.rc->flags |= RCF_STONEGOLEM;
rc = test_create_race("stonegolem");
rc->flags |= RCF_STONEGOLEM;
btype = bt_find("castle"); btype = bt_find("castle");
assert(btype && rc); assert(btype);
assert(btype->construction); assert(btype->construction);
r = findregion(0, 0);
assert(!r->buildings); set_level(bf.u, SK_BUILDING, 1);
f = test_create_faction(rc);
assert(r && f);
u = test_create_unit(f, r);
assert(u);
set_level(u, SK_BUILDING, 1);
CuAssertIntEquals(tc, 1, build_building(u, btype, 0, 4, 0)); CuAssertIntEquals(tc, 1, build_building(u, btype, 0, 4, 0));
CuAssertPtrNotNull(tc, r->buildings); CuAssertPtrNotNull(tc, u->region->buildings);
CuAssertIntEquals(tc, 1, r->buildings->size); CuAssertIntEquals(tc, 1, u->region->buildings->size);
CuAssertIntEquals(tc, 0, u->number); CuAssertIntEquals(tc, 0, u->number);
} }
static void test_build_building_success(CuTest *tc) { static void test_build_building_success(CuTest *tc) {
unit *u; unit *u;
region *r; build_fixture bf = { 0 };
faction *f;
race *rc;
const building_type *btype; const building_type *btype;
const resource_type *rtype; const resource_type *rtype;
test_cleanup(); u = setup_build(&bf);
test_create_world();
rc = test_create_race("human");
rtype = get_resourcetype(R_STONE); rtype = get_resourcetype(R_STONE);
btype = bt_find("castle"); btype = bt_find("castle");
assert(btype && rc && rtype && rtype->itype); assert(btype && rtype && rtype->itype);
assert(btype->construction); assert(btype->construction);
r = findregion(0, 0); assert(!u->region->buildings);
assert(!r->buildings);
f = test_create_faction(rc); i_change(&bf.u->items, rtype->itype, 1);
assert(r && f);
u = test_create_unit(f, r);
assert(u);
i_change(&u->items, rtype->itype, 1);
set_level(u, SK_BUILDING, 1); set_level(u, SK_BUILDING, 1);
CuAssertIntEquals(tc, 1, build_building(u, btype, 0, 4, 0)); CuAssertIntEquals(tc, 1, build_building(u, btype, 0, 4, 0));
CuAssertPtrNotNull(tc, r->buildings); CuAssertPtrNotNull(tc, u->region->buildings);
CuAssertPtrEquals(tc, r->buildings, u->building); CuAssertPtrEquals(tc, u->region->buildings, u->building);
CuAssertIntEquals(tc, 1, u->building->size); CuAssertIntEquals(tc, 1, u->building->size);
CuAssertIntEquals(tc, 0, i_get(u->items, rtype->itype)); CuAssertIntEquals(tc, 0, i_get(u->items, rtype->itype));
} }
@ -100,6 +124,7 @@ static void test_build_building_success(CuTest *tc) {
CuSuite *get_build_suite(void) CuSuite *get_build_suite(void)
{ {
CuSuite *suite = CuSuiteNew(); CuSuite *suite = CuSuiteNew();
SUITE_ADD_TEST(suite, test_build);
SUITE_ADD_TEST(suite, test_build_building_success); SUITE_ADD_TEST(suite, test_build_building_success);
SUITE_ADD_TEST(suite, test_build_building_with_golem); SUITE_ADD_TEST(suite, test_build_building_with_golem);
SUITE_ADD_TEST(suite, test_build_building_no_materials); SUITE_ADD_TEST(suite, test_build_building_no_materials);

View File

@ -101,24 +101,29 @@ static int res_changepeasants(unit * u, const resource_type * rtype, int delta)
return u->region->land->peasants; return u->region->land->peasants;
} }
static int golem_factor(const unit *u, const resource_type *rtype) {
if (rtype == get_resourcetype(R_STONE) && (u_race(u)->flags & RCF_STONEGOLEM)) {
return GOLEM_STONE;
}
if (rtype == get_resourcetype(R_IRON) && (u_race(u)->flags & RCF_IRONGOLEM)) {
return GOLEM_IRON;
}
return 0;
}
static int res_changeitem(unit * u, const resource_type * rtype, int delta) static int res_changeitem(unit * u, const resource_type * rtype, int delta)
{ {
int num; int num;
if (rtype == get_resourcetype(R_STONE) && u_race(u) == get_race(RC_STONEGOLEM) int gf = (delta>0) ? 0 : golem_factor(u, rtype);
&& delta <= 0) { if (gf>0) {
int reduce = delta / GOLEM_STONE; if (delta != 0) {
if (delta % GOLEM_STONE != 0) int reduce = delta / gf;
--reduce; if (delta % gf != 0) {
scale_number(u, u->number + reduce); --reduce;
num = u->number * GOLEM_STONE; }
} if (reduce) scale_number(u, u->number + reduce);
else if (rtype == get_resourcetype(R_IRON) }
&& u_race(u) == get_race(RC_IRONGOLEM) && delta <= 0) { num = u->number * gf;
int reduce = delta / GOLEM_IRON;
if (delta % GOLEM_IRON != 0)
--reduce;
scale_number(u, u->number + reduce);
num = u->number * GOLEM_IRON;
} }
else { else {
const item_type *itype = resource2item(rtype); const item_type *itype = resource2item(rtype);

View File

@ -1,7 +1,7 @@
/* /*
Copyright (c) 1998-2010, Enno Rehling <enno@eressea.de> Copyright (c) 1998-2010, Enno Rehling <enno@eressea.de>
Katja Zedel <katze@felidae.kn-bremen.de Katja Zedel <katze@felidae.kn-bremen.de
Christian Schlittchen <corwin@amber.kn-bremen.de> Christian Schlittchen <corwin@amber.kn-bremen.de>
Permission to use, copy, modify, and/or distribute this software for any Permission to use, copy, modify, and/or distribute this software for any
purpose with or without fee is hereby granted, provided that the above purpose with or without fee is hereby granted, provided that the above
@ -39,212 +39,216 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
int get_resource(const unit * u, const resource_type * rtype) int get_resource(const unit * u, const resource_type * rtype)
{ {
assert(rtype); assert(rtype);
if (rtype->uget) { if (rtype->uget) {
/* this resource is probably special */ /* this resource is probably special */
int i = rtype->uget(u, rtype); int i = rtype->uget(u, rtype);
if (i >= 0) if (i >= 0)
return i; return i;
}
else if (rtype->uchange) {
/* this resource is probably special */
int i = rtype->uchange((unit *)u, rtype, 0);
if (i >= 0)
return i;
}
if (rtype->itype) {
if (rtype == get_resourcetype(R_STONE) && (u_race(u)->flags & RCF_STONEGOLEM)) {
return u->number * GOLEM_STONE;
} else if (rtype == get_resourcetype(R_IRON) && (u_race(u)->flags & RCF_IRONGOLEM)) {
return u->number * GOLEM_IRON;
} else {
return i_get(u->items, rtype->itype);
} }
} else if (rtype->uchange) {
if (rtype == get_resourcetype(R_AURA)) /* this resource is probably special */
return get_spellpoints(u); int i = rtype->uchange((unit *)u, rtype, 0);
if (rtype == get_resourcetype(R_PERMAURA)) if (i >= 0)
return max_spellpoints(u->region, u); return i;
log_error("trying to get unknown resource '%s'.\n", rtype->_name[0]); }
return 0; if (rtype->itype) {
return i_get(u->items, rtype->itype);
}
if (rtype == get_resourcetype(R_AURA)) {
return get_spellpoints(u);
}
if (rtype == get_resourcetype(R_PERMAURA)) {
return max_spellpoints(u->region, u);
}
log_error("trying to get unknown resource '%s'.\n", rtype->_name[0]);
return 0;
} }
int change_resource(unit * u, const resource_type * rtype, int change) int change_resource(unit * u, const resource_type * rtype, int change)
{ {
int i = 0; int i = 0;
if (rtype->uchange) if (rtype->uchange)
i = rtype->uchange(u, rtype, change); i = rtype->uchange(u, rtype, change);
else if (rtype == get_resourcetype(R_AURA)) else if (rtype == get_resourcetype(R_AURA))
i = change_spellpoints(u, change); i = change_spellpoints(u, change);
else if (rtype == get_resourcetype(R_PERMAURA)) else if (rtype == get_resourcetype(R_PERMAURA))
i = change_maxspellpoints(u, change); i = change_maxspellpoints(u, change);
else else
assert(!"undefined resource detected. rtype->uchange not initialized."); assert(!"undefined resource detected. rtype->uchange not initialized.");
assert(i==get_resource(u, rtype)); assert(i == get_resource(u, rtype));
assert(i >= 0); assert(i >= 0);
if (i >= 100000000) { if (i >= 100000000) {
log_warning("%s has %d %s\n", unitname(u), i, rtype->_name[0]); log_warning("%s has %d %s\n", unitname(u), i, rtype->_name[0]);
} }
return i; return i;
} }
int get_reservation(const unit * u, const resource_type * rtype) int get_reservation(const unit * u, const resource_type * rtype)
{ {
reservation *res = u->reservations; reservation *res = u->reservations;
if (rtype == get_resourcetype(R_STONE) && (u_race(u)->flags & RCF_STONEGOLEM)) if (rtype == get_resourcetype(R_STONE) && (u_race(u)->flags & RCF_STONEGOLEM))
return (u->number * GOLEM_STONE); return (u->number * GOLEM_STONE);
if (rtype == get_resourcetype(R_IRON) && (u_race(u)->flags & RCF_IRONGOLEM)) if (rtype == get_resourcetype(R_IRON) && (u_race(u)->flags & RCF_IRONGOLEM))
return (u->number * GOLEM_IRON); return (u->number * GOLEM_IRON);
while (res && res->type != rtype) while (res && res->type != rtype)
res = res->next; res = res->next;
if (res) if (res)
return res->value; return res->value;
return 0; return 0;
} }
int change_reservation(unit * u, const resource_type * rtype, int value) int change_reservation(unit * u, const resource_type * rtype, int value)
{ {
reservation *res, **rp = &u->reservations; reservation *res, **rp = &u->reservations;
if (!value) if (!value)
return 0; return 0;
while (*rp && (*rp)->type != rtype) while (*rp && (*rp)->type != rtype)
rp = &(*rp)->next; rp = &(*rp)->next;
res = *rp; res = *rp;
if (!res) { if (!res) {
*rp = res = calloc(sizeof(reservation), 1); *rp = res = calloc(sizeof(reservation), 1);
res->type = rtype; res->type = rtype;
res->value = value; res->value = value;
} else if (res && res->value + value <= 0) { }
*rp = res->next; else if (res && res->value + value <= 0) {
free(res); *rp = res->next;
return 0; free(res);
} else { return 0;
res->value += value; }
} else {
return res->value; res->value += value;
}
return res->value;
} }
int set_resvalue(unit * u, const resource_type * rtype, int value) int set_resvalue(unit * u, const resource_type * rtype, int value)
{ {
reservation *res, **rp = &u->reservations; reservation *res, **rp = &u->reservations;
while (*rp && (*rp)->type != rtype) while (*rp && (*rp)->type != rtype)
rp = &(*rp)->next; rp = &(*rp)->next;
res = *rp; res = *rp;
if (!res) { if (!res) {
if (!value) if (!value)
return 0; return 0;
*rp = res = calloc(sizeof(reservation), 1); *rp = res = calloc(sizeof(reservation), 1);
res->type = rtype; res->type = rtype;
res->value = value; res->value = value;
} else if (res && value <= 0) { }
*rp = res->next; else if (res && value <= 0) {
free(res); *rp = res->next;
return 0; free(res);
} else { return 0;
res->value = value; }
} else {
return res->value; res->value = value;
}
return res->value;
} }
int int
get_pooled(const unit * u, const resource_type * rtype, unsigned int mode, get_pooled(const unit * u, const resource_type * rtype, unsigned int mode,
int count) int count)
{ {
const faction *f = u->faction; const faction *f = u->faction;
unit *v; unit *v;
int use = 0; int use = 0;
region *r = u->region; region *r = u->region;
int have = get_resource(u, rtype); int have = get_resource(u, rtype);
if ((u_race(u)->ec_flags & GETITEM) == 0) { if ((u_race(u)->ec_flags & GETITEM) == 0) {
mode &= (GET_SLACK | GET_RESERVE); mode &= (GET_SLACK | GET_RESERVE);
} }
if ((mode & GET_SLACK) && (mode & GET_RESERVE)) if ((mode & GET_SLACK) && (mode & GET_RESERVE))
use = have; use = have;
else { else {
int reserve = get_reservation(u, rtype); int reserve = get_reservation(u, rtype);
int slack = _max(0, have - reserve); int slack = _max(0, have - reserve);
if (mode & GET_RESERVE) if (mode & GET_RESERVE)
use = have - slack; use = have - slack;
else if (mode & GET_SLACK) else if (mode & GET_SLACK)
use = slack; use = slack;
} }
if (rtype->flags & RTF_POOLED && mode & ~(GET_SLACK | GET_RESERVE)) { if (rtype->flags & RTF_POOLED && mode & ~(GET_SLACK | GET_RESERVE)) {
for (v = r->units; v && use < count; v = v->next) for (v = r->units; v && use < count; v = v->next)
if (u != v) { if (u != v) {
int mask; int mask;
if (v->items == NULL && rtype->uget == NULL) if (v->items == NULL && rtype->uget == NULL)
continue; continue;
if ((urace(v)->ec_flags & GIVEITEM) == 0) if ((urace(v)->ec_flags & GIVEITEM) == 0)
continue; continue;
if (v->faction == f) { if (v->faction == f) {
mask = (mode >> 3) & (GET_SLACK | GET_RESERVE); mask = (mode >> 3) & (GET_SLACK | GET_RESERVE);
} else if (alliedunit(v, f, HELP_MONEY)) }
mask = (mode >> 6) & (GET_SLACK | GET_RESERVE); else if (alliedunit(v, f, HELP_MONEY))
else mask = (mode >> 6) & (GET_SLACK | GET_RESERVE);
continue; else
use += get_pooled(v, rtype, mask, count - use); continue;
} use += get_pooled(v, rtype, mask, count - use);
} }
return use; }
return use;
} }
int int
use_pooled(unit * u, const resource_type * rtype, unsigned int mode, int count) use_pooled(unit * u, const resource_type * rtype, unsigned int mode, int count)
{ {
const faction *f = u->faction; const faction *f = u->faction;
unit *v; unit *v;
int use = count; int use = count;
region *r = u->region; region *r = u->region;
int n = 0, have = get_resource(u, rtype); int n = 0, have = get_resource(u, rtype);
if ((u_race(u)->ec_flags & GETITEM) == 0) { if ((u_race(u)->ec_flags & GETITEM) == 0) {
mode &= (GET_SLACK | GET_RESERVE); mode &= (GET_SLACK | GET_RESERVE);
}
if ((mode & GET_SLACK) && (mode & GET_RESERVE)) {
n = _min(use, have);
} else {
int reserve = get_reservation(u, rtype);
int slack = _max(0, have - reserve);
if (mode & GET_RESERVE) {
n = have - slack;
n = _min(use, n);
change_reservation(u, rtype, -n);
} else if (mode & GET_SLACK) {
n = _min(use, slack);
} }
}
if (n > 0) {
change_resource(u, rtype, -n);
use -= n;
}
if (rtype->flags & RTF_POOLED && mode & ~(GET_SLACK | GET_RESERVE)) { if ((mode & GET_SLACK) && (mode & GET_RESERVE)) {
for (v = r->units; use > 0 && v != NULL; v = v->next) n = _min(use, have);
if (u != v) { }
int mask; else {
if ((urace(v)->ec_flags & GIVEITEM) == 0) int reserve = get_reservation(u, rtype);
continue; int slack = _max(0, have - reserve);
if (v->items == NULL && rtype->uget == NULL) if (mode & GET_RESERVE) {
continue; n = have - slack;
n = _min(use, n);
change_reservation(u, rtype, -n);
}
else if (mode & GET_SLACK) {
n = _min(use, slack);
}
}
if (n > 0) {
change_resource(u, rtype, -n);
use -= n;
}
if (v->faction == f) { if (rtype->flags & RTF_POOLED && mode & ~(GET_SLACK | GET_RESERVE)) {
mask = (mode >> 3) & (GET_SLACK | GET_RESERVE); for (v = r->units; use > 0 && v != NULL; v = v->next)
} else if (alliedunit(v, f, HELP_MONEY)) if (u != v) {
mask = (mode >> 6) & (GET_SLACK | GET_RESERVE); int mask;
else if ((urace(v)->ec_flags & GIVEITEM) == 0)
continue; continue;
use -= use_pooled(v, rtype, mask, use); if (v->items == NULL && rtype->uget == NULL)
} continue;
}
return count - use; if (v->faction == f) {
mask = (mode >> 3) & (GET_SLACK | GET_RESERVE);
}
else if (alliedunit(v, f, HELP_MONEY))
mask = (mode >> 6) & (GET_SLACK | GET_RESERVE);
else
continue;
use -= use_pooled(v, rtype, mask, use);
}
}
return count - use;
} }

View File

@ -16,7 +16,6 @@ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
**/ **/
#include <autoconf.h>
#include <platform.h> #include <platform.h>
#include <util/log.h> #include <util/log.h>