Update of /cvsroot/netrek/server/Vanilla/ntserv
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv11423/ntserv
Added Files:
balance.c
Log Message:
--- NEW FILE: balance.c ---
#include <stdio.h>
#include <stdlib.h>
#include "defs.h"
#include "struct.h"
#include "data.h"
#include "gencmds.h"
#include "proto.h"
#ifdef TRIPLE_PLANET_MAYHEM
/*
** 16-Jul-1994 James Cameron
**
** Balances teams according to player statistics.
** Intended for use with Triple Planet Mayhem
*/
/*
** Tell all that player will be moved
*/
static void moveallmsg(int p_no, int ours, int theirs, int p_value)
{
struct player *k = &players[p_no];
pmessage(0, MALL, addr_mess(p_no, MALL),
"Balance: %16s (slot %c, rating %.2f) is to join the %s",
k->p_name, shipnos[p_no],
(float) ( p_value / 100.0 ),
team_name(ours));
}
/*
** Move a player to the specified team, if they are not yet there.
** Make them peaceful with the new team, and hostile/at war with the
** other team.
*/
static void move(int p_no, int ours, int theirs)
{
struct player *k = &players[p_no];
int queue;
if ( k->p_team != ours ) {
pmessage(k->p_no, MINDIV, addr_mess(k->p_no,MINDIV),
"%s: please SWAP SIDES to the --> %s <--", k->p_name, team_name(ours));
}
else {
pmessage(k->p_no, MINDIV, addr_mess(k->p_no,MINDIV),
"%s: please remain with the --> %s <--", k->p_name, team_name(ours));
}
printf("Balance: %16s (%s) is to join the %s\n",
k->p_name, k->p_mapchars, team_name(ours));
/* cope with a balance during INL pre-game, if we don't shift players who
are on the QU_HOME or QU_AWAY queues then the queue masks will force
them to join the team they were on anyway. */
queue = ( ours == FED ) ? QU_HOME : QU_AWAY;
if (k->w_queue != QU_PICKUP && k->w_queue != queue) {
queues[k->w_queue].free_slots++;
k->w_queue = queue;
queues[k->w_queue].free_slots--;
}
k->p_hostile |= theirs;
k->p_swar |= theirs;
k->p_hostile &= ~ours;
k->p_swar &= ~ours;
k->p_war = (k->p_hostile | k->p_swar);
k->p_team = ours;
sprintf(k->p_mapchars, "%c%c", teamlet[k->p_team], shipnos[p_no]);
sprintf(k->p_longname, "%s (%s)", k->p_name, k->p_mapchars);
k->p_status = PEXPLODE;
k->p_whydead = KPROVIDENCE; /* should be KTOURNSTART? */
if (k->p_ship.s_type == STARBASE)
k->p_explode = 2 * SBEXPVIEWS;
else
k->p_explode = 10;
k->p_ntorp = 0;
k->p_nplasmatorp = 0;
k->p_hostile = (FED | ROM | ORI | KLI);
k->p_war = (k->p_hostile | k->p_swar);
}
/*
** Return two team masks corresponding to the teams of the first two
** teams found in the player list.
*/
static void sides (int *one, int *two)
{
struct player *k;
int i;
int unseen;
unseen = (FED | ROM | ORI | KLI);
*one = 0;
*two = 0;
k = &players[0];
for(i=0;i<MAXPLAYER;i++)
{
if ( (k->p_status != PFREE) && (!(k->p_flags & PFROBOT)))
{
if ( ( unseen & k->p_team ) != 0 )
{
if ( *one == 0 )
{
*one = k->p_team;
unseen &= ~k->p_team;
k++;
continue;
}
*two = k->p_team;
return;
}
}
k++;
}
}
/*
** Calculate a player value
*/
static int value (struct player *k)
{
return
(int)
(
(float)
(
#ifdef LTD_STATS
ltd_bombing_rating(k) * BALANCE_BOMBING +
ltd_planet_rating(k) * BALANCE_PLANET +
ltd_defense_rating(k) * BALANCE_DEFENSE +
ltd_offense_rating(k) * BALANCE_OFFENSE
#else
bombingRating(k) * BALANCE_BOMBING +
planetRating(k) * BALANCE_PLANET +
defenseRating(k) * BALANCE_DEFENSE +
offenseRating(k) * BALANCE_OFFENSE
#endif /* LTD_STATS */
)
);
}
/*
** Balance the teams
**
** Uses an exhaustive algorithm (I'm exhausted!) to find the best combination
** of the current players that balances the teams in terms of statistics.
** The algorithm will support only the number of players that fits into the
** number of bits in an int.
**
** If there are multiple "best" combinations, then the combination
** involving the least number of team swaps will be chosen.
*/
void do_balance(void)
{
int i, j; /* miscellaneous counters */
int records; /* number of players in game */
int one; /* team number one mask */
int two; /* team number two mask */
struct player *k; /* pointer to current player */
struct item
{
int p_no; /* player number */
int p_value; /* calculated player value */
int p_team; /* team player on previously */
} list[MAXPLAYER]; /* working array */
struct
{
int combination; /* combination number */
int value; /* team balance difference */
int one; /* team one total value */
int two; /* team two total value */
int swaps; /* number of swaps involved */
} best; /* best team combination */
/* which teams are playing? give up if only one found */
sides ( &one, &two );
if ( two == 0 )
{
/* addr_mess shouldn't be called with 0 as first arg
* for MALL, but we don't have a player numer here.
* Let addr_mess catch it. -da
*/
pmessage ( 0, MALL, addr_mess(0 ,MALL),
"Can't balance only one team!" );
pmessage ( 0, MALL, addr_mess(0 ,MALL),
"Please could somebody move to another team, then all vote again?" );
return;
}
/* initialise best to worst case */
best.combination = -1;
best.value = 1<<30;
best.one = 0;
best.two = 0;
best.swaps = 1<<30;
/* reset working array */
for(i=0;i<MAXPLAYER;i++)
{
list[i].p_no = 0;
list[i].p_value = 0;
}
/* insert players in working array */
records = 0;
k = &players[0];
for(i=0;i<MAXPLAYER;i++)
{
if ( (k->p_status != PFREE) && (!(k->p_flags & PFROBOT)))
{
list[records].p_no = k->p_no;
list[records].p_value = value ( k );
list[records].p_team = k->p_team;
records++;
}
k++;
}
/* randomise the working array; may cause different team mixes */
for(i=0;i<records;i++)
{
int a, b;
struct item swapper;
a = random() % records;
b = random() % records;
swapper = list[a];
list[a] = list[b];
list[b] = swapper;
}
/* loop for every _possible_ combination to find the best */
for(i=0;i<(1<<records);i++)
{
int difference; /* difference in team total */
int value_a, value_b; /* total stats per team */
int count_a, count_b; /* total count of players on team */
int swaps; /* number of swaps involved */
/* if this a shadow combination already considered, ignore it */
/* if ( ( i ^ ( ( 1<<records ) - 1 ) ) < i ) continue; */
/* disabled - it will interfere with swap minimisation goal */
/* is this combination an equal number of players each side? */
count_a = 0;
count_b = 0;
for(j=0;j<records;j++)
if((1<<j)&i)
count_a++;
else
count_b++;
/* skip this combination if teams are significantly unequal */
if ( abs ( count_a - count_b ) > 1 ) continue;
/* reset team total for attempt */
value_a = 0;
value_b = 0;
/* calculate team total stats */
for(j=0;j<records;j++)
if((1<<j)&i)
value_a += list[j].p_value;
else
value_b += list[j].p_value;
/* calculate number of swaps this combination produces */
swaps = 0;
for(j=0;j<records;j++)
if((1<<j)&i)
{
if ( list[j].p_team != one ) swaps++;
}
else
{
if ( list[j].p_team != two ) swaps++;
}
/* calculate difference in team total stats */
difference = abs ( value_a - value_b );
/* if this combo is better than the previous one we had,
or the combo is the same and the number of swaps is lower... */
if ( ( difference < best.value )
|| ( ( difference == best.value )
&& ( swaps < best.swaps ) ) )
{
/* remember it */
best.value = difference;
best.combination = i;
best.one = value_a;
best.two = value_b;
best.swaps = swaps;
}
}
/* don't do balance if not worth it */
if ( best.swaps != 0 )
{
/* move players to their teams */
for(j=0;j<records;j++)
if ( (1<<j)&best.combination )
move ( list[j].p_no, one, two );
else
move ( list[j].p_no, two, one );
/* build messages to ALL about the results */
for(j=0;j<records;j++)
if ( (1<<j)&best.combination )
moveallmsg ( list[j].p_no, one, two, list[j].p_value );
/* addr_mess ignores first arg if second is MALL. -da
*/
pmessage(0, MALL, addr_mess(0, MALL),
"Balance: total rating %.2f",
(float) ( best.one / 100.0 ) );
for(j=0;j<records;j++)
if ( !((1<<j)&best.combination) )
moveallmsg ( list[j].p_no, two, one, list[j].p_value );
pmessage(0, MALL, addr_mess(0, MALL),
"Balance: total rating %.2f",
(float) ( best.two / 100.0 ) );
}
else
{
pmessage ( 0, MALL, addr_mess(0, MALL),
"No balance performed, this is the best: %-3s %.2f, %-3s %.2f",
team_name(one), (float) ( best.one / 100.0 ),
team_name(two), (float) ( best.two / 100.0 ) );
}
}
void do_triple_planet_mayhem(void)
{
int i;
/* balance the teams */
do_balance();
/* move all planets off the galaxy */
for (i=0; i<MAXPLANETS; i++)
{
planets[i].pl_flags = 0;
planets[i].pl_owner = 0;
planets[i].pl_x = -10000;
planets[i].pl_y = -10000;
planets[i].pl_info = 0;
planets[i].pl_armies = 0;
strcpy ( planets[i].pl_name, "" );
}
/* disable Klingon and Orion teams; stop people from joining them */
planets[20].pl_couptime = 999999; /* no Klingons */
planets[30].pl_couptime = 999999; /* no Orions */
/* initialise earth */
i = 0;
planets[i].pl_flags |= FED | PLHOME | PLCORE | PLAGRI | PLFUEL | PLREPAIR;
planets[i].pl_x = 40000;
planets[i].pl_y = 65000;
planets[i].pl_armies = 40;
planets[i].pl_info = FED;
planets[i].pl_owner = FED;
strcpy ( planets[i].pl_name, "Earth" );
/* initialise romulus */
i = 10;
planets[i].pl_flags |= ROM | PLHOME | PLCORE | PLAGRI | PLFUEL | PLREPAIR;
planets[i].pl_x = 40000;
planets[i].pl_y = 35000;
planets[i].pl_armies = 40;
planets[i].pl_info = ROM;
planets[i].pl_owner = ROM;
strcpy ( planets[i].pl_name, "Romulus" );
/* initialise indi */
i = 18;
planets[i].pl_flags |= PLFUEL | PLREPAIR;
planets[i].pl_flags &= ~PLAGRI;
planets[i].pl_x = 15980;
planets[i].pl_y = 50000;
planets[i].pl_armies = 4;
planets[i].pl_info &= ~ALLTEAM;
strcpy ( planets[i].pl_name, "Indi" );
/* fix all planet name lengths */
for (i=0; i<MAXPLANETS; i++)
{
planets[i].pl_namelen = strlen(planets[i].pl_name);
}
/* advise players */
{
char *list[] =
{
"Galaxy reset for triple planet mayhem!",
"Rule 1: they take Indi, they win,",
"Rule 2: they take your home planet, they win,",
"Rule 3: you can't bomb Indi,",
"Rule 4: you may bomb their home planet, and;",
"Rule 5: all planets are FUEL & REPAIR, home planets are AGRI.",
""
};
for ( i=0; strlen(list[i])!=0; i++ )
/* addr_mess ignores first arg for MALL. -da
*/
pmessage ( 0, MALL, addr_mess(0,MALL), list[i] );
}
}
#endif /* TRIPLE_PLANET_MAYHEM */