Skip to content

Commit

Permalink
Modify QM distance calculation configurably.
Browse files Browse the repository at this point in the history
* Added a new config value for transport to use linear connected
  distance (with a cross-level penalty for going through a shaft)
* Wrote a new a* search linear connected distance function.
* modified the evaluation of the order to compute the distance using
  either the old or new function based on the config.
* store the distance on the transport order for payment later rather
  than recomputing it a second time.
  • Loading branch information
jt-traub committed Jun 5, 2024
1 parent 11f1ab2 commit 6227158
Show file tree
Hide file tree
Showing 6 changed files with 93 additions and 9 deletions.
78 changes: 78 additions & 0 deletions aregion.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@
#include <random>
#include <ctime>
#include <cassert>
#include <unordered_set>
#include <queue>

using namespace std;

#ifndef M_PI
#define M_PI 3.14159265358979323846
Expand Down Expand Up @@ -2345,6 +2349,80 @@ ARegion *ARegionList::FindNearestStartingCity(ARegion *start, int *dir)
return 0;
}

// Some structures for the get_connected_distance function
// structures to allow us to do an efficient search
struct RegionVisited {
int x, y, z;
bool operator==(const RegionVisited &v) const { return x == v.x && y == v.y && z == v.z; }
};
class RegionVisitHash {
public:
size_t operator()(const RegionVisited v) const {
return std::hash<uint32_t>()(v.x) ^ std::hash<uint32_t>()(v.y) ^ std::hash<uint32_t>()(v.z);
}
};
struct QEntry { ARegion *r; int dist; };
class QEntryCompare {
public:
// We want to sort by min distance
bool operator()(const QEntry &below, const QEntry &above) const { return above.dist < below.dist; }
};

// This doesn't really need to be on the ARegionList but, it's okay for now.
int ARegionList::get_connected_distance(ARegion *start, ARegion *target, int penalty, int maxdist) {
unordered_set<RegionVisited, RegionVisitHash> visited_regions;
// We want to search the closest regions first so that as soon as we find one that is too far we know *all* the
// rest will be too far as well.
priority_queue<QEntry, vector<QEntry>, QEntryCompare> q;

if (start == 0 || target == 0) {
// We were given some unusual (nonexistant) regions
return 10000000;
}
ARegion *cur = start;
int cur_dist = 0;

while (maxdist == -1 || cur_dist <= maxdist) {
// If we have hit our target, we are done
if (cur == target) {
// found our target within range
return cur_dist;
}

// Add my current region to the visited set to make sure we don't loop
visited_regions.insert({cur->xloc, cur->yloc, cur->zloc});

// Add all neighbors to the queue as long as we haven't visited them yet
for (int i = 0; i < NDIRS; i++) {
ARegion *n = cur->neighbors[i];
// cur and n *should* have the same zloc, but ... let's just future-proof in case that changes sometime
int cost = (cur->zloc == n->zloc ? 1 : penalty);
if (n && visited_regions.insert({n->xloc, n->yloc, n->zloc}).second) {
q.push({n, cur_dist + cost });
}
}
// Add any inner regions to the queue as long as we haven't visited them yet
forlist(&cur->objects) {
Object *o = (Object *) elem;
if (o->inner != -1) {
ARegion *inner = GetRegion(o->inner);
int cost = (cur->zloc == inner->zloc ? 1 : penalty);
if (visited_regions.insert({inner->xloc, inner->yloc, inner->zloc}).second) {
q.push({inner, cur_dist + cost});
}
}
}

cur = q.top().r;
cur_dist = q.top().dist;
q.pop();

}

// Should never happen
return 10000000;
}

int ARegionList::GetPlanarDistance(ARegion *one, ARegion *two,
int penalty, int maxdist)
{
Expand Down
3 changes: 2 additions & 1 deletion aregion.h
Original file line number Diff line number Diff line change
Expand Up @@ -462,7 +462,8 @@ class ARegionList : public AList
int maxY);

