December 29, 2022

Combat Simulation I

My party has just run through their first combat. It went well, especially considering this was their first combat in Authentic D&D. Over the holiday, I had some time to think about that combat, and whether or not I could use any algorithms to offload any of the work in running the NPCs, particularly dumb ones with simple motivations. In any case, an exploration of that project is sure to be enlightening in itself.

I'll make the code available here.

Another advantage of a system like this is that we can capture a combat in a repeatable (and version-controllable) way. At least to a degree: the beauty of D&D is that a rule system never fully captures the range of human ingenuity, and so to some extent the rule system must always be fluidly updated to match feedback from actual play.

We can break combat action at its simplest level down into:

  • move somewhere
  • attack something
  • interact with something

These actions can be done more than once per turn in any order, depending on the available action points and circumstances, with the goal of breaking the enemy's will through death or morale collapse.

Every action taken by an active participant in the combat should be in service of this goal. Participants who are more intelligent or better trained will select better options than others. [we can also invert this selection function to simulate the actions of PCs, who will choose the worst option in any possible circumstance]

I'll start by tackling movement, which I thought would be the easiest of the three actions. That turned out to not bode well for the others.

Let's consider a battlemap with no obstacles and two actors. We'll play with the actor on the right, giving it 3 AP and the ability to move according to the Authentic D&D rules. Without considering other constraints, this means that for each AP, the actor can move between 1 and 8 hexes in a straight line. This generates the following possibility space:

Movement possibilities: darker hexes mean more possible paths to reach that hex

However, there are several constraints we must add, which proved to be non-trivial to program. The actor can only change speed once, they can only rotate left or right 60$^\circ$ at the end of each AP, and they can't move through obstacles (in this case, that is only the other actor). That reduces our space to the following:

Possibility space with constraints added

The space is still mostly symmetrical, but we can also see the disruption caused by the presence of another actor. Keep in mind also that this is the possibility space for exactly 3 AP; in many cases our actor would only want to move one or two AP, or perhaps even look ahead multiple turns.

Movement in 1 AP, darker hexes are a higher stride

For example, if the actor on the right wants to attack the actor on the left, they will move at stride 2 for 2 hexes to enter melee and then have 2 AP left to attack. In this way, we can build up a desirability score for each possible hex, and take an optimal or near-optimal combination of actions as a result.

I'm not quite done with movement yet. Right now, we only avoid obstacles of other actors. I'll need to do some thinking on how to add simple static obstacles such as walls or buildings.

November 25, 2022

Physics Sage Ability, Initial Investigation

Ludwig Boltzman, who spent much of his life studying statistical mechanics, died in 1906, by his own hand. Paul Ehrenfest, carrying on the work, died similarly in 1933. Now it is our turn to study statistical mechanics. Perhaps it will be wise to approach the subject cautiously. - Goldstein, States of Matter

I am working to add another possible player to my game, and they have chosen (in what appears to be a theme) another backlog study, this time the Illusionist study of Reality $\to$ Physics:

Physics is a sage study in the field of Reality by which the character learns the physical laws associated with the movement of bodies and objects, vibration, sound, optics and light, fire and heat, the movement of water, air and earth, flight, pressure, electricity, magnetism and many similar branches and topics that give the character insight into the way that energy (including magic) ebbs and flows through the environment. This knowledge provides opportunity for the character to understand and ultimately control, to some degree, these forces.

There is a sense in which my background prepares me well to contribute to this study. On the other hand, the immense depth of this study presents a challenge. To begin with, therefore, I'll work from a framework as follows to start developing progressively more complex abilities, relying somewhat on how I remember my own training progressing.

Identification $\to$ Manipulation $\to$ Fabrication

These won't map directly to Amateur $\to$ Authority $\to$ Expert $\to$ Sage, but I'm finding it a helpful rubric nonetheless.

Based on the description on the Authentic Wiki, as well as the typical definitions of the broader fields of physics, we can line out four distinct areas of study: Mechanics, Electromagnetism, Thermodynamics, and Thaumaturgy (the physical study of magic). My gut feeling right now is that Thaumaturgy belongs in a "higher level" of ability, and so we can start to build out our abilities structure. I'll focus on Identification for now; there is plenty of time to mull on the rest.

