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