Merge pull request #287 from ennorehling/feature/issue-282-link-seen

eliminate duplicate call to link_seen
This commit is contained in:
Enno Rehling 2015-09-09 12:41:14 +02:00
commit 6f212686f3
29 changed files with 663 additions and 208 deletions

1
.gitignore vendored
View File

@ -19,7 +19,6 @@ ipch/
*.opensdf
*.pdb
*.sdf
*.sh
*.suo
*.user

24
process/compress.sh Executable file
View File

@ -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 -

10
process/functions.sh Normal file
View File

@ -0,0 +1,10 @@
#!/bin/bash
PATH=$ERESSEA/bin:$PATH
function abort() {
if [ $# -gt 0 ]; then
echo $@
fi
exit -1
}

1
process/received-mail.sh Normal file
View File

@ -0,0 +1 @@
ls -1 orders.dir/turn* | sed -e 's/.*turn-\(.*\),gruenbaer.*/\1/' | sort -u

1
process/received.sh Normal file
View File

@ -0,0 +1 @@
grep -hiw ERESSEA orders.dir/turn-* | cut -d\ -f2 | sort -u

22
process/run-turn.sh Executable file
View File

@ -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

33
process/sendreports.sh Executable file
View File

@ -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

View File

@ -3598,11 +3598,11 @@
</string>
<string name="p2">
<text locale="en">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.</text>
<text locale="de">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.</text>
<text locale="de">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.</text>
</string>
<string name="p3">
<text locale="en">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.</text>
<text locale="de">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.</text>
<text locale="de">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.</text>
</string>
<string name="ointment">
<text locale="en">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.</text>
@ -3626,7 +3626,7 @@
</string>
<string name="p9">
<text locale="en">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.</text>
<text locale="de">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.</text>
<text locale="de">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.</text>
</string>
<string name="p10">
<text locale="en">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.</text>
@ -3638,7 +3638,7 @@
</string>
<string name="truthpotion">
<text locale="en">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.</text>
<text locale="de">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.</text>
<text locale="de">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.</text>
</string>
<string name="p13">
<text locale="en">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. </text>
@ -4902,7 +4902,7 @@
<string name="shockwave">
<text locale="de">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.</text>
<text locale="en">A wave of pure force spreads out from the magician,
crashing into the enemy ranks. Many warriors are thrown off balance and

View File

@ -19,3 +19,4 @@ inifile
s/runtests
cd tests
./write-reports.sh
./run-turn.sh

View File

@ -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

View File

@ -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"

View File

@ -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 <attributes/racename.h>

View File

@ -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 <kernel/config.h>

View File

@ -11,6 +11,7 @@ without prior permission by the authors of Eressea.
#include <kernel/config.h>
#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);

View File

@ -1,5 +1,7 @@
#include "reports.h"
#include "jsreport.h"
#include "seen.h"
#include <kernel/faction.h>
#include <kernel/region.h>
#include <kernel/terrain.h>
#include <kernel/terrainid.h>
@ -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) {

View File

@ -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;
}

View File

@ -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);

View File

@ -22,6 +22,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#include <kernel/config.h>
#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;

View File

@ -20,6 +20,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#include <kernel/config.h>
#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;
}

View File

@ -26,7 +26,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
#include <kernel/types.h>
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,

View File

@ -4,6 +4,7 @@
#include "report.h"
#include "creport.h"
#include "move.h"
#include "seen.h"
#include "travelthru.h"
#include <kernel/building.h>

168
src/seen.c Normal file
View File

@ -0,0 +1,168 @@
/*
Copyright (c) 1998-2015, Enno Rehling <enno@eressea.de>
Katja Zedel <katze@felidae.kn-bremen.de
Christian Schlittchen <corwin@amber.kn-bremen.de>
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 <platform.h>
#include <kernel/config.h>
#include "seen.h"
#include <kernel/region.h>
#include <kernel/faction.h>
#include <assert.h>
#include <stdlib.h>
#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);
}

62
src/seen.h Normal file
View File

@ -0,0 +1,62 @@
#pragma once
/*
Copyright (c) 1998-2015, Enno Rehling <enno@eressea.de>
Katja Zedel <katze@felidae.kn-bremen.de
Christian Schlittchen <corwin@amber.kn-bremen.de>
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

199
src/seen.test.c Normal file
View File

@ -0,0 +1,199 @@
#include <platform.h>
#include <config.h>
#include "seen.h"
#include "reports.h"
#include "travelthru.h"
#include <kernel/region.h>
#include <kernel/unit.h>
#include <kernel/faction.h>
#include <CuTest.h>
#include <tests.h>
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;
}

View File

@ -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);

View File

@ -1,6 +1,7 @@
#include <platform.h>
#include "tests.h"
#include "keyword.h"
#include "seen.h"
#include <kernel/config.h>
#include <kernel/region.h>
@ -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));

Binary file not shown.

View File

@ -0,0 +1,4 @@
ERESSEA 6rLo "4jLm82"
EINHEIT 7Lgf
NACH NW NW
NAECHSTER

56
tests/run-turn.sh Executable file
View File

@ -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