Skip to content

Search is only available in production builds. Try building and previewing the site to test it out locally.

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.

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.
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 }.

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.

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 scores

On 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.

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.

The state is S as above. The inputs are player actions and the clock. The outputs are the interface and the saved JSON.

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