use indexmap::IndexMap;
use rand::Rng;
use std::collections::HashMap;
use system::EventInfo;
pub mod config;
mod system;
use crate::config::GalaxyConfig;
use crate::system::System;
pub use crate::system::{Event, EventCallback, StructureType};
#[derive(Debug)]
pub struct Galaxy {
config: GalaxyConfig,
systems: HashMap<Coords, System>,
tick: usize,
}
pub type SystemProduction = Resources;
#[derive(Clone, Debug, Default)]
pub struct Resources {
pub metal: usize,
pub crew: usize,
pub water: usize,
}
impl std::ops::Add for Resources {
type Output = Resources;
fn add(self, other: Resources) -> Resources {
Resources {
metal: self.metal + other.metal,
crew: self.crew + other.crew,
water: self.water + other.water,
}
}
}
impl std::ops::Sub for Resources {
type Output = Resources;
fn sub(self, other: Resources) -> Resources {
Resources {
metal: self.metal - other.metal,
crew: self.crew - other.crew,
water: self.water - other.water,
}
}
}
impl std::ops::Mul<usize> for Resources {
type Output = Resources;
fn mul(self, other: usize) -> Resources {
Resources {
metal: self.metal * other,
crew: self.crew * other,
water: self.water * other,
}
}
}
#[derive(Clone, Debug, Default)]
pub struct SystemInfo {
pub score: usize,
pub resources: Resources,
pub production: SystemProduction,
pub structures: IndexMap<StructureType, usize>,
pub events: Vec<EventInfo>,
}
#[derive(Clone, Debug, Default)]
pub struct Cost {
pub metal: usize,
pub water: usize,
pub crew: usize,
pub ticks: usize,
}
impl Cost {
pub fn from_map(cost: &HashMap<String, usize>) -> Cost {
Cost {
metal: *cost.get("metal").unwrap_or(&0),
water: *cost.get("water").unwrap_or(&0),
crew: *cost.get("crew").unwrap_or(&0),
ticks: *cost.get("ticks").unwrap_or(&0),
}
}
}
#[derive(Clone, Debug, Default)]
pub struct StructureInfo {
pub level: usize,
pub production: Option<SystemProduction>,
pub builds: Option<IndexMap<StructureType, Cost>>,
}
#[derive(Clone, Debug)]
pub enum Details {
System(SystemInfo),
Structure(StructureInfo),
}
impl Galaxy {
pub fn new(config: GalaxyConfig, initial_tick: usize) -> Self {
let mut systems = HashMap::new();
let mut rng = rand::thread_rng();
for _ in 0..config.system_count {
let x: usize = rng.gen_range(0..=config.size.x);
let y: usize = rng.gen_range(0..=config.size.y);
if systems.contains_key(&(x, y).into()) {
continue;
}
let system = System::new(initial_tick, &config.systems, &config);
systems.insert((x, y).into(), system);
}
Self {
config,
systems,
tick: initial_tick,
}
}
pub fn get_details(
&mut self,
tick: usize,
coords: Coords,
structure: Option<StructureType>,
) -> Result<Details, String> {
self.update_tick(tick)?;
let system = self.systems.get_mut(&coords).unwrap();
system.get_details(tick, &self.config, structure)
}
pub fn get_config(&self) -> &GalaxyConfig {
&self.config
}
pub fn stats(&mut self, tick: usize) -> Result<String, String> {
self.update_tick(tick)?;
let mut stats = format!("System count: {}\n", self.config.system_count);
for (coords, system) in self.systems.iter_mut() {
stats.push_str(&format!(
"System at {:?} has score {} and metal {}\n",
coords,
system.score(tick, &self.config),
system.metal(tick, &self.config),
));
}
Ok(stats)
}
pub fn systems(&self) -> &HashMap<Coords, System> {
&self.systems
}
pub fn build(
&mut self,
tick: usize,
coords: Coords,
structure: StructureType,
) -> Result<Event, String> {
self.update_tick(tick)?;
let system = self.systems.get_mut(&coords).unwrap();
system.build(tick, &self.config, structure)
}
fn update_tick(&mut self, tick: usize) -> Result<(), String> {
if tick < self.tick {
return Err("Tick is out of order".to_string());
}
self.tick = tick;
Ok(())
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Coords {
pub x: usize,
pub y: usize,
}
impl From<(usize, usize)> for Coords {
fn from(coords: (usize, usize)) -> Self {
Coords {
x: coords.0,
y: coords.1,
}
}
}