forked from envs/starlanes
384 lines
14 KiB
C
384 lines
14 KiB
C
|
/* Artificial Inteligence -anything in relation to non human players */
|
||
|
|
||
|
/*
|
||
|
** This program is free software; you can redistribute it and/or
|
||
|
** modify it under the terms of the GNU General Public License
|
||
|
** as published by the Free Software Foundation; either version 2
|
||
|
** of the License, or (at your option) any later version.
|
||
|
**
|
||
|
** This program is distributed in the hope that it will be useful,
|
||
|
** but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||
|
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||
|
** GNU General Public License for more details.
|
||
|
**
|
||
|
** You should have received a copy of the GNU General Public License
|
||
|
** along with this program; if not, write to the Free Software
|
||
|
** Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
||
|
*/
|
||
|
|
||
|
#include <stdio.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <string.h>
|
||
|
#include "common.h"
|
||
|
|
||
|
|
||
|
|
||
|
/*
|
||
|
int move;
|
||
|
char map[120]; */
|
||
|
|
||
|
|
||
|
/*
|
||
|
**void generate_nme(nme)
|
||
|
**used in place of getting name from humans
|
||
|
*/
|
||
|
#define up_obj(move) (((move)-MAPX < 0)?OFFMAP:map[(move)-MAPX])
|
||
|
#define down_obj(move) (((move)+MAPX >= MAPX*MAPY)?OFFMAP:map[(move)+MAPX])
|
||
|
#define left_obj(move) (((move)%MAPX)?map[(move)-1]:OFFMAP)
|
||
|
#define right_obj(move) (((move)%MAPX == MAPX-1)?OFFMAP:map[(move)+1])
|
||
|
|
||
|
void generate_nme(char *nme)
|
||
|
{
|
||
|
static int nmeNmr=0;
|
||
|
|
||
|
switch(nmeNmr) {
|
||
|
case 0: strcpy(nme, "Vorg");break;
|
||
|
case 1: strcpy(nme, "Zhara");break;
|
||
|
case 2: strcpy(nme, "Dushan");break;
|
||
|
case 3: strcpy(nme, "Peko Pekovich");break;
|
||
|
case 4: strcpy(nme, "Agram Anastija");break;
|
||
|
default: strcpy(nme, "Man with no name");break;
|
||
|
};
|
||
|
nmeNmr++;
|
||
|
}
|
||
|
|
||
|
enum compass { North,East ,West,South } ;//type;
|
||
|
|
||
|
enum farcompass {NorthW , FNorth, NorthE, FEast ,SouthE ,FSouth, SouthW ,FWest};// type;
|
||
|
|
||
|
enum investstatus { none,small ,signif,main };// type;
|
||
|
|
||
|
/* getfarmove gets the charecter value of a location 2 steps away.
|
||
|
** It makes no distiction between an empty space and an off map
|
||
|
** location
|
||
|
*/
|
||
|
char getfarmove(enum farcompass fcom, char *map, int loc)
|
||
|
{
|
||
|
static int MAPX=Mx ;
|
||
|
static int MAPY=My;
|
||
|
char ch;
|
||
|
switch(fcom) {
|
||
|
case NorthW: if ((loc-1*MAPX )< 0) ch=SPACE;
|
||
|
else {
|
||
|
ch=left_obj(loc-1*MAPX);
|
||
|
}
|
||
|
break;
|
||
|
case FNorth: if ((loc-2*MAPX )< 0) ch=SPACE; else ch=map[loc-2*MAPX];break;
|
||
|
case NorthE: if ((loc-1*MAPX )< 0) ch=SPACE;
|
||
|
else {
|
||
|
ch=right_obj(loc-1*MAPX);
|
||
|
}
|
||
|
break;
|
||
|
case FEast: if ((loc%MAPX)!=((loc+2)%MAPX)) ch=map[loc+2];
|
||
|
else ch=SPACE ;
|
||
|
break;
|
||
|
case SouthE: if ((loc+MAPX )> (MAPX*MAPY)) ch=SPACE;
|
||
|
else {
|
||
|
ch=right_obj(loc+1*MAPX);
|
||
|
}
|
||
|
case FSouth: if ((loc+2*MAPX) > (MAPX*MAPY)) ch=SPACE; else ch=map[loc-2*MAPX];break;
|
||
|
/*(((loc)+2*MAPX > MAPX*MAPY)?SPACE:map[(loc)+2*MAPX]);break;*/
|
||
|
case SouthW: if ((loc+MAPX )> (MAPX*MAPY)) ch=SPACE;
|
||
|
else {
|
||
|
ch=left_obj(loc+1*MAPX);
|
||
|
}
|
||
|
case FWest: if ((loc%MAPX)!=((loc-2)%MAPX)) ch=map[loc-2];
|
||
|
else ch=SPACE ;
|
||
|
break;
|
||
|
/*if ((loc%MAPX)!=(loc-2)%MAPX))) ch=map[(loc)-2];else ch=SPACE;break;*/
|
||
|
default: ch= '?';break;
|
||
|
};
|
||
|
return ch;
|
||
|
}
|
||
|
|
||
|
enum compass revdir(enum compass dir)
|
||
|
{
|
||
|
enum compass rev;
|
||
|
switch(dir) {
|
||
|
case North:rev=South; break;
|
||
|
case East:rev=West; break;
|
||
|
case West:rev=East; break;
|
||
|
case South:rev=North; break;
|
||
|
}
|
||
|
return rev;
|
||
|
}
|
||
|
|
||
|
int onestep(enum compass dir, int loc)
|
||
|
{
|
||
|
int newloc;
|
||
|
switch(dir) {
|
||
|
case North:if ((loc)-Mx < 0) newloc=-1;
|
||
|
else newloc=loc-Mx;
|
||
|
break;
|
||
|
case South:if ((loc)+Mx > Mx*My) newloc=-1;
|
||
|
else newloc=loc+Mx;
|
||
|
break;
|
||
|
case West: if ((loc)%Mx ==0)newloc=-1;
|
||
|
else newloc=loc-1;
|
||
|
break;
|
||
|
case East: if ((loc)%Mx ==Mx-1)newloc=-1;
|
||
|
else newloc=loc+1;
|
||
|
break;
|
||
|
}
|
||
|
return newloc;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
** placemove( move)
|
||
|
** place move is passed the location of the numbers
|
||
|
** that appear on the screen to humans and selects one of these
|
||
|
** locations for the computer players move
|
||
|
*/
|
||
|
|
||
|
char placemove(PLAYER *pla,int *move, COMPANY *co, int turn,char *map, int Difficulty)
|
||
|
{
|
||
|
static int MAPX=Mx ,MAPY=My;
|
||
|
char mvch; long moveval[NUMMOVES]; int neighb[4];int nc, ncom;
|
||
|
enum compass dir;
|
||
|
enum investstatus curst[NUMCO];
|
||
|
double coholdings[NUMCO]; double tothold[NUMCO];int curpl;
|
||
|
char locs[South];
|
||
|
int seed,intchar ,mvop,curCo,curMax,
|
||
|
smallC,bigC; /*this is for the implications of each move */
|
||
|
long bgain,sgain; /* gains in mergers */
|
||
|
|
||
|
mvch= '1';
|
||
|
curMax=-1000;
|
||
|
if ((Difficulty>1 )||(rand()%100>50))
|
||
|
for (curCo=0;curCo<=NUMCO;curCo++){
|
||
|
tothold[curCo] =0;
|
||
|
/*finds the proportion of holdings relativ to other players*/
|
||
|
for (curpl=0;curpl<MAXPLAYERS;curpl++)
|
||
|
tothold[curCo]=pla[curpl].holdings[curCo] +tothold[curCo];
|
||
|
if (tothold[curCo]==0) coholdings[curCo]=0;
|
||
|
else coholdings[curCo]=pla[turn].holdings[curCo]/tothold[curCo];
|
||
|
}
|
||
|
for (curCo=0;curCo<=NUMCO;curCo++){
|
||
|
if (pla[turn].holdings[curCo]>0) curst[curCo]=signif;
|
||
|
else curst[curCo]=none;
|
||
|
}
|
||
|
if ((Difficulty>1 )||(rand()%100>50))
|
||
|
for (mvop=0;mvop<=4;mvop++){
|
||
|
seed=0;
|
||
|
locs[North] = up_obj(move[mvop]);
|
||
|
locs[South] = down_obj(move[mvop]);
|
||
|
locs[West] = left_obj(move[mvop]);
|
||
|
locs[East] = right_obj(move[mvop]);
|
||
|
moveval[mvop]=NEWCOCOST;
|
||
|
ncom=0;
|
||
|
for(dir=North;dir<=South;dir++){
|
||
|
if (locs[dir]== STAR) moveval[mvop]=moveval[mvop]+STARCOST ;
|
||
|
if (locs[dir]== BLACKHOLE) moveval[mvop]=moveval[mvop]+BLACKHOLECOST ;
|
||
|
if (locs[dir]== NEWCO) {
|
||
|
moveval[mvop]=moveval[mvop]+NEWCOCOST ;
|
||
|
}
|
||
|
if iscompany(locs[dir]) {
|
||
|
nc=locs[dir]-'A';
|
||
|
if ((ncom==0)||(nc!=neighb[1])){
|
||
|
ncom++; /* todo add for cases when 3 co ajacent*/
|
||
|
neighb[ncom]= nc;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if (ncom==0) if (moveval[mvop]>100) moveval[mvop]*=FOUNDER_BONUS; else moveval[mvop]=0 ;
|
||
|
/*need to check here whether company eliminated by black hole */
|
||
|
else if (ncom==1){
|
||
|
/*if company next door and holding is minimal then value will be reversed*/
|
||
|
moveval[mvop]=moveval[mvop]*( coholdings[neighb[1]]-0.25)*tothold[ncom];
|
||
|
}
|
||
|
else if (ncom>1){
|
||
|
/*calculate value of merger*/
|
||
|
smallC=1;
|
||
|
bigC=1;
|
||
|
for (curCo=2;curCo<=ncom;curCo++){
|
||
|
if (co[neighb[curCo]].price<co[neighb[smallC]].price)
|
||
|
smallC= curCo;
|
||
|
if (co[neighb[curCo]].price>co[neighb[bigC]].price)
|
||
|
bigC= curCo;
|
||
|
}
|
||
|
sgain=co[neighb[smallC]].price*tothold[neighb[smallC]]*
|
||
|
(coholdings[neighb[smallC]]-0.25)/2;/*bonus*/
|
||
|
sgain+=co[bigC].price*tothold[neighb[smallC]]*
|
||
|
(coholdings[neighb[smallC]]-0.25);
|
||
|
bgain=(moveval[mvop]+co[neighb[smallC]].price)*
|
||
|
tothold[neighb[bigC]]*(coholdings[neighb[bigC]]-0.25);
|
||
|
moveval[mvop]=sgain+bgain;
|
||
|
|
||
|
}
|
||
|
if (moveval[mvop]>curMax) {
|
||
|
intchar= mvop +(int)'1';
|
||
|
mvch= (char) intchar;
|
||
|
curMax=moveval[mvop] ;
|
||
|
}
|
||
|
}
|
||
|
else mvch=1;
|
||
|
return mvch;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
** distantblackhole starts at the black hole, moves two away and then
|
||
|
** looks at the three ajacent points for companies. It then increases
|
||
|
** the risk factor for any companies found. It isn't
|
||
|
** always able to detect when the route between the black hole
|
||
|
** and company is blocked so sometimes sees a risk that isn't there.
|
||
|
*/
|
||
|
|
||
|
void distantblackhole(COMPANY *co,char *map,int loc)
|
||
|
{
|
||
|
int step,cloc;
|
||
|
enum compass dir,end;
|
||
|
int neighb[4], ncom, new,isnew,curCo;
|
||
|
|
||
|
for (dir=North;dir<=South;dir++){
|
||
|
step =onestep(dir, loc);
|
||
|
if (step>-1) if (map[step]==SPACE){
|
||
|
step =onestep(dir, step);
|
||
|
ncom=0;
|
||
|
if (step>-1) if (!(iscompany(map[step]))) for (end=North;end<=South;end++)
|
||
|
if (end!=revdir(dir)){ /*point ajacent bh checked elsewhere*/
|
||
|
cloc= onestep(end, step);
|
||
|
if iscompany(map[cloc]) {
|
||
|
new=map[cloc]-'A';
|
||
|
isnew=1;
|
||
|
curCo=0;
|
||
|
while ((curCo<=ncom) && (isnew==1)){
|
||
|
if (curCo==ncom) /* no need to check if co already found if first */
|
||
|
neighb[curCo]=new;
|
||
|
else if (neighb[curCo]==new) isnew=0;
|
||
|
curCo++;
|
||
|
}
|
||
|
if (isnew==1) {
|
||
|
ncom++;
|
||
|
neighb[curCo]=new;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
curCo=1;
|
||
|
while (curCo<=ncom){
|
||
|
co[neighb[curCo]].risk+= BLACKHOLECOST/2;
|
||
|
curCo++;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
**ai_buy_sell not only choses the most promising investments but
|
||
|
**actually executes the sales
|
||
|
*/
|
||
|
|
||
|
void ai_buy_sell(PLAYER *pl, COMPANY *co, int turn,char *map, int Difficulty)
|
||
|
{
|
||
|
int curloc,curCo,BuyCo,SecCo,amt ;
|
||
|
static int MAPX=Mx , MAPY=My;
|
||
|
int localpt, localris;
|
||
|
enum compass dir;
|
||
|
int neighb[4];
|
||
|
int nc, ncom,smallC,bigC;
|
||
|
char locs[South];
|
||
|
|
||
|
for(curCo=0;curCo<NUMCO;curCo++) { /*reset values*/
|
||
|
co[curCo].potential=co[curCo].risk=0;
|
||
|
}
|
||
|
for (curloc=1;curloc<=(Mx*My);curloc++){
|
||
|
if (map[curloc]==BLACKHOLE) distantblackhole(co,map,curloc);
|
||
|
if (map[curloc]==SPACE);{
|
||
|
locs[North] = up_obj(curloc); /*get the chars of the 4 ajacent points*/
|
||
|
locs[South] = down_obj(curloc);
|
||
|
locs[West] = left_obj(curloc);
|
||
|
locs[East] = right_obj(curloc);
|
||
|
localris=0;
|
||
|
localpt=NEWCOCOST; /*the gain from the point itself */
|
||
|
for(dir=North;dir<=South;dir++){
|
||
|
if (locs[dir]== STAR) localpt=localpt+STARCOST ;/*First check the value or */
|
||
|
if ((Difficulty>2 )||(rand()%100>50))
|
||
|
if (locs[dir]== BLACKHOLE)
|
||
|
localris=localris+BLACKHOLECOST;/*loss from the point*/
|
||
|
if (locs[dir]== NEWCO) localpt=localpt+NEWCOCOST ;
|
||
|
}
|
||
|
}
|
||
|
ncom=0;
|
||
|
for(dir=North;dir<=South;dir++){ /*Then add those values to the potetial of companies*/
|
||
|
if (iscompany(locs[dir])) { /*that are in the vicinity */
|
||
|
nc=locs[dir]-'A';
|
||
|
if ((ncom==0)||(nc!=neighb[1])){
|
||
|
ncom++; /* todo add for cases when 3 co ajacent*/
|
||
|
neighb[ncom]= nc;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if (ncom==1 ){
|
||
|
co[nc].potential=co[nc].potential+localpt;
|
||
|
co[nc].risk=co[nc].risk+localris;
|
||
|
}
|
||
|
else if (ncom>1){
|
||
|
smallC=1;
|
||
|
bigC=1;
|
||
|
for (curCo=2;curCo<=ncom;curCo++){
|
||
|
if (co[neighb[curCo]].price<co[neighb[smallC]].price)
|
||
|
smallC= curCo;
|
||
|
if (co[neighb[curCo]].price>co[neighb[bigC]].price)
|
||
|
bigC= curCo;
|
||
|
}
|
||
|
/* bonus ignored for now*/
|
||
|
co[neighb[bigC]].potential=co[neighb[bigC]].potential+(co[neighb[smallC]].price);
|
||
|
co[neighb[smallC]].potential=co[neighb[smallC]].potential+co[neighb[bigC]].price/2;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
SecCo=BuyCo=-1 ;
|
||
|
for(curCo=0;curCo<NUMCO;curCo++) /*Find the co with greatest potential*/
|
||
|
if (co[curCo].price>0) {
|
||
|
co[curCo].netpot=(co[curCo].potential+co[curCo].risk)/
|
||
|
(co[curCo].price);
|
||
|
if ((co[curCo].netpot>0)){
|
||
|
if (BuyCo==-1) BuyCo=curCo;
|
||
|
else {
|
||
|
if (co[curCo].netpot>co[BuyCo].netpot) {
|
||
|
SecCo=BuyCo;
|
||
|
BuyCo=curCo;
|
||
|
}
|
||
|
else {
|
||
|
if (SecCo==-1) SecCo=curCo;
|
||
|
else if (co[curCo].netpot>co[SecCo].netpot)
|
||
|
SecCo=curCo;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
if ((Difficulty>2 )||((Difficulty>1 )&&(rand()%100>50)) )
|
||
|
for(curCo=0;curCo<NUMCO;curCo++){ /*sell all*/
|
||
|
pl[turn].cash += pl[turn].holdings[curCo]*co[curCo].price;
|
||
|
pl[turn].holdings[curCo]=0;
|
||
|
}
|
||
|
if (BuyCo>-1){ /*now buy shares in the company with the best prospects*/
|
||
|
if (co[BuyCo].risk<0) { /*don't put all your eggs into one basket*/
|
||
|
amt= pl[turn].cash/(co[BuyCo].price*2);
|
||
|
pl[turn].cash += (-amt * co[BuyCo].price);
|
||
|
pl[turn].holdings[BuyCo] += amt;
|
||
|
if (SecCo>-1){
|
||
|
amt= pl[turn].cash/co[SecCo].price;
|
||
|
pl[turn].cash += (-amt * co[SecCo].price);
|
||
|
pl[turn].holdings[SecCo] += amt;
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
amt= pl[turn].cash/co[BuyCo].price;
|
||
|
pl[turn].cash += (-amt * co[BuyCo].price);
|
||
|
pl[turn].holdings[BuyCo] += amt;
|
||
|
}
|
||
|
}
|
||
|
}
|