#!/bin/awk -f
#
#	cmvr [ -v variable=value] [file1] [file2]
#
#	Based on simu.  Doesn't move into or out of plague sectors or sectors
#	with unrest.
#
#	   Delete all the lines before #!/bin/awk, and save as cmvr.  Then
#		chmod +x mmvr
#	   Create a file or files with the output of dump and the 
#	   production report
#		dump * >empire.data
#		read y >prod.report
#	   Then feed the file(s) into mmvr
#		cmvr empire.data prod.report
#
#
#	Supports the following input forms:
#		dump		nearly any version, deity or player
#		prod report	any version?  player only
#
#	Variables:		(see BEGIN for defaults)
#		etu		number of etus
#		myno		Country number
#		myname		Country name	(can obtain from nation)
#		
#
#
#		-harmless


function num(str) {
  return substr(str,1,length(str)-1);
}


#
#	Turns a letter into a name.  Tries the first letter, if that
#	isn't a commodity, tries the last letter (useful for ship, unit,
#	and prod outputs)
#
function commstr(str) {
  char=substr(str,1,1);
  if (char == "c") return "civ";
  if (char == "m") return "mil";
  if (char == "u") return "uw";
  if (char == "f") return "food";
  if (char == "s") return "shell";
  if (char == "g") return "gun";
  if (char == "p") return "pet";
  if (char == "i") return "iron";
  if (char == "d") return "dust";
  if (char == "b") return "bar";
  if (char == "o") return "oil";
  if (char == "l") return "lcm";
  if (char == "h") return "hcm";
  if (char == "r") return "rad";

  char=substr(str,length(str),1);
  if (char == "c") return "civ";
  if (char == "m") return "mil";
  if (char == "u") return "uw";
  if (char == "f") return "food";
  if (char == "s") return "shell";
  if (char == "g") return "gun";
  if (char == "p") return "pet";
  if (char == "i") return "iron";
  if (char == "d") return "dust";
  if (char == "b") return "bar";
  if (char == "o") return "oil";
  if (char == "l") return "lcm";
  if (char == "h") return "hcm";
  if (char == "r") return "rad";


  return "";
}




BEGIN {
  SUBSEP=",";
  mode="";
  stat=0;

  eatrate = 0.001;
  babyeat = 0.012;
  obrate  = 0.005;
  uwbrate = 0.0025;

  if (etu=="") etu=16;
  if (myno=="") myno=41;
  if (myname=="") myname="harmless";
  if (minmob=="") minmob=etu;
  if (minmil=="") minmil=5;
  if (maxpop=="") maxpop = 999;
  if (safeciv=="") safeciv = (maxpop * 100 ) / (100 + etu/2);
  if (safeuw=="") safeuw = (maxpop * 100 ) / (100 + etu/4);
  if (work=="") work=1;

  weight["civ"]=1;	packing["civ"]=1;
  weight["mil"]=1;	packing["mil"]=1;
}



#
#	Single line parsings
#
/^PLAGUE deaths reported in/ { plague[$5]=1; next; }
/^Outbreak of PLAGUE in/ { plague[substr($5,1,length($5)-1)]=1; next; }
/^.*,.* battling PLAGUE/ { plague[$1]=1; next; }

/^Civil unrest in / { unrest[substr($4,1,length($4)-1)]=1; next; }
/^Production .* disrupted by terrorists in/ { che[$7]=1; next; }
/^Guerrilla warfare in/ { che[$4]=1; }



/Technology\.* * [0-9.]+ * Research\.* [0-9.]+/ {
  tech = $2;
  research= $4;
  next;
}

/Education\.* * [0-9.]+ * Happiness\.* [0-9.]+/ {
  edu = $2;
  happiness= $4;
  next;
}

/Max population : [0-9]+/ { maxpop = $2 };

/Max safe population for civs\/uws:/ { 
  split($6,a,"/");
  safeciv=a[1];
  safeuw=a[2];
  next;
}

