From 472579aa2a69a40c9879c4867463d8119cefd2c7 Mon Sep 17 00:00:00 2001 From: Allanis <allanis@saracraft.net> Date: Tue, 23 Apr 2013 22:57:46 +0100 Subject: [PATCH] [Add] A* algorithm to get paths between systems. --- src/space.c | 222 ++++++++++++++++++++++++++++++++++++++++++++++++++-- src/space.h | 21 ++--- 2 files changed, 227 insertions(+), 16 deletions(-) diff --git a/src/space.c b/src/space.c index cdaab00..e3760cc 100644 --- a/src/space.c +++ b/src/space.c @@ -1,6 +1,9 @@ #include <malloc.h> #include <math.h> +#include <float.h> +#include "xml.h" +#include "lephisto.h" #include "log.h" #include "physics.h" #include "rng.h" @@ -47,9 +50,9 @@ static int spacename_nstack = 0; // Star system stack and co. StarSystem* systems_stack = NULL; // Star system stack. -int systems_nstack = 0; // Number of star systems. -static int nplanets = 0; // Total number of loaded planets - A little silly. -StarSystem* cur_system = NULL; // Current star system. +int systems_nstack = 0; // Number of star systems. +static int nplanets = 0; // Total number of loaded planets - A little silly. +StarSystem* cur_system = NULL; // Current star system. // Fleet spawn rate. unsigned int spawn_timer = 0; // Controls spawn rate. @@ -62,10 +65,11 @@ typedef struct Star_ { } Star; static Star* stars = NULL; // Star array. -static int nstars = 0; // Total stars. -static int mstars = 0; // Memory stars are taking. +static int nstars = 0; // Total stars. +static int mstars = 0; // Memory stars are taking. -// Intern +// Intern. +static StarSystem* system_get(const char* sysname); static Planet* planet_get(const char* name); static void space_addFleet(Fleet* fleet); static StarSystem* system_parse(const xmlNodePtr parent); @@ -139,6 +143,200 @@ void planets_minimap(const double res, } #undef PIXEL +// A* Algorithm fo shortest path finding. + +// The node struct. +typedef struct SysNode_ { + struct SysNode_* next; + + struct SysNode_* parent; + StarSystem* sys; + double r; // Ranking. + int g; // Step. +} SysNode; + +static SysNode* gc; +// Prototypes. +static SysNode* A_newNode(StarSystem* sys, SysNode* parent); +static double A_h(StarSystem* n, StarSystem* g); +static double A_g(SysNode* n); +static SysNode* A_add(SysNode* first, SysNode* cur); +static SysNode* A_rm(SysNode* first, StarSystem* cur); +static int A_in(SysNode* first, StarSystem* cur); +static SysNode* A_lowest(SysNode* first); +static void A_freeList(SysNode* first); + +// Creates a new node link to star system. +static SysNode* A_newNode(StarSystem* sys, SysNode* parent) { + SysNode* n; + + n = malloc(sizeof(SysNode)); + + n->next = NULL; + n->parent = parent; + n->sys = sys; + n->r = DBL_MAX; + n->g = 0.; + + A_add(gc, n); + + return n; +} + +static double A_h(StarSystem* n, StarSystem* g) { + // Euclidean distance. + return sqrt(pow2(n->pos.x - g->pos.x) + pow2(n->pos.y - g->pos.y))/100.; +} + +// Get the g from a node. +static double A_g(SysNode* n) { + return n->g; +} + +// Add a node to the linkes list. +static SysNode* A_add(SysNode* first, SysNode* cur) { + SysNode* n; + + if(first == NULL) + return cur; + + n = first; + while(n->next != NULL) + n = n->next; + n->next = cur; + + return first; +} + +// Remove a node from a linked list. +static SysNode* A_rm(SysNode* first, StarSystem* cur) { + SysNode* n, *p; + + if(first->sys == cur) { + first->next = NULL; + n = first->next; + return n; + } + + p = first; + n = p->next; + do { + if(n->sys == cur) { + n->next = NULL; + p->next = n->next; + break; + } + } while((n=n->next) != NULL); + + return first; +} + +// Check if node is in linked list. +static int A_in(SysNode* first, StarSystem* cur) { + SysNode* n; + + if(first == NULL) + return 0; + + n = first; + do { + if(n->sys == cur) + return 1; + } while((n=n->next) != NULL); + return 0; +} + +// Return the lowest ranking node from a linked list of nodes. +static SysNode* A_lowest(SysNode* first) { + SysNode* lowest, *n; + + if(first == NULL) + return NULL; + + n = first; + lowest = n; + do { + if(n->r < lowest->r) + lowest = n; + } while((n=n->next) != NULL); + + return lowest; +} + +// Free a linked list. +static void A_freeList(SysNode* first) { + SysNode* p, *n; + + if(first == NULL) + return; + + p = NULL; + n = first; + do { + if(p != NULL) + free(p); + p = n; + } while((n=n->next) != NULL); + + free(p); +} + +StarSystem** system_getJumpPath(int* njumps, char* sysstart, char* sysend) { + int i, cost; + + StarSystem* ssys, *esys, **res; + + SysNode* cur, *neighbour; + SysNode* open, *closed; + + gc = NULL; + + // Initial and target systems. + ssys = system_get(sysstart); // Start. + ssys = system_get(sysend); // End. + + // Start the linked lists. + open = closed = NULL; + cur = A_newNode(ssys, NULL); + open = A_add(open, cur); // Initial open node is the start system. + + while((cur = A_lowest(open))->sys != esys) { + // Get best from open and toss to closed. + open = A_rm(open, cur->sys); + closed = A_add(closed, cur); + + for(i = 0; i < cur->sys->njumps; i++) { + neighbour = A_newNode(&systems_stack[cur->sys->jumps[i]], cur); + cost = A_g(cur) + 1; + + if(A_in(open, neighbour->sys) && (cost < A_g(neighbour))) + open = A_rm(open, neighbour->sys); // New path is better. + + if(A_in(closed, neighbour->sys) && (cost < A_g(neighbour))) + closed = A_rm(closed, neighbour->sys); // Shouldn't happen. + + if(!A_in(open, neighbour->sys) && !A_in(closed, neighbour->sys)) { + neighbour->g = cost; + open = A_add(open, neighbour); + neighbour->r = (double)A_g(neighbour) + A_h(neighbour->sys, esys); + } + } + } + // Build the path backwards. + (*njumps) = A_g(cur); + res = malloc(sizeof(StarSystem*) * (*njumps)); + for(i = 0; i < (*njumps); i++) { + res[(*njumps)-i-1] = cur->sys; + cur = cur->parent; + } + + // Free the linked list. + //A_freeList(open); + //A_freeList(closed); + A_freeList(gc); + return res; +} + static PlanetClass planetclass_get(const char a) { switch(a) { // Planets use letters. @@ -256,6 +454,18 @@ char* space_getRndPlanet(void) { return res; } +// Get the system from it's name. +static StarSystem* system_get(const char* sysname) { + int i; + + for(i = 0; i < systems_nstack; i++) + if(strcmp(sysname, systems_stack[i].name)==0) + return &systems_stack[i]; + + DEBUG("System '%s' not found in stack", sysname); + return NULL; +} + // Get the name of a system from a planetname. char* planet_getSystem(char* planetname) { int i; diff --git a/src/space.h b/src/space.h index 379f675..5e28907 100644 --- a/src/space.h +++ b/src/space.h @@ -69,26 +69,26 @@ typedef struct Planet_ { // Star systems. typedef struct SystemFleet_ { - Fleet* fleet; // Fleet to appear. - int chance; // Chance of fleet appearing in the system. + Fleet* fleet; // Fleet to appear. + int chance; // Chance of fleet appearing in the system. } SystemFleet; typedef struct StarSystem_ { - char* name; // Star system identifier. - Vec2 pos; // Position. + char* name; // Star system identifier. + Vec2 pos; // Position. int stars, asteroids; // Un numero! double interference; // Un uh.. Percentage. - int faction; // Overall faction. + int faction; // Overall faction. - Planet* planets; // Planets. - int nplanets; // Total number of planets. + Planet* planets; // Planets. + int nplanets; // Total number of planets. SystemFleet* fleets; // Fleets that can appear in the current system. - int nfleets; // Total number of fleets. + int nfleets; // Total number of fleets. - int* jumps; // Adjacent star system index number. - int njumps; // Number of adjacent jumps. + int* jumps; // Adjacent star system index number. + int njumps; // Number of adjacent jumps. } StarSystem; extern StarSystem* cur_system; // Current star system. @@ -109,6 +109,7 @@ void planets_render(void); void space_update(const double dt); // Misc. +StarSystem** system_getJumpPath(int* njumps, char* sysstart, char* sysend); int space_canHyperspace(Pilot* p); int space_hyperspace(Pilot* p); char** space_getFactionPlanet(int* nplanets, int* factions, int nfactions);