Amateur

  • Identify Mechanics. Recognize the physical laws governing the observed mechanical effects of the movement of bodies and objects, allowing limited prediction of their origin and future disposition. Examples include analysis of a potential landslide or the stability of a built structure, or the origin of a thrown or shot projectile.
  • Identify Electromagnetism. Recognize the physical laws governing the observed electromagnetic effects of natural and artificial sources of electricity, magnetism, and light. Examples include the identification of naturally magnetized lodestone deposits or the distance to a light source at night.
  • Identify Thermodynamics. Recognize the physical laws governing the observed electromagnetic effects of natural and artificial sources of fire and heat. Examples include determining the temperature of a heated metal by its color or the placement of proper ventilation when making a fire in an enclosed space.

Authority

  • Identify Thaumaturgy. Recognize the presence of magical energy and identify its likely intent. Examples include predicting a spell during its preparation but before its casting based on the ebb and flow of magical energy the spell's preparation produces.
We could even imagine an additional tier of identification for certain areas of physics which would be more difficult to learn, such as astrophysics (or even potentially anachronistic developments such as relativity or quantum effects).

I have more to think through on this but I feel this is a good start to the endeavor.

October 19, 2022

Detail XV: Farms in the Hinterland

What lies between the settled areas and the "true" wilderness? And how can we determine this from the wilderness maps?

Let's begin with an example region featuring two settlements. The settlement to the left is about 6000 persons, and the right much smaller, about 130. These numbers refer only to the urban population of the settlement proper. This population number is the source of infrastructure numbers, and for that reason is fixed.

lighter "wilderness" hexes can be considered cultivated

Of course, not everyone lives in the settlement, and so we consider that 85% of the 20hex's total population is rural. This comes out to about 15000, and then increases to 18000 due to market spread. Already we have a problem: how are all these people fed?

As a note: the intent is not to micromanage the result such that every individual of hundreds of thousands of people each have their determined job, place, and caloric intake. However, we do want to ensure that our results are consistent and coherent. The players are not interested in the type of detail produced by slavery to the numbers. They want to know (usually) how far outside the town is the dungeon? It is unlikely that a typical dungeon (as understood by most parties) would remain uncleared within several miles of the farming hinterland. We must make the numbers serve us instead.

For some reason, the online worldbuilding community is convinced that a square mile of farmland can support 180 people. I'm not sure what the origin of this number is, and I can't exactly say that it's wrong, but it certainly ignores the incredible variability of arable land and agricultural technology. But let's pretend it's a viable average at least, so we can see where we land.

Our total population of ~24000 people, then, will require some 153 1hexes of farmland outside the city (each 1hex is 0.866 sq mi). The 20hex itself is only 400 1hexes. That certainly reduces the tight feel of the map above into something essentially cleared of all trees and wilderness.

Alternatively, we can take the 6000 as the total population. That requires about 41 1hexes to feed. There are 26 1hexes on the above map which are adjacent to settled hexes. So let us assume that the heavy cultivation of those hexes (including farming, orchards, timber management, and hunting) added to any small-plot cultivation of the denser settled hexes yields the caloric requirements of the 20hex.

Quite a lot of work to end up back at the same square. However, there are some key takeaways from this experiment nonetheless.

  1. The adjacent wilderness hexes are not pure wilderness, and in fact are probably heavily farmed. No orc lairs here.
  2. For this map in particular, the smaller settlement, although tiny in comparison to the larger, controls a slight majority of the productive land (which it likely sends a few miles down the road to the larger settlement). This is probably a collection of holdings of 1-3 knights who are tied in some way to the larger settlement.
  3. As a corollary, the smaller settlement will have about the same number of agricultural class with a much smaller merchant/elite class. They will depend heavily on the larger settlement for military protection and most market services.
  4. There are about 75% fewer people in these hexes than I'd previously thought. That will have significant implications if questions arise about hex wealth or the mustering of an army.

October 14, 2022

Falconry

Turning and turning in the widening gyre

The falcon cannot hear the falconer;

Things fall apart; the centre cannot hold;

One of my players wants to begin with a focus in Falconry. I will try my hand at expanding out the rules for the Amateur status. As an ancient and on-going pastime, there is no shortage of real-world information regarding the training and behavior of these birds.