#
#	Version parsing stuff
#
/An update consists of [0-9]+ empire time units/ {  etu = $5; next; }
/No food is needed!!/ { nofood=1; next; }
/1000 civilians will give birth to [0-9\.]+ babies per etu./ { 
  obrate = $7/1000; next;
}
/In one time unit, 1000 people eat [0-9\.]+ units of food./ {
  eatrate = $8/1000; next;
}
/1000 babies eat [0-9\.]+ units of food becoming adults./ {
  babyeat = $4/1000; next;
}
/1000 uncompensated workers will give birth to [0-9\.]+ babies./ {
  uwbrate = $8/1000; next;
}




#
#	Headers for various multi-line read modes
#
/ *DUMP SECTOR/ { mode="dump"; stat=1; next; }
/CENSUS/ { mode="census"; stat=1; next; }
/COMMODITIES/ { mode="comm"; stat=1; next; }
/PRODUCTION/ {mode="prod"; stat=1; next; }
/EFFICIENCY SIMULATION/ {mode="neweff"; stat=1; next; }



#
#	Parse continuation lines of multi-line modes
#
{

  if (mode=="dump") {
    if (stat==1) {
      for (i=1;i<=NF;i++) header[i]=$i;
      nheader=NF;
      stat=2;
      dsects=0;
      for (i in order) delete order[i];
      next;
    } else if (NF != nheader) {
      stat=0;
      mode="";
    } else if (stat==2) {
      for (i=1;i<=nheader;i++) temp[header[i]] = $i;
      sect = temp["x"] "," temp["y"];
      if (temp["own"]=="") temp["own"]=myno;
      for (i=1;i<=nheader;i++) val[sect,header[i]] = $i;
      sects[sect]="1";
      order[dsects++] = sect;
      next;
    }
    mode="";
  } else if (mode=="neweff") {
    if (stat==1) {
      if ($0=="   sect  des    projected eff") {
	stat=2;
	next;
      }
    } else if (stat==2 && NF==3) {
      newdes[$1]=$2;
      neweff[$1]=num($3);
      sects[$1]="1";
      next;
    }
    mode="";
  } else if (mode="prod") {
    if (stat==1) {
      if ($0 =="   sect  des eff wkfc will make- p.e. cost  use1 use2 use3  max1 max2 max3  max" ) {
	stat=2;
	next;
      } else {
	mode="";
      }
    } else if (stat==2 && NF >2) {
      sect=$1;
      sects[sect]="1";
      newdes[sect]=$2;
      will[sect]=$5;
      make[sect]=$6;
      max[sect]=$(NF);
      if (NF==11) {
	use1[sect]=commstr($9);
	amt1[sect]=num($9);
	max1[sect]=num($10);
      } else if (NF==13) {
	use1[sect]=commstr($9);
	amt1[sect]=num($9);
	max1[sect]=num($11);
	use2[sect]=commstr($10);
	amt2[sect]=num($10);
	max2[sect]=num($12);
      } else if (NF==15) {
	use1[sect]=commstr($9);
	amt1[sect]=num($9);
	max1[sect]=num($12);
	use2[sect]=commstr($10);
	amt2[sect]=num($10);
	max2[sect]=num($13);
	use3[sect]=commstr($11);
	amt3[sect]=num($1);
	max3[sect]=num($14);
      }
      next;
    } else {
      mode="";
      stat=0;
    }
  }
}



#
#	Subroutines that may be used by END {}
#


#  cost to move through sector sect (des is local)
function movcost(sect,des,  mobility)
{
  des=val[sect,"des"];
  mobility = (0.002 * (200-val[sect,"eff"]-100*(des=="+" || des=="=") + 200*(des=="-") + 12700*(des=="^")));
  if (mobility<0.01) mobility=0.01;
  return mobility;
}


