Commit graph

5429 commits

Author SHA1 Message Date
001674e5c5 fairland: Drop place_island() parameters @xp, @yp
There is no need to pass coordinates back to the caller.  Replace
parameters @xp, @yp by local variables @x, @y.

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
2021-01-05 10:41:36 +01:00
4bbd8b9fb3 fairland: Precompute "exclusive zone" for speed
grow_one_sector() and place_island() pass candidate sectors to
try_to_grow() until it succeeds.

try_to_grow() fails when the candidate isn't water, or there are
sectors belonging to other islands within a minimum distance.
It does that the obvious way: it searches for such sectors.

When there is plenty of space, try_to_grow() rarely fails when it
searches.  For each land sector, we visit the sectors within minimum
distance, plus a little extra work for the rare failures.

In a more crowded setup, however, try_to_grow() fails a lot, and we
visit sectors many times more.

Example: Hvy Fever

    8 continents
    continent size: 60
    number of islands: 64
    average size of islands: 10
    spike: 0%
    0% of land is mountain (each continent will have 0 mountains)
    minimum distance between continents: 6
    minimum distance from islands to continents: 3
    World dimensions: 140x68

This is a crowded setup.  With -R 1, try_to_grow() fails almost
750,000 times due to minimum distance, and takes more than 18 Million
iterations.

With default minimum distances 2 and 1, it fails less than 700 times,
taking less than 14,000 iterations.

Instead of searching the surrounding sectors every time we check a
candidate, precompute an "exclusive zone" around each island where
only this island may grow the obvious way: when adding a sector, visit
the sectors within minimum distance and add them to the island's
exclusive zone.

When @extra_distance is non-zero, try_to_grow() still has to search,
Only place_island() passes non-zero @extra_distance.  The next few
commits will get rid of that.

Complication: since the minimum distance for growing islands may
differ from the minimum distance for growing continents, we have to
recompute exclusive zones between growing continents and islands.

For the Hvy Fever example above, this reduces the number of sector
visits by more than 90%, and run time by more than 70% for me.  With
default distances, it's a wash.

Of course, fairland performance is hardly an issue on today's
machines: it takes fairly impractical setups to push run time over a
second.

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
2021-01-05 10:41:36 +01:00
6a7eaee42f fairland: Change try_to_grow()'s last argument to extra_dist
Callers pass @di for continents, @id for islands, possibly plus some
extra distance.  Pass just the extra distance, and compute the rest in
try_to_grow().  This  prepares for the next commit.

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
2021-01-05 10:41:36 +01:00
cd7c4cdf20 fairland: Rename INFINITY to INFINITE_ELEVATION
Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
2021-01-05 10:41:36 +01:00
380d2e9f55 fairland: Simplify search for next wilderness to elevate
To find the wilderness sector to elevate next, elevate_land() searches
the non-mountain, non-capital sectors of the island for one that
maximizes a function of its distance to mountains and to sea.

The search ignores already elevated sectors in a less than obvious
way: 1. it never picks a sector where the function yields -INFINITY or
less, and 2. when elevating a wilderness, its (cached) distances get
reset to values that make the function return a more negative value.

Use a more direct check of "not yet elevated": elevation is still the
initial -INFINITY.

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
2021-01-05 10:41:36 +01:00
4f0af0bae0 fairland: Use INT_MAX as initial minimal distance in iso()
Simpler and more obviously correct than WORLD_X + WORLD_Y.

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
2021-01-05 10:41:36 +01:00
d4dcfeec54 fairland: Nicer & much faster replacement for next_vector()
next_vector() is kind of cute, but it is also unobvious, cumbersome to
use, and slow for arguments greater than one.

Replace it by hexagon_first(), hexagon_next().  The new code takes
O(1) time, whereas the old code takes O(n).  Iterating over a hexagon
changes from

       for (i = 0; i < n; ++i)
           vector[i] = 0;
       do {
           x = x0;
           y = y0;
           for (i = 0; i < n; ++i) {
               x = new_x(x + dirx[vector[i]]);
               y = new_y(y + diry[vector[i]]);
           }
	   do stuff with @x, @y...
       } while (next_vector(n));

to

       hexagon_first(&hexit, x0, y0, n, &x, &y);
       do {
	   do stuff with @x, @y...
       } while (hexagon_next(&hexit, &x, &y));

