149 lines
3.4 KiB
Go
149 lines
3.4 KiB
Go
// 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"
|
|
|
|
"halcyon/config"
|
|
"halcyon/fixed"
|
|
"halcyon/market"
|
|
"halcyon/vm"
|
|
"halcyon/world"
|
|
)
|
|
|
|
// Hull describes the physical properties of a ship design.
|
|
type Hull struct {
|
|
DryMass int64 // kg
|
|
FuelCap 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,
|
|
Thrust: 6000,
|
|
ExhaustVel: 30000,
|
|
MineRate: 10,
|
|
}
|
|
|
|
// Docking / mining limits.
|
|
const (
|
|
MineRangeKm = 5 // max distance to an asteroid for mining
|
|
DockRangeKm = 20 // how close counts as "near the station"
|
|
MaxRelSpeedM = 100 // max relative speed (m/s) for mining or docking
|
|
StarRadiusKm = 200_000
|
|
ScanRangeKm = 500_000 // the scanner sees nothing farther away than this
|
|
)
|
|
|
|
type Ship struct {
|
|
ID int64
|
|
Owner int64
|
|
Hull Hull
|
|
Pos fixed.Vec
|
|
Vel fixed.Vec
|
|
Fuel fixed.F
|
|
// Cargo is the hold of the earlier design, before ore travelled in containers.
|
|
// It is only read to convert what old ships still carry (migrateLegacyCargo).
|
|
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
|
|
}
|
|
|
|
// baseMass is the hull and its fuel; towed containers add to it (State.shipMass).
|
|
func (s *Ship) baseMass() fixed.F {
|
|
return fixed.FromInt(s.Hull.DryMass) + 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
|
|
// Containers are sorted by ID; NextContainer is the last ID handed out.
|
|
Containers []*Container
|
|
NextContainer int64
|
|
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
|
|
Scraps []int64 // ships to remove from the world before the day starts, in slice order
|
|
}
|
|
|
|
// 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)
|
|
}
|