# create an array cost[] with movement costs to sect
function buildcost(cost,  added,i,x,y,tmp)
{

  added=0;
  for (i in sects) if (!(i in cost)) {
    x=val[i,"x"];
    y=val[i,"y"];

    bestcost=9999;

    tmp= (x+2) "," y;
    if ((tmp in cost) && cost[tmp]<bestcost) bestcost=cost[tmp]; 
    tmp= (x-2) "," y;
    if ((tmp in cost) && cost[tmp]<bestcost) bestcost=cost[tmp]; 
    tmp= (x+1) "," (y+1);
    if ((tmp in cost) && cost[tmp]<bestcost) bestcost=cost[tmp]; 
    tmp= (x+1) "," (y-1);
    if ((tmp in cost) && cost[tmp]<bestcost) bestcost=cost[tmp]; 
    tmp= (x-1) "," (y+1);
    if ((tmp in cost) && cost[tmp]<bestcost) bestcost=cost[tmp]; 
    tmp= ((x-1) "," (y-1));
    if ((tmp in cost) && cost[tmp]<bestcost) bestcost=cost[tmp]; 

    if (bestcost<9999) { cost[i]=bestcost+movcost(i); added++; }
  }

  if (added) buildcost(cost);
}


#
#	Push from normal overgarrisoned sectors to sectors with che, etc..
#
function mmvr1(sect,  i,mob,amt,srcmob,dest,added,civ,movamt)
{
  civ=val[sect,"civ"]*(1+obrate*etu);
  if (civ>maxpop) civ=maxpop;

  if (val[sect,"*"]=="." && !(sect in plague) && !(sect in che) && val[sect,"mil"]>int(civ/20) && !match(val[sect,"des"],"[f*h!]")) {

    excess = val[sect,"mil"]-int(civ/20);
    if (val[sect,"mil"]-excess < minmil) excess=val[sect,"mil"]-minmil;
    if (val[sect,"sdes"]=="e" || (val[sect,"des"]=="e" && val[sect,"sdes"]=="_"))
      excess = val[sect,"mil"] - int((10*civ - 10*etu)/(20+etu));


    for (i in sects) delete cost[i];
    cost[sect]=0;
    buildcost(cost);

    do {

      dest=sect;
      srcmob=9999;
      for (i in cost) {
	if ((i in che) || (i in unrest) || val[sect,"*"]=="*" || val[i,"work"]<100) {
	  civ=val[i,"civ"]*(1+obrate*etu);
	  if (civ>maxpop) civ=maxpop;
	  amt=int(val[i,"civ"]/20);
	  if ((i in che) || val[i,"*"]=="*") amt=int(val[i,"civ"]/10);
	  amt -= val[i,"mil"];

	  if (amt>=1) {
	    if (cost[i]<srcmob) {
	      dest=i;
	      srcmob=cost[i];
	      movamt=amt;
	      if (movamt > excess) movamt=excess;
	    }
	  }
	}
      }
      srcmob *= movamt;
      if (srcmob>val[sect,"mob"]) {
	movamt = int(movamt*val[sect,"mob"]/srcmob);
	srcmob=val[sect,"mob"];
      }

      if (dest!=sect && movamt>=1 && !(dest in plague)) {
	val[sect,"mil"] -= movamt;
	val[dest,"mil"] += movamt;
	val[sect,"mob"] -= srcmob;
	excess -= movamt;
	printf("mov m %s %d %s\n",sect,movamt,dest);
      }
      delete cost[dest];
    } while (dest!=sect && excess>0 && val[sect,"mob"]>0) ;
  }

  return 0;
}