In my measurements, it's ~8% slower for n == 1, 25% faster for n == 2,
and more than three times faster for n == 6.

fairland's performance is dominated by it.  Creating worlds for the
Common Fever blitz (distance arguments 3 and 2) and the Hvy Fever
Blitz (distance arguments 6 and 3) on my machine speeds up by a factor
of 1.6 and 2.1, respectively.

Of course, fairland performance is hardly an issue on today's
machines: it takes fairly impractical setups to push run time over a
second.

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
2021-01-05 10:41:36 +01:00
68375704b8 fairland: Report missing and stunted islands
fairland can create fewer and smaller islands than the user requested.
Report like this:

    No room for island #13
    6 stunted islands

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
2021-01-05 10:41:36 +01:00
41d9882860 fairland: Check first two continent sectors properly
grow_continents() places the first two continent sectors without
checking for collisions or minimum distance.  Unlikely to be an issue
in practice, as growing such a continent will almost certainly fail.
Fix it anyway.

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
2021-01-05 10:41:36 +01:00
f314ead777 fairland: Move "is water" check into try_to_grow()
Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
2021-01-05 10:41:36 +01:00
3fc24e35b9 fairland: Move capital initialization to drift() & simplify
Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
2021-01-05 10:41:36 +01:00
cde2497a75 fairland: Move initialization of elev into create_elevations()
Move it so the next commit can inline init() into drift().

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
2021-01-05 10:41:36 +01:00
29992a90a8 fairland: Move global variables mc[] into stable(), eliminate mcc
Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
2021-01-05 10:41:36 +01:00
9623f3e038 fairland: Fix "Only managed to grow" error message off by one
Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
2021-01-05 10:41:36 +01:00
b392b68bd5 fairland: Eliminate global variable @fl_status
grow_one_sector() sets @fl_status when it fails to grow a continent.
grow_continents() uses @fl_status to find out whether
grow_one_sector() succeeded, and main() uses it to find out whether
grow_continents() succeeded.

Change grow_continents() to return success / failure.
grow_one_sector() already does.  Use the return values, and eliminate
@fl_status.

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
2021-01-05 10:41:36 +01:00
cf27d16e47 fairland: Eliminate global variable @secs
Move global variable @secs into grow_islands() and grow_continents().
Its other users can use isecs[c] instead.

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
2021-01-05 10:41:36 +01:00
fd0ef2c645 fairland: Move global variable @spike into grow_one_sector()
Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
2021-01-05 10:41:36 +01:00
b71abefaf0 fairland: Global variable @mind is write-only, drop
Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
2021-01-05 10:41:36 +01:00
ee318d8e9e fairland: Set isecs[] for continents as well & simplify
isecs[] is left zero for continents.  Set it to @sc instead, and
simplify two loops over land sectors.

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
2021-01-05 10:41:36 +01:00
b53ecacf40 fairland: De-duplicate map from elevation to sector type
Both write_sects() and map_symbol() map from elevation to sector type.
Factor out as elev_to_sct_type().  Inline map_symbol() into output()
and simplify.

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
2021-01-05 10:41:36 +01:00
db71fa497c fairland: Fix checking of distance arguments
main() rejects the distance between continents when it exceeds WORLD_X
/ 2 and WORLD_Y / 2, and the distance between continents and islands
when it exceeds WORLD_Y and WORLD_Y.  Nuts.  Has always been that way.

Reject either when it exceeds the maximum distance between sectors.

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
2021-01-05 10:41:36 +01:00
21c311dba2 Revert "Make fairland finish argument parsing before reading econfig"
This reverts commit d2a7bb6b6f.

parse_args() uses WORLD_X and WORLD_Y to check the distance arguments.
Calling it before reading econfig is wrong, because at that time
WORLD_X and WORLD_Y still have the compiled-in default values instead
of the actual ones set in econfig.

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
2021-01-05 10:41:36 +01:00
7c593f7e86 fairland: Reject continent size 1
fairland creates continents of size 1 just fine, but the newcap_script
it emits doesn't work: the newcap command requires a second wilderness
sector to the right of the first sector.

