Town mills
Three mills along the river buy logs from the landing deck. Each mill bids per log by species. The bid falls as the mill’s log yard fills. A wagon carries the whole deck to one mill and returns with lumber.
Constants
Section titled “Constants”| Mill | Direction | Miles | Haul share | Trip (min) | Yard cap | Rate (logs/hr) | Period (hr) | Phase |
|---|---|---|---|---|---|---|---|---|
| Harrison mill | west | 3 | 4% | 1 | 160 | 10 | 9 | 0 |
| Medimont mill | east | 6 | 8% | 1.5 | 120 | 8 | 7 | 2.1 |
| Springston mill | west | 10 | 13% | 2 | 240 | 14 | 13 | 4.2 |
| Mill | Multipliers by species key |
|---|---|
| Harrison mill | – |
| Medimont mill | rc 1.3, l 1.15, lp 1.1, _ 0.8 |
| Springston mill | wp 1.3, pp 1.12, df 1.05, cw 0.85, _ 0.95 |
The bid
Section titled “The bid”yardFactor(mill, stock) = 1.3 - 0.9 * (stock / mill.cap)
multiplier = mill.mult[species] if present else mill.mult["_"] if present else 1.0
bid(mill, stock, species) = max( 20, round( value(species) * multiplier * yardFactor ) )The unit is bd ft of lumber per log. An empty yard multiplies value by 1.3. A full yard multiplies it by 0.4. A half-full yard multiplies it by 0.85.
The log yard
Section titled “The log yard”Each mill has a yard stock in logs. Stock is stored with the time it was last advanced, and is brought up to date by stepping forward in pieces of at most 10 minutes.
inflow(t) = rate * (1 + 0.9 * sin( 2*pi * (t / 3600000) / period + phase ))for each step of dt (at most 10 minutes), h = dt / 1 hour: stock += (inflow(t) - rate) * h - 0.15 * rate * h * (stock / cap - 0.5) stock = clamp(stock, 0, cap)The sine term uses absolute hours since 1970, so every mill follows the same curve for every player. Over a full period the inflow averages the processing rate, and the second term pulls stock gently toward half full.
A mill’s first stock, when it is first read, is:
stock0 = round( cap * (0.35 + 0.4 * fractionalPart(phase * 7)) )That is about 56 logs at Harrison, 76 at Medimont and 122 at Springston.
The offer
Section titled “The offer”The offer for a deck is computed from the mill’s current stock.
gross = sum over species of deck[species] * bid(mill, stock, species)net = round( gross * (1 - mill.haul * (0.5 if the player owns a teamster stable else 1)) )The interface also shows a trend, which compares the yard factor two hours ahead (stock projected with the model above) to the factor now. A change of less than 3 percent reads as steady.
Hauling and settling
Section titled “Hauling and settling”haul(mill): requires: no wagon already out, and at least 1 log in the landing deck offer = offer(mill, deck) (bids are locked now) wagon = { leftAt: now, logs: count, deck, mill, net: offer.net } deck = empty
settle(wagon), when now - leftAt >= mill.trip: lumber += wagon.net produced += wagon.net (counts toward the deed) mill stock = min(cap, stock + wagon.logs) ledger entry dated leftAt + tripOnly one wagon travels at a time. A wagon always carries the whole landing deck. Logs that arrive at the landing while the wagon is out wait for the next trip. The price is fixed at departure; yard changes during the trip do not affect it. A settled delivery raises the mill’s yard stock by the number of logs, up to the cap.
If the player is away, the wagon settles on the next load of the game, and the ledger entry carries the arrival time.
Inputs, outputs and state
Section titled “Inputs, outputs and state”Inputs are the landing deck, the chosen mill, the clock and whether the player owns a teamster stable. Outputs are lumber, a changed yard stock, and an increase in produced. Time-based state: for each mill, S.mills[id] = { stock, at }, and the wagon record { leftAt, logs, deck, mill, net }.
The constant WAGON_TRIP (four minutes) exists in the script but is not read anywhere. Trip length comes from each mill.
For the shared game
Section titled “For the shared game”In the prototype each player has a private copy of every mill yard that follows the same sine curve, so a delivery by one player changes nothing for another. In a shared game the yard is one number per mill, and it must belong to the server, because bids are the main price signal and every player’s deliveries move it. The server also has to own the wagon: departure time, locked bid and arrival. The closed-form inflow is a stand-in for other settlers; once real players deliver, it can be reduced or removed. Bids that are locked at departure mean a server must store the net payout with the trip, not recompute it on arrival.
Source: prototype/index.html · constants MILLS, WAGON_TRIP in rules.json · logic bidFor, yardFactor, inflow, projectStock, updateMills, millState, offer, haul, settleWagon