Game state
The whole game is one object, S. It is saved as one JSON string. Every timestamp in it is a millisecond clock reading from Date.now() on the player’s device.
Top-level fields
Section titled “Top-level fields”| Field | Type | Meaning |
|---|---|---|
v |
number | Save version. Always 2. |
created |
ms | When the claim was filed. Seeds the claim and the Reserve. |
lastSeen |
ms | Last save. Used for the “while you were away” notice and for choosing between local and remote saves. |
tiles |
64 records | The parcels, row-major. See below. |
lumber |
number | The currency, in bd ft. |
logs |
number | Legacy. Moved into deck.df on load, then set to 0. |
deck |
map species to number | The landing log deck. |
crewUsed |
list of ms | Timestamps of crew-days in use. |
wagon |
record or null | { leftAt, logs, deck, mill, net }. At most one. |
log |
list | Ledger entries { t, text, amt }, newest first, kept to 40. The interface shows 20. |
built |
map | Count of each building kind, and biz:id entries. |
replanted, cleared |
numbers | Deed counters (events, not parcels). |
provedUp |
ms or null | Set when the deed is earned. |
listing |
record or null | { price, at }. |
guideHidden |
boolean | Whether the first-run guide was dismissed. |
saw |
number or null | Sharpness 0 to 1. Null reads as 1. |
produced |
number | Work toward the deed and the Timber baron board. |
mills |
map | Per mill { stock, at }. Created on first read. |
own |
record or null | The player’s sawmill { r, c, level, deck, at, bid, bought, paid, made }. |
town |
record | { founded, at, comp, earned, biz, level }. biz[id] is { r, c, since }. |
reserve |
record | { stands, at, sold, soldToYou, revenue }. Each stand is { id, sp, band, planted }. |
merc |
record | { at, stock }. |
produce |
map | Unsold harvested crops. |
farmed |
number | Lifetime Mercantile income. Written, never read. |
fair |
record | { best, ribbons, plays, flagged }. |
cabinAt |
ms | Legacy. Set when a cabin is built. Not read. |
Parcel record
Section titled “Parcel record”| Field | Meaning |
|---|---|
slope |
Steep ground. Fixed. |
meadow |
River meadow. Fixed. |
sp |
Species key. Kept after a cut until replanted. |
planted |
Planting time, or null when cleared. Age is derived from it. |
b |
Building kind, biz:id, or null. |
road |
Has a road. |
cut |
Times cut. Written, never read. |
field |
Null, or { crop, planted, fert, bearing }. |
Time-based systems
Section titled “Time-based systems”Each system stores the time it was last advanced and catches up when read. Nothing runs on a server clock, so every number is derived from timestamps and the clock of the device.
| System | Stored time | Catch-up step | Called from |
|---|---|---|---|
| Timber growth | each parcel’s planted |
none: derived on read | everywhere |
| Crew-days | each entry of crewUsed |
none: filtered on read | crewReady |
| Wagon | wagon.leftAt |
none: settled when now - leftAt >= trip |
each second, and on load |
| Town mill yards | mills[id].at |
10 minutes | updateMills |
| Your sawmill | own.at |
10 minutes | updateOwn |
| Town, competitors, income | town.at |
30 minutes | updateTown |
| Forest Reserve | reserve.at |
30 minutes | updateReserve |
| Mercantile shelves | merc.at |
30 minutes | updateMerc |
| Crops | each field’s planted |
none: derived on read | cropState |
The page also runs a once-a-second timer that settles the wagon, advances the sawmill, and redraws everything. The town, Reserve and Mercantile updates run on that tick through the panel renderers. Where a system draws a number from a clock-derived hash (town competitors and Reserve buyers), the length of the step changes the result; see town and markets and Forest Reserve.
Saving and loading
Section titled “Saving and loading”A change calls commit, which checks the deed, redraws, and schedules a save 600 milliseconds later. A save also runs when the page is hidden.
save(): lastSeen = now if connected to an account store: write one document { claim: JSON text of S, savedAt } else: write localStorage key "hh-claim-v1" then record the visit and publish the six leaderboard scoresOn load the local save is read first. If the account store holds a claim whose lastSeen is at least the local one, it replaces the local claim. A load runs migrate, which fills in anything missing (species, deck, town, Reserve, Mercantile and fair state), then settles a finished wagon. If the player was away 30 minutes or more, a notice states how many stands grew a stage, how many became mature, whether the town grew, and whether the crew is rested.
The account store also holds a visits document per player (days played, first and last visit, building count, deed flag) and a scores document (six numbers). Only the page owner sees the visits table.
Sandbox test tools
Section titled “Sandbox test tools”Shown only to the person who published the page. “No waiting” makes crew-days and the wagon instant. “Grow timber 12 hours” moves every stored timestamp back by 12 hours (parcels, Reserve stands, fields, the wagon, mills, sawmill, town and shelf clocks) and then catches up each system. “Start a new claim” replaces the claim after a second tap.
Inputs, outputs and state
Section titled “Inputs, outputs and state”The state is S as above. The inputs are player actions and the clock. The outputs are the interface and the saved JSON.
For the shared game
Section titled “For the shared game”Nearly everything in S is either one player’s private record (parcels, crew, saw, sawmill, produce) or a copy of something that should be shared (mill yards, the town, the Reserve, the Mercantile, the neighbor list). A server needs to split these: per-player records stay with the player, and the shared ones become single world records with one clock. Timestamps must come from the server. The save format is a JSON string written whole, so two devices can overwrite each other; the prototype resolves it by lastSeen. A server should accept actions and apply them in order, not accept whole states. The catch-up steps (10 and 30 minutes) are part of the game’s results, so a server should keep them fixed and run them on its own schedule.
Source: prototype/index.html · constants none (state is not a constant) in rules.json · logic newClaim, migrate, commit, save, boot, awaySummary, testTools