#!/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 (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; }



/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 excess civs out of over populated sectors into nearest working
#	sector.
#
function cmvr1(sect,  i,mob,amt,srcmob,dest)
{
  if (val[sect,"*"]=="." && (work==0 || val[sect,"work"]==100) && val[sect,"des"]!="w" && !(sect in plague) && val[sect,"civ"]>safeciv)  {

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

    do {
      
      dest=sect;
      srcmob=9999;
      amt = val[sect,"civ"]-safeciv;
      for (i in cost) {
	if (val[i,"civ"] <= safeciv-amt && val[i,"*"]==".") {
	  
	  if (val[i,"eff"]<100 || val[sect,"sdes"]!="_" || match(val[i,"des"],"[mgouajklt%idh\*!]")) {
	    
	    eat=etu*eatrate*(val[i,"mil"]+val[i,"civ"]+val[i,"uw"]+amt);
	    eat+=etu*babyeat*(uwbrate*val[i,"uw"] + obrate*(val[i,"civ"]+amt));
	    if (eat<1) eat=1;
	    if (val[i,"food"] > eat || nofood) {
	      mob = cost[i]*amt;
	      if ((mob<val[sect,"mob"] || mob==0) && mob<srcmob && val[sect,"des"]!="w" && (work==0 || val[i,"work"]==100)) {
		dest=i; srcmob=mob;
	      }
	    }
	  }
	}
	if (dest!=sect && amt>=1 && !(dest in plague)) {
	  printf("mov c %s %d %s\n",sect,amt,dest);
	  val[sect,"civ"] -= amt;
	  val[dest,"civ"] += amt;
	  val[sect,"mob"] -= srcmob;
	  return 1;
	}
	delete cost[dest];
      }
    } while (val[sect,"civ"]>safeciv && val[sect,"mob"]>0 && dest!=sect);
  }
  return 0;
}

#
#	Push excess civs out of over populated sectors into nearest working
#	sector.
#
function cmvr2(sect,  i,mob,amt,srcmob,dest)
{

  if (val[sect,"*"]=="." && (work==0 || val[sect,"work"]==100) && val[sect,"des"]!="w" && !(sect in plague) && val[sect,"civ"]>safeciv)  {

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

    do {
      
      dest=sect;
      srcmob=9999;
      amt = val[sect,"civ"]-safeciv;
      for (i in cost) {
	if (val[i,"civ"] <= safeciv-amt && val[i,"*"]==".") {
	  
	  eat=etu*eatrate*(val[i,"mil"]+val[i,"civ"]+val[i,"uw"]+amt);
	  eat+=etu*babyeat*(uwbrate*val[i,"uw"] + obrate*(val[i,"civ"]+amt));
	  if (eat<1) eat=1;
	  if (val[i,"food"] > eat || nofood) {
	    mob = cost[i]*amt;
	    if ((mob<val[sect,"mob"] || mob==0) && mob<srcmob && val[sect,"des"]!="w" && (work==0 || val[i,"work"]==100)) {
	      dest=i; srcmob=mob;
	    }
	  }
	}
	if (dest!=sect && amt>=1 && !(dest in plague)) {
	  printf("mov c %s %d %s\n",sect,amt,dest);
	  val[sect,"civ"] -= amt;
	  val[dest,"civ"] += amt;
	  val[sect,"mob"] -= srcmob;
	  return 1;
	}
	delete cost[dest];
      }
    } while (val[sect,"civ"]>safeciv && val[sect,"mob"]>0 && dest!=sect);
  }
  return 0;
}

#
#	Try to push civs from non-working sectors into working sectors.
#	Always leave one civ.  (Non-working sectors are +,b without 5 dust,
#	),
#
function cmvr3(sect,  i,mob,amt,srcmob,dest,added)
{

  if (val[sect,"eff"]==100 && val[sect,"sdes"]=="_" && match(val[sect,"des"],"[+=b)f]") && val[sect,"*"]=="." && (work==0 || val[sect,"work"]==100) && !(sect in plague) && val[sect,"des"]!="w") {
    if (val[sect,"des"]!="b" || val[sect,"dust"]<5) {


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

      do {

	dest=sect;
	srcmob=9999;
	for (i in cost) {
	  if (val[i,"civ"] <= safeciv-amt && val[i,"*"]==".") {

	    if (val[i,"eff"]==100 && val[i,"sdes"]=="_" && match(val[i,"des"],"[+=)f]")) continue;
	    if (val[i,"des"]=="b" && val[i,"dust"]<5) continue;

	    amt=safeciv-val[i,"civ"];
	    if (amt>=val[sect,"civ"]) amt=val[sect,"civ"]-1;

	    if (amt>=1) {
	      eat=etu*eatrate*(val[i,"mil"]+val[i,"civ"]+val[i,"uw"]+amt);
	      eat+=etu*babyeat*(uwbrate*val[i,"uw"] + obrate*(val[i,"civ"]+amt));
	      if (eat<1) eat=1;
	      if (val[i,"food"] > eat || nofood) {
		mob = cost[i]*amt;
		if (mob<srcmob && (work==0 || val[i,"work"]==100)) {
		  dest=i;
		  srcmob=mob;
		  movamt=amt;
		}
	      }
	    }
	  }
	}
	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,"civ"] -= movamt;
	  val[dest,"civ"] += movamt;
	  val[sect,"mob"] -= srcmob;
	  while (movamt>500) {
	    printf("mov c %s %d %s\n",sect,500,dest);
	    movamt-=500;
	  }
	  printf("mov c %s %d %s\n",sect,movamt,dest);
	}
	delete cost[dest];
      } while (dest!=sect && val[sect,"civ"]>1 && 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 to working sectors\n");
    for (sect in sects) cmvr1(sect);
  }
  if (stage=="" || stage==2) {
    printf("# pass 2 - excess to unpopulated sectors\n");
    for (sect in sects) cmvr2(sect);
  }
  if (stage=="" || stage==3) {
    printf("# pass 3 - nonworking sectors to working sectors\n");
    for (sect in sects) cmvr3(sect);
  }
}

