#!/bin/awk -f
#
#	fmvr [minmob=value] [partial=1] [feedall=1] [warehouse=1] [file1] [file2]
#
#	Reads dump file as input (other files are optional).  Moves food from
#	non-warehouse sectors to sectors without enough food (handles more
#	than just starvation- does babies as well).  Won't push a sector
#	below minmob.
#
#		-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=",";
  if (etu=="") etu=60;
  if (myno=="") myno=82;
  if (myname=="") myname="harmless";
  if (minmob=="") minmob=etu;


  mode="";
  stat=0;

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

  weight["civ"]=1;	packing["civ"]=1;
  weight["mil"]=1;	packing["mil"]=1;
  weight["uw"]=2;	packing["uw"]=1;
  weight["food"]=1;	packing["food"]=1;
  weight["shell"]=1;	packing["shell"]=1;
  weight["gun"]=1;	packing["gun"]=1;
  weight["pet"]=1;	packing["pet"]=1;
  weight["iron"]=1;	packing["iron"]=1;
  weight["dust"]=1;	packing["dust"]=1;
  weight["bar"]=1;	packing["bar"]=1;
  weight["oil"]=1;	packing["oil"]=1;
  weight["lcm"]=1;	packing["lcm"]=1;
  weight["hcm"]=1;	packing["hcm"]=1;
  weight["rad"]=1;	packing["rad"]=1;
}



#
#	Single line parsings
#

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

/Max safe population for civs\/uws:/ { 
  split($6,a,"/");
  maxciv=a[1];
  maxuw=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)
{
  des=val[sect,"des"];
  return (0.002 * (200-val[sect,"eff"]-100*(des=="+" || des=="=") + 200*(des=="-") + 12700*(des=="^")));
}


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

  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_1(cost);
}

function buildcost(sect,cost,  i)
{
  cost[sect]=movcost(sect);
  buildcost_1(cost);
}

function feed(sect,food,  i,mob,amt,srcmob)
{
  for (i in cost) delete cost[i];
  buildcost(sect,cost);


  source=sect;
  srcmob=9999;
  amt = -food[sect];
  for (i in cost) {
    if ( food[i] > amt && val[i,"*"]=="." && (warehouse || val[i,"des"]!="w") && !(i in plague)) {
      if (food[i]<amt) amt=food[i];
      mob = cost[i]-movcost(i);
      if (val[i,"des"]=="w" && val[i,"eff"]>60) mob /= 10;
      mob *= amt;
      if (mob<val[i,"mob"] && mob<srcmob) { source=i; srcmob=mob; }
    }
  }
  if (source==sect) {
    for (i in cost) {
      if ( food[i] >= 1 && val[i,"*"]=="." && (warehouse || val[i,"des"]!="w") && !(i in plague)) {
	mob = cost[i]-movcost(i);
	if (val[i,"des"]=="w" && val[i,"eff"]>60) mob /= 10;
	if (val[i,"mob"]>1) { source=i; srcmob=mob; }
      }
    }
    amt=food[source];
    srcmob *= amt;
    if (srcmob>val[source,"mob"]) {
      amt *= val[source,"mob"]/srcmob;
      srcmob=val[source,"mob"];
    }
  }

  if (source!=sect) {
    printf("mov f %s %d %s\n",source,amt,sect);
    food[sect] += amt;
    food[source]-=amt;
    val[source,"mob"] -= srcmob;
    if (food[sect]<0) return 0;
    return 1;
  } else {
    return -1;
  }
}


#
#	What to do at the end
#

END {

  for (sect in sects) {
    if (val[sect,"des"]!="^") {
      food[sect]=val[sect,"food"];
      if (val[sect,"civ"]+val[sect,"mil"]+val[sect,"uw"] > 5) {
	food[sect]-=eatrate*etu*(val[sect,"civ"]+val[sect,"mil"]+val[sect,"uw"]);
	food[sect]-=babyeat*etu*(val[sect,"civ"]*obrate+val[sect,"uw"]*uwbrate);
	food[sect]= int(food[sect]-0.9999);
      }
      val[sect,"mob"] -= minmob;
    }
  }


  for (sect in food) {
    if (food[sect]<0) {
      if (feedall || val[sect,"*"]=="." || val[sect,"uw"]>0) {
	do {
	  printf("# %s needs %d food\n",sect,-food[sect]);
	} while  (food[sect]<0 && !feed(sect,food));
      }
    }
  }

}
