// Package sim is the deterministic core: given a State, the day's inputs and // a Config it advances the world by one day. It performs no I/O. package sim import ( "fmt" "wh/config" "wh/fixed" "wh/market" "wh/vm" "wh/world" ) // Hull describes the physical properties of a ship design. type Hull struct { DryMass int64 // kg FuelCap int64 // kg CargoCap int64 // kg Thrust int64 // newtons at full throttle ExhaustVel int64 // m/s MineRate int64 // kg mined per tick } // CommandShip is the only hull for now. var CommandShip = Hull{ DryMass: 8000, FuelCap: 4000, CargoCap: 6000, Thrust: 6000, ExhaustVel: 30000, MineRate: 10, } // Docking / mining limits. const ( MineRangeKm = 5 // max distance to an asteroid for mining DockRangeKm = 20 // max distance to the station for selling MaxRelSpeedM = 100 // max relative speed (m/s) for mining or docking StarRadiusKm = 200_000 ) type Ship struct { ID int64 Owner int64 Hull Hull Pos fixed.Vec Vel fixed.Vec Fuel fixed.F Cargo [world.NumOre]int64 CPU *vm.CPU Alive bool Earned int64 // credits earned so far // Peripheral state. Throttle int32 Azimuth int32 Pitch int32 Target int64 Mining bool UplinkNew bool UplinkLen int32 MathX int32 MathY int32 MathZ int32 } func (s *Ship) CargoTotal() int64 { var t int64 for _, v := range s.Cargo { t += v } return t } func (s *Ship) Mass() fixed.F { return fixed.FromInt(s.Hull.DryMass+s.CargoTotal()) + s.Fuel } // State is everything that persists between daily runs. type State struct { Day int64 Asteroids []world.Asteroid // sorted by ID Station world.Station Ships []*Ship // sorted by ID Credits map[int64]int64 Market market.Market } // NewState builds the initial world from the config seed. func NewState(cfg config.Config) *State { asts, st := world.Generate(cfg) return &State{ Asteroids: asts, Station: st, Credits: map[int64]int64{}, Market: market.New(), } } func (s *State) asteroid(id int64) *world.Asteroid { // Asteroids are numbered 1..N in slice order. if id >= 1 && id <= int64(len(s.Asteroids)) { return &s.Asteroids[id-1] } return nil } // Launch describes a ship entering the belt at the start of a day. type Launch struct { ShipID int64 Owner int64 Program []byte } // Uplink is a message delivered to a ship's comm buffer at the start of a day. type Uplink struct { ShipID int64 Data []byte } // DayInput is everything external the simulation consumes for one day. type DayInput struct { Launches []Launch // processed in slice order (must be deterministic) Uplinks []Uplink } // Downlink is the content of a ship's transmit buffer at the end of the day. type Downlink struct { ShipID int64 Data []byte } type Event struct { Tick int ShipID int64 Kind string Detail string } type DayResult struct { Day int64 Downlinks []Downlink Events []Event Hash [32]byte } func (e Event) String() string { return fmt.Sprintf("t%04d ship %d %s %s", e.Tick, e.ShipID, e.Kind, e.Detail) }