static char *rcsid = "$Header: /plaid/homes/zeus/Blind-2.47/RCS/new-game.c,v 2.47 1998/04/15 21:34:17 zeus Exp zeus $";

/*
 * $Log: new-game.c,v $
 * Revision 2.47  1998/04/15 21:34:17  zeus
 * *** empty log message ***
 *
 * Revision 2.46.0.1  1997/05/27 21:04:56  zeus
 * Revision 2.46  1997/04/18 12:06:12  zeus
 * Revision 2.45  1996/12/31  23:40:54  zeus
 * Revision 2.44  1995/12/31  19:45:18  zeus
 * Revision 2.43  1995/03/20  01:43:09  zeus
 * Revision 2.42  1994/05/17  13:43:15  bampton
 * Revision 2.41  1994/04/08  03:11:28  bampton
 * Revision 2.40  1994/03/09  20:22:55  bampton
 * Revision 2.38  1993/09/27  17:15:54  bampton
 * Revision 2.37  1993/09/07  20:50:32  bampton
 * Revision 2.33  1993/08/03  23:10:13  bampton
 * Revision 2.30  1993/07/14  14:59:26  bampton
 * Revision 0.0  1993/07/14  13:42:19  bampton RCS prototype.
 *
 */


#define LOWHW 99                /* Homeworlds must be numbered higher than this */
#define HIGHW 99                /* Homeworlds numbered lower than numplanets less this */
#define RELPOS 1.4              /* The best and worst positions differ by a factor of this */

#define NOTIMEB

#include "protos/common.h"
#include "protos/prototypes.h"
#include "protos/new-game.h"
#include "protos/i-o.h"

extern planet *planets;
extern player *players;
extern double galaxysize;
extern int nplanets;

#ifdef GAMENAME
extern char gamename[];
extern char buf[];

#endif
int file;

/* Algorithm: for creating new galaxies:
   1) Do players, and make them at least MINSEP apart.
   2) Create new systems and place
   3) If BALANCE is set, possibly distribute galaxy to something like average.

   check doc/Starting-a-game for details */

void
new_game_rcsid (void)
{
  printf ("%s\n", rcsid);
}

void
create_galaxy (void)
{
  int i;
  struct planetstruct *p;

#ifdef GAMENAME
  printf ("Name of game?");
  getstr ();
  hstrncpy (gamename, buf, MAXTEXT);
#endif

  do
  {
    printf ("Total number of systems? ");
    nplanets = geti ();
  }
  while (nplanets < 100 + HIGHW + LOWHW);

  /* Make a total of nplanets planets, so subract off homeworld count */
  for (i = listlen (planets); i < nplanets; i++)
  {
    p = addplanet ();
    set_spice (p);
    p->x = frand (galaxysize);
    p->y = frand (galaxysize);
    p->owner = NULL;
  }

#ifdef BALANCE
  /* Galaxy created, do balace stuff */
  balance_galaxy ();
#endif

  /* Everything OK, so _now_ swap homeworlds. */
  {
    planet *q;

    for (p = planets; p->owner; p = q)
    {
      q = p->next;
      swap_planet (p);
    }
  }
}

planet *
addplanet (void)
{
  planet *p;
  int i;

  for (p = planets, i = 0; p; p = p->next, i++);
  p = alloc (sizeof (planet));
  addlist (&planets, p);
  p->producing = PR_CAP;
  sprintf (p->name, "%d", i);
  sprintf (p->num, "%d", i);
  return p;
}

/* Set planet size/resources */

void
set_spice (planet * p)
{
  double spice;

  spice = frand (10.0);
  if (spice > 9.8)              /* double-planet system */
  {
    p->size = frand (MAXPOP) + MAXPOP;  /* double-planet system */
    p->resources = frand (10.0);        /* higher resource than average */
  }
  else if (spice > 5.0)         /* normal system */
  {
    p->size = frand (MAXPOP);
    p->resources = pow (10.0, (double) (frand (2.0) - 1.0));
  }
  else if (spice > 4.0)         /* minor but resource-rich */
  {
    p->size = frand (MAXPOP / 5.0) + 50.0;      /* minor, resource-rich */
    p->resources = frand (6.0) + 4.0;
  }
  else if (spice > .7)          /* very tiny, bad resources */
  {
    p->size = frand (MAXPOP / 50.0);    /* very minor, resource-poor */
    p->resources = pow (2.0, (double) (frand (2.0) - 1.0));
  }                             /* else unsettleable, so leave as all 0's  */
}