Reject continent size 1.

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
2021-01-05 10:41:36 +01:00
6aa3d37d6c fairland: Simplify defaulting optional positional arguments
Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
2021-01-05 10:41:36 +01:00
3501df487f fairland: Check positional arguments more thoroughly
fairland silently "corrects" some bad arguments.  Reject them instead.
It neglects to check others completely.  Fix that.

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
2021-01-05 10:41:36 +01:00
c206fe83c2 fairland: Consistent error message format
fairland prefixes error messages with several variations of "fairland:
error -- ", but also with "ERROR: " and nothing at all.  Consistently
prefix them with just the program name.

Some error messages end with a period, most don't.  Drop the periods.

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
2021-01-05 10:41:36 +01:00
9c07d02519 fairland: Report errors to stderr, not stdout
fairland reports some errors to stdout instead of stderr.  Fix that.

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
2021-01-05 10:41:36 +01:00
581d1bae12 fairland: Tweak progress messages
"fairland: unstable drift -- try increasing DRIFT_MAX" is confusing:
it looks like an error, but isn't, and increasing DRIFT_MAX requires a
recompile.  I'm not sure it can happen.  Replace by just "unstable
drift".

"fairland: error -- continent %c had no room to grow!" is pretty
redundant: it's always followed by "Only managed to grow %d out of %d
sectors."  and then "ERROR: World not large enough to hold
continents".  All it adds is which of the continents failed to grow,
and that's not actionable.  Drop the message.

The message sequence "designating sectors...", "adding resources...",
"setting coastal flags...", and "writing to sectors file..." is a bit
of a lie as these four tasks aren't actually done one after the other.
Replace by just "writing to sectors file..."

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
2021-01-05 10:41:36 +01:00
a6ac42d5f6 fairland: Drop "might be too small to fit continents" warning
Commit de81e4e20 "Change fairland not to reject small worlds without
trying" (v4.3.25) downgraded it from error to warning, pointing out it
the size may well work, and when it doesn't, fairland fails cleanly.

When it works, the warning is pointless.  When it doesn't, it's
redundant.  Drop it.

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
2021-01-05 10:41:36 +01:00
2e63b6e02a fairland: Collect command line global variables in one place
Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
2021-01-05 10:41:36 +01:00
92fa1aa917 man/fairland: Fix typos, polish markup, clarify text
Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
2021-01-05 10:41:36 +01:00
41a2a62273 fairland: Drop option -o
With -o, fairland doesn't add resources.  This is pretty redundant;
the deity can unset resources with "edit l * i 0 g 0 f 0 c 0 u 0".
Drop the option.

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
2021-01-05 10:41:36 +01:00
98e5b4867a fairland: Drop option -a
With -a, fairland makes the capital sector an airfield to "mark the
continents [...] so that you can tell them from the islands".  This is
pretty redundant since commit afc0ef94e "Make fairland record the
island number in the deity territory", v4.3.31.  Drop it.

The map fairland prints is not affected.  The continents are clearly
visible there.

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
2021-01-05 10:41:36 +01:00
1faab45815 fairland: Make write_newcap_script() failure fatal
When write_newcap_script() fails, it complains to stderr and fails.
main() doesn't bother to check for failure.  Has always been that way.
Fix main() to check.  Also adjust write_newcap_script() to return one
on success, zero on failure, like the other functions do.

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
2021-01-05 10:41:36 +01:00
b28090a6b3 fairland: Move #include to the beginning where they belong
Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
2021-01-05 10:41:35 +01:00
c34df29c6c fairland: Add a comment describing how fairland works
A comment describing how fairland works was lost some time after
Chainsaw 3.31.  This is the last known version:

    The algorithm starts out by placing the "capitols" on the torus in
    such a way so as to maximize their distances from one another (this
    uses the perterbation technique of calculus of variations).  Then from
    these seeds, the continents are grown.  The kind of shape they grow
    into is determined by the "spike" argument <sp>--the higher the spike,
    the more spindly they will be.  If you lower the spike, the continents
    will be more round.  The continents never touch one another, the
    argument <di> specifies how many sectors of water should be between
    the continents at all times.  The continents grow to the size you
    specify with <sc> and they all get the same number of mountains
    (specified by <pm>).  The other arguments should be self explanitory.
    If #define ORE 1, then resources will be placed in the sectors as well.
    You can alter the #define ORE_MAX, IRON_MIN, GOLD_MIN, FERT_MAX and
    URAN_MIN to affect what kind of resources the world gets.