Bird Companionship is an amateur-status sage ability where the character gains personal satisfaction and increased experienced bonuses from the presence and use of falcons. For any combat where the falconer's bird participates in the fight, the falconer receives an additional 5% experience bonus. If the falcon is killed in combat, this bonus is not awarded.

Rouse to Hunt is an amateur-status sage ability where the character may send the falcon to hunt for game food. Refer to the Hunting sage ability for the time necessary to locate a target.  A successful hit which results in a stun or kill will allow the bird to grip the game up to its own bodyweight in its talons and return to the falconer. Heavier prey which is stunned or killed must be killed or retrieved by the falconer.

Train Falcon is an amateur-status sage ability which allows the falconer to educate a young bird so that it will perform as desired. Training a bird is a long and complex process which requires near-constant attention. The process begins with either capture or hatching. A wild hawk under a year of age (a passager) is suitable for training. These can actually be easier to handle than a fletching (called an eyass) obtained either from a hatchery or a wild nest, since an older bird has had time to develop some natural instincts. Falcon training proceeds through three stages: Manning, Behavior, and Hunting. These stages are attained by a series of ability checks (using wisdom, intelligence, or charisma) made by the falconer. One roll may be made per day; the ability stat attempted on a given day cannot be the one used the day before. Each stage requires that a total of 10 successful rolls be made out of a minimum of 15. The last four rolls of the 15-roll sequence must be successes (or the last three rolls in the case of a passager). The falconer receives a +1 bonus to the check for each 20 points possessed in Falconry. Once trained, the bird may be used by the falconer or sold; however, a purchased bird will require 1-2 weeks of acclimation to its new owner before its benefits can be realized.

Manning is the process of acclimating the bird to society and in particular to the falconer. 

Behavior trains the bird to respond properly to necessary equipment such as gloves, perches, hoods, and leashes, and to follow simple vocal or visual signals. While falcons cannot be trained to the same degree as dogs, they can nonetheless learn to obey commands such as returning to glove, perching, or hovering.

Hunting trains the bird to attack ground and air prey, and to retrieve prey for the falconer when possible. The falcon will rely on its own instincts and the body language of the falconer when choosing a target while hunting or in combat.

Waken Regard is an amateur-status sage ability which affords the falconer opportunities to meet and conspire with persons of local authority. An authority not more than one rank above the character may extend an invitation to join them on a hunt.

I am not yet satisfied with Waken Regard. Perhaps it may also grant a bonus when making a request of an authority figure. However, it will need to differ at least in degree from Waken Admiration, the Expert-status sage ability.

I'm sure there are other considerations and issues which will arise, but that is part of the iterative process of design.

September 15, 2022

Trade VII: Why Can't I Find What I'm Looking For?

The market system is based on rarity, a calculation derived from the local availability of a good and the total available amount of that good (or at least, the amount that is reachable). Goods are arranged hierarchically into stages, from "raw" materials all the way up to complex goods which require many precursors which themselves are often manufactured.

The system I've written to handle these complex goods works great. I can add "recipes" to a master file and calculate a price for any good I can dream up (and properly research).

A core concept of the system is that of a reference, which represents the amount of a good which can be purchased by 1500 oz of gold (approximately 3,125 gp). For small goods, like crops, a single reference can represent tons of material.

The question I am currently working through is: how to take the bits of information I have and translate them into an availability metric.

