use serde::{
Deserialize,
Serialize
};
use std::fs::{
self,
File
};
mod control;
mod field;
mod neighbourhood;
mod render;
use crate::{
base::{
Complex,
COMPLEX_ZERO,
PrependErrorString
},
master::{
Context,
Mastermind,
MSync,
MTelemetry
},
sys::{
Key,
System
}
};
use self::{
control::MControl,
field::Field,
neighbourhood::Neighbourhood,
render::MRender
};
const DEF_AMPLIFICATION: f64 = 1.0;
const DEF_NOISE: f64 = 0.01;
const DEF_SYNCHRONICITY: f64 = 1.0;
const FIELD_FILENAME: &str = "field.bin";
const NEIGHBOURHOOD_FILENAME: &str = "neighbourhood.bin";
const DEF_MAGLASS_RADIUS: i32 = 8;
const DEF_NBHOOD_RADIUS: i32 = 4;
const NUM_SPS_CYCLES: usize = 100;
const STATE_DIRNAME: &str = "automaton";
const STATE_FILENAME: &str = "automaton.json";
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Serialize, Deserialize)]
pub enum EditMode {
Field,
Neighbourhood
}
#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug, Serialize, Deserialize)]
pub enum ColorMode {
HueLum,
Lum,
Hue
}
#[derive(Serialize, Deserialize)]
pub struct Automaton {
#[serde(skip, default="def_field")] pub field: Field,
#[serde(skip, default="def_neighbourhood")] pub neighbourhood: Neighbourhood,
pub amplification: f64,
pub noise: f64,
synchronicity: f64,
pub edit_mode: EditMode,
pub color_mode: ColorMode,
pub pointer_field_u: i32,
pub pointer_field_y: i32,
pub pointer_field_x: i32,
pub cell_size: i32,
pub show_nbhood: bool,
pub nbhood_radius: i32,
pub nbhood_cell_size: i32,
pub pointer_nbhood_y: i32,
pub pointer_nbhood_x: i32,
pub brush: Complex,
pub brush_radius: i32,
pub show_palette: bool,
pub nbhood_du: isize,
pub show_maglass: bool,
pub maglass_radius: i32,
pub maglass_cell_size: i32,
pub show_field: bool,
pub scale_lum: bool,
pub manual_step: bool,
pub show_settings_stats: bool,
#[serde(skip, default="def_t_last_cycle")] pub t_last_cycle: i128,
#[serde(skip, default="def_dur_cycles")] pub dur_cycles: Vec<i128>,
#[serde(skip, default="def_i_cycle")] pub i_cycle: usize
}
fn def_field() -> Field {
Field::new_default()
}
fn def_neighbourhood() -> Neighbourhood {
Neighbourhood::new_default()
}
fn def_t_last_cycle() -> i128 {
0
}
fn def_dur_cycles() -> Vec<i128> {
vec![0i128; NUM_SPS_CYCLES]
}
fn def_i_cycle() -> usize {
0
}
impl Automaton {
pub fn new_default(sys: &System) -> Self {
let field = def_field();
let (f_height, f_width) = (field.height(), field.width());
Self{
field,
neighbourhood: def_neighbourhood(),
amplification: DEF_AMPLIFICATION,
noise: DEF_NOISE,
synchronicity: DEF_SYNCHRONICITY,
edit_mode: EditMode::Field,
color_mode: ColorMode::HueLum,
pointer_field_u: 0,
pointer_field_y: 0,
pointer_field_x: 0,
cell_size: 1,
show_nbhood: true,
nbhood_radius: DEF_NBHOOD_RADIUS,
nbhood_cell_size: (((f_height - 1 + f_width) as i32) / (2 * DEF_NBHOOD_RADIUS + 1)).max(1),
pointer_nbhood_y: 0,
pointer_nbhood_x: 0,
brush: COMPLEX_ZERO,
brush_radius: 4,
show_palette: true,
nbhood_du: 0,
show_maglass: true,
maglass_radius: DEF_MAGLASS_RADIUS,
maglass_cell_size: ((sys.height() - ((f_height - 1 + f_width) as i32)) / (2 * DEF_MAGLASS_RADIUS + 1)).max(1),
show_field: true,
scale_lum: false,
manual_step: false,
show_settings_stats: true,
t_last_cycle: def_t_last_cycle(),
dur_cycles: def_dur_cycles(),
i_cycle: def_i_cycle()
}
}
pub fn count_new_cycle(&mut self, t: i128) {
self.dur_cycles[self.i_cycle] = t - self.t_last_cycle;
self.t_last_cycle = t;
self.i_cycle = (self.i_cycle + 1) % NUM_SPS_CYCLES;
}
pub fn sps(&self) -> f64 {
1e6 * (NUM_SPS_CYCLES as f64) / (self.dur_cycles.iter().sum::<i128>() as f64)
}
pub fn load(dirpath: impl ToString) -> Result<Self, String> {
let dirpath = format!("{}/{}", dirpath.to_string(), STATE_DIRNAME);
let state_filepath = format!("{}/{}", &dirpath, STATE_FILENAME);
File::open(&state_filepath).map_err(|e| e.to_string())?;
let json = fs::read(&state_filepath).pre_err(format!("cannot read from '{}'", &state_filepath))?;
let mut automaton: Self = serde_json::from_slice(&json).pre_err(format!("cannot deserialize (inflated) '{}' to state", &state_filepath))?;
automaton.field = Field::load(& format!("{}/{}", &dirpath, FIELD_FILENAME))?;
automaton.neighbourhood = Neighbourhood::load(& format!("{}/{}", &dirpath, NEIGHBOURHOOD_FILENAME))?;
Ok(automaton)
}
pub fn save(&self, dirpath: impl ToString) -> Result<(), String> {
let dirpath = format!("{}/{}", dirpath.to_string(), STATE_DIRNAME);
fs::create_dir_all(&dirpath).unwrap_or(());
self.field.save(& format!("{}/{}", dirpath, FIELD_FILENAME))?;
self.neighbourhood.save(& format!("{}/{}", dirpath, NEIGHBOURHOOD_FILENAME))?;
let state_filepath = format!("{}/{}", &dirpath, STATE_FILENAME);
let json = serde_json::to_vec_pretty(self).pre_err("cannot serialize state")?;
fs::write(&state_filepath, &json).pre_err(format!("cannot write to '{}'", &state_filepath))?;
Ok(())
}
}
pub fn play(ctx: &mut Context) -> Result<(), String> {
ctx.lingua.load(&["automaton"])?;
let mut mastermind = Mastermind::new();
mastermind.add(vec![
MControl::new(),
MRender::new(),
MTelemetry::new(),
MSync::new(),
]);
mastermind.start(ctx)?;
while (!ctx.ether.quit) && (!ctx.ether.replay) {
mastermind.run(ctx)?;
if !ctx.ether.meta {
if ctx.sys.poll_key(Key::Escape) {
ctx.ether.meta = true;
}
}
}
mastermind.finish(ctx)?;
Ok(())
}