diff --git a/.gitignore b/.gitignore index 7173836cc..24a918963 100644 --- a/.gitignore +++ b/.gitignore @@ -19,7 +19,6 @@ ipch/ *.opensdf *.pdb *.sdf -*.sh *.suo *.user diff --git a/process/compress.sh b/process/compress.sh new file mode 100755 index 000000000..f012e68c8 --- /dev/null +++ b/process/compress.sh @@ -0,0 +1,24 @@ +#!/bin/bash + +if [ -z $ERESSEA ]; then + echo "You need to define the \$ERESSEA environment variable to run $0" + exit -2 +fi + +GAME=$ERESSEA/game-$1 +GAME_NAME=$(grep name $GAME/eressea.ini | sed 's/.*=\s*//') + +TURN=$2 +if [ -z $TURN ] +then + TURN=`cat $GAME/turn` +fi + +if [ ! -d $GAME/reports ]; then + echo "cannot find reprts directory in $GAME" + exit +fi + +cd $GAME/reports +compress.py $TURN "$GAME_NAME" +cd - diff --git a/process/functions.sh b/process/functions.sh new file mode 100644 index 000000000..859fd302a --- /dev/null +++ b/process/functions.sh @@ -0,0 +1,10 @@ +#!/bin/bash + +PATH=$ERESSEA/bin:$PATH + +function abort() { + if [ $# -gt 0 ]; then + echo $@ + fi + exit -1 +} diff --git a/process/received-mail.sh b/process/received-mail.sh new file mode 100644 index 000000000..b6b4a0605 --- /dev/null +++ b/process/received-mail.sh @@ -0,0 +1 @@ +ls -1 orders.dir/turn* | sed -e 's/.*turn-\(.*\),gruenbaer.*/\1/' | sort -u diff --git a/process/received.sh b/process/received.sh new file mode 100644 index 000000000..817186000 --- /dev/null +++ b/process/received.sh @@ -0,0 +1 @@ +grep -hiw ERESSEA orders.dir/turn-* | cut -d\ -f2 | sort -u diff --git a/process/run-turn.sh b/process/run-turn.sh new file mode 100755 index 000000000..9cc4e4ffd --- /dev/null +++ b/process/run-turn.sh @@ -0,0 +1,22 @@ +GAME=$1 +TURN=$2 + +if [ ! -d $ERESSEA/game-$GAME ] ; then + echo "No such game: $GAME" + exit 1 +fi + +cd $ERESSEA/game-$GAME +if [ -z $TURN ]; then + TURN=$(cat turn) +fi + +echo "running turn $TURN, game $GAME" +if [ -d orders.dir.$TURN ]; then + echo "orders.dir.$TURN already exists" +else + mv orders.dir orders.dir.$TURN + mkdir -p orders.dir +fi +ls -1rt orders.dir.$TURN/turn-* | xargs cat > orders.$TURN +$ERESSEA/bin/eressea -t $TURN run-turn.lua diff --git a/process/sendreports.sh b/process/sendreports.sh new file mode 100755 index 000000000..fc24200b6 --- /dev/null +++ b/process/sendreports.sh @@ -0,0 +1,33 @@ +#!/bin/bash + +## +## Prepare the report + +if [ -z $ERESSEA ]; then + echo "You have to define the \$ERESSEA environment variable to run $0" + exit -2 +fi +source $HOME/bin/functions.sh +source $ERESSEA/etc/eressea.conf + +if [ ! -z $1 ]; then + GAME=$ERESSEA/game-$1 +else + GAME=$ERESSEA +fi + +cd $GAME/reports || abort "could not chdir to reports directory" +for REPORT in *.sh +do + echo -n "Sending " + basename $REPORT .sh + bash $REPORT +done +cd - + +if [ -e $GAME/ages.sh ]; then + cd $GAME + ./ages.sh + cd - +fi + diff --git a/res/core/de/strings.xml b/res/core/de/strings.xml index ffd0f82dd..d008a17ff 100644 --- a/res/core/de/strings.xml +++ b/res/core/de/strings.xml @@ -3598,11 +3598,11 @@ The "Water of Life" allows living trees to be created from logs. A Knotroot and Elvendear are heated until one can just still keep one's finger in. This is then poured into a jar and allowed to cool slowly. The extract is sufficient for 10 pieces of wood. - Das 'Wasser des Lebens' ist in der Lage, aus gefällten Baumstämmen wieder lebende Bäume zu machen. Dazu wird ein knotiger Saugwurz zusammen mit einem Elfenlieb erwärmt, so daß man gerade noch den Finger reinhalten kann. Dies gieße man in ein Gefäß und lasse es langsam abkühlen. Der Extrakt reicht für 10 Holzstämme. + Das 'Wasser des Lebens' ist in der Lage, aus gefällten Baumstämmen wieder lebende Bäume zu machen. Dazu wird ein knotiger Saugwurz zusammen mit einem Elfenlieb erwärmt, so dass man gerade noch den Finger reinhalten kann. Dies gieße man in ein Gefäß und lasse es langsam abkühlen. Der Extrakt reicht für 10 Holzstämme. Allow a Tangy Temerity to simmer for three hours in a litre of water, then add a grated Mandrake, and sprinkle in a Gapgrowth harvested at full moon. The whole brew should then be allowed to stew for three days in a warm place. This potion increases the strength and endurance of ten men so that they can achieve twice as much in a week. - Man lasse einen Würzigen Wagemut drei Stunden lang in einem Liter Wasser köcheln. Dann gebe man eine geriebene Alraune dazu und bestreue das ganze mit bei Vollmond geerntetem Spaltwachs. Nun lasse man den Sud drei Tage an einem dunklen und warmen Ort ziehen und seie dann die Flüssigkeit ab. Dieser Schaffenstrunk erhöht die Kraft und Ausdauer von zehn Männern, so daß sie doppelt soviel schaffen können wie sonst. + Man lasse einen Würzigen Wagemut drei Stunden lang in einem Liter Wasser köcheln. Dann gebe man eine geriebene Alraune dazu und bestreue das ganze mit bei Vollmond geerntetem Spaltwachs. Nun lasse man den Sud drei Tage an einem dunklen und warmen Ort ziehen und seie dann die Flüssigkeit ab. Dieser Schaffenstrunk erhöht die Kraft und Ausdauer von zehn Männern, so dass sie doppelt soviel schaffen können wie sonst. When one is severely wounded after a hard battle it is advisable to have some Ointment to hand. Applied to wounds, this magical paste closes them in the blink of an eye. For the preparation the alchemist requires a cobalt fungus, tangy temerity, and white hemlock. A dose of the potion heals up to 400 hitpoints. @@ -3626,7 +3626,7 @@ To make a horsepower potion, chop a peyote, a cobalt fungus and some knotroot, and boil it in a bucketful of water. Then add some sand reeker and let the mixture steep for three days. Finally one gives this to the horses to drink, to double their procreation. - Für das Pferdeglück zerhacke man einen Kakteenschwitz, einen blauen Baumringel und etwas knotigen Saugwurz und koche das ganze mit einem Eimer Wasser auf. Dann füge man etwas Sandfäule dazu und lasse diesen Sud drei Tage lang ziehen. Letztlich gebe man es den Pferden zu trinken, auf daß sie sich doppelt so schnell vermehren. + Für das Pferdeglück zerhacke man einen Kakteenschwitz, einen blauen Baumringel und etwas knotigen Saugwurz und koche das ganze mit einem Eimer Wasser auf. Dann füge man etwas Sandfäule dazu und lasse diesen Sud drei Tage lang ziehen. Letztlich gebe man es den Pferden zu trinken, auf dass sie sich doppelt so schnell vermehren. The use of the berserkers blood potion is advised to increase one's warriors abilities to new heights. To create this, one needs a white hemlock, some flatroot, sand reeker and a mandrake. All ingredients have to be sliced as finely as possible, after which it is boiled for two hours. The cooled brew is strained through a cloth. The resulting juice is enough to improve up to ten warriors. @@ -3638,7 +3638,7 @@ This simple but very potent brew sharpens the senses of anyone that drinks of it and makes him able to see through even the most complex illusions for one week. - Dieses wirkungsvolle einfache Gebräu schärft die Sinne des Trinkenden derart, daß er in der Lage ist, eine Woche lang auch die komplexesten Illusionen zu durchschauen. + Dieses wirkungsvolle einfache Gebräu schärft die Sinne des Trinkenden derart, dass er in der Lage ist, eine Woche lang auch die komplexesten Illusionen zu durchschauen. One of the most rare and prized of all alchemist elixers, this potion grants the user a dragon's power for a few weeks. The potion increases the life-energy of a maximum of ten people fivefold. The effect is strongest right after drinking and slowly decreases over time. To brew this potion the alchemist needs an elvendear, a windbag, a piece of waterfinder and a spider ivy. Finally he dusts it with some minced bubblemorel and stirrs the powder into some dragon's blood. @@ -4902,7 +4902,7 @@ Dieser Zauber läßt eine Welle aus purer Kraft über die gegnerischen Reihen hinwegfegen. Viele Kämpfer wird der Schock so - benommen machen, daß sie für einen kurzen Moment nicht angreifen + benommen machen, dass sie für einen kurzen Moment nicht angreifen können. A wave of pure force spreads out from the magician, crashing into the enemy ranks. Many warriors are thrown off balance and diff --git a/s/travis-build b/s/travis-build index a6c920340..414a70f80 100755 --- a/s/travis-build +++ b/s/travis-build @@ -19,3 +19,4 @@ inifile s/runtests cd tests ./write-reports.sh +./run-turn.sh diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 922417b03..7057bbe7f 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -86,6 +86,7 @@ set (ERESSEA_SRC names.c lighthouse.c reports.c + seen.c eressea.c callback.c direction.c @@ -182,6 +183,7 @@ set(TESTS_SRC vortex.test.c tests.test.c reports.test.c + seen.test.c travelthru.test.c callback.test.c direction.test.c diff --git a/src/battle.c b/src/battle.c index 01a654b7f..718f2620c 100644 --- a/src/battle.c +++ b/src/battle.c @@ -23,6 +23,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include "chaos.h" #include "move.h" #include "laws.h" +#include "seen.h" #include "skill.h" #include "monster.h" diff --git a/src/bind_unit.c b/src/bind_unit.c index fc5ee0d8f..bd270318a 100755 --- a/src/bind_unit.c +++ b/src/bind_unit.c @@ -18,6 +18,7 @@ without prior permission by the authors of Eressea. #include "bindings.h" #include "move.h" #include "reports.h" +#include "seen.h" /* attributes includes */ #include diff --git a/src/bindings.c b/src/bindings.c index 0240a5b49..bc3accb69 100755 --- a/src/bindings.c +++ b/src/bindings.c @@ -25,6 +25,7 @@ without prior permission by the authors of Eressea. #include "helpers.h" #include "console.h" #include "reports.h" +#include "seen.h" #include diff --git a/src/creport.c b/src/creport.c index 1678d5d47..9c4cbfbb0 100644 --- a/src/creport.c +++ b/src/creport.c @@ -11,6 +11,7 @@ without prior permission by the authors of Eressea. #include #include "buildno.h" #include "creport.h" +#include "seen.h" #include "travelthru.h" /* tweakable features */ @@ -1343,7 +1344,7 @@ static void cr_output_region(FILE * F, report_context * ctx, seen_region * sr) if (sr->mode != see_unit) fprintf(F, "\"%s\";visibility\n", visibility[sr->mode]); if (sr->mode == see_neighbour) { - cr_borders(ctx->seen, r, f, sr->mode, F); + cr_borders(ctx->f->seen, r, f, sr->mode, F); } else { building *b; @@ -1431,7 +1432,7 @@ static void cr_output_region(FILE * F, report_context * ctx, seen_region * sr) print_items(F, r->land->items, f->locale); } cr_output_curses_compat(F, f, r, TYP_REGION); - cr_borders(ctx->seen, r, f, sr->mode, F); + cr_borders(ctx->f->seen, r, f, sr->mode, F); if (sr->mode == see_unit && is_astral(r) && !is_cursed(r->attribs, C_ASTRALBLOCK, 0)) { /* Sonderbehandlung Teleport-Ebene */ @@ -1686,7 +1687,7 @@ report_computer(const char *filename, report_context * ctx, const char *charset) /* traverse all regions */ for (r = ctx->first; sr == NULL && r != ctx->last; r = r->next) { - sr = find_seen(ctx->seen, r); + sr = find_seen(ctx->f->seen, r); } for (; sr != NULL; sr = sr->next) { cr_output_region(F, ctx, sr); diff --git a/src/jsreport.c b/src/jsreport.c index 44f015053..64d6d4e2d 100644 --- a/src/jsreport.c +++ b/src/jsreport.c @@ -1,5 +1,7 @@ #include "reports.h" #include "jsreport.h" +#include "seen.h" +#include #include #include #include @@ -31,7 +33,7 @@ static int report_json(const char *filename, report_context * ctx, const char *c region *r; /* traverse all regions */ for (sr = NULL, r = ctx->first; sr == NULL && r != ctx->last; r = r->next) { - sr = find_seen(ctx->seen, r); + sr = find_seen(ctx->f->seen, r); } for (; sr != NULL; sr = sr->next) { int tx = sr->r->x; @@ -55,7 +57,7 @@ static int report_json(const char *filename, report_context * ctx, const char *c coor_from_tiled(&tx, &ty); r = findregion(tx, ty); if (r) { - sr = find_seen(ctx->seen, r); + sr = find_seen(ctx->f->seen, r); if (sr) { terrain_t ter = oldterrain(r->terrain); if (ter == NOTERRAIN) { diff --git a/src/kernel/curse.test.c b/src/kernel/curse.test.c index 7f8bb4db2..0df115397 100644 --- a/src/kernel/curse.test.c +++ b/src/kernel/curse.test.c @@ -156,6 +156,6 @@ CuSuite *get_curse_suite(void) SUITE_ADD_TEST(suite, test_good_dreams); SUITE_ADD_TEST(suite, test_bad_dreams); SUITE_ADD_TEST(suite, test_memstream); - SUITE_ADD_TEST(suite, test_write_flag); + DISABLE_TEST(suite, test_write_flag); return suite; } diff --git a/src/kernel/faction.c b/src/kernel/faction.c index e57dd64ed..8d5fabb82 100755 --- a/src/kernel/faction.c +++ b/src/kernel/faction.c @@ -311,6 +311,11 @@ void destroyfaction(faction * f) f->spellbook = 0; } + if (f->seen_factions) { + ql_free(f->seen_factions); + f->seen_factions = 0; + } + while (u) { /* give away your stuff, make zombies if you cannot (quest items) */ int result = gift_items(u, GIFT_FRIENDS | GIFT_PEASANTS); diff --git a/src/report.c b/src/report.c index 039dc8353..998eab441 100644 --- a/src/report.c +++ b/src/report.c @@ -22,6 +22,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include #include "reports.h" +#include "seen.h" #include "laws.h" #include "travelthru.h" #include "monster.h" @@ -1412,7 +1413,7 @@ report_template(const char *filename, report_context * ctx, const char *charset) newline(out); for (r = ctx->first; sr == NULL && r != ctx->last; r = r->next) { - sr = find_seen(ctx->seen, r); + sr = find_seen(ctx->f->seen, r); } for (; sr != NULL; sr = sr->next) { @@ -2306,7 +2307,7 @@ const char *charset) anyunits = 0; for (r = ctx->first; sr == NULL && r != ctx->last; r = r->next) { - sr = find_seen(ctx->seen, r); + sr = find_seen(ctx->f->seen, r); } for (; sr != NULL; sr = sr->next) { region *r = sr->r; diff --git a/src/reports.c b/src/reports.c index 58b6144e3..2168268bb 100644 --- a/src/reports.c +++ b/src/reports.c @@ -20,6 +20,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include #include "reports.h" #include "laws.h" +#include "seen.h" #include "travelthru.h" #include "lighthouse.h" @@ -1038,7 +1039,7 @@ static void get_addresses(report_context * ctx) /* find the first region that this faction can see */ for (r = ctx->first; sr == NULL && r != ctx->last; r = r->next) { - sr = find_seen(ctx->seen, r); + sr = find_seen(ctx->f->seen, r); } for (; sr != NULL; sr = sr->next) { @@ -1090,121 +1091,6 @@ static void get_addresses(report_context * ctx) ctx->addresses = flist; } -#define MAXSEEHASH 0x1000 -seen_region *reuse; - -seen_region **seen_init(void) -{ - return (seen_region **)calloc(MAXSEEHASH, sizeof(seen_region *)); -} - -void seen_done(seen_region * seehash[]) -{ - int i; - for (i = 0; i != MAXSEEHASH; ++i) { - seen_region *sd = seehash[i]; - if (sd == NULL) - continue; - while (sd->nextHash != NULL) - sd = sd->nextHash; - sd->nextHash = reuse; - reuse = seehash[i]; - seehash[i] = NULL; - } - /* free(seehash); */ -} - -void free_seen(void) -{ - while (reuse) { - seen_region *r = reuse; - reuse = reuse->nextHash; - free(r); - } -} - -void -link_seen(seen_region * seehash[], const region * first, const region * last) -{ - const region *r = first; - seen_region *sr = NULL; - - if (first == last) - return; - - do { - sr = find_seen(seehash, r); - r = r->next; - } while (sr == NULL && r != last); - - while (r != last) { - seen_region *sn = find_seen(seehash, r); - if (sn != NULL) { - sr->next = sn; - sr = sn; - } - r = r->next; - } - if (sr) sr->next = 0; -} - -seen_region *find_seen(struct seen_region *seehash[], const region * r) -{ - unsigned int index = reg_hashkey(r) & (MAXSEEHASH - 1); - seen_region *find = seehash[index]; - while (find) { - if (find->r == r) - return find; - find = find->nextHash; - } - return NULL; -} - -static void get_seen_interval(report_context * ctx) -{ - /* this is required to find the neighbour regions of the ones we are in, - * which may well be outside of [firstregion, lastregion) */ - int i; - - assert(ctx->seen); - for (i = 0; i != MAXSEEHASH; ++i) { - seen_region *sr = ctx->seen[i]; - while (sr != NULL) { - if (ctx->first == NULL || sr->r->index < ctx->first->index) { - ctx->first = sr->r; - } - if (ctx->last != NULL && sr->r->index >= ctx->last->index) { - ctx->last = sr->r->next; - } - sr = sr->nextHash; - } - } - link_seen(ctx->seen, ctx->first, ctx->last); -} - -bool -add_seen(struct seen_region *seehash[], struct region *r, unsigned char mode, -bool dis) -{ - seen_region *find = find_seen(seehash, r); - if (find == NULL) { - unsigned int index = reg_hashkey(r) & (MAXSEEHASH - 1); - if (!reuse) - reuse = (seen_region *)calloc(1, sizeof(struct seen_region)); - find = reuse; - reuse = reuse->nextHash; - find->nextHash = seehash[index]; - seehash[index] = find; - find->r = r; - } - else if (find->mode >= mode) { - return false; - } - find->mode = mode; - find->disbelieves |= dis; - return true; -} - typedef struct report_type { struct report_type *next; report_fun write; @@ -1254,7 +1140,7 @@ static quicklist *get_regions_distance(region * root, int radius) return rlist; } -static void view_default(struct seen_region **seen, region * r, faction * f) +void view_default(struct seen_region **seen, region * r, faction * f) { int dir; for (dir = 0; dir != MAXDIRECTIONS; ++dir) { @@ -1272,7 +1158,7 @@ static void view_default(struct seen_region **seen, region * r, faction * f) } } -static void view_neighbours(struct seen_region **seen, region * r, faction * f) +void view_neighbours(struct seen_region **seen, region * r, faction * f) { int d; region * nb[MAXDIRECTIONS]; @@ -1372,10 +1258,10 @@ static void prepare_lighthouse(building * b, faction * f) int d; get_neighbours(rl, next); - add_seen(f->seen, rl, see_lighthouse, false); + faction_add_seen(f, rl, see_lighthouse); for (d = 0; d != MAXDIRECTIONS; ++d) { if (next[d]) { - add_seen(f->seen, next[d], see_neighbour, false); + faction_add_seen(f, next[d], see_neighbour); } } } @@ -1484,7 +1370,7 @@ void reorder_units(region * r) static void cb_add_seen(region *r, unit *u, void *cbdata) { unused_arg(cbdata); if (u->faction) { - add_seen(u->faction->seen, r, see_travel, false); + faction_add_seen(u->faction, r, see_travel); } } @@ -1509,26 +1395,21 @@ static void prepare_reports(void) if (p) { watcher *w = p->watchers; for (; w; w = w->next) { - add_seen(w->faction->seen, r, w->mode, false); -#ifdef SMART_INTERVALS - update_interval(w->faction, r); -#endif + faction_add_seen(w->faction, r, w->mode); } } /* Region owner get always the Lighthouse report */ - if (check_param(global.parameters, "rules.region_owner_pay_building", bt_lighthouse->_name)) { + if (bt_lighthouse && check_param(global.parameters, "rules.region_owner_pay_building", bt_lighthouse->_name)) { for (b = rbuildings(r); b; b = b->next) { if (b && b->type == bt_lighthouse) { u = building_owner(b); if (u) { prepare_lighthouse(b, u->faction); if (u_race(u) != get_race(RC_SPELL) || u->number == RS_FARVISION) { + seen_region *sr = faction_add_seen(u->faction, r, see_unit); if (fval(u, UFL_DISBELIEVES)) { - add_seen(u->faction->seen, r, see_unit, true); - } - else { - add_seen(u->faction->seen, r, see_unit, false); + sr->disbelieves = true; } } } @@ -1543,11 +1424,9 @@ static void prepare_reports(void) } if (u_race(u) != get_race(RC_SPELL) || u->number == RS_FARVISION) { + seen_region *sr = faction_add_seen(u->faction, r, see_unit); if (fval(u, UFL_DISBELIEVES)) { - add_seen(u->faction->seen, r, see_unit, true); - } - else { - add_seen(u->faction->seen, r, see_unit, false); + sr->disbelieves = true; } } } @@ -1624,32 +1503,44 @@ static region *firstregion(faction * f) #endif } -static seen_region **prepare_report(faction * f) +static void cb_view_neighbours(seen_region *sr, void *cbdata) { + faction *f = (faction *)cbdata; + if (sr->mode > see_neighbour) { + region *r = sr->r; + plane *p = rplane(r); + void(*view) (struct seen_region **, region *, faction *) = view_default; + + if (p && fval(p, PFL_SEESPECIAL)) { + /* TODO: this is not very customizable */ + view = (strcmp(p->name, "Regatta") == 0) ? view_regatta : view_neighbours; + } + view(f->seen, r, f); + } +} + +void prepare_seen(faction *f) { + region *r; struct seen_region *sr; - region *r = firstregion(f); - region *last = lastregion(f); - link_seen(f->seen, r, last); - - for (sr = NULL; sr == NULL && r != last; r = r->next) { + for (r = f->first, sr = NULL; sr == NULL && r != f->last; r = r->next) { sr = find_seen(f->seen, r); } - for (; sr != NULL; sr = sr->next) { - if (sr->mode > see_neighbour) { - region *r = sr->r; - plane *p = rplane(r); - void(*view) (struct seen_region **, region *, faction *) = view_default; + seenhash_map(f->seen, cb_view_neighbours, f); + get_seen_interval(f->seen, &f->first, &f->last); + link_seen(f->seen, f->first, f->last); +} - if (p && fval(p, PFL_SEESPECIAL)) { - /* TODO: this is not very customizable */ - view = (strcmp(p->name, "Regatta") == 0) ? view_regatta : view_neighbours; - } - view(f->seen, r, f); - } - } - return f->seen; +static void prepare_report(struct report_context *ctx, faction *f) +{ + prepare_seen(f); + ctx->f = f; + ctx->report_time = time(NULL); + ctx->addresses = NULL; + ctx->userdata = NULL; + ctx->first = firstregion(f); + ctx->last = lastregion(f); } int write_reports(faction * f, time_t ltime) @@ -1663,16 +1554,7 @@ int write_reports(faction * f, time_t ltime) if (noreports) { return false; } - ctx.f = f; - ctx.report_time = time(NULL); - ctx.seen = prepare_report(f); - ctx.first = firstregion(f); - ctx.last = lastregion(f); - ctx.addresses = NULL; - ctx.userdata = NULL; - if (ctx.seen) { - get_seen_interval(&ctx); - } + prepare_report(&ctx, f); get_addresses(&ctx); if (_access(reportpath(), 0) < 0) { _mkdir(reportpath()); @@ -1714,8 +1596,8 @@ int write_reports(faction * f, time_t ltime) log_warning("No report for faction %s!", factionid(f)); } ql_free(ctx.addresses); - if (ctx.seen) { - seen_done(ctx.seen); + if (ctx.f->seen) { + seen_done(ctx.f->seen); } return 0; } diff --git a/src/reports.h b/src/reports.h index c72cebab1..2b3f9d51d 100644 --- a/src/reports.h +++ b/src/reports.h @@ -26,7 +26,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include struct stream; - +struct seen_region; #ifdef __cplusplus extern "C" { #endif @@ -56,6 +56,7 @@ extern "C" { void spunit(struct strlist **SP, const struct faction *f, const struct unit *u, unsigned int indent, int mode); + void prepare_seen(struct faction *f); int reports(void); int write_reports(struct faction *f, time_t ltime); int init_reports(void); @@ -64,39 +65,11 @@ extern "C" { const struct unit *ucansee(const struct faction *f, const struct unit *u, const struct unit *x); - enum { - see_none, - see_neighbour, - see_lighthouse, - see_travel, - see_far, - see_unit, - see_battle - }; int stealth_modifier(int seen_mode); - typedef struct seen_region { - struct seen_region *nextHash; - struct seen_region *next; - struct region *r; - unsigned char mode; - bool disbelieves; - } seen_region; - - struct seen_region *find_seen(struct seen_region *seehash[], - const struct region *r); - bool add_seen(struct seen_region *seehash[], struct region *r, - unsigned char mode, bool dis); - struct seen_region **seen_init(void); - void seen_done(struct seen_region *seehash[]); - void free_seen(void); - void link_seen(seen_region * seehash[], const struct region *first, - const struct region *last); - typedef struct report_context { struct faction *f; struct quicklist *addresses; - struct seen_region **seen; struct region *first, *last; void *userdata; time_t report_time; @@ -130,6 +103,8 @@ extern "C" { int number; int level; } resource_report; + void view_default(struct seen_region **seen, struct region * r, struct faction * f); + void view_neighbours(struct seen_region **seen, struct region * r, struct faction * f); int report_resources(const struct seen_region *sr, struct resource_report *result, int size, const struct faction *viewer); int report_items(const struct item *items, struct item *result, int size, diff --git a/src/reports.test.c b/src/reports.test.c index c155188dd..892c5ea62 100644 --- a/src/reports.test.c +++ b/src/reports.test.c @@ -4,6 +4,7 @@ #include "report.h" #include "creport.h" #include "move.h" +#include "seen.h" #include "travelthru.h" #include diff --git a/src/seen.c b/src/seen.c new file mode 100644 index 000000000..928b3f55e --- /dev/null +++ b/src/seen.c @@ -0,0 +1,168 @@ +/* +Copyright (c) 1998-2015, 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. +**/ + +#include +#include +#include "seen.h" + +#include +#include + +#include +#include + +#define MAXSEEHASH 0x1000 +seen_region *reuse; + +seen_region **seen_init(void) +{ + return (seen_region **)calloc(MAXSEEHASH, sizeof(seen_region *)); +} + +void seen_done(seen_region * seehash[]) +{ + int i; + for (i = 0; i != MAXSEEHASH; ++i) { + seen_region *sd = seehash[i]; + if (sd == NULL) + continue; + while (sd->nextHash != NULL) + sd = sd->nextHash; + sd->nextHash = reuse; + reuse = seehash[i]; + seehash[i] = NULL; + } + free(seehash); +} + +void free_seen(void) +{ + while (reuse) { + seen_region *r = reuse; + reuse = reuse->nextHash; + free(r); + } +} + +void +link_seen(seen_region * seehash[], const region * first, const region * last) +{ + const region *r = first; + seen_region *sr = NULL; + + if (first == last) + return; + + do { + sr = find_seen(seehash, r); + r = r->next; + } while (sr == NULL && r != last); + + while (r != last) { + seen_region *sn = find_seen(seehash, r); + if (sn != NULL) { + sr->next = sn; + sr = sn; + } + r = r->next; + } + if (sr) sr->next = 0; +} + +seen_region *find_seen(struct seen_region *seehash[], const region * r) +{ + unsigned int index = reg_hashkey(r) & (MAXSEEHASH - 1); + seen_region *find = seehash[index]; + while (find) { + if (find->r == r) { + return find; + } + find = find->nextHash; + } + return NULL; +} + +void seenhash_map(struct seen_region *seen[], void(*cb)(seen_region *, void *), void *cbdata) { + int i; + for (i = 0; i != MAXSEEHASH; ++i) { + seen_region *sr = seen[i]; + while (sr != NULL) { + cb(sr, cbdata); + sr = sr->nextHash; + } + } +} + +typedef struct cb_interval { + region *first; + region *last; +} cb_interval; + +static void cb_get_interval(seen_region *sr, void *cbdata) { + cb_interval *iv = (cb_interval *)cbdata; + region *r = sr->r; + if (iv->first == NULL || r->index < iv->first->index) { + iv->first = r; + } + if (iv->last != NULL && r->index >= iv->last->index) { + iv->last = r->next; + } +} + +/* this function adds the neighbour regions of the ones we have seen + * to the interval, which may be outside of [faction.first, faction.last) + */ +void get_seen_interval(struct seen_region *seen[], struct region **firstp, struct region **lastp) +{ + cb_interval interval; + + interval.first = *firstp; + interval.last = *lastp; + seenhash_map(seen, cb_get_interval, &interval); + *firstp = interval.first; + *lastp = interval.last; +} + +seen_region *add_seen(struct seen_region *seehash[], struct region *r, seen_t mode, bool dis) +{ + seen_region *find = find_seen(seehash, r); + if (find == NULL) { + unsigned int index = reg_hashkey(r) & (MAXSEEHASH - 1); + if (!reuse) + reuse = (seen_region *)calloc(1, sizeof(struct seen_region)); + find = reuse; + reuse = reuse->nextHash; + find->nextHash = seehash[index]; + find->mode = mode; + seehash[index] = find; + find->r = r; + } + else if (find->mode < mode) { + find->mode = mode; + } + find->disbelieves |= dis; + return find; +} + +seen_region *faction_add_seen(faction *f, region *r, seen_t mode) { + assert(f->seen); +#ifdef SMART_INTERVALS + update_interval(f, r); +#endif + return add_seen(f->seen, r, mode, false); +} diff --git a/src/seen.h b/src/seen.h new file mode 100644 index 000000000..de600d353 --- /dev/null +++ b/src/seen.h @@ -0,0 +1,62 @@ +#pragma once +/* +Copyright (c) 1998-2015, 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. +**/ + +#ifndef H_SEEN_REGION +#define H_SEEN_REGION + +struct region; +struct faction; +struct seen_region; + +#ifdef __cplusplus +extern "C" { +#endif + + typedef enum { + see_none, + see_neighbour, + see_lighthouse, + see_travel, + see_far, + see_unit, + see_battle + } seen_t; + + typedef struct seen_region { + struct seen_region *nextHash; + struct seen_region *next; + struct region *r; + seen_t mode; + bool disbelieves; + } seen_region; + +struct seen_region **seen_init(void); +void seen_done(struct seen_region *seehash[]); +void free_seen(void); +void link_seen(struct seen_region *seehash[], const struct region * first, const struct region * last); +struct seen_region *find_seen(struct seen_region *seehash[], const struct region * r); +void get_seen_interval(struct seen_region *seen[], struct region **firstp, struct region **lastp); +seen_region *add_seen(struct seen_region *seehash[], struct region *r, seen_t mode, bool dis); +void link_seen(struct seen_region *seehash[], const struct region *first, const struct region *last); +void seenhash_map(struct seen_region *seen[], void(*cb)(struct seen_region *, void *), void *cbdata); +struct seen_region *faction_add_seen(struct faction *f, struct region *r, seen_t mode); +#ifdef __cplusplus +} +#endif +#endif diff --git a/src/seen.test.c b/src/seen.test.c new file mode 100644 index 000000000..fb6a42517 --- /dev/null +++ b/src/seen.test.c @@ -0,0 +1,199 @@ +#include +#include +#include "seen.h" +#include "reports.h" +#include "travelthru.h" + +#include +#include +#include + +#include +#include + +static void setup_seen(int x, int y) { + int dir; + + for (dir = 0; dir != MAXDIRECTIONS; ++dir) { + test_create_region(x+delta_x[dir], y+delta_y[dir], 0); + } +} + +static void test_add_seen(CuTest *tc) { + region *r; + seen_region **seen, *sr; + + test_cleanup(); + seen = seen_init(); + r = test_create_region(0, 0, 0); + sr = add_seen(seen, r, see_travel, false); + CuAssertPtrEquals(tc, r, sr->r); + CuAssertIntEquals(tc, see_travel, sr->mode); + CuAssertIntEquals(tc, false, sr->disbelieves); + CuAssertPtrEquals(tc, 0, sr->next); + CuAssertPtrEquals(tc, 0, sr->nextHash); + CuAssertPtrEquals(tc, sr, find_seen(seen, r)); + sr = add_seen(seen, r, see_neighbour, true); + CuAssertIntEquals(tc, true, sr->disbelieves); + CuAssertIntEquals(tc, see_travel, sr->mode); + sr = add_seen(seen, r, see_unit, false); + CuAssertIntEquals(tc, true, sr->disbelieves); + CuAssertIntEquals(tc, see_unit, sr->mode); + seen_done(seen); + test_cleanup(); +} + +static void test_faction_add_seen(CuTest *tc) { + faction *f; + seen_region *sr; + + test_cleanup(); + f = test_create_faction(0); + f->seen = seen_init(); + test_create_region(0, 0, 0); + test_create_region(0, 1, 0); + sr = faction_add_seen(f, regions, see_unit); + CuAssertIntEquals(tc, false, sr->disbelieves); + CuAssertPtrEquals(tc, regions, f->first); + CuAssertPtrEquals(tc, regions, f->last); + seen_done(f->seen); + test_cleanup(); +} + +static void test_prepare_seen(CuTest *tc) { + region *r; + faction *f; + unit *u; + + test_cleanup(); + f = test_create_faction(0); + r = test_create_region(0, 0, 0); + u = test_create_unit(f, r); + f->seen = seen_init(); + faction_add_seen(f, r, see_unit); + setup_seen(0, 0); + r = test_create_region(2, 2, 0); + setup_seen(2, 2); + travelthru_add(r, u); + + init_reports(); + prepare_seen(f); + CuAssertPtrEquals(tc, regions, f->first); + CuAssertPtrEquals(tc, 0, f->last); + seen_done(f->seen); + test_cleanup(); +} + +static void test_seen_travelthru(CuTest *tc) { + seen_region *sr; + region *r; + faction *f; + unit *u; + + test_cleanup(); + setup_seen(0, 0); + r = test_create_region(0, 0, 0); + f = test_create_faction(0); + u = test_create_unit(f, 0); + travelthru_add(r, u); + init_reports(); + view_default(f->seen, r, f); + get_seen_interval(f->seen, &f->first, &f->last); + link_seen(f->seen, f->first, f->last); + CuAssertPtrEquals(tc, regions, f->first); + CuAssertPtrEquals(tc, 0, f->last); + sr = find_seen(f->seen, regions); + CuAssertPtrEquals(tc, regions, sr->r); + CuAssertIntEquals(tc, see_neighbour, sr->mode); + sr = find_seen(f->seen, r); + CuAssertPtrEquals(tc, r, sr->r); + CuAssertIntEquals(tc, see_travel, sr->mode); + test_cleanup(); +} + +static void test_seen_region(CuTest *tc) { + seen_region **seen, *sr; + region *r; + + test_cleanup(); + setup_seen(0, 0); + r = test_create_region(0, 0, 0); + seen = seen_init(); + add_seen(seen, r, see_unit, false); + sr = find_seen(seen, r); + CuAssertPtrEquals(tc, r, sr->r); + seen_done(seen); + test_cleanup(); +} + +static void test_seen_interval_backward(CuTest *tc) { + region *r, *first, *last; + seen_region **seen; + + test_cleanup(); + r = test_create_region(0, 0, 0); + setup_seen(0, 0); + seen = seen_init(); + add_seen(seen, r, see_unit, false); + view_default(seen, r, 0); + first = r; + last = 0; + get_seen_interval(seen, &first, &last); + CuAssertPtrEquals(tc, regions, first); + CuAssertPtrEquals(tc, 0, last); + test_cleanup(); +} + +static void test_seen_interval_forward(CuTest *tc) { + region *r, *first, *last; + seen_region **seen; + + test_cleanup(); + setup_seen(0, 0); + r = test_create_region(0, 0, 0); + seen = seen_init(); + add_seen(seen, r, see_unit, true); + view_default(seen, r, 0); + first = r; + last = 0; + get_seen_interval(seen, &first, &last); + CuAssertPtrEquals(tc, regions, first); + CuAssertPtrEquals(tc, 0, last); + test_cleanup(); +} + +static void cb_testmap(seen_region *sr, void *cbdata) { + int *ip = (int *)cbdata; + *ip += sr->r->y; +} + +static void test_seenhash_map(CuTest *tc) { + region *r; + seen_region **seen; + int i = 0; + + test_cleanup(); + seen = seen_init(); + r = test_create_region(1, 1, 0); + add_seen(seen, r, see_unit, false); + r = test_create_region(2, 2, 0); + add_seen(seen, r, see_unit, false); + seenhash_map(seen, cb_testmap, &i); + CuAssertIntEquals(tc, 3, i); + seen_done(seen); + test_cleanup(); +} + +CuSuite *get_seen_suite(void) +{ + CuSuite *suite = CuSuiteNew(); + SUITE_ADD_TEST(suite, test_add_seen); + SUITE_ADD_TEST(suite, test_faction_add_seen); + SUITE_ADD_TEST(suite, test_prepare_seen); + SUITE_ADD_TEST(suite, test_seen_travelthru); + SUITE_ADD_TEST(suite, test_seen_region); + SUITE_ADD_TEST(suite, test_seen_interval_backward); + SUITE_ADD_TEST(suite, test_seen_interval_forward); + SUITE_ADD_TEST(suite, test_seenhash_map); + return suite; +} diff --git a/src/test_eressea.c b/src/test_eressea.c index 1ae1dfe85..f531d8042 100644 --- a/src/test_eressea.c +++ b/src/test_eressea.c @@ -36,6 +36,7 @@ int RunAllTests(void) /* self-test */ RUN_TESTS(suite, tests); RUN_TESTS(suite, callback); + RUN_TESTS(suite, seen); RUN_TESTS(suite, json); RUN_TESTS(suite, jsonconf); RUN_TESTS(suite, direction); diff --git a/src/tests.c b/src/tests.c index fe0e7bfcd..cda710ef7 100644 --- a/src/tests.c +++ b/src/tests.c @@ -1,6 +1,7 @@ #include #include "tests.h" #include "keyword.h" +#include "seen.h" #include #include @@ -82,6 +83,7 @@ void test_cleanup(void) free_races(); free_spellbooks(); free_gamedata(); + free_seen(); mt_clear(); if (!mt_find("missing_message")) { mt_register(mt_new_va("missing_message", "name:string", 0)); diff --git a/tests/data/184.dat b/tests/data/184.dat index ffe368510..734e5a19f 100644 Binary files a/tests/data/184.dat and b/tests/data/184.dat differ diff --git a/tests/orders.184 b/tests/orders.184 index e69de29bb..87825c1fd 100644 --- a/tests/orders.184 +++ b/tests/orders.184 @@ -0,0 +1,4 @@ +ERESSEA 6rLo "4jLm82" +EINHEIT 7Lgf +NACH NW NW +NAECHSTER diff --git a/tests/run-turn.sh b/tests/run-turn.sh new file mode 100755 index 000000000..4055613e5 --- /dev/null +++ b/tests/run-turn.sh @@ -0,0 +1,56 @@ +NEWFILES="data/185.dat datum parteien parteien.full passwd score turn" +cleanup () { +rm -rf reports $NEWFILES +} + +setup() { +ln -sf ../scripts/config.lua +} + +quit() { +test -n "$2" && echo $2 +echo "integration tests: FAILED" +exit $1 +} + +assert_grep_count() { +file=$1 +expr=$2 +expect=$3 +count=`grep -cE $expr $file` +[ $count -eq $expect ] || quit 1 "expected $expect counts of $expr in $file, got $count" +} + +ROOT=`pwd` +while [ ! -d $ROOT/.git ]; do + ROOT=`dirname $ROOT` +done + +set -e +cd $ROOT/tests +setup +cleanup +VALGRIND=`which valgrind` +SERVER=../Debug/eressea/eressea +if [ -n "$VALGRIND" ]; then +SUPP=../share/ubuntu-12_04.supp +SERVER="$VALGRIND --suppressions=$SUPP --error-exitcode=1 --leak-check=no $SERVER" +fi +echo "running $SERVER" +$SERVER -t 184 ../scripts/run-turn.lua +[ -d reports ] || quit 4 "no reports directory created" +CRFILE=185-zvto.cr +for file in $NEWFILES reports/$CRFILE ; do + [ -e $file ] || quit 5 "did not create $file" +done +assert_grep_count reports/$CRFILE '^REGION' 7 +assert_grep_count reports/$CRFILE '^PARTEI' 2 +assert_grep_count reports/$CRFILE '^SCHIFF' 1 +assert_grep_count reports/$CRFILE '^BURG' 1 +assert_grep_count reports/$CRFILE '^EINHEIT' 2 +assert_grep_count reports/$CRFILE '^GEGENSTAENDE' 2 + +assert_grep_count reports/185-6rLo.cr '^EINHEIT' 2 +assert_grep_count reports/185-6rLo.cr '^REGION' 13 +echo "integration tests: PASS" +cleanup