#
#	Push from overgarrisoned sectors to other sectors
#
function mmvr2(sect,  i,mob,amt,srcmob,dest,added,civ,movamt)
{
  civ=val[sect,"civ"]*(1+obrate*etu);
  if (civ>maxpop) civ=maxpop;

  if (val[sect,"*"]=="." && !(sect in plague) && !(sect in che) && val[sect,"mil"]>int(civ/20) && !match(val[sect,"des"],"[f*h!]")) {

    excess = val[sect,"mil"]-int(civ/20);
    if (val[sect,"mil"]-excess < minmil) excess=val[sect,"mil"]-minmil;
    if (val[sect,"sdes"]=="e" || (val[sect,"des"]=="e" && val[sect,"sdes"]=="_"))
      excess = val[sect,"mil"] - int((10*civ - 10*etu)/(20+etu));

      
    for (i in sects) delete cost[i];
    cost[sect]=0;
    buildcost(cost);

    do {

      dest=sect;
      srcmob=9999;
      for (i in cost) {
	civ=val[i,"civ"]*(1+obrate*etu);
	if (civ>maxpop) civ=maxpop;
	amt=int(val[i,"civ"]/20);
	if ((i in che) || val[i,"*"]=="*") amt=int(val[i,"civ"]/10);
	amt -= val[i,"mil"];

	if (amt>=1) {
	  if (cost[i]<srcmob) {
	    dest=i;
	    srcmob=cost[i];
	    movamt=amt;
	    if (movamt > excess) movamt=excess;
	  }
	}
      }
      srcmob *= movamt;
      if (srcmob>val[sect,"mob"]) {
	movamt = int(movamt*val[sect,"mob"]/srcmob);
	srcmob=val[sect,"mob"];
      }

      if (dest!=sect && movamt>=1 && !(dest in plague)) {
	val[sect,"mil"] -= movamt;
	val[dest,"mil"] += movamt;
	val[sect,"mob"] -= srcmob;
	excess -= movamt;
	printf("mov m %s %d %s\n",sect,movamt,dest);
      }
      delete cost[dest];
    } while (dest!=sect && excess>0 && val[sect,"mob"]>0) ;
  }

  return 0;
}


#
#	mov excess mil from enlistment centers to sects with .4 mob per mil
#
function mmvr3(sect,  i,mob,amt,srcmob,dest,added,civ,movamt)
{

  if (val[sect,"sdes"]=="e" || (val[sect,"des"]=="e" && val[sect,"sdes"]=="_")) {

    excess = int(val[sect,"mil"] - int((10*civ - 10*etu)/(20+etu)));
    
    if (excess>0) {

      for (i in sects) delete cost[i];
      cost[sect]=0;
      buildcost(cost);

      do {

	dest=sect;
	srcmob=9999;
	for (i in cost) {  
	  amt = val[i,"mob"]/0.4 - val[i,"mil"];
	  if (amt>=1) {
	    if (cost[i]<srcmob) {
	      dest=i;
	      srcmob=cost[i];
	      movamt=amt;
	      if (movamt > excess) movamt=excess;
	    }
	  }
	}
	srcmob *= movamt;
	if (srcmob>val[sect,"mob"]) {
	  movamt = int(movamt*val[sect,"mob"]/srcmob);
	  srcmob=val[sect,"mob"];
	}

	if (dest!=sect && movamt>=1 && !(dest in plague)) {
	  val[sect,"mil"] -= movamt;
	  val[dest,"mil"] += movamt;
	  val[sect,"mob"] -= srcmob;
	  excess -= movamt;
	  printf("mov m %s %d %s\n",sect,movamt,dest);
	}
	delete cost[dest];
      } while (dest!=sect && excess>0 && val[sect,"mob"]>0) ;
    }
  }
  return 0;
}


#
#	What to do at the end
#

END {

  for (sect in sects) val[sect,"mob"] -= minmob;

  if (stage=="" || stage==1) {
    printf("# pass 1 - excess mil from enlistment centers\n");
    for (sect in sects) mmvr3(sect);
  }
  if (stage=="" || stage==2) {
    printf("# pass 2 - excess mil to rebellious sectors\n");
    for (sect in sects) mmvr1(sect);
  }
  if (stage=="" || stage==3) {
    printf("# pass 3 - excess mil to all sectors\n");
    for (sect in sects) mmvr2(sect);
  }
}