It leaves much to be desired.  Add a more thorough one.

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
2021-01-05 10:41:35 +01:00
883f53be74 fairland: Add print_own_map(), print_elev_map() for debugging
Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
2021-01-05 10:41:35 +01:00
89a386aa60 tests/fairland: Cover "plenty of space" better
Reduce number of islands in fairland run "plain" so there's plenty of
space for them.  Scarce space is still covered by run "stunted".

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
2021-01-05 10:41:35 +01:00
f18159fb23 tests/fairland: Improve coverage
Rename the existing fairland run to "plain".

New run "stunted" to cover larger minimal distances, islands
that can't fully grow, and islands that can't be placed.

New run "no-spike" to cover 0% spike.

New run "spike" to cover high spike percentage, mountains and -i.

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
2021-01-05 10:41:35 +01:00
c4441014e3 fairland: Show a more useful map
The map fairland shows has absolute 0,0 in the top-left corner, while
POGO's map * has it in the center.  Shift fairland's map to match
POGO's.

The map shows sea as '.', island sectors as '%', capitals as '#',
mountains as '^', and other continental sectors as a letter or digit
that encodes the continent number modulo 62.  When a continent has no
"other" sectors, its continent number is not shown.  Remove this
pathological case by using letter/digit for capitals, and '#' for
other continental sectors.

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
2021-01-05 10:41:35 +01:00
1cc54f3625 tests/empdump: Fix to honor "make check-accept"
Delete stray EMPIRE_CHECK_ACCEPT=.  Goes back all the way to commit
5a1544f92 "tests/empdump: New; exercising the empdump utility".

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
2021-01-05 10:41:35 +01:00
ddefd7173e tests: Make "make check-accept" accept new files
Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
2021-01-05 10:41:35 +01:00
e823a4f055 tests: Drop some dead code from test-common.sh
Left behind by commit c594b6120 "tests: New helper cmp_out1".

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
2021-01-05 10:41:35 +01:00
4a1ec06364 Update copyright notice
Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
2021-01-05 10:41:28 +01:00
7d8e2aed16 damage: Shield embarked planes and land units from sector damage
Damage to a ship or land unit, say via pinpoint bombing, doesn't
damage loaded planes and land units.

Damage to a sector, say via strategic bombing, doesn't damage ships
there, but it does damage planes, even when loaded on a land unit (but
not when loaded on a ship), and land units, even when loaded on a land
unit or a ship.

This makes no sense.  Sector damage spills over to land units that way
since Chainsaw 3 added them, and to planes since 4.0.9.

Change sectdamage() not to damage land units and planes loaded on
ships or land units.

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
2021-01-05 10:41:28 +01:00
b109430b8c load lload: Refuse to load satellites in space
Doesn't affect the stock game, because none of its satellites are
light, let alone x-light.

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
2021-01-05 10:41:28 +01:00
6ce25bdd4f launch edit: Take satellite off carrier on launch
Doesn't affect the stock game, because none of its satellites are
light, let alone x-light.

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
2021-01-05 10:41:05 +01:00
bf9cb33fc2 load: Move check for hardened plane into plane_loadable()
This de-duplicates the check, and skips it when unloading.  It never
made sense there, and can't happen anymore since the previous commit.

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
2021-01-05 10:40:40 +01:00
8b9ce7be43 subs: Maintain plane and land unit fortification invariant
Make lnd_prewrite() and pln_prewrite() ensure that land units and
missiles loaded on a land unit or ship are never fortified / hardened.

This takes care of edit neglecting to zap fortification on load.

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
2021-01-05 10:40:34 +01:00
f5514cb452 edit: Fix fortification limit of embarked planes and land units
We generally preserve the invariant "land units and missiles loaded on
a land unit or ship are never fortified / hardened": fortify and
harden refuse to touch embarked land units and missiles, load and
lload zap land unit fortification on load, and refuse to load hardened
missiles.

The exception is edit, which happily fortifies embarked land units
(edit u key 'F'), hardens embarked missiles (edit p key 'F'), loads
fortified land units (edit u keys 'S' and 'Y') and hardened planes
(edit p keys 's' and 'y').

Fix the first two by correcting the new value's upper limit to zero
for embarked land units and planes.  The next commit will take care of
the rest.

The fix is visible in tests where test setup fortifies land units with
"edit u * F ?..." without excepting embarked ones.

Signed-off-by: Markus Armbruster <armbru@pond.sub.org>
2021-01-05 10:38:57 +01:00