The market and price list provide two important functions to the party: they generate striving (to aspire towards a piece of equipment they can't yet afford) and scarcity (equipment that is not available for any cost).

We can, of course, allow the price itself to perform both of these functions. However, all equipment should not be available at all times simply because it exists somewhere in the system. The question is then how to determine how often a good will appear in a given market.

My initial thought is to use the local number of references to determine this rarity. If less than a single reference exists in the local market, then there is a good chance it'll not be available that week (or similar period of time). This means that we can recursively build up a local reference amount for manufactured goods based on the local references of its components.

Once I have this number, I can either use it as a probability straight up, or mitigate it a bit by comparing it to the rarity constant (itself a function of the size of the market). For now, I'm simplifying the final product by taking the log of that number and using that to determine a "rarity score."

\[\mathit{rarity} = \left\lceil-\log_{10}\left(\mathit{ref}_\ell\right)\right\rceil\]

Example: for a good with a 0.002 local references ($\mathit{ref}_\ell=0.002$), the rarity score is 3. That can be further used for a roll as needed. We could say that an item with a rarity of 3 will be available in a market on a 1 in 1d30, for example. This makes rare items difficult to find, but not impossible. The party could either hang around until some were available, or travel to another (and perhaps more favorable) market.

The simplest method to determine local references is to use the minimum of all constituent components, without regard to the amounts needed. I'm not super comfortable with this, because my conception of references is tied more tightly to actual production numbers (which I generate from the working population availability). But it has led to some bizarre cascades. Fodder, for example, is very cheap, but a lot of it is needed to raise a foal into an adult horse. This also means that the number of fodder references needed per horse is very very low. Both of these can quickly get out of hand as very small or large numbers are passed through several iterations of the recipe parser.

I tried dividing local references by the amounts needed, or comparing the local references available to the number needed. Neither are really satisfactory. I think I will use the simple method described above, and if I get a lot of complaints in play then I will revisit it.

The recipe system is built and effective. There are a lot of recipes to write, however.

August 10, 2022

Weather Engine

Way back when I first started coding up this project (several years ago now), one of the first things I implemented was Alexis' weather generator.

It works pretty well. Working with real-world data as the seed input is easy. I've extended the system a little bit to make it more versatile throughout the year.

The only bits of data I need are: minimum and maximum monthly temperature in Fahrenheit (high and low), and the months in which the high occurs. I also collect high and lows for the precipitation (in mm) and the month in which that high occurs.

"climate": {
	"temperature": {
		"minimum": {
			"high": 77,
			"low": 47,
			"highMonth": 7
		},
		"maximum": {
			"high": 89,
			"low": 62,
			"highMonth": 7
		}
	},
	"precipitation": {
		"high": 126,
		"low": 70,
		"highMonth": 7
	}
}

With these numbers, it's a simple enough matter to fit a sinusoidal curve to give me the monthly average high and low for any given month. Admittedly, very few places on earth have a perfectly balanced summer/winter cycle, but this can be forgiven, and the players are unlikely to notice in any case (particularly across such a relatively small area).

A whole year of temperatures

I've incorporated a few extra bits into my version of the weather engine. The first is apparent temperature. At higher temperatures (80F+), high humidity makes the air seem hotter. This is usually reported as Heat Index. Conversely, when the air is cold, wind can make it feel even colder (Wind Chill).

Next, I used a IDW algorithm (my go-to) to spoof data for all hexes based on just a few inputs. I grabbed a few stations from around the State, but with this method, I don't need to worry about researching all 170+ points.

On one of the original weather posts, Vlad made a comment about how much rain it generates. These are some good thoughts for possible mods. I don't see that problem cropping up at the moment, but it's something I'll keep an eye on.

As another potential update, I could use a Gaussian distribution to generate the temperature for each day, or calculate the daily drift differently. But for now this works well.

June 22, 2022

Preparing a Setting

I have been working hard on a setting of my home state in another universe. It's been way too long since I've run a game and I'm getting itchy. So far, this has been a good test of things I've learned and things I'm learning, and its given me an opportunity to re-write a lot of the market code in particular. That's helped clean things up and hopefully erased some hidden errors.

I've divided up the region into nine "regions." Not kingdoms, exactly. But distinct culturally (in the real world) and thus a good proxy for translation into a fantasy version. Some populations get swapped over to halfling, elven, orc, dwarf, etc.

The main challenge now is to apply these principles to the regions. There are some differences in tech level here, ranging from a T-8 chiefdom all the way up to T-12. The danger is that I will not be clever enough to avoid "sameness" in the relatively small areas in question (around twenty 20mile hexes). Of course, sameness is not necessarily a bad thing - these regions will share trade routes as well as language - but we want the variety to mean something. Otherwise, there is no reason for them to be separate autonomous regions.

My familiarity with the real-world terrain is also a crutch. Because the farmland is so fertile, nearly every square inch of land that can be farmed is being farmed, a development only in the last 100 or so years. Reconstructing the "original" terrain for a much less populous society has been an interesting challenge.

I will no doubt continue to refine the descriptions of and relationships between the regions, and my next task will be to seed the commodity list with enough stuff to satisfy a party starting out. A work like this is never done, but I want to get it into a playable state. I also have 1mile maps ready for many of the hexes, particularly in the northern part of the state.

April 21, 2022

Trade VI: Simplifying to Complexity

I've been working on my algorithms to generate trade networks and manufactured goods. Although probably several years out of date now, Tao's Trade System remains required reading for anyone wanting to do something similar.

This iteration was a much-needed rework of the way a lot of the prices were generated. I had gone through Alexis' work linked above and simplified it to my needs. He is using references based in the real world, whereas I am trying to generate from scratch.

I found that of all the equations, the final price of an object simplified to just a few variables. The first is the local value of a unit (oz) of gold (in cp, all prices are expressed in cp and can then be abstracted back up to sp or gp as needed). I'll call this $g$. To find this number, we need the local availability $g_\ell$ and the total availability $g_t$. $g_t$ is the total number of references reachable from the point in question. This is easy to do with network algorithms. Local availability is a weighted distance calculation over all locations $i$ reachable from $\ell$: \[g_\ell = \sum_i \frac{g_i}{\textrm{dist}\left(\ell, i\right) + 1}\] By this calculation, if $i$ has 2 gold references, but is 4 days away, it contributes $\frac{2}{4+1} = 0.4$ references to $g_\ell$.

Right now, I am treating each hex as a node in the graph, but if there is no defined settlement there, all its resources are given to the closest city hex for pricing purposes.

We need a rarity factor $r$ that scales with the size of the network. As the network grows larger, a smaller $r$ is needed to balance things out. I'm trying this out for now: \[r = -\frac{1}{n}\] where $n$ is the number of nodes in the network.

The last two constants are the number of gold pieces per oz ($p = \frac{1\textrm{ gp}}{0.48\textrm{ oz}}$) and ratio of cp to gp ($c = \frac{100}{1}$). These are easy to change. A half-ounce gold piece is somewhat hefty; many coins in history would have been much smaller amounts. Gold is valuable enough that a small bit is worth a lot, and hence a great deal of value can be expressed in quite tiny coins. I like the idea of a gp being a weightier coin. I also try to base the sizes of my coins on current analogs that I can actually show to my players.

A thousand of these is no joke

So the final equation comes together: \[g = \left(r \cdot \frac{g_t}{g_\ell} + 1\right) \cdot p \cdot c \]

For an individual resource $q$, the equation is similar. First, we have to define the base cost $b$ of a unit of $q$. I found that this was the most important factor; it essentially represents the ideal economy where everything is in perfect supply. Whether a board-foot of wood is defined as 1 cp or 18 cp will have approximately a 18x effect on the final price of wood no matter where you are in the world. This becomes the key object of research when sketching out the system. It is then an easy matter to determine how many units of $q$ are equivalent to one reference of gold (assuming a gold ref = 1500 oz): \[\mathit{ref}_q = \frac{b}{\mathit{ref}_g = 1500\textrm{ oz}}\]

We obtain the rarity $r_q$ in a similar way as above with gold, using the distance weighted availability. The final price (in cp) of an item at location $\ell$ is then: \[\$_q = \frac{g}{\mathit{ref}_q} \cdot \left(r \cdot \frac{q_t}{q_\ell} + 1\right) \mathit{ref}_g\]

There are some other ways to view this equation. It can simplify again to: \[\$_q = \frac{g}{b} \cdot \left(r \cdot \frac{q_t}{q_\ell} + 1\right)\]

The next step is to determine the availability of labor references. I haven't quite decided how to assign these so I'll save that for a future post. We obtain the available references by once again iterating on the network: \[L_\ell = \sum_i \frac{L_i}{\textrm{dist}\left(\ell, i\right) + 1}\]

The cost of a material $m$ at a given stage (eg, hematite $\to$ iron ore) is then the cost of the raw materials (the unit cost $\$_m$ times the number of units $n_m$) plus the labor cost, which is raw material cost divided by the labor references: \[\$_m = \$_q \cdot n_m + \frac{\$_q \cdot n_m}{\mathit{ref}_L}\]

This step is repeated for each stage of the process, which can be quite complex. I developed a JSON schema to represent each manufactured material. To raise an auroch from a calf to weaned, you require the following.

{
  "item": "auroch (weaned)",
  "unit": "hd",
  "stage": 1,
  "tech": 7,
  "weight": 200,
  "recipe": {
    "materials": {
      "auroch (calf)": 1,
      "min": [
        {
          "maize": 483,
          "oats": 483,
          "barley": 483,
          "cassava": 483,
          "rice": 483,
          "wheat": 483
        }
      ]
    },
    "labor": "herdsman"
  }
}

Our raw materials are 1 auroch calf + whichever is cheaper between 483 lbs of feed, plus the labor of a herdsman. As long as every manufactured item "downstream" exists, this "item" will be available for purchase.

April 8, 2022

Resources XXIII: Crops and Climates

I've recently completed a little side project that will help resource placement significantly.

I cross-referenced the crop yields from EarthStat with Koppen climate data to get the prevalence for 175 different categories. The data isn't perfect - it doesn't take politics or demographics into account, for example - but I think it will be useful either in automation (as I plan to use it) or in beginning to think about where crops should be placed on a world map.

More details and the data itself can be found here in its own git repo.

March 24, 2022

Detail XIV: Benefits

I have put the world project on hold for a bit to get refreshed with other work.

Over at the Tao, Alexis has been reworking his hammers/coins/food system. It's a brilliant system with endless opportunity.

My own use of programmatic aids lets me break a 20-mile hex down into not just 6 6-mile hexes, but 400 1-mile hexes. This is a lot of detail; not something to be done by hand. And I do think the 20$\to$6$\to$2 system generates very beautiful results. I want to consider working on this for a while to see where it leads. By saving the random seeds that the model is built from, I can replicate a nice result without storing too much data.

This was the result from last year:

Last year's detailed hex

My latest version has some upgrades with color, but also additions of hex-type and benefits (hammers/coins/food). One major change is that the scale will be much different than the Higher Path: with many more discrete points, the number of benefits will be correspondingly higher.

An example (not of the same hex as above, I couldn't find the old version of this one) of a 20hex with an infrastructure of only 20. Yet, due to its presence in a forest, with some substantial clustering of hexes (producing a large number of high hex types), it generates a total of 134 hammers, 168 food, and 78 coins, calculated for each 1hex according to a binary system such that $B = 2 ^ {b - 1} + 1$, then added for the whole 20hex. This doesn't yet consider any benefits that would be added for the presence of a trade reference.

Updated hex

There are two paths forward. The first is to adjust the benefits conferred by each hex type such that the numbers become more reasonable. The 20hex total may not be as important as the individual 1hex number: eg, the settlement hex generates {'hammers': 5, 'food': 5, 'coins': 4} in the binary system, or {'hammers': 15, 'food': 5, 'coins': 4} in the decimal system. Without context, it is impossible to determine what constitutes a "reasonable" number. The second is to adjust the results of those benefits.

If a system is to work properly, it has to generate desirable and consistent results. The advantage of doing this programmatically is that I can implement totally different systems at the drop of a hat. The design loop is much tighter than hand calculation (although I am trying to avoid something that is so opaque it can't be replicated by hand).

March 4, 2022

Ex Nihilo VIII: Pressure

Pressure is easily defined based on latitude and the presence of landforms. The rules are defined based on posts from here.

July
January

These parts aren't very exciting. But they're helpful as a public log of my progress.

February 18, 2022

Ex Nihilo VII: Currents

Once the coastline is clearly defined, currents can be determined. I've detailed that process elsewhere, and these are the results for the map we're working with. The blue-white scale indicates the angle of the current in each cell from 0 to 360. There's probably a better way I can show this but for now this'll do.

This algorithm takes a really long time to run, so I think I'll accept these results as is, unless some hidden issue rears its head along the line.

February 15, 2022

Ex Nihilo VI: Tectonics III

Continuing on with the work of generating terrain from the tectonics. My first fresh crack uses both tectonic uplift and droplet erosion. These processes are cycled over and over until some condition is met. In this case, I start from a flat plane and stop when at least one cell is at its maximum height (25599 ft). The sea level is determined such that 29% of the total surface is land. I also apply a hypsometric curve to the land area so that higher elevations appear in roughly the same proportions that they do on our earth.

However, this algorithm generates a pretty boring topography. The mountains slope up uniformly from the coast, and the tectonic uniquenesses are not presevered. Usable, but disappointing.

The problem is that the erosion cycle is too powerful as the terrain grows from zero, and only the center of the continents (that is, the areas with the least erosion) have any chance of growing at all. To fix this, I began with a terrain generated directly from the relative uplift scaled to max height. The warping effects are clearly visible here but these will be smoothed out by the erosion algorithm.

Next, I again cycle through the erosion, but this time I rescale the height at the end of each cycle and apply the same hypsometric distribution. This yields a much more interesting topography.

The grayscale map is a bit hard to parse, so I threw the map into GIMP and applied a simple colormap. where 10,000 ft begins to turn into gray/white.

There are still some issues I can see, or improvements that can be made. Coastal areas are pretty uniformly low for many hexes inland (no Chilean Andes). Most areas do not have significant mountain ranges, although there is one range similar in size to the Tibetian plateau.

But this is the process. Design, test, repeat. This will be good for now and I'll move on to some other elements of the climate system.

February 4, 2022

Ex Nihilo V: Tectonics II

In the previous iteration, the tectonic plates were generated from a Voronoi algorithm, and thus had very straight edges. Here, I've made those more jagged, which should result in a more interesting coastal topography.

And it does! There is still a good deal of far-too-straight lines, but we can live with some of that. The ripple algorithm, which I'll tackle next, should help.

A bit of Gaussian blur:

Next, to closer approximate real-world distributions, I'll apply my hypsometric scaling.

Lastly, to get a feel for how this will shake out, I use the uplift values to generate a quick and dirty initial altitude map. The coastlines are a bit blobby, but I'm happy with this part of the process.

Getting the ripple algorithm to work will be my next order of business.

January 18, 2022

Ex Nihilo IV: Tectonics I

My previous work involved manually drawing out the tectonic boundaries. With the advantage of distance, I'm no longer married to that concept. Instead, we can generate them from scratch.

To approximate an irregular but blue-noised grid, I'll grab a Poisson Disk Sample from all points on the map. This is a pretty useful algorithm to know, so it's a good chance to rewrite it to be a bit more efficient. After generating the points, tectonic plates are generated as a Voronoi map with the Poisson Disks as the centers (the shapes it generates are too uniform, so I'll revisit that later). A Poisson radius of 100 hexes yields 36 centers and the following map. Oceanic plates (approximately % of the total area, close to Earth) are shown in a lighter shade.

Each plate is assigned a random Euler pole and angular rate of rotation. I can use this to find the strength of collision at each boundary. I'm not super satisfied with the equations I'm using but they can always be modified.

Next, the rate of tectonic uplift for a given hex is determined by each fault's effect on that hex, with a bonus for continental plates (which are lighter and tend to "float"). High uplift values will generate mountains and island chains.

In the past I've played around with various combinations of Perlin noise, domain warping, and other forms of distortion applied to the uplift map to get interesting topographies. The final topography is heavily dependent on the uplift value: the droplet model algorithm gives interesting local topography but is ultimately overpowered by the underlying uplift. So it's important to get it mostly right to begin with. That being said, I find it important not to get too caught up in fine details when there is underlying code to fix. And there is a lot of code to fix and refactor.

To get a rough idea of the kinds of terrain this will generate, we can mock up the elevation values from uplift (essentially scaling from 0 to our max height 25599) and apply a sea height of whatever makes the land percentage 29%. I changed the continents slightly from the plate image above.

Note how straight the lines are, something that will need to be fixed.

The elevation generator is quite slow (particularly given the size of the map), and so it may be a while before I have this round of kinks worked through. My basic algorithm is a droplet model which erodes land based on the tectonic uplift, water erosion, sediment deposition, and coastal erosion. In the past I have used the Wei-Zhou-Dong algorithm for depression filling (which makes all rivers flow to the sea), but I am not terribly pleased with it this time around. We'll see.