Compare commits
1
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9ce8660fd2 |
@@ -1,5 +1,9 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
|
## v0.47.0 — 2026-09-21 23:01
|
||||||
|
- **Job payoff retuned so weight dominates (ISS-090).** `REWARD_PER_KG` in `lib/game/seed.ts` rises from €0.5/kg to €2.0/kg — the linear form is unchanged, only the constant moves. Before, the weight term was small vs base+distance, so heavier jobs barely paid more (a 1000 km leg: €3,250 @500 kg → €5,000 @4,000 kg — per-kg rate fell from €4.59 to €1.01) and there was no sense in taking heavy jobs; after the retune the same leg pays €4,000 / €11,000, so weight clearly drives payoff. Safe for viability: fuel burn (`lib/sim/movement.ts`) is weight-independent, so this only raises income
|
||||||
|
- **Tests.** `seed.test.ts`: stale test name "… 0.5 * weightKg …" made generic; new literal pins in the `rewardFor` formula test — `rewardFor(1000, 4000) === 11_000` and `rewardFor(1000, 500) === 4_000` — so future retunes are deliberate. All other reward assertions recompute through the exported constants and pass unchanged
|
||||||
|
|
||||||
## v0.46.0 — 2026-09-21 22:32
|
## v0.46.0 — 2026-09-21 22:32
|
||||||
- **Queue consumption on dispatch (ISS-088).** `flyPlane` (`lib/game/actions.ts`) now consumes `queue[0]` when the requested destination IS the first queued leg, so a landing never chains a zero-distance self-hop (the "phantom hop" — a→a completing instantly before reaching the real next leg). Flying to a non-first queued leg stays an explicit override and leaves the queue untouched; the persisted queue is written back in the same update
|
- **Queue consumption on dispatch (ISS-088).** `flyPlane` (`lib/game/actions.ts`) now consumes `queue[0]` when the requested destination IS the first queued leg, so a landing never chains a zero-distance self-hop (the "phantom hop" — a→a completing instantly before reaching the real next leg). Flying to a non-first queued leg stays an explicit override and leaves the queue untouched; the persisted queue is written back in the same update
|
||||||
- **Engine auto-chain hardening.** `deliverPackages` (`lib/sim/engine.ts`) gains two guards: (1) defensive skip — if `queue[0]` equals the arrival airport (only reachable from legacy/corrupt rows now that dispatch consumes), it drops the leg without flying; (2) full-tank backstop — if the next queued leg does not fit even a full tank, the plane grounds as `'available'` with the queue kept intact instead of chaining an unflyable leg (mirrors `flyPlane`'s 409 without throwing mid-tick)
|
- **Engine auto-chain hardening.** `deliverPackages` (`lib/sim/engine.ts`) gains two guards: (1) defensive skip — if `queue[0]` equals the arrival airport (only reachable from legacy/corrupt rows now that dispatch consumes), it drops the leg without flying; (2) full-tank backstop — if the next queued leg does not fit even a full tank, the plane grounds as `'available'` with the queue kept intact instead of chaining an unflyable leg (mirrors `flyPlane`'s 409 without throwing mid-tick)
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ describe('buildSeed', () => {
|
|||||||
expect(new Set(seed.packages.map((c) => c.id)).size).toBe(seed.packages.length);
|
expect(new Set(seed.packages.map((c) => c.id)).size).toBe(seed.packages.length);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('applies the reward formula: round(500 + 2.5 * km + 0.5 * weightKg), × special multiplier', () => {
|
it('applies the reward formula (BASE_REWARD + REWARD_PER_KM * km + REWARD_PER_KG * weightKg) × special multiplier', () => {
|
||||||
const seed = seedWith(42);
|
const seed = seedWith(42);
|
||||||
for (const c of seed.packages) {
|
for (const c of seed.packages) {
|
||||||
const o = AIRPORTS.find((a) => a.icao === c.originIcao)!;
|
const o = AIRPORTS.find((a) => a.icao === c.originIcao)!;
|
||||||
@@ -134,6 +134,9 @@ describe('buildSeed', () => {
|
|||||||
it('rewardFor applies the documented formula', () => {
|
it('rewardFor applies the documented formula', () => {
|
||||||
expect(rewardFor(0, 0)).toBe(BASE_REWARD);
|
expect(rewardFor(0, 0)).toBe(BASE_REWARD);
|
||||||
expect(rewardFor(1000, 2_000)).toBe(Math.round(BASE_REWARD + REWARD_PER_KM * 1000 + REWARD_PER_KG * 2_000));
|
expect(rewardFor(1000, 2_000)).toBe(Math.round(BASE_REWARD + REWARD_PER_KM * 1000 + REWARD_PER_KG * 2_000));
|
||||||
|
// v0.47.0 (ISS-090): literal pins so future retunes are deliberate — weight must dominate payoff.
|
||||||
|
expect(rewardFor(1000, 500)).toBe(4_000);
|
||||||
|
expect(rewardFor(1000, 4_000)).toBe(11_000);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
+4
-1
@@ -90,9 +90,12 @@ export const MARKET_TARGET_OPEN_PACKAGES = 10;
|
|||||||
// Reward formula (v0.6.0 distance part, deliberately simple; real revenue/cost
|
// Reward formula (v0.6.0 distance part, deliberately simple; real revenue/cost
|
||||||
// modeling is a later increment; v0.27.0 weight part): reward = BASE_REWARD +
|
// modeling is a later increment; v0.27.0 weight part): reward = BASE_REWARD +
|
||||||
// REWARD_PER_KM * great-circle km + REWARD_PER_KG * package weight kg.
|
// REWARD_PER_KM * great-circle km + REWARD_PER_KG * package weight kg.
|
||||||
|
// v0.47.0 (ISS-090) retune: REWARD_PER_KG 0.5 → 2.0 so the weight term dominates
|
||||||
|
// base+distance — heavier jobs must clearly pay more per kg to be worth taking
|
||||||
|
// (a 1000 km leg went from €3,250 @500 kg / €5,000 @4,000 kg to €4,000 / €11,000).
|
||||||
export const BASE_REWARD = 500;
|
export const BASE_REWARD = 500;
|
||||||
export const REWARD_PER_KM = 2.5;
|
export const REWARD_PER_KM = 2.5;
|
||||||
export const REWARD_PER_KG = 0.5;
|
export const REWARD_PER_KG = 2.0;
|
||||||
|
|
||||||
// Job reward bonus (v0.38.0, ISS-072): reward ×= 1 + repRate(reputation, …) — better jobs at
|
// Job reward bonus (v0.38.0, ISS-072): reward ×= 1 + repRate(reputation, …) — better jobs at
|
||||||
// higher reputation, exactly ×1 at the starting value.
|
// higher reputation, exactly ×1 at the starting value.
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "flight-ops",
|
"name": "flight-ops",
|
||||||
"version": "0.46.0",
|
"version": "0.47.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "next dev -p 3000",
|
"dev": "next dev -p 3000",
|
||||||
|
|||||||
Reference in New Issue
Block a user