#!/bin/awk -f # # simu [ -v variable=value] [file1] [file2] # # Basic tool to read and summarize empire data. Based on awk, so # variables may be set. See below for details on which variables # are important. stdin is read after the last file if it is not a tty. # # Delete all the lines before #!/bin/awk, and save as simu. Then # chmod +x simu # # Sun users may need to replace /bin/awk with /bin/nawk. # /usr/local/bin/gawk should also work if that is available. # a2p has been used to create a perl version of simu in the past. # # Create a file or files with the output of nation, dump, neweff, # and prod. # nation >empire.data # prod * >>empire.data # neweff * >>empire.data # dump * >>empire.data # Then feed the file(s) into simu # simu empire.data # # # Supports the following input forms: # dump nearly any version, deity or player # neweff Chainsaw 3.0 # prod any version? player only # show ship Chainsaw 3.2 # ship Chainsaw 3.2 # cargo Chainsaw 3.2 # bestpath Chainsaw 3.0 # nation any? Reads tech/research/edu/happy # version Chainsaw 3.0. any? # # Variables: (see BEGIN for defaults) # etu number of etus # myno Country number # myname Country name (can obtain from nation) # showall show all sectors, not just interesting ones # # Many thanks to Felix Lee for fixes, fixes, and more fixes. # # -the harmless conquerer (dld@chem.psu.edu) 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 ""; } # # Truncate starting and ending whitespace from a string # function truncwhite(str) { for (i=1;i<=length(str) && substr(str,i,1) ~ "[ \t\r]";i++) ; for (j=length(str);j>i && substr(str,j,1) ~ "[ \t\r]";j--) ; return (substr(str,i,j-i+1)); } BEGIN { SUBSEP=","; if (etu=="") etu=60; if (myno=="") myno=41; if (myname=="") myname="harmless"; if (showall=="") showall=0; mode=""; stat=0; shipnamlen=16; eatrate = 0.001; babyeat = 0.012; obrate = 0.005; uwbrate = 0.0025; easytech= 1.0; logtech = 1.55; if (maxpop=="") maxpop=550; if (maxciv=="") maxciv=maxpop/(1+etu*obrate); if (maxuw=="") maxuw=maxpop/(1+etu*uwbrate); weight["civ"]=1; packing["civ"]=1; weight["mil"]=1; packing["mil"]=1; weight["uw"]=2; packing["uw"]=1; weight["food"]=1; packing["food"]=10; weight["shell"]=1; packing["shell"]=10; weight["gun"]=10; packing["gun"]=10; weight["pet"]=1; packing["pet"]=10; weight["iron"]=1; packing["iron"]=10; weight["dust"]=5; packing["dust"]=10; weight["bar"]=50; packing["bar"]=5; weight["oil"]=1; packing["oil"]=10; weight["lcm"]=1; packing["lcm"]=10; weight["hcm"]=1; packing["hcm"]=10; weight["rad"]=8; packing["rad"]=10; } # # Single line parsings # /Technology\.* * .+ * Research\.* .+/ { tech = $2; research= $4; next; } /Education\.* * .+ * Happiness\.* .+/ { edu = $2; happiness= $4; next; } /Max population : [0-9]+/ { maxpop = $4 } /Max safe population for civs\/uws:/ { split($6,a,"/"); maxciv=a[1]; maxuw=a[2]; next; } /^\[fert:[0-9]+\]/ { for (i=4;i<=NF;i++) if ($i=="@") break; fert[$(i+1)]=substr($1,7,length($1)-7); } /^\[oil:[0-9]+\]/ { for (i=4;i<=NF;i++) if ($i=="@") break; oil[$(i+1)]=substr($1,6,length($1)-6); } /Best path from .* to .* is .* \(cost .*\)/ { bestpath[$4,$6]=substr($10,1,length($10)-1); } # # 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; } /Tech Buildup is limited to logarithmic growth \(base .+\) after .+/ { easytech = substr($11,1,length($11)-1); logtech = $9; } /Max mob gain per update/ { secmob = $6; maxshipmob = $7; planemob = $8; unitmob = $9; } /Max eff gain per update/ { if ($6>0) seceff = $6; if ($7>0) maxshipeff = $7; if ($8>0) planeeff = $8; if ($9>0) uniteff = $9; } / NEWFISH,/ { newfish=1; } # # 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; } / *lcm hcm avail tech \$/ {mode="ssb"; stat=1; next;} / *cargos & capabilities/ {mode="ssc"; stat=1; next; } / *def d s y g r d n l l/ {mode="sss"; stat=1; next; } /^shp# ship type x,y fl eff civ mil uw fd pn he xl ln mob fuel tech/ { mode="ship"; stat=1; next; } /^shp# ship type x,y flt eff sh gun pet irn dst bar oil lcm hcm rad/ { mode="cargo"; 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; if (make[sect]=="bars") make[sect]="bar"; if (make[sect]=="guns") make[sect]="gun"; 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($11); max3[sect]=num($14); } next; } else { mode=""; stat=0; } } else if (mode=="ssb") { if (stat==1 && NF > 5) { name=truncwhite(substr($0,1,shipnamlen)); shipcost[name]=substr($NF,2); shiptech[name]=$(NF-1); shipavail[name]=$(NF-2); shiplcm[name]=$(NF-3); shiphcm[name]=$(NF-4); next; } else { mode=""; stat=0; next; } } else if (mode=="sss") { if (stat==1 && NF > 11) { name=truncwhite(substr($0,1,shipnamlen)); nf=split(substr($0,21),a,"[ \t/]*"); shipdef[name]=a[2]; shipspd[name]=a[3]; shipvis[name]=a[4]; shipspy[name]=a[5]; shiprng[name]=a[6]; shipfir[name]=a[7]; shiplnd[name]=a[8]; shippln[name]=a[9]; shiphel[name]=a[10]; shipxpl[name]=a[11]; shipfc[name]=a[12]; shipfu[name]=a[13]; next; } else { mode=""; stat=0; next; } } else if (mode=="ssc") { if (stat==1 && NF > 3) { name=truncwhite(substr($0,1,shipnamlen)); nf=split(substr($0,21),a,"[ \t/]*"); for (i=2;i<=nf;i++) { if (a[i] ~ "[0-9]+[cmufsgpidbolhr]") { shipcargo[name,commstr(a[i])] = num(a[i]); } else { shipcap[name] = shipcap[name] " " a[i]; } } next; } else { mode=""; stat=0; next; } } else if (mode=="ship") { if (stat==1) { ships=0; for (i in shiporder) delete shiporder[i]; stat=2; } if (stat==2 && NF > 11) { name=truncwhite(substr($0,6,shipnamlen)); shiporder[ships++]=$1; shipname[$1]=name; shiptech[$1]=$NF; shipfuel[$1]=$(NF-1); shipmob[$1]=$(NF-2); shiplnd[$1]=$(NF-3); shipxlnd[$1]=$(NF-4); shiphel[$1]=$(NF-5); shippln[$1]=$(NF-6); shipcargo[$1,"food"]=$(NF-7); shipcargo[$1,"uw"]=$(NF-8); shipcargo[$1,"mil"]=$(NF-9); shipcargo[$1,"civ"]=$(NF-10); shipeff[$1]=num($(NF-11)); shipfleet[$1]=substr($0,33,1); shipsect[$1]=truncwhite(substr($0,22,9)); next; } else if ($0 ~ /^ * /) { next; } else { mode=""; stat=0; next; } } else if (mode=="cargo") { if (stat==1) { ships=0; for (i in shiporder) delete shiporder[i]; stat=2; } if (stat==2 && NF > 10) { name=truncwhite(substr($0,6,shipnamlen)); shiporder[ships++]=$1; shipname[$1]=name; shipcargo[$1,"rad"]=$NF; shipcargo[$1,"hcm"]=$(NF-1); shipcargo[$1,"lcm"]=$(NF-2); shipcargo[$1,"oil"]=$(NF-3); shipcargo[$1,"bar"]=$(NF-4); shipcargo[$1,"dust"]=$(NF-5); shipcargo[$1,"iron"]=$(NF-6); shipcargo[$1,"pet"]=$(NF-7); shipcargo[$1,"gun"]=$(NF-8); shipcargo[$1,"shell"]=$(NF-9); shipeff[$1]=num($(NF-10)); shipfleet[$1]=substr($0,31,1); shipsect[$1]=truncwhite(substr($0,22,9)); next; } else if ($0 ~ /^ * /) { next; } else { mode=""; stat=0; next; } } } # # Subroutines that may be used by END {} # # cost to move through sector sect # memoizes results in the mem_movcost[] array. function movcost(sect, des, c) { if (sect in mem_movcost) return mem_movcost[sect]; des=val[sect,"des"]; if (des == "." || des == "s" || des == "\\") { c = 1e9; # something large } else if (des == "^") { c = 25; } else if (des == "+" || des == "=") { c = 1; } else { c = 2; } c = (100*c - val[sect,"eff"])/500; if (c == 0.0) c = 0.01; # min_movecost return mem_movcost[sect] = c; } function pathcost(from, to) # Find the cost of moving from sect from to sect to. # Uses results memoized in the mem_pathcost[] array. { if (! ((from,from) in mem_pathcost) ) { if ((to,to) in mem_pathcost) { # cost of inverse path return pathcost(to, from) - movcost(from) + movcost(to); } else { fillpath(from); } } if ((from,to) in mem_pathcost) { return mem_pathcost[from,to]; } else { return 1e9; } } function precompute_dist_paths( s, t) # Prep the pathcost data for all dist sectors. # The idea is to avoid running fillpath() for every sector, which # is expensive in both time and memory. { for (s in sects) { t = val[s, "dist_x"] "," val[s, "dist_y"]; if (s != t) fillpath(t); } } function fillpath(from, todo, done, i, x, y) # Computes all mincost paths from sect from, in mem_pathcost[]. { mem_pathcost[from,from] = 0; todo[from] = 1; done = 0; while (!done) { done = 1; for (i in todo) if (todo[i]) { done = 0; todo[i] = 0; x = val[i, "x"]; y = val[i, "y"]; growpath(todo, from, i, (x+2) "," y); growpath(todo, from, i, (x-2) "," y); growpath(todo, from, i, (x+1) "," (y+1)); growpath(todo, from, i, (x+1) "," (y-1)); growpath(todo, from, i, (x-1) "," (y+1)); growpath(todo, from, i, (x-1) "," (y-1)); } } } function growpath(todo, from, to, adj, c) # Used by fillpath. { if (adj in sects) { c = mem_pathcost[from, to] + movcost(adj); if ( !((from, adj) in mem_pathcost) || c < mem_pathcost[from, adj] ) { mem_pathcost[from, adj] = c; todo[adj] = 1; } } } # # Simulates a production update (no delivery or distribution) # ( Requires valid neweff data and prod data ) # function update( sect, old_civ, old_uw) { for (sect in sects) { if (!nofood) { eat = (val[sect,"civ"]+val[sect,"mil"]+val[sect,"uw"])*etu*eatrate; eat += (val[sect,"civ"]*obrate + val[sect,"uw"]*uwbrate)*etu*babyeat; val[sect,"food"] -= int(eat); consume["food"] += eat; } old_civ = val[sect, "civ"]; old_uw = val[sect, "uw"]; if (val[sect,"food"]< -1) { printf("** Warning: need %d more food in %s **\n",-val[sect,"food"],sect); val[sect,"work"]=0; val[sect,"civ"] /= 2; val[sect,"mil"] /= 2; val[sect,"uw"] /= 2; val[sect,"food"] = 0; } else { n = int(val[sect,"civ"]*etu*obrate); val[sect,"civ"] += n; n = int(val[sect,"uw"]*etu*uwbrate); val[sect,"uw"] += n; } if (val[sect,"civ"] > maxpop) val[sect,"civ"]=maxpop; if (val[sect,"uw"] > maxpop) val[sect,"uw"]=maxpop; n = val[sect,"civ"] - old_civ; if (n > 0) { produce["civ"] += n; } else { consume["civ"] -= n; } n = val[sect,"uw"] - old_uw; if (n > 0) { produce["uw"] += n; } else { consume["uw"] -= n; } } for (sect in neweff) { interesting[sect] = 1; val[sect,"des"]=newdes[sect]; val[sect,"eff"]=neweff[sect]; } for (sect in will) if (will[sect]+val[sect,make[sect]] > 999) val[sect,make[sect]] = 999; else { val[sect,make[sect]] += will[sect]; } for (sect in use1) val[sect,use1[sect]] -= amt1[sect]; for (sect in use2) val[sect,use2[sect]] -= amt2[sect]; for (sect in use3) val[sect,use3[sect]] -= amt3[sect]; } # # Two functions to simulate distribution # function distexport() { distribute(0); } function distimport() { distribute(1); } function distribute(importp, k, sect, dist, src, dst, pcost, mcost, comm, thresh, amt, ware, bank) { for (k = 0; k < dsects; ++k) { sect = order[k]; dist = val[sect, "dist_x"] "," val[sect, "dist_y"]; if (sect == dist) continue; if (importp) { src = dist; dst = sect; } else { src = sect; dst = dist; } pcost = pathcost(src, dst); if (1000 < pcost) continue; ware = ( (val[sect, "des"] == "w" && 60 <= val[sect, "eff"]) || (val[dist, "des"] == "w" && 60 <= val[dist, "eff"]) ); bank = (val[src, "des"] == "b" && 60 <= val[src, "eff"]); for (comm in weight) { thresh = val[sect, substr(comm, 1, 1) "_dist"]; if (!thresh || thresh == val[sect, comm]) continue; if (importp) { amt = thresh - val[sect, comm]; } else { amt = val[sect, comm] - thresh; } if (amt <= 0) continue; if (val[src, comm] < amt) amt = val[src, comm]; if (9999 < val[dst, comm] + amt) amt = 9999 - val[dst, comm]; mcost = pcost * weight[comm] / 4; if (ware) mcost /= packing[comm]; if (bank && comm == "bar") mcost /= 4; if (val[src, "mob"] < amt * mcost) { amt = int(val[src, "mob"] / mcost); } val[src, comm] -= amt; exported[src, comm] += amt; val[src, "mob"] -= int(amt * mcost); val[dst, comm] += amt; imported[dst, comm] += amt; interesting[dst] = interesting[src] = 1; } } } function shipupdate( i,n,name,sect,wf,avail,wpf,crew) { for (i in shiporder) { n=shiporder[i]; name=shipname[n]; sect=shipsect[n]; # repair ship wpf=20+shiplcm[name]+shiphcm[name]*2; if (shipfir[name]) { wf=int(shipcargo[n,"mil"]*etu/2); crew="mil"; } else { wf=etu*(int(shipcargo[n,"civ"]/2)+int(shipcargo[n,"mil"]/5)); crew="civ"; } if ((sect,"des") in val) { if (val[sect,"des"]=="h") { avail=wf+val[sect,"avail"]*100; } else { avail = wf/3; avail -= etu*(100-((shipcargo[n,crew]*100)/shipcargo[name,crew]))/7; } } if (avail<=0) { gain = -int(-avail/wpf); if (gain<0) { printf("** Warning: ship #%d will lose %d efficiency\n",n,-gain); } shipeff[n] += gain; } left=100-shipeff[n]; gain=int(avail/wpf); if (gain>maxshipeff) gain=maxshipeff; if (gain>left) gain=100-shipeff[n]; if (((sect,"des") in val) && val[sect,"des"]=="h") { need=shiplcm[name]*gain/100; if (val[sect,"lcm"]80) gain=80-shipeff[n]; } shipeff[n] += gain; if (match(shipcap[name],"oil") && !match(shipcap[name],"oiler")) { gain = shipcargo[n,"civ"]*etu*oil[sect]/10000; if (shipcargo[n,"oil"]+gain>shipcargo[name,"oil"]) gain=shipcargo[name,"oil"]-shipcargo[n,"oil"]; oil[sect] -= int(gain/10); shipcargo[n,"oil"] += gain; } if (match(shipcap[name],"fert")) { if (newfish) { gain = shipcargo[n,"civ"]*etu*fert[sect]/1000; } else { gain = shipcargo[n,"civ"]*etu*fert[sect]/10000; } if (shipcargo[n,"oil"]+gain>shipcargo[name,"oil"]) gain=shipcargo[name,"oil"]-shipcargo[n,"oil"]; shipcargo[n,"food"] += gain; } # feed ship # plague stuff # shipmobility gain=127-shipmob[n]; if (gain>maxshipmob) gain=maxshipmob; if (shipfu[name]) { if (shipfuel[n]*10/shipfu[name]0 && will[sect]+val[sect,make[sect]] > 999) { printf(" *** Warning *** %d surplus %s in %s\n", will[sect]+val[sect,make[sect]]-999,make[sect],sect); produce[make[sect]] += 999 - val[sect,make[sect]]; } else { produce[make[sect]] += will[sect]; } } for (sect in use1) { consume[use1[sect]] += amt1[sect]; } for (sect in use2) { consume[use2[sect]] += amt2[sect]; } for (sect in use3) { consume[use3[sect]] += amt3[sect]; } shipupdate(); update(); precompute_dist_paths(); distexport(); distimport(); for (i=0;i127) mob=127; val[sect,"mob"]=mob; } totalpop=0; for (sect in sects) { if (val[sect,"*"]==".") totalpop+=val[sect,"civ"]; } consume["food"] = int(consume["food"]+0.9999); for (comm in start) { delta[comm] = produce[comm] - consume[comm]; net[comm] = start[comm] + delta[comm]; } if ("tech" in delta) { if (delta["tech"]>easytech) { delta["tech"] = easytech + log(delta["tech"]-easytech+1)/log(logtech); delta["tech"] = (int(delta["tech"]*100))/100; } net["tech"] = start["tech"] + delta["tech"]; } if ("edu" in delta) { delta["edu"] *= 4000 * int(etu/12) /totalpop; if (delta["edu"]>5) { delta["edu"] = (delta["edu"]-5)/(log(1+delta["edu"])/log(4)) + 5; } net["edu"] = (start["edu"]*(192-etu) + delta["edu"]*etu)/192; } if (dsects) { printf(" com start + - delta net\n"); printf("------------------------------------------------------\n"); for (comm in net) { printf("%5s %8g %8g %8g %8g %8g\n", comm, start[comm], produce[comm], consume[comm], delta[comm], net[comm]); } } for (sect in make) interesting[sect]=1; if (dsects) printf("\n sect des eff mob make imports exports commodities\n"); for (i=0;i0) impstr = impstr sprintf(" %d%s",imported[sect,comm],substr(comm,1,1)); } printf("%-14s ",impstr); expstr=""; for (comm in weight) { if (((sect,comm) in exported) && exported[sect,comm]>0) expstr = expstr sprintf(" %d%s",exported[sect,comm],substr(comm,1,1)); } printf("%-14s ",expstr); for (comm in weight) { amount = val[sect, comm]; thresh = val[sect, substr(comm, 1, 1) "_dist"]; if (amount || thresh) { printf(" %d%s", amount, substr(comm, 1, 1)); if (thresh) { if (amount < thresh) { printf "-"; } else if (thresh < amount) { printf "+"; } } } } printf("\n"); } } if (ships) printf("\nshp# type sect eff mob fuel cargo\n"); for (i=0;ishipcargo[n,"food"]) { printf("** Warning: ship #%d needs %d food\n", n,int(0.9999+crew*etu*eatrate-shipcargo[n,"food"])); } printf("%4d %-16s %7s %3d%% %3d %3d ", n,name,shipsect[n],shipeff[n],shipmob[n],shipfuel[n]); for (comm in weight) { if ((n,comm) in shipcargo) if (shipcargo[n,comm]) printf(" %d%s",shipcargo[n,comm],substr(comm,1,1)); } printf("\n"); } }