ARegion *FindGate(int);
int GetPlanarDistance(ARegion *, ARegion *, int penalty, int maxdist = -1);
int GetPlanarDistance(ARegion *one, ARegion *two, int penalty, int maxdist = -1);
int get_connected_distance(ARegion *start, ARegion *target, int penalty, int maxdist = -1);
int GetWeather(ARegion *pReg, int month);

ARegionArray *GetRegionArray(int level);
Expand Down
1 change: 1 addition & 0 deletions gamedefs.h
Original file line number Diff line number Diff line change
Expand Up @@ -667,6 +667,7 @@ class GameDefs {
QM_AFFECT_COST = 0x02, // QM level affect shipping cost?
// actual distance will be NONLOCAL_TRANSPORT + ((level + 1)/3)
QM_AFFECT_DIST = 0x04, // QM level affect longrange dist?
USE_CONNECTED_DISTANCES = 0x08, // Use connected distance instead of planar distance
};
int TRANSPORT;

Expand Down
3 changes: 2 additions & 1 deletion neworigins/rules.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,8 @@ static GameDefs g = {
0, // GATES_NOT_PERENNIAL
0, // START_GATES_OPEN
0, // SHOW_CLOSED_GATES
GameDefs::ALLOW_TRANSPORT | GameDefs::QM_AFFECT_COST | GameDefs::QM_AFFECT_DIST, // TRANSPORT
GameDefs::ALLOW_TRANSPORT | GameDefs::QM_AFFECT_COST |
GameDefs::QM_AFFECT_DIST | GameDefs::USE_CONNECTED_DISTANCES, // TRANSPORT
2, // LOCAL_TRANSPORT
3, // NONLOCAL_TRANSPORT
5, // SHIPPING_COST
Expand Down
1 change: 1 addition & 0 deletions orders.h
Original file line number Diff line number Diff line change
Expand Up @@ -404,6 +404,7 @@ class TransportOrder : public Order {
// any other amount is also checked at transport time
int amount;
int except;
int distance;

enum TransportPhase {
SHIP_TO_QM,
Expand Down
16 changes: 9 additions & 7 deletions runorders.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3125,16 +3125,21 @@ void Game::CheckTransportOrders()
int penalty = 10000000;
RangeType *rt = FindRange("rng_transport");
if (rt) penalty = rt->crossLevelPenalty;
o->distance = Globals->LOCAL_TRANSPORT; // default to local max distance
if (maxdist > 0) {
// 0 maxdist represents unlimited range
dist = regions.GetPlanarDistance(r, tar->region, penalty, maxdist);
if (Globals->TRANSPORT & GameDefs::USE_CONNECTED_DISTANCES) {
dist = regions.get_connected_distance(r, tar->region, penalty, maxdist);
} else {
dist = regions.GetPlanarDistance(r, tar->region, penalty, maxdist);
}
if (dist > maxdist) {
u->error("TRANSPORT: Recipient " + string(tar->unit->name->const_str()) + " is too far away.");
o->type = NORDERS;
continue;
}
} else {
dist = regions.GetPlanarDistance(r, tar->region, penalty, Globals->LOCAL_TRANSPORT);
// Store off the distance for later use so we don't need to recompute it.
o->distance = dist;
}

// We will check the amount at transport time so that if you receive items in you can tranport them
Expand Down Expand Up @@ -3258,10 +3263,7 @@ void Game::RunTransportPhase(TransportOrder::TransportPhase phase) {
}

// now see if the unit can pay for shipping
int penalty = 10000000;
RangeType *rt = FindRange("rng_transport");
if (rt) penalty = rt->crossLevelPenalty;
int dist = regions.GetPlanarDistance(r, tar->region, penalty, Globals->LOCAL_TRANSPORT);
int dist = t->distance;
int weight = ItemDefs[t->item].weight * amt;
if (weight == 0 && Globals->FRACTIONAL_WEIGHT > 0)
weight = (amt/Globals->FRACTIONAL_WEIGHT) + 1;
Expand Down

0 comments on commit 6227158

Please sign in to comment.