use crate::{
cells::Coord,
error::Error,
rules::{Life, LifeGen, NtLife, NtLifeGen, Rule},
traits::Search,
world::World,
};
use derivative::Derivative;
use std::{
cmp::Ordering,
fmt::{self, Debug, Formatter},
str::FromStr,
};
#[cfg(feature = "serialize")]
use serde::{Deserialize, Serialize};
#[derive(Clone, Copy, Derivative, PartialEq, Eq)]
#[derivative(Default)]
#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
pub enum Transform {
#[derivative(Default)]
Id,
Rotate90,
Rotate180,
Rotate270,
FlipRow,
FlipCol,
FlipDiag,
FlipAntidiag,
}
impl FromStr for Transform {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"Id" => Ok(Transform::Id),
"R90" => Ok(Transform::Rotate90),
"R180" => Ok(Transform::Rotate180),
"R270" => Ok(Transform::Rotate270),
"F-" => Ok(Transform::FlipRow),
"F|" => Ok(Transform::FlipCol),
"F\\" => Ok(Transform::FlipDiag),
"F/" => Ok(Transform::FlipAntidiag),
_ => Err(String::from("invalid Transform")),
}
}
}
impl Debug for Transform {
fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
let s = match self {
Transform::Id => "Id",
Transform::Rotate90 => "R90",
Transform::Rotate180 => "R180",
Transform::Rotate270 => "R270",
Transform::FlipRow => "F-",
Transform::FlipCol => "F|",
Transform::FlipDiag => "F\\",
Transform::FlipAntidiag => "F/",
};
write!(f, "{}", s)?;
Ok(())
}
}
impl Transform {
pub fn square_world(self) -> bool {
match self {
Transform::Rotate90
| Transform::Rotate270
| Transform::FlipDiag
| Transform::FlipAntidiag => true,
_ => false,
}
}
}
#[derive(Clone, Copy, Derivative, PartialEq, Eq)]
#[derivative(Default)]
#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
pub enum Symmetry {
#[derivative(Default)]
C1,
C2,
C4,
D2Row,
D2Col,
D2Diag,
D2Antidiag,
D4Ortho,
D4Diag,
D8,
}
impl FromStr for Symmetry {
type Err = String;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"C1" => Ok(Symmetry::C1),
"C2" => Ok(Symmetry::C2),
"C4" => Ok(Symmetry::C4),
"D2-" => Ok(Symmetry::D2Row),
"D2|" => Ok(Symmetry::D2Col),
"D2\\" => Ok(Symmetry::D2Diag),
"D2/" => Ok(Symmetry::D2Antidiag),
"D4+" => Ok(Symmetry::D4Ortho),
"D4X" => Ok(Symmetry::D4Diag),
"D8" => Ok(Symmetry::D8),
_ => Err(String::from("invalid symmetry")),
}
}
}
impl Debug for Symmetry {
fn fmt(&self, f: &mut Formatter) -> Result<(), fmt::Error> {
let s = match self {
Symmetry::C1 => "C1",
Symmetry::C2 => "C2",
Symmetry::C4 => "C4",
Symmetry::D2Row => "D2-",
Symmetry::D2Col => "D2|",
Symmetry::D2Diag => "D2\\",
Symmetry::D2Antidiag => "D2/",
Symmetry::D4Ortho => "D4+",
Symmetry::D4Diag => "D4X",
Symmetry::D8 => "D8",
};
write!(f, "{}", s)?;
Ok(())
}
}
impl Symmetry {
pub fn square_world(self) -> bool {
match self {
Symmetry::C4
| Symmetry::D2Diag
| Symmetry::D2Antidiag
| Symmetry::D4Diag
| Symmetry::D8 => true,
_ => false,
}
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
pub enum SearchOrder {
RowFirst,
ColumnFirst,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
pub enum NewState {
ChooseDead,
ChooseAlive,
Random,
}
impl Default for NewState {
fn default() -> Self {
NewState::ChooseAlive
}
}
#[derive(Clone, Debug, Derivative, PartialEq, Eq)]
#[derivative(Default)]
#[cfg_attr(feature = "serialize", derive(Serialize, Deserialize))]
pub struct Config {
#[derivative(Default(value = "16"))]
pub width: isize,
#[derivative(Default(value = "16"))]
pub height: isize,
#[derivative(Default(value = "1"))]
pub period: isize,
pub dx: isize,
pub dy: isize,
pub transform: Transform,
pub symmetry: Symmetry,
pub search_order: Option<SearchOrder>,
pub new_state: NewState,
pub max_cell_count: Option<usize>,
#[derivative(Default(value = "true"))]
pub non_empty_front: bool,
pub reduce_max: bool,
#[derivative(Default(value = "String::from(\"B3/S23\")"))]
pub rule_string: String,
}
impl Config {
pub fn new(width: isize, height: isize, period: isize) -> Self {
Config {
width,
height,
period,
..Config::default()
}
}
pub fn set_translate(mut self, dx: isize, dy: isize) -> Self {
self.dx = dx;
self.dy = dy;
self
}
pub fn set_transform(mut self, transform: Transform) -> Self {
self.transform = transform;
self
}
pub fn set_symmetry(mut self, symmetry: Symmetry) -> Self {
self.symmetry = symmetry;
self
}
pub fn set_search_order(mut self, search_order: Option<SearchOrder>) -> Self {
self.search_order = search_order;
self
}
pub fn set_new_state(mut self, new_state: NewState) -> Self {
self.new_state = new_state;
self
}
pub fn set_max_cell_count(mut self, max_cell_count: Option<usize>) -> Self {
self.max_cell_count = max_cell_count;
self
}
pub fn set_non_empty_front(mut self, non_empty_front: bool) -> Self {
self.non_empty_front = non_empty_front;
self
}
pub fn set_reduce_max(mut self, reduce_max: bool) -> Self {
self.reduce_max = reduce_max;
self
}
pub fn set_rule_string(mut self, rule_string: String) -> Self {
self.rule_string = rule_string;
self
}
pub(crate) fn auto_search_order(&self) -> SearchOrder {
self.search_order.unwrap_or_else(|| {
let (width, height) = match self.symmetry {
Symmetry::D2Row => (self.width, (self.height + 1) / 2),
Symmetry::D2Col => ((self.width + 1) / 2, self.height),
_ => (self.width, self.height),
};
match width.cmp(&height) {
Ordering::Greater => SearchOrder::ColumnFirst,
Ordering::Less => SearchOrder::RowFirst,
Ordering::Equal => {
if self.dx.abs() >= self.dy.abs() {
SearchOrder::ColumnFirst
} else {
SearchOrder::RowFirst
}
}
}
})
}
pub(crate) fn translate(&self, coord: Coord) -> Coord {
let (mut x, mut y, mut t) = coord;
while t < 0 {
t += self.period;
let (new_x, new_y) = match self.transform {
Transform::Id => (x, y),
Transform::Rotate90 => (self.height - 1 - y, x),
Transform::Rotate180 => (self.width - 1 - x, self.height - 1 - y),
Transform::Rotate270 => (y, self.width - 1 - x),
Transform::FlipRow => (x, self.height - 1 - y),
Transform::FlipCol => (self.width - 1 - x, y),
Transform::FlipDiag => (y, x),
Transform::FlipAntidiag => (self.height - 1 - y, self.width - 1 - x),
};
x = new_x - self.dx;
y = new_y - self.dy;
}
while t >= self.period {
t -= self.period;
x += self.dx;
y += self.dy;
let (new_x, new_y) = match self.transform {
Transform::Id => (x, y),
Transform::Rotate90 => (y, self.width - 1 - x),
Transform::Rotate180 => (self.width - 1 - x, self.height - 1 - y),
Transform::Rotate270 => (self.height - 1 - y, x),
Transform::FlipRow => (x, self.height - 1 - y),
Transform::FlipCol => (self.width - 1 - x, y),
Transform::FlipDiag => (y, x),
Transform::FlipAntidiag => (self.height - 1 - y, self.width - 1 - x),
};
x = new_x;
y = new_y;
}
(x, y, t)
}
pub fn world(&self) -> Result<Box<dyn Search>, Error> {
if let Ok(rule) = self.rule_string.parse::<Life>() {
Ok(Box::new(World::new(&self, rule)))
} else if let Ok(rule) = self.rule_string.parse::<NtLife>() {
Ok(Box::new(World::new(&self, rule)))
} else if let Ok(rule) = self.rule_string.parse::<LifeGen>() {
if rule.gen() > 2 {
Ok(Box::new(World::new(&self, rule)))
} else {
let rule = rule.non_gen();
Ok(Box::new(World::new(&self, rule)))
}
} else {
let rule = self
.rule_string
.parse::<NtLifeGen>()
.map_err(Error::ParseRuleError)?;
if rule.gen() > 2 {
Ok(Box::new(World::new(&self, rule)))
} else {
let rule = rule.non_gen();
Ok(Box::new(World::new(&self, rule)))
}
}
}
}