awlite

POSIX AWK port of Text Elite game
git clone git://git.luxferre.top/awlite.git
Log | Files | Refs | README

awlite.awk (27333B)


      1 # awlite: POSIX AWK port of Text Elite
      2 # Version 1.8.1
      3 # Original high-level algorithms from Text Elite 1.5 by Ian Bell, 1999, 2015
      4 # Porting, corrections, improvements and optimizations by Luxferre, 2024
      5 #
      6 # Tested on: nawk/one-true-awk, busybox awk, gawk --posix, mawk -W posix
      7 # Should run on any current POSIX-compliant AWK implementation but
      8 # must be run with LC_ALL=C environment variable to work correctly!
      9 #
     10 # Gameplay differences from the original C version of TE 1.5:
     11 # - multiple typos corrected in text strings
     12 # - galaxy jumps are no longer free and cost 5000 credits each (like in classic Elite and Oolite)
     13 # - cargo hold expansion also is non-free and (since v1.8) multi-tiered
     14 # - alien items are available by default, can be overridden with -v NO_ALIEN_ITEMS=1
     15 # - economy names are no longer shortened
     16 # - the "politically correct" goods names are turned on with -v CENSORED=1
     17 # - the following goods have been renamed: "Robot Slaves" to "Robots", "Liquor/Wines" to "Liquors", "Gem-Strones" to "Gem-stones"
     18 # - market and local info tables are better aligned
     19 # - the "cash" and "sneak" commands are removed since v1.6 (use saves to cheat)
     20 # - state saving functionality since v1.6 with "save" and "load" commands
     21 # - since v1.6.1, the game gives you a hint if you might have found Raxxla
     22 # - since v1.6.3, galaxy names are actually displayed (taken from ArcElite)
     23 # - since v1.7, "stat" command and basic ranking was introduced
     24 # - since v1.8, "hold" command was replaced with "upgrade" with several ships
     25 # ... to be continued ...
     26 
     27 # utility functions
     28 
     29 # determine (ordered) array length
     30 function alen(a, i, k) {
     31   k = 0
     32   for(i in a) k++
     33   return k
     34 }
     35 
     36 # split a string with separator into an array but with 0-based indexes
     37 function asplit(s, a, sep,  len, i) {
     38   split(s, a, sep) # get 1-based-index array in a
     39   len = alen(a)
     40   for(i=0;i<len;i++) a[i] = a[i+1] # move everything to the left
     41   delete a[len] # delete the last element after moving
     42 }
     43 
     44 # serialize any associative array into a string
     45 # sep must be a single char not occurring in any key or value
     46 function assocser(aa, sep,  outs, k) {
     47   outs = "" # initialize an ordered array string
     48   for(k in aa) # iterate over keys
     49     outs = outs sep k sep aa[k]
     50   return substr(outs, 2) # delete the first sep
     51 }
     52 
     53 # deserialize any associative array from a string
     54 # sep must be a single char used when calling assocser() function
     55 function assocdes(s, aa, sep,  oa, i, len) {
     56   split("", aa) # erase aa array
     57   split(s, oa, sep) # get 1-based ordered array in oa
     58   len = alen(oa) # ordered array length
     59   for(i=1;i<=len;i+=2) # populate aa
     60     aa[oa[i]] = oa[i+1]
     61 }
     62 
     63 # get the ASCII code of a character
     64 # usage: ord(c) => integer
     65 function ord(c, b) {
     66   # init char-to-ASCII mapping if it's not there yet
     67   if(!TGL_ORD["#"]) for(b=0;b<256;b++) TGL_ORD[sprintf("%c", b)] = b
     68   return int(TGL_ORD[c])
     69 }
     70 
     71 # Required bitwise operations (as POSIX AWK doesn't support them)
     72 
     73 function bw_and(a, b, v, r) {
     74   v = 1; r = 0
     75   while(a > 0 || b > 0) {
     76     if((a%2) == 1 && (b%2) == 1) r += v
     77     a = int(a/2)
     78     b = int(b/2)
     79     v *= 2
     80   }
     81   return int(r)
     82 }
     83 
     84 # ============================ #
     85 # Game code itself starts here #
     86 # ============================ #
     87 
     88 # PRNG part
     89 
     90 function mysrand(seed) { srand(seed); rng_lastrand = seed - 1 }
     91 
     92 function myrand(r) {
     93   if(game["use_native_rand"] > 0) return int(rand() * 65536)
     94   else { # attempt to optimize McDonnell's generator
     95     r = (rng_lastrand * 3677 + 3680) % 2147483648
     96     rng_lastrand = r -1
     97     return r
     98   }
     99 }
    100 
    101 function randbyte() {return myrand()%256}
    102 
    103 # other functions
    104 
    105 function mymin(a,b) {return (a < b) ? a : b}
    106 
    107 # distance between 2 planets
    108 function distance(p1x, p1y, p2x, p2y,  dx, dy) {
    109   dx = p1x - p2x
    110   dy = p1y - p2y
    111   return int(0.5 + 4*sqrt(dx*dx + dy*dy/4))
    112 }
    113 
    114 function rotl(x) { # rotate left 1 bit mod 256
    115   x = x % 256 # truncate to a byte first
    116   return (x * 2 + ((x > 127) ? 1 : 0)) % 256
    117 }
    118 
    119 function twist(x) { # twister function
    120   return (256 * rotl(int(x/256)) + rotl(x%256)) % 65536
    121 }
    122 
    123 function galswitch(seeds) { # galaxy switcher (accepts an array)
    124   seeds[0] = twist(seeds[0])
    125   seeds[1] = twist(seeds[1])
    126   seeds[2] = twist(seeds[2])
    127 }
    128 
    129 function tweakseed(seeds,  t) { # another seed tweaker
    130   t = (seeds[0] + seeds[1] + seeds[2]) % 65536
    131   seeds[0] = seeds[1]
    132   seeds[1] = seeds[2]
    133   seeds[2] = t
    134 }
    135 
    136 # Generate system info from seed
    137 # populates the sinfo map
    138 function makesystem(seeds, sinfo,  p1, p2, p3, p4, lnf, ecof, sname) {
    139   lnf = bw_and(seeds[0], 64)
    140   sinfo["x"] = int(seeds[1] / 256)
    141   sinfo["y"] = int(seeds[0] / 256)
    142   sinfo["govtype"] = int(seeds[1] / 8) % 8
    143   sinfo["economy"] = int(seeds[0] / 256) % 8
    144   if(sinfo["govtype"] <= 1)
    145     sinfo["economy"] = 2 + (sinfo["economy"]%2) + (sinfo["economy"]>3?4:0)
    146   ecof = 7 - sinfo["economy"]
    147   sinfo["techlev"] = (sinfo["x"] % 4) + ecof + int(sinfo["govtype"] / 2)
    148   if((sinfo["govtype"] % 2) == 1) sinfo["techlev"]++
    149   sinfo["population"] = 4 * (sinfo["techlev"]) + sinfo["economy"] + sinfo["govtype"] + 1
    150   sinfo["productivity"] = ((ecof + 3) * (sinfo["govtype"] + 4)) * sinfo["population"] * 8
    151   sinfo["radius"] = 256 * ((int(seeds[2] / 256) % 16) + 11) + sinfo["x"]
    152   # goat soup seed parts
    153   sinfo["gsseed_a"] = seeds[1] % 256
    154   sinfo["gsseed_b"] = int(seeds[1] / 256) % 256
    155   sinfo["gsseed_c"] = seeds[2] % 256
    156   sinfo["gsseed_d"] = int(seeds[2] / 256) % 256
    157   # name pair indexes
    158   p1 = 2 * (int(seeds[2] / 256) % 32); tweakseed(seeds) 
    159   p2 = 2 * (int(seeds[2] / 256) % 32); tweakseed(seeds) 
    160   p3 = 2 * (int(seeds[2] / 256) % 32); tweakseed(seeds) 
    161   p4 = 2 * (int(seeds[2] / 256) % 32); tweakseed(seeds) 
    162   sname = pairs[p1] pairs[p1+1] pairs[p2] pairs[p2+1] pairs[p3] pairs[p3+1]
    163   if(lnf) sname = sname pairs[p4] pairs[p4+1]
    164   gsub(/\./, "", sname) # remove all dots from sname
    165   sinfo["name"] = sname # write the final name variant
    166   # return sinfo
    167 }
    168 
    169 # "Goat Soup" planetary description string code 
    170 
    171 function gs_rnd(sinfo,  a, x) { # accepts system info to modify gsseed
    172   x = (sinfo["gsseed_a"] * 2) % 256
    173   a = x + sinfo["gsseed_c"]
    174   if(sinfo["gsseed_a"] > 127) a++
    175   sinfo["gsseed_a"] = a % 256
    176   sinfo["gsseed_c"] = x
    177   x = sinfo["gsseed_b"]
    178   a = (int(a / 256) + x + sinfo["gsseed_d"]) % 256
    179   sinfo["gsseed_b"] = a
    180   sinfo["gsseed_d"] = x
    181   return a
    182 }
    183 
    184 # main desc generator, recursive, modifies sinfo as well
    185 function goat_soup(source, sinfo,  l, c, i, j, k, v, rnd, dsel) {
    186   l = length(source)
    187   for(i=0;i<l;i++) {
    188     c = ord(substr(source, i+1, 1))
    189     if(c < 128) printf("%c", c)
    190     else {
    191       if(c <= 164) {
    192         rnd = gs_rnd(sinfo)
    193         asplit(desc_list[c - 129], dsel, ",") # select the string from list
    194         j = ((rnd >= 51) ? 1 : 0) + ((rnd >= 102) ? 1 : 0)
    195         j += ((rnd >= 153) ? 1 : 0) + ((rnd >= 204) ? 1 : 0)
    196         goat_soup(dsel[j], sinfo)
    197       } else {
    198         if(c == 176) { # planet name
    199           v = sinfo["name"]
    200           v = substr(v, 1, 1) tolower(substr(v, 2))
    201           printf("%s", v)
    202         } else if(c == 177) { # planet name + ian
    203           v = sinfo["name"]
    204           v = substr(v, 1, 1) tolower(substr(v, 2))
    205           # find last "e" or "i" occurrence in v
    206           j = match(v, /[eia]+$/)
    207           if(j == 0) j = length(v) + 1
    208           printf("%sian", substr(v, 1, j - 1))
    209         } else if(c == 178) { # random name
    210           v = gs_rnd(sinfo) % 4
    211           for(j = 0; j <= v; j++) {
    212             k = bw_and(62, gs_rnd(sinfo))
    213             printf("%c", (j > 0) ? tolower(pairs0[k]) : pairs0[k])
    214             printf("%c", tolower(pairs0[k+1]))
    215           }
    216         } else { printf("<bad char in data [%X]>",c); return }
    217       }
    218     }
    219   }
    220 }
    221 
    222 # Galaxy builder (populates seeds array and global galaxy arrays)
    223 function buildgalaxy(gnum,  seeds, si, i) {
    224   asplit("23114 584 46931", seeds, " ")
    225   for(i=1;i<gnum;i++) galswitch(seeds) 
    226   split("", galaxy) # global galaxy array
    227   split("", plannames) # global galaxy planet names array
    228   split("", economies) # economy mapping
    229   for(i=0;i<galsize;i++) {
    230     split("", si) # init a new system info array
    231     makesystem(seeds, si) # populate it
    232     galaxy[i] = assocser(si, "|") # serialize into the field
    233     plannames[i] = si["name"] # populate the name
    234     galcoords_x[i] = si["x"] # populate x coordinate
    235     galcoords_y[i] = si["y"] # populate y coordinate
    236     economies[i] = si["economy"] # populate economy type
    237   }
    238 }
    239 
    240 
    241 # Market functions
    242 
    243 # buy amount a of product i, return amount bought
    244 function gamebuy(i, a,  t) {
    245   if(player["cash"] < 0) t = 0
    246   else {
    247     t = mymin(localmarket_quantity[i], a)
    248     if(goods_units[i] == 0) t = mymin(player["holdspace"], t)
    249     t = mymin(t, int(player["cash"] / localmarket_price[i]))
    250   }
    251   shipshold[i] = int(shipshold[i]) + t
    252   localmarket_quantity[i] -= t
    253   player["cash"] -= t * localmarket_price[i]
    254   player["expense"] += t * localmarket_price[i]
    255   if(goods_units[i] == 0) player["holdspace"] -= t
    256   return t
    257 }
    258 
    259 # sell amount a of product i, return amount sold
    260 function gamesell(i, a,  t) {
    261   t = mymin(shipshold[i], a)
    262   shipshold[i] -= t
    263   localmarket_quantity[i] += t
    264   if(goods_units[i] == 0) player["holdspace"] += t
    265   player["cash"] += t * localmarket_price[i]
    266   return t
    267 }
    268 
    269 # generate market for a particular planetary system
    270 # accepts fluctuation byte and economy type number
    271 # recreates localmarket_quantity and localmarket_price arrays
    272 function genmarket(fluct, econtype,  i, q, product, changing) {
    273   for(i=0;i<=IDX_ALIEN_ITEMS;i++) {
    274     product = econtype * goods_grads[i]
    275     changing = bw_and(fluct, goods_masks[i])
    276     q = (goods_bquants[i] + changing - product + 256) % 256
    277     if(q > 127) q = 0
    278     localmarket_quantity[i] = q % 64
    279     q = (goods_bprices[i] + changing + product)  % 256
    280     localmarket_price[i] = q * 4
    281   }
    282   if(NO_ALIEN_ITEMS) localmarket_quantity[IDX_ALIEN_ITEMS] = 0
    283 }
    284 
    285 # display local market from localmarket_quantity and localmarket_price
    286 function displaymarket(i) {
    287   for(i=0;i<=IDX_ALIEN_ITEMS;i++) {
    288     printf("\n%-16s", goods_names[i])
    289     printf("%-5s", sprintf("%.1f", localmarket_price[i]/10))
    290     printf("\t%u%s", localmarket_quantity[i], unitnames[goods_units[i]])
    291     printf("\t\t%u", shipshold[i])
    292   }
    293 }
    294 
    295 # move to system i
    296 function gamejump(i, si) {
    297   player["currentplanet"] = i
    298   player["jumps"]++
    299   assocdes(galaxy[i], si, "|")
    300   genmarket(randbyte(), int(si["economy"]))
    301 }
    302 
    303 # print data for given system
    304 function prisys(sinfo, compressed) {
    305   if(compressed) {
    306     printf("%10s TL: %2i ", sinfo["name"], sinfo["techlev"] + 1)
    307     printf("%-20s %-15s", econnames[sinfo["economy"]], govnames[sinfo["govtype"]])
    308   } else {
    309     printf("\nSystem: %s", sinfo["name"])
    310     printf("\nPosition (%i, %i)", sinfo["x"], sinfo["y"])
    311     printf("\nEconomy: (%i) %s", sinfo["economy"], econnames[sinfo["economy"]])
    312     printf("\nGovernment: (%i) %s", sinfo["govtype"], govnames[sinfo["govtype"]])
    313     printf("\nTech level: %2i", sinfo["techlev"] + 1)
    314     printf("\nTurnover: %u", sinfo["productivity"])
    315     printf("\nRadius: %u", sinfo["radius"])
    316     printf("\nPopulation: %u billion\n", int(sinfo["population"]/8))
    317     goat_soup("\217 is \227.", sinfo) # generate and print the description
    318     if(match(sinfo["name"], /RA..LA/)) # found Raxxla?
    319       printf("\n%s", "Wait a sec... Is this the legendary Raxxla?\nWe'll never know for sure...")
    320   }
    321 }
    322 
    323 # return id of the planet whose name matches passed string
    324 # closest to current planet - if none, return current planet
    325 function matchsys(s,  d, i, p, cd) {
    326   p = player["currentplanet"]
    327   d = 9999
    328   s = toupper(s) # system names are stored in uppercase
    329   for(i=0;i<galsize;i++) {
    330     if(index(plannames[i], s) == 1) { # found i-th system
    331       cd = distance(galcoords_x[i], galcoords_y[i], galcoords_x[p], galcoords_y[p])
    332       if(cd < d) {d = cd; p = i}
    333     }
    334   }
    335   return p
    336 }
    337 
    338 # direct command implementations (may be further merged into the REPL directly)
    339 
    340 # rand command implementation
    341 function dotweakrand() { game["use_native_rand"] = 1 - game["use_native_rand"] }
    342 
    343 # local command implementation
    344 function dolocal(d, i, p, si) {
    345   printf("\nGalaxy %i - %s\n", player["galaxynum"], galnames[player["galaxynum"]])
    346   p = int(player["currentplanet"])
    347   for(i=0;i<galsize;i++) {
    348     d = distance(galcoords_x[i], galcoords_y[i], galcoords_x[p], galcoords_y[p])
    349     if(d <= game["maxfuel"]) {
    350       printf("\n %s ", (d <= player["fuel"]) ? "*" : "-")
    351       assocdes(galaxy[i], si, "|") # deserialize current system
    352       prisys(si, 1)
    353       printf(" (%.1f LY)", d/10)
    354     }
    355   }
    356 }
    357 
    358 # jump to planet name s
    359 function dojump(s, dest, d, p, si) {
    360   dest = matchsys(s) # find the destination
    361   p = int(player["currentplanet"])
    362   if(dest == p) { printf("\nBad jump"); return 0}
    363   d = distance(galcoords_x[dest], galcoords_y[dest], galcoords_x[p], galcoords_y[p])
    364   if(d > player["fuel"]) {printf("\nJump too far"); return 0}
    365   player["fuel"] -= d
    366   gamejump(dest, si)
    367   prisys(si, 0)
    368   return 1
    369 }
    370 
    371 # jump to next galaxy (planet number is preserved)
    372 function dogalhyp() {
    373   if(player["cash"] > game["galhypcost"]) {
    374     player["cash"] -= game["galhypcost"]
    375     player["expense"] += game["galhypcost"]
    376     player["galaxynum"]++
    377     player["jumps"]++
    378     if(player["galaxynum"] == 9) player["galaxynum"] = 1
    379     buildgalaxy(player["galaxynum"], globalseeds) # build a new galaxy
    380   }
    381   else printf("Not enough credits for hyperspace jump!\nMust have at least %.1f CR", game["galhypcost"]/10)
    382 }
    383 
    384 # print planet info
    385 function doinfo(s,  dest, si) {
    386   dest = matchsys(s) # find the system
    387   assocdes(galaxy[dest], si, "|") # deserialize found system
    388   prisys(si, 0) # print its info
    389 }
    390 
    391 # upgrade/up command implementation
    392 function doup(a,  t, i, ns) {
    393   ns = 6 # number of upgrades, including the basic version
    394   if(player["upglvl"] == ns - 1)
    395     printf("\nYou already are on the maximum upgrade level, enjoy!")
    396   else if(a == "") { # no parameter, list available ships
    397     printf("\nAvailable upgrades:\n")
    398     for(i=player["upglvl"]+1;i<ns;i++) {
    399       printf("\n%u. %-13s %-5s %-10s", i, shipnames[i], sprintf("%ut", cargoholds[i]), sprintf("%.1f CR",shipprices[i]/10))
    400     }
    401     printf("\n\nType upgrade [number] to upgrade")
    402   } else {
    403     a = int(a)
    404     if(a >= ns || a <= player["upglvl"])
    405       printf("\nInvalid upgrade number!")
    406     else if(player["cash"] < shipprices[a])
    407       printf("\nYou need at least %.1f CR to upgrade to %s!", shipprices[a]/10, shipnames[a])
    408     else { # perform the upgrade if cash allows
    409       player["upglvl"] = a
    410       a = int(cargoholds[a])
    411       t = 0
    412       for(i=0;i<=IDX_ALIEN_ITEMS;i++)
    413         if(goods_units[i] == 0) t += shipshold[i]
    414       if(t > a) {printf("\nHold too full"); return 0}
    415       player["holdspace"] = a - t
    416       player["cash"] -= shipprices[player["upglvl"]]
    417       player["expense"] += shipprices[player["upglvl"]]
    418       printf("\nBought %s", shipnames[player["upglvl"]])
    419       printf("\nCargo bay expanded to %dt", cargoholds[player["upglvl"]])
    420     }
    421   }
    422 }
    423 
    424 # check string s against n options in array a
    425 # if matches ith element return i+1 else return 0
    426 function stringmatch(s, a, n, i) {
    427   s = tolower(s)
    428   for(i=0;i<n;i++)
    429     if(index(tolower(a[i]), s) == 1) return i+1
    430   return 0
    431 }
    432 
    433 # sell command implementation (accepts space-separated name and amount)
    434 function dosell(s, sp, pname, pquant, i, t) {
    435   split(s, sp, " ")
    436   pname = sp[1]
    437   pquant = int(sp[2])
    438   if(pquant < 1) pquant = 1
    439   i = stringmatch(pname, goods_names, IDX_ALIEN_ITEMS + 1)
    440   if(i == 0) {printf("\nUnknown product"); return 0}
    441   i -= 1 # switch to the real product index
    442   t = gamesell(i, pquant)
    443   pname = goods_names[i] # update real product name
    444   if(t > 0) {
    445     printf("\nSelling %i%s of %s", t, unitnames[goods_units[i]], pname)
    446   } else printf("\nCannot sell any %s", pname)
    447   return 1
    448 }
    449 
    450 # buy command implementation (accepts space-separated name and amount)
    451 function dobuy(s, sp, pname, pquant, i, t) {
    452   split(s, sp, " ")
    453   pname = sp[1]
    454   pquant = int(sp[2])
    455   if(pquant < 1) pquant = 1
    456   i = stringmatch(pname, goods_names, IDX_ALIEN_ITEMS + 1)
    457   if(i == 0) {printf("\nUnknown product"); return 0}
    458   i -= 1 # switch to the real product index
    459   t = gamebuy(i, pquant)
    460   pname = goods_names[i] # update real product name
    461   if(t > 0) {
    462     printf("\nBuying %i%s of %s", t, unitnames[goods_units[i]], pname)
    463   } else printf("\nCannot buy any %s", pname)
    464   return 1
    465 }
    466 
    467 # attempt to buy f tonnes of fuel
    468 function gamefuel(f) {
    469   if((f + player["fuel"]) > game["maxfuel"])
    470     f = game["maxfuel"] - player["fuel"]
    471   if(game["fuelcost"] > 0 && (f * game["fuelcost"] > player["cash"]))
    472     f = int(player["cash"] / game["fuelcost"])
    473   player["fuel"] += f
    474   player["cash"] -= f * game["fuelcost"]
    475   player["expense"] += f * game["fuelcost"]
    476   return f
    477 }
    478 
    479 # fuel command implementation
    480 function dofuel(f) {
    481   f = gamefuel(int(10 * f))
    482   if(f == 0) printf("\nCan't buy any fuel")
    483   else printf("\nBuying %.1fLY fuel", f/10)
    484   return 1
    485 }
    486 
    487 # show stock market
    488 function domkt() {
    489   displaymarket()
    490   printf("\n\nFuel: %.1f\nFree cargo space: %it", player["fuel"]/10, player["holdspace"])
    491 }
    492 
    493 # display help
    494 function dohelp() {
    495   printf("\nCommands are:")
    496   printf("\nBuy   product amount")
    497   printf("\nSell  product amount")
    498   printf("\nFuel  amount     (buy amount LY of fuel)")
    499   printf("\nJump  planetname (limited by fuel)")
    500   printf("\nGalhyp           (jump to next galaxy)")
    501   printf("\nInfo  planetname (print info on system)")
    502   printf("\nMkt              (show market prices)")
    503   printf("\nLocal            (list systems within 7 lightyears)")
    504   printf("\nUpgrade          (upgrade your ship)")
    505   printf("\nStat             (show player statistics)")
    506   printf("\nSave  playername (save game into current working directory)")
    507   printf("\nLoad  playername (load game from current working directory)")
    508   printf("\nQuit or ^D       (exit)")
    509   printf("\nHelp             (display this text)")
    510   printf("\nRand             (toggle RNG)")
    511   printf("\n\nAbbreviations allowed eg. b fo 5 = Buy Food 5, m = Mkt")
    512 }
    513 
    514 # Save/load functionality
    515 
    516 function dosave(savename, fs, fn) {
    517   gsub(/[^[:alnum:]]/, "_", savename) # sanitize all non-alnum chars
    518   player["cargo"] = assocser(shipshold, "@")
    519   fs = assocser(player, "|")
    520   delete player["cargo"]
    521   fn = savename ".awlite"
    522   printf("%s\n", fs) > fn # write the savefile
    523   close(fn)
    524   printf("\nGame saved in %s", fn)
    525 }
    526 
    527 function doload(savename, fn, fs) {
    528   gsub(/[^[:alnum:]]/, "_", savename) # sanitize all non-alnum chars
    529   fn = savename ".awlite"
    530   if((getline fs < fn) > 0) { # read the savefile
    531     split("", player)   # clear the player array
    532     split("", shipshold) # clear the shipshold array
    533     assocdes(fs, player, "|")
    534     assocdes(player["cargo"], shipshold, "@")
    535     delete player["cargo"]
    536     split("", globalseeds) # init galaxy seeds
    537     buildgalaxy(player["galaxynum"], globalseeds) # build the current galaxy
    538     genmarket(0, economies[player["currentplanet"]]) # build local market
    539     printf("\nGame loaded from %s", fn)
    540   } else printf("\nError reading the savefile!")
    541   close(fn)
    542 }
    543 
    544 # player statistics and ranking display
    545 # ranking formula might be subject to change in future versions
    546 function dostat(profind, rank) {
    547   profind = 0 # profit margin index
    548   rank = 0
    549   if(player["expense"] > 0) {
    550     profind = (player["cash"]-1000) / player["expense"]
    551     if(profind > 1) rank = int(log(profind) / log(2)) + 1 # log base 2
    552     if(rank < 0) rank = 0
    553     if(rank > 8) rank = 8
    554   }
    555   printf("\nRank: %s", ranknames[rank])
    556   printf("\n\n========= Money =========")
    557   printf("\nBalance %17s", sprintf("%.1f CR",player["cash"]/10))
    558   printf("\nExpenses %16s", sprintf("%.1f CR", player["expense"]/10))
    559   printf("\nProfit margin %11s", sprintf("%.2f%%", profind*100))
    560   printf("\n\n======= Ship info =======")
    561   printf("\nName %20s", shipnames[player["upglvl"]])
    562   printf("\nFuel %20s", sprintf("%.1f LY", player["fuel"]/10))
    563   printf("\nCargo space %13s", sprintf("%ut", cargoholds[player["upglvl"]]))
    564   printf("\nFree space %14s", sprintf("%ut", player["holdspace"]))
    565   printf("\nJumps %19s", sprintf("%u", player["jumps"]))
    566 }
    567 
    568 # Game init procedure: first thing to call in the BEGIN block
    569 function gameinit() {
    570   rng_lastrand = 0 # state for custom PRNG
    571   mysrand(12345); # ensure repeatability
    572   numForLave = 7 # Lave is the 7th planet in galaxy 1
    573   
    574   split("",game) # overall game state is stored here
    575   game["use_native_rand"] = 1
    576   game["galhypcost"] = 50000 # 5000 CR
    577   game["cargoexpcost"] = 4000 # 400 CR for one-time upgrade
    578   game["cargoexpsize"] = 35 # 35t after the upgrade
    579   game["fuelcost"] = 2 # 0.2 CR/lightyear
    580   game["maxfuel"] = 70 # 7.0 LY tank
    581 
    582   split("",player) # current (saveable) player state is stored here
    583   player["currentplanet"] = numForLave # current planet (1 to 256)
    584   player["galaxynum"] = 1 # current galaxy (1 to 8)
    585   player["cash"] = 1000 # current cash (100 CR)
    586   player["expense"] = 0 # for tracking overall expenses
    587   player["jumps"] = 0 # for counting interplanetary jumps
    588   player["fuel"] = game["maxfuel"] # current fuel amount
    589   player["upglvl"] = 0 # player's upgrade level
    590   split("",shipshold) # (saveable) contents of cargo bay, up to 16 items
    591 
    592   # constants and tables
    593 
    594   # unit names
    595   asplit("t kg g", unitnames, " ")
    596 
    597   # galaxy names (1-indexed)
    598   split("Santaari Colesque Lara'tan Angiana Proximus Sol Jaftra Xrata", galnames, " ")  
    599 
    600   # government names
    601   asplit("Anarchy,Feudal,Multi-gov,Dictatorship,Communist,Confederacy,Democracy,Corporate State", govnames, ",")
    602 
    603   # economy names
    604   asplit("Rich Industrial,Average Industrial,Poor Industrial,Mainly Industrial,Mainly Agricultural,Rich Agricultural,Average Agricultural,Poor Agricultural", econnames, ",")
    605 
    606   # rank names
    607   asplit("Penniless,Mostly Penniless,Peddler,Dealer,Merchant,Broker,Entrepreneur,Tycoon,Elite", ranknames, ",")
    608 
    609   # ship names
    610   asplit("Cobra Mk3,Cobra Mk3 Ext,Python,Boa,Boa 2,Anaconda", shipnames, ",")
    611 
    612   # upgrade prices
    613   asplit("0 4000 2000000 4500000 4950000 6500000", shipprices, " ")
    614 
    615   # cargo hold values, t
    616   asplit("20 35 100 125 175 750", cargoholds, " ")
    617 
    618   player["holdspace"] = cargoholds[player["upglvl"]] # max cargo hold space
    619 
    620   # Goods
    621 
    622   # Data for DB's price/availability generation system:
    623   # Base price, Gradient, Base quantity, Mask, Unit, Name
    624   split("", commodities) # will split on demand
    625   commodities[0] = "19,-2,6,1,0,Food"
    626   commodities[1] = "20,-1,10,3,0,Textiles"
    627   commodities[2] = "65,-3,2,7,0,Radioactives"
    628   commodities[3] = "40,-5,226,31,0," (CENSORED ? "Robots" : "Slaves")
    629   commodities[4] = "83,-5,251,15,0," (CENSORED ? "Beverages" : "Liquors")
    630   commodities[5] = "196,8,54,3,0,Luxuries"
    631   commodities[6] = "235,29,8,120,0," (CENSORED ? "Rare Species" : "Narcotics")
    632   commodities[7] = "154,14,56,3,0,Computers"
    633   commodities[8] = "117,6,40,7,0,Machinery"
    634   commodities[9] = "78,1,17,31,0,Alloys"
    635   commodities[10] = "124,13,29,7,0,Firearms"
    636   commodities[11] = "176,-9,220,63,0,Furs"
    637   commodities[12] = "32,-1,53,3,0,Minerals"
    638   commodities[13] = "97,-1,66,7,1,Gold"
    639   commodities[14] = "171,-2,55,31,1,Platinum"
    640   commodities[15] = "45,-1,250,15,2,Gem-stones"
    641   commodities[16] = "53,15,192,7,0,Alien Items"
    642 
    643   IDX_ALIEN_ITEMS=16
    644 
    645   split("", goods_bprices) # holds base prices
    646   split("", goods_grads)   # holds gradients
    647   split("", goods_bquants) # holds base quantities 
    648   split("", goods_masks)   # holds masks
    649   split("", goods_units)   # holds measurement units
    650   split("", goods_names)   # holds goods names
    651   for(i in commodities) { # it's pre-ordered anyway
    652     split(commodities[i], parts, ",") # we can use 1-based here
    653     goods_bprices[i] = int(parts[1])
    654     goods_grads[i] = int(parts[2])
    655     goods_bquants[i] = int(parts[3])
    656     goods_masks[i] = int(parts[4])
    657     goods_units[i] = int(parts[5])
    658     goods_names[i] = parts[6]
    659   }
    660 
    661   # localmarket assoc arrays
    662   split("", localmarket_quantity)
    663   split("", localmarket_price)
    664 
    665   # digrams for planet names
    666   asplit("ABOUSEITILETSTONLONUTHNOALLEXEGEZACEBISOUSESARMAINDIREA.ERATENBERALAVETIEDORQUANTEISRION", pairs0, "")
    667   asplit("..LEXEGEZACEBISOUSESARMAINDIREA.ERATENBERALAVETIEDORQUANTEISRION", pairs, "")
    668 
    669   # planet description string parts
    670   desc_list_str = "fabled,notable,well known,famous,noted|very,mildly,most,reasonably,|ancient,\225,great,vast,pink|\236 \235 plantations,mountains,\234,\224 forests,oceans|shyness,silliness,mating traditions,loathing of \206,love for \206|food blenders,tourists,poetry,discos,\216|talking tree,crab,bat,lobst,\262|beset,plagued,ravaged,cursed,scourged|\226 civil war,\233 \230 \231s,a \233 disease,\226 earthquakes,\226 solar activity|its \203 \204,the \261 \230 \231,its inhabitants' \232 \205,\241,its \215 \216|juice,brandy,water,brew,gargle blasters|\262,\261 \231,\261 \262,\261 \233,\233 \262|fabulous,exotic,hoopy,unusual,exciting|cuisine,night life,casinos,sit coms, \241 |\260,The planet \260,The world \260,This planet,This world|n unremarkable, boring, dull, tedious, revolting|planet,world,place,little planet,dump|wasp,moth,grub,ant,\262|poet,arts graduate,yak,snail,slug|tropical,dense,rain,impenetrable,exuberant|funny,weird,unusual,strange,peculiar|frequent,occasional,unpredictable,dreadful,deadly|\202 \201 for \212,\202 \201 for \212 and \212,\210 by \211,\202 \201 for \212 but \210 by \211,a\220 \221|\233,mountain,edible,tree,spotted|\237,\240,\207oid,\223,\222|ancient,exceptional,eccentric,ingrained,\225|killer,deadly,evil,lethal,vicious|parking meters,dust clouds,ice bergs,rock formations,volcanoes|plant,tulip,banana,corn,\262weed|\262,\261 \262,\261 \233,inhabitant,\261 \262|shrew,beast,bison,snake,wolf|leopard,cat,monkey,goat,fish|\214 \213,\261 \237 \242,its \215 \240 \242,\243 \244,\214 \213|meat,cutlet,steak,burgers,soup|ice,mud,Zero-G,vacuum,\261 ultra|hockey,cricket,karate,polo,tennis"
    671   asplit(desc_list_str, desc_list, "|")
    672 
    673   galsize = 256 # number of systems in one galaxy
    674 
    675 }
    676 
    677 # main code block
    678 BEGIN {
    679   gameinit() # init everything
    680   split("", globalseeds) # init galaxy seeds
    681   buildgalaxy(player["galaxynum"], globalseeds) # build the current galaxy
    682   genmarket(0, economies[player["currentplanet"]]) # build local market
    683   printf("\nWelcome to awlite 1.8.1\n")
    684   dohelp()
    685   # start the REPL
    686   printf("\n\nCash: %.1f> ", player["cash"]/10)
    687   while((getline cmd) > 0) {
    688     cmd = tolower(cmd) # accept both uppercase and lowercase
    689     spi = index(cmd, " ") # first whitespace in cmd
    690     action = (spi == 0) ? cmd : substr(cmd, 1, spi - 1)
    691     paramstr = (spi == 0) ? "" : substr(cmd, spi+1)
    692     if(cmd == "q" || cmd == "quit") break
    693     else if(cmd == "h" || cmd == "help") dohelp()
    694     else if(cmd == "r" || cmd == "rand") dotweakrand()
    695     else if(cmd == "m" || cmd == "mkt") domkt()
    696     else if(cmd == "l" || cmd == "local") dolocal()
    697     else if(cmd == "g" || cmd == "galhyp") dogalhyp()
    698     else if(cmd == "st" || cmd == "stat") dostat()
    699     else if(action == "i" || action == "info") doinfo(paramstr)
    700     else if(action == "b" || action == "buy")  dobuy(paramstr)
    701     else if(action == "s" || action == "sell") dosell(paramstr)
    702     else if(action == "j" || action == "jump") dojump(paramstr)
    703     else if(action == "f" || action == "fuel") dofuel(paramstr)
    704     else if(action == "up" || action == "upgrade") doup(paramstr)
    705     else if(action == "save") dosave(paramstr)
    706     else if(action == "load") doload(paramstr)
    707     else printf("Unknown command")
    708     printf("\n\nCash: %.1f> ", player["cash"]/10)
    709   }
    710   print "\nBye!"
    711 }