Skip to content

hornvale/spec

Repository files navigation

Hornvale::Specification

A semi-formal specification of Hornvale.

Rather than getting bogged down in implementation details immediately, I want to declare this idea 1 in some kind of coherent, unambiguous fashion. You know, so I can just write what I want instead of having to specify ownership semantics.

World

The Game World can be modeled as a directed graph G = (V, E) where:

  • V is a set of vertices, with each vertex vV representing a region in the game.
  • E is a set of edges, with each edge eE representing a passage between regions. An edge is an ordered pair (vi, vj) indicating a passage from region vi to region vj.

World Seed

Each world is generated by a distinct seed value, the World Seed, which determines pseudorandom number generation through each of the subsequent layers of the world structure. Specifics of this are implementation details.

Graph Generation

Vertices are generated on an infinite unit cartesian coordinate plane.

The initial vertex is always generated at (0, 0). It is given a seed value, a Region Seed, equal to that of the World Seed.

We expand outward, adding new vertices, using a discretized version of Bridson's algorithm (see reference implementation and example).

Bridson's Algorithm

We connect the vertices into a minimum spanning tree using Prim's algorithm, which seems to be well-suited to incremental growth (see reference implementation and example).

Prim's algorithm

Then we can randomly add cycles (see reference implementation).

Cycles example

This can easily generate more regions than anyone could explore, but we can modify these algorithms to expand incrementally in the direction of exploration.

Vertex/Region

Each vertex is given a seed value equal to the World Seed modified by its own x and y coordinates (specifics are an implementation detail; this could be just concatenating the world seed and the x and y coordinates and hashed, or ( W + Ax2 + By3 ) mod M where A and B are prime numbers and M is a very large prime number close to INT_MAX, etc).

Juice and Chunk Loading and Unloading

Each vertex corresponds to a region of the game world. The world is infinite but our hardware is not; consequently, we must use a chunk management system to determine how to direct computing resources to the world. This leads us to a concept of "juice," which is an attempt to model the engagement and interest of the player in a given region, and use that to determine how computing resources are allocated.

We can describe Jcurrent, the total current juice of the region, as follows:

  • Jcurrent = wproximityJproximity + wlingerJlinger
    • wproximity is some fractional weight for the proximity factor
    • wlinger is some fractional weight for the lingering factor
    • Jproximity (d) = J0e-λd
      • Jproximity (d) is the juice level at distance d
      • J0 is the juice level at the player's location
      • λ is the decay constant, determining how quickly the juice level decreases with distance
      • d is the distance from the player’s current position.
    • Jlinger (t) = L ÷ ( 1 + e-k(t - t0) )
      • Jlinger (t) is the juice level at time t
      • L is the maximum juice level
      • k is the steepness of the curve, determining how quickly the juice level falls off
      • t0 is the time at which the player leaves the region, serving as the midpoint of the sigmoid curve

If Jcurrent falls below some threshold value Jmin, the region is declared to be effectively devoid of interest and can be unloaded until such a time that its juice once again exceeds that threshold.

The following visualization simulates these changes to Jcurrent; the character moves between chunks, and chunks are loaded (invisible -> visible) and are unloaded (visible -> invisible).

Jcurrent changes

More Fun With Juice

In addition, we can also monitor a longer-term cumulative trend:

  • Jengagement = Jengagement + Δ Jt
    • Jengagement is a cumulative measurement of how much a player has engaged with a region.
    • Δ Jt is a measure of how much the juice has changed this turn. This will always be at least zero, though it may be rounded down to zero judiciously.

And we can compare the current state to that historic trend:

  • Jdecay = Jengagemente-λ(t - t0)
    • Jdecay is an indicator of how much a player has engaged with a region recently
    • λ is the decay constant, determining how quickly the juice level decreases with time since last engagement
    • t0 is the time at which the player last interacted with the region

This can be used as a trigger for "hey, remember me?" type events.

Footnotes

  1. "You can't just say that your game exists." "I didn't say it. I declared it."

About

A semi-formal specification of Hornvale

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages