1use indexmap::IndexMap;
2use rand::Rng;
3use std::collections::HashMap;
4use system::EventInfo;
5
6pub mod config;
7mod system;
8use crate::config::GalaxyConfig;
9use crate::system::System;
10
11pub use crate::system::{Event, EventCallback, StructureType};
12
13#[derive(Debug)]
14pub struct Galaxy {
15 config: GalaxyConfig,
17
18 systems: HashMap<Coords, System>,
20
21 tick: usize,
25}
26
27pub type SystemProduction = Resources;
31
32#[derive(Clone, Debug, Default)]
34pub struct Resources {
35 pub metal: usize,
36 pub crew: usize,
37 pub water: usize,
38}
39
40impl std::ops::Add for Resources {
41 type Output = Resources;
42
43 fn add(self, other: Resources) -> Resources {
44 Resources {
45 metal: self.metal + other.metal,
46 crew: self.crew + other.crew,
47 water: self.water + other.water,
48 }
49 }
50}
51
52impl std::ops::Sub for Resources {
53 type Output = Resources;
54
55 fn sub(self, other: Resources) -> Resources {
56 Resources {
57 metal: self.metal - other.metal,
58 crew: self.crew - other.crew,
59 water: self.water - other.water,
60 }
61 }
62}
63
64impl std::ops::Mul<usize> for Resources {
65 type Output = Resources;
66
67 fn mul(self, other: usize) -> Resources {
68 Resources {
69 metal: self.metal * other,
70 crew: self.crew * other,
71 water: self.water * other,
72 }
73 }
74}
75
76#[derive(Clone, Debug, Default)]
77pub struct SystemInfo {
78 pub score: usize,
80
81 pub resources: Resources,
83
84 pub production: SystemProduction,
88
89 pub structures: IndexMap<StructureType, usize>,
91
92 pub events: Vec<EventInfo>,
96}
97
98#[derive(Clone, Debug, Default)]
100pub struct Cost {
101 pub metal: usize,
102 pub water: usize,
103 pub crew: usize,
104 pub ticks: usize,
105}
106
107impl Cost {
108 pub fn from_map(cost: &HashMap<String, usize>) -> Cost {
112 Cost {
113 metal: *cost.get("metal").unwrap_or(&0),
114 water: *cost.get("water").unwrap_or(&0),
115 crew: *cost.get("crew").unwrap_or(&0),
116 ticks: *cost.get("ticks").unwrap_or(&0),
117 }
118 }
119}
120
121#[derive(Clone, Debug, Default)]
125pub struct StructureInfo {
126 pub level: usize,
128 pub production: Option<SystemProduction>,
130 pub builds: Option<IndexMap<StructureType, Cost>>,
132}
133
134#[derive(Clone, Debug)]
139pub enum Details {
140 System(SystemInfo),
141 Structure(StructureInfo),
142}
143
144impl Galaxy {
145 pub fn new(config: GalaxyConfig, initial_tick: usize) -> Self {
149 let mut systems = HashMap::new();
150 let mut rng = rand::thread_rng();
151 for _ in 0..config.system_count {
152 let x: usize = rng.gen_range(0..=config.size.x);
154 let y: usize = rng.gen_range(0..=config.size.y);
155 if systems.contains_key(&(x, y).into()) {
156 continue;
158 }
159 let system = System::new(initial_tick, &config.systems, &config);
160 systems.insert((x, y).into(), system);
161 }
162 Self {
163 config,
164 systems,
165 tick: initial_tick,
166 }
167 }
168
169 pub fn get_details(
171 &mut self,
172 tick: usize,
173 coords: Coords,
174 structure: Option<StructureType>,
175 ) -> Result<Details, String> {
176 self.update_tick(tick)?;
177 let system = self.systems.get_mut(&coords).unwrap();
178 system.get_details(tick, &self.config, structure)
179 }
180
181 pub fn get_config(&self) -> &GalaxyConfig {
183 &self.config
184 }
185
186 pub fn stats(&mut self, tick: usize) -> Result<String, String> {
188 self.update_tick(tick)?;
189 let mut stats = format!("System count: {}\n", self.config.system_count);
190 for (coords, system) in self.systems.iter_mut() {
191 stats.push_str(&format!(
192 "System at {:?} has score {} and metal {}\n",
193 coords,
194 system.score(tick, &self.config),
195 system.metal(tick, &self.config),
196 ));
197 }
198 Ok(stats)
199 }
200
201 pub fn systems(&self) -> &HashMap<Coords, System> {
203 &self.systems
204 }
205
206 pub fn build(
208 &mut self,
209 tick: usize,
210 coords: Coords,
211 structure: StructureType,
212 ) -> Result<Event, String> {
213 self.update_tick(tick)?;
214 let system = self.systems.get_mut(&coords).unwrap();
215 system.build(tick, &self.config, structure)
216 }
217
218 fn update_tick(&mut self, tick: usize) -> Result<(), String> {
220 if tick < self.tick {
221 return Err("Tick is out of order".to_string());
222 }
223 self.tick = tick;
224 Ok(())
225 }
226}
227
228#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
230pub struct Coords {
231 pub x: usize,
232 pub y: usize,
233}
234
235impl From<(usize, usize)> for Coords {
236 fn from(coords: (usize, usize)) -> Self {
237 Coords {
238 x: coords.0,
239 y: coords.1,
240 }
241 }
242}