/* Swap planet p somewhere into the middle of the planet list */
void
swap_planet (planet * p)
{                               /* do the switcheroo of this planet & another */
  int oldnum;
  planet *p2, *p3;
  char oldnums[MAXTEXT];

  do
  {
    do
    {
      oldnum = rnd (nplanets);
      sprintf (oldnums, "%d", oldnum);
    }
    while (!(strcmp (oldnums, p->num)) || (oldnum < LOWHW) ||
           (nplanets - oldnum < HIGHW));

    /* Find planet to swap */
    for (p2 = planets; p2; p2 = p2->next)
      if (!strcmp (p2->num, oldnums))
        break;
  }
  while (p2->owner);            /* Don't want to swap player into begining... */

  /* Swap p & p' where p' is oldnum */
  p3 = alloc (sizeof (planet));
  p3->x = p->x;
  p3->y = p->y;
  p3->size = p->size;
  p3->resources = p->resources;
  p3->owner = p->owner;
  p3->pop = p->pop;
  p3->ind = p->ind;
  p3->producing = p->producing;
  p->x = p2->x;
  p->y = p2->y;
  p->size = p2->size;
  p->resources = p2->resources;
  p->owner = p2->owner;
  p->pop = p2->pop;
  p->ind = p2->ind;
  p->producing = p2->producing;
  p2->x = p3->x;
  p2->y = p3->y;
  p2->size = p3->size;
  p2->resources = p3->resources;
  p2->owner = p3->owner;
  p2->pop = p3->pop;
  p2->ind = p3->ind;
  p2->producing = p3->producing;
  free (p3);
}

#ifdef BALANCE
void
balance_galaxy (void)
{
  double *vals, min, max;
  int num;
  player *P;
  planet *p, *ph;

  vals = alloc ((unsigned) sizeof (double) * (listlen (players) + 1));

  do
  {
    max = 0.0;

#ifdef ULTRIX
    min = MAXFLOAT;
#else
    min = HUGE_VAL;
#endif

    for (P = players, ph = planets; P; P = P->next, ph = ph->next)
    {
      num = ptonum (players, P);
      vals[num] = 0.0;
      for (p = planets; p; p = p->next)
      {
        vals[num] += planet_value (ph, p);
      }

#ifdef VERBOSE
      printf ("%s's weighted planet mass %11.2f\n", P->name, (float) vals[num]);
#endif

      if (vals[num] > max)
        max = vals[num];
      if ((vals[num] < min) && (min > 0.0))
        min = vals[num];
    }
    if (min * RELPOS < max)
    {

#ifdef VERBOSE
      printf ("Max:%11.2f Min:%11.2f\n", (double) max, (double) min);
      printf ("Press <RETURN> to redistrbute planets");
      getstr ();
#else
      printf ("Unbalanced, redistributing\n");
#endif

      for (P = players, ph = planets; P; P = P->next, ph = ph->next)
      {
        num = ptonum (players, P);
        if (max == vals[num])
        {
          find_top (P, ph);
          break;
        }
      }
      for (P = players, ph = planets; P; P = P->next, ph = ph->next)
      {
        num = ptonum (players, P);
        if (min == vals[num])
        {
          find_bottom (P, ph);
          break;
        }
      }

    }
  }
  while (min * RELPOS < max);
  free (vals);
}

/*
   Planets have a value related to their dist to your homeworld.
   Don't consider resources ('cause then we'd have to worry about 
   proximity to mat rich worlds...)

   Dist    Size mult
   >MINSEP 0.00
   <2      2.50
   <5      1.25
   <7      1.00
   <10     0.80
   <15     0.50
   <30     0.25
   >=30    0.00

   Planet size fudge factor

   Size    Size mult
   <10     0.00
   <100    0.50
   <500    0.70
   <800    0.80
   <1000   0.9
   1000    1
   >1000   1.1

   Planet resources fudge factor

   Resources  Value mult

   < 0.50     0.70
   < 2.00     1.00
   >= 2.00    1.30

 */

double
p_size_fudge (double size)
{
  if (size < 10.0)
    return (0.0);
  if (size < 100.0)
    return (size * 0.50);
  if (size < 500.0)
    return (size * 0.70);
  if (size < 800.0)
    return (size * 0.80);
  if (size < 1000.0)
    return (size * 0.90);
  if (size == 1000.0)
    return (size);
  if (size > 1000.0)
    return (size * 1.10);
  assert (0);
  return (0);                   /* Should never be reached */
}

double
p_resource_fudge (double resources)
{
  if (resources < 0.50)
    return (0.70);
  if (resources < 2.00)
    return (1.00);
  return (1.30);
}

double
p_dist_fudge (planet * ph, planet * p)
{
  double d;

  d = dist (ph, p);

  if (d > MINSEP)
    return (0.0);
  if (d < 2.0)
    return (2.50);
  if (d < 5.0)
    return (1.25);
  if (d < 7.0)
    return (1.00);
  if (d < 10.0)
    return (0.80);
  if (d < 15.0)
    return (0.50);
  if (d < 30.0)
    return (0.25);
  return (0.0);
}

