feat: balance checks and README - v1 complete
This commit is contained in:
@@ -0,0 +1,29 @@
|
||||
# Probability
|
||||
|
||||
A neon space-trading sim where unobserved ships are probability waves.
|
||||
|
||||
Inspired by Space Rangers. Every NPC trader plans real routes and real trades,
|
||||
but between observations its position is a probability cloud smeared along the
|
||||
jump lanes — and so is your knowledge of every market more than a news-day away.
|
||||
Observation collapses the cloud: enter a system and ships condense out of the
|
||||
static; dock at a planet and its prices become real.
|
||||
|
||||
Under the hood the universe is deterministic ("hidden variables" drawn lazily
|
||||
from per-ship seeds) — the probability is *yours*, not the universe's.
|
||||
|
||||
## Run
|
||||
|
||||
npm install
|
||||
npm run dev # play at http://localhost:5173 (add ?seed=anything for a new galaxy)
|
||||
npm test # headless sim test suite
|
||||
|
||||
## How to play
|
||||
|
||||
- You start docked. Buy what a planet produces cheap, sell where it's consumed.
|
||||
- Sidebar: navigation, market (when docked), cargo, intel, log. Space = pause,
|
||||
Tab = map/system view. Click planets/gates/systems to move; click systems on
|
||||
the map for price intel — note the age tag, old news lies.
|
||||
- Lanes are tinted by pirate danger. Dangerous shortcuts are profitable until
|
||||
they aren't.
|
||||
- Orange smears on the map are ships you've met: where they probably are now.
|
||||
- Reach 100,000 credits to win. Lose your ship and it's over.
|
||||
@@ -0,0 +1,31 @@
|
||||
import { describe, it, expect } from 'vitest';
|
||||
import { createWorld, advance } from '../src/sim/world.js';
|
||||
import { priceOf, advancePlanet } from '../src/sim/economy.js';
|
||||
import { GOODS, BASE_PRICE } from '../src/sim/constants.js';
|
||||
|
||||
describe('balance', () => {
|
||||
it('the NPC economy stays alive over a long horizon', () => {
|
||||
const w = createWorld('balance-1');
|
||||
advance(w, 120);
|
||||
const alive = w.npcs.filter((n) => n.alive);
|
||||
expect(alive.length).toBeGreaterThan(w.npcs.length * 0.5);
|
||||
expect(w.stats.trades).toBeGreaterThan(w.npcs.length * 3);
|
||||
});
|
||||
|
||||
it('price gradients survive NPC arbitrage (the player can still profit)', () => {
|
||||
const w = createWorld('balance-1');
|
||||
advance(w, 60);
|
||||
let bestSpread = 0;
|
||||
for (const good of GOODS) {
|
||||
let lo = Infinity, hi = 0;
|
||||
for (const planet of w.galaxy.planets) {
|
||||
advancePlanet(planet, w.t);
|
||||
const price = priceOf(good, planet.stock[good]);
|
||||
lo = Math.min(lo, price);
|
||||
hi = Math.max(hi, price);
|
||||
}
|
||||
bestSpread = Math.max(bestSpread, (hi - lo) / BASE_PRICE[good]);
|
||||
}
|
||||
expect(bestSpread).toBeGreaterThan(0.5); // at least one good still has a fat margin somewhere
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,5 @@
|
||||
import { defineConfig } from 'vite';
|
||||
|
||||
export default defineConfig({
|
||||
build: { target: 'esnext' }, // top-level await in src/main.js
|
||||
});
|
||||
Reference in New Issue
Block a user