A SpaceTraders Agent
1const std = @import("std");
2const Allocator = std.mem.Allocator;
3const json = std.json;
4
5const prettym = @import("pretty");
6
7const m = @import("../models.zig");
8
9pub const Symbol = union(Symbol.Type) {
10 sector: u8,
11 system: Symbol.System,
12 waypoint: Symbol.Waypoint,
13
14 pub const Type = enum { sector, system, waypoint };
15
16 pub const System = struct {
17 sector: u8,
18 symbol: []const u8,
19
20 pub fn format(this: @This(), writer: *std.Io.Writer) std.Io.Writer.Error!void {
21 try writer.print("X{}-{s}", .{ this.sector, this.symbol });
22 }
23 };
24
25 pub const Waypoint = struct {
26 system: Symbol.System,
27 symbol: []const u8,
28
29 pub fn format(this: @This(), writer: *std.Io.Writer) std.Io.Writer.Error!void {
30 try writer.print("{f}-{s}", .{ this.system, this.symbol });
31 }
32 };
33
34 pub fn parseSlice(slice: []const u8) !Symbol {
35 var tokens = std.mem.tokenizeScalar(u8, slice, '-');
36 const sector_raw = tokens.next() orelse return error.InvalidSymbol;
37
38 std.debug.assert(sector_raw.len == 2);
39 std.debug.assert(sector_raw[0] == 'X');
40
41 const sector = std.fmt.parseInt(u8, sector_raw[1..], 10) catch return error.InvalidSector;
42 const system_raw = tokens.next();
43
44 if (system_raw) |system| {
45 const sys = Symbol.System{ .sector = sector, .symbol = system };
46 if (tokens.next()) |waypoint| {
47 return Symbol{ .waypoint = .{
48 .system = sys,
49 .symbol = waypoint,
50 } };
51 }
52 return Symbol{ .system = sys };
53 } else {
54 return Symbol{ .sector = sector };
55 }
56 }
57
58 pub fn jsonParse(
59 allocator: Allocator,
60 source: anytype,
61 options: json.ParseOptions,
62 ) json.ParseError(@TypeOf(source.*))!Symbol {
63 const string = try json.innerParse([]const u8, allocator, source, options);
64 return parseSlice(string) catch return error.MissingField;
65 }
66
67 pub fn format(this: @This(), writer: *std.Io.Writer) std.Io.Writer.Error!void {
68 switch (this) {
69 .sector => |sector| {
70 try writer.print("X{}", .{sector});
71 },
72 inline else => |e| try writer.print("{f}", .{e}),
73 }
74 }
75
76 pub fn pretty(
77 this: *const @This(),
78 comptime ctx: prettym.Context,
79 run: *const prettym.Runtime,
80 ) error{WriteFailed}!void {
81 run.setColor(ctx, .value);
82 try run.print("{f}", .{this});
83 run.resetColor();
84 }
85};
86
87pub const System = struct {
88 constellation: []const u8,
89 symbol: Symbol,
90 sectorSymbol: Symbol,
91 type: Type,
92 x: i64,
93 y: i64,
94 waypoints: []WaypointPreview,
95 factions: []Faction,
96 name: []const u8,
97
98 pub const Type = enum {
99 NEUTRON_STAR,
100 RED_STAR,
101 ORANGE_STAR,
102 BLUE_STAR,
103 YOUNG_STAR,
104 WHITE_DWARF,
105 BLACK_HOLE,
106 HYPERGIANT,
107 NEBULA,
108 UNSTABLE,
109 };
110};
111
112pub const Waypoint = struct {
113 symbol: Symbol,
114 systemSymbol: Symbol,
115 type: Type,
116 x: i64,
117 y: i64,
118 faction: Faction,
119 traits: []m.SymbolWrapper(Trait),
120 modifiers: []Modifier,
121 chart: Chart,
122 isUnderConstruction: bool,
123
124 orbitals: []m.SymbolWrapper([]const u8),
125 orbits: ?[]const u8 = null,
126
127 pub fn unwrapTraits(this: *const @This(), alloc: std.mem.Allocator) ![]Trait {
128 var buf = try alloc.alloc(Trait, this.traits.len);
129 for (this.traits, 0..) |trait, i| {
130 buf[i] = trait.symbol;
131 }
132 return buf;
133 }
134
135 pub const Type = enum {
136 PLANET,
137 GAS_GIANT,
138 MOON,
139 ORBITAL_STATION,
140 JUMP_GATE,
141 ASTEROID_FIELD,
142 ASTEROID,
143 ENGINEERED_ASTEROID,
144 ASTEROID_BASE,
145 NEBULA,
146 DEBRIS_FIELD,
147 GRAVITY_WELL,
148 ARTIFICIAL_GRAVITY_WELL,
149 FUEL_STATION,
150 };
151
152 pub const Trait = enum {
153 UNCHARTED,
154 UNDER_CONSTRUCTION,
155 MARKETPLACE,
156 SHIPYARD,
157 OUTPOST,
158 SCATTERED_SETTLEMENTS,
159 SPRAWLING_CITIES,
160 MEGA_STRUCTURES,
161 PIRATE_BASE,
162 OVERCROWDED,
163 HIGH_TECH,
164 CORRUPT,
165 BUREAUCRATIC,
166 TRADING_HUB,
167 INDUSTRIAL,
168 BLACK_MARKET,
169 RESEARCH_FACILITY,
170 MILITARY_BASE,
171 SURVEILLANCE_OUTPOST,
172 EXPLORATION_OUTPOST,
173 MINERAL_DEPOSITS,
174 COMMON_METAL_DEPOSITS,
175 PRECIOUS_METAL_DEPOSITS,
176 RARE_METAL_DEPOSITS,
177 METHANE_POOLS,
178 ICE_CRYSTALS,
179 EXPLOSIVE_GASES,
180 STRONG_MAGNETOSPHERE,
181 VIBRANT_AURORAS,
182 SALT_FLATS,
183 CANYONS,
184 PERPETUAL_DAYLIGHT,
185 PERPETUAL_OVERCAST,
186 DRY_SEABEDS,
187 MAGMA_SEAS,
188 SUPERVOLCANOES,
189 ASH_CLOUDS,
190 VAST_RUINS,
191 MUTATED_FLORA,
192 TERRAFORMED,
193 EXTREME_TEMPERATURES,
194 EXTREME_PRESSURE,
195 DIVERSE_LIFE,
196 SCARCE_LIFE,
197 FOSSILS,
198 WEAK_GRAVITY,
199 STRONG_GRAVITY,
200 CRUSHING_GRAVITY,
201 TOXIC_ATMOSPHERE,
202 CORROSIVE_ATMOSPHERE,
203 BREATHABLE_ATMOSPHERE,
204 THIN_ATMOSPHERE,
205 JOVIAN,
206 ROCKY,
207 VOLCANIC,
208 FROZEN,
209 SWAMP,
210 BARREN,
211 TEMPERATE,
212 JUNGLE,
213 OCEAN,
214 RADIOACTIVE,
215 MICRO_GRAVITY_ANOMALIES,
216 DEBRIS_CLUSTER,
217 DEEP_CRATERS,
218 SHALLOW_CRATERS,
219 UNSTABLE_COMPOSITION,
220 HOLLOWED_INTERIOR,
221 STRIPPED,
222 };
223
224 pub const Modifier = struct {
225 symbol: ModSymbol,
226 name: []const u8,
227 description: []const u8,
228
229 pub const ModSymbol = enum {
230 STRIPPED,
231 UNSTABLE,
232 RADIATION_LEAK,
233 CRITICAL_LIMIT,
234 CIVIL_UNREST,
235 };
236 };
237
238 pub const Chart = struct {
239 waypointSymbol: Symbol,
240 submittedBy: []const u8,
241 submittedOn: []const u8,
242 };
243};
244
245pub const WaypointPreview = struct {
246 symbol: Symbol,
247 type: Waypoint.Type,
248 x: i64,
249 y: i64,
250 orbitals: []m.SymbolWrapper(Symbol) = &.{},
251 orbits: ?Symbol = null,
252
253 pub fn prettyInline(
254 this: *const @This(),
255 comptime ctx: prettym.Context,
256 run: *const prettym.Runtime,
257 ) error{WriteFailed}!void {
258 run.setColor(ctx, .value);
259 try run.print("{f}", .{this.symbol});
260 run.setColor(ctx, .dim);
261 try run.print("[{s}]({}, {}) ", .{ @tagName(this.type), this.x, this.y });
262 run.resetColor();
263 }
264};
265
266pub const ConstructionSite = struct {
267 symbol: Symbol,
268 isComplete: bool,
269 materials: []Material,
270
271 pub const Material = struct {
272 tradeSymbol: m.inventory.AllItems.Combined,
273 required: i64,
274 fulfilled: i64,
275 };
276};
277
278pub const Market = struct {
279 symbol: Symbol,
280 exports: []m.inventory.Item = &.{},
281 imports: []m.inventory.Item = &.{},
282 exchange: []m.inventory.Item = &.{},
283 transactions: ?[]Transaction = null,
284 tradeGoods: ?[]TradeGood = null,
285
286 pub const Transaction = struct {
287 waypointSymbol: Symbol,
288 shipSymbol: []const u8,
289 tradeSymbol: []const u8,
290 type: Type,
291 units: u64,
292 pricePerUnit: u64,
293 totalPrice: u64,
294 timestamp: []const u8,
295
296 pub const Type = enum { PURCHASE, SELL };
297 };
298
299 pub const TradeGood = struct {
300 symbol: m.inventory.AllItems.Combined,
301 type: TradeGood.Type,
302 tradeVolume: u64,
303 supply: Supply,
304 activity: ?Activity = null,
305 purchasePrice: u64,
306 sellPrice: u64,
307
308 pub const Type = enum { EXPORT, IMPORT, EXCHANGE };
309 };
310
311 pub const Supply = enum { SCARCE, LIMITED, MODERATE, HIGH, ABUNDANT };
312 pub const Activity = enum { WEAK, GROWING, STRONG, RESTRICTED };
313};
314
315pub const JumpGate = struct {
316 symbol: Symbol,
317 connections: []Symbol,
318};
319
320pub const Shipyard = struct {
321 symbol: Symbol,
322 shipTypes: struct { type: m.ships.Type },
323 transactions: []Transaction,
324 modificationsFee: i64,
325
326 pub const Transaction = struct {
327 waypointSymbol: Symbol,
328 shipType: m.ships.Type,
329 price: u64,
330 agentSymbol: []const u8,
331 timestamp: []const u8,
332 };
333
334 pub const Ship = struct {
335 activity: Market.Activity,
336 supply: Market.Supply,
337 purchasePrice: u64,
338
339 type: m.ships.Type,
340 name: []const u8,
341 description: []const u8,
342 crew: m.ships.Crew,
343 frame: m.ships.Frame,
344 reactor: m.ships.Reactor,
345 engine: m.ships.Engine,
346 modules: []m.ships.Module,
347 mounts: []m.ships.Mount,
348 };
349};
350
351pub const Faction = m.SymbolWrapper(m.factions.Symbol);