double
planet_value (planet * ph, planet * p)
{
  double pdf, prf, psf;

  pdf = p_dist_fudge (ph, p);
  prf = p_resource_fudge (p->resources);
  psf = p_size_fudge (p->size);
  return (pdf * prf * psf * p->size);
}

/* find and move largest contibutor to player's cumulative planet total */
void
find_top (player * P, planet * ph)
{
  double max = 0.0, t;
  planet *p, *psave;

  psave = NULL;
  for (p = planets; p; p = p->next)
  {
    t = planet_value (ph, p);
    if ((t > max) && !p->owner)
    {
      max = t;
      psave = p;
    }
  }
  if (psave)
    move_planet (psave, ph, 1.0);
}

/* find and move smallest contibutor to player's cumulative planet total 
   with some limits */
void
find_bottom (player * P, planet * ph)
{
  double min, t, d;
  planet *p, *psave;

#ifdef ULTRIX
  min = MAXFLOAT;
#else
  min = HUGE_VAL;
#endif

  psave = NULL;
  for (p = planets; p; p = p->next)
  {
    d = dist (p, ph);
    /* Not worth it to move distant worlds. 
       Really close ones don't gain you either.
       Small ones aren't worth it. */
    if ((d < 25.0) && (d > 2.0) && (p->size > 50.0))
    {
      t = planet_value (ph, p);
      /* Bad idea to move size 0 world or homeworld */
      if ((t < min) && (p->size) && (!p->owner))
      {
        min = t;
        psave = p;
      }
    }
  }
  if (psave)
    move_planet (psave, ph, -1.0);

#ifdef VERBOSE
  else
    printf ("Couldn't find system to move closer\n");
#endif
}

void
move_planet (planet * p_to_move, planet * home, double dir)
{
  double d, mult, newd = 0.0;   /* For lint */

  d = dist (p_to_move, home);

  /* Figure out new distance world will be from HW.
     Note: ones larger than 10 are quite improbable */
  /* Might want a random factor here, someday */

  if (d < 2.0)
  {
    newd = d + (2.0 * dir);
    assert (dir > 0.0);
  }
  else if (d < 5.0)
    newd = d + (2.0 * dir);
  else if (d < 7.0)
    newd = d + (2.0 * dir);
  else if (d < 10.0)
    newd = d + (3.0 * dir);
  else if (d < 15.0)
    newd = d + (5.0 * dir);
  else if (d < 30.0)
    newd = d + (5.0 * dir);


  mult = dir * ((newd + frand (1.0)) / d);      /* Proportional offset */

#ifdef VERBOSE
  printf ("Moving planet %s, size/r %.2f/%4.2f ", p_to_move->name, p_to_move->size, p_to_move->resources);
  if (dir < 0)
    printf ("closer");
  else
    printf ("farther");
  printf (" relative to %s\n", home->owner->name);
#endif

  p_to_move->x += (p_to_move->x - home->x) * mult;
  p_to_move->y += (p_to_move->y - home->y) * mult;
  while (p_to_move->x < 0.0)
    p_to_move->x += galaxysize;
  while (p_to_move->y < 0.0)
    p_to_move->y += galaxysize;
  while (p_to_move->x > galaxysize)
    p_to_move->x -= galaxysize;
  while (p_to_move->y > galaxysize)
    p_to_move->y -= galaxysize;
  assert (p_to_move->x >= 0.0);
  assert (p_to_move->y >= 0.0);
  assert (p_to_move->x <= galaxysize);
  assert (p_to_move->y <= galaxysize);

}
#endif

void
manual_move_planet (void)
{
  planet *p;
  double newx, newy;

  p = inputplanet (planets, "Planet? ");
  if (!p)
    return;
  printf ("New X location? ");
  newx = getf ();
  printf ("New Y location? ");
  newy = getf ();
  if ((newx > galaxysize) || (newx < 0.0))
  {
    printf ("New X co-ordinate out of range!\n");
    return;
  }

  if ((newy > galaxysize) || (newy < 0.0))
  {
    printf ("New Y co-ordinate out of range!\n");
    return;
  }
  p->x = newx;
  p->y = newy;
}

/* Lifted from cmds/Cmds-lib.c so we don't have to include it */
/* This is a "safe" strncpy- null termination guarenteed, and no
   overrun (given correct sizes) */

void
hstrncpy (char *a, const char *b, const int sz)
{
  int x;

  a[0] = '\0';
  if (b == NULL)
    return;
  for (x = 0; b[x] && (x < sz); x++)
  {
    a[x] = b[x];
  }
  if (sz == MAXTEXT)
    a[sz - 1] = '\0';
  if (x < sz)
    a[x] = '\0';
}
