use super::results::{Pool, Results, Value};
use rand::prelude::*;
use std::cmp::Ordering;
use std::fmt;
#[derive(Debug, PartialEq)]
pub struct Generator {
pub mul_div: MulDivGenerator,
pub op: Option<ComparisonOp>,
}
impl fmt::Display for Generator {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.mul_div)?;
if let Some(op) = &self.op {
write!(f, " {}", op)?;
}
write!(f, "")
}
}
impl Generator {
pub fn generate<R: Rng + ?Sized>(&self, rng: &mut R) -> Results {
let lhs = self.mul_div.generate(rng);
let (rhs, value) = match &self.op {
Some(op) => match op {
ComparisonOp::GT(rhs) => {
let rhs = rhs.generate(rng);
let val = if lhs.value() > rhs.value() { 1 } else { 0 };
(Some(rhs), val)
}
ComparisonOp::GE(rhs) => {
let rhs = rhs.generate(rng);
let val = if lhs.value() >= rhs.value() { 1 } else { 0 };
(Some(rhs), val)
}
ComparisonOp::LT(rhs) => {
let rhs = rhs.generate(rng);
let val = if lhs.value() < rhs.value() { 1 } else { 0 };
(Some(rhs), val)
}
ComparisonOp::LE(rhs) => {
let rhs = rhs.generate(rng);
let val = if lhs.value() <= rhs.value() { 1 } else { 0 };
(Some(rhs), val)
}
ComparisonOp::EQ(rhs) => {
let rhs = rhs.generate(rng);
let val = if lhs.value() == rhs.value() { 1 } else { 0 };
(Some(rhs), val)
}
ComparisonOp::CMP(rhs) => {
let rhs = rhs.generate(rng);
let val = match lhs.value().cmp(&rhs.value()) {
Ordering::Less => -1,
Ordering::Greater => 1,
Ordering::Equal => 0,
};
(Some(rhs), val)
}
},
None => (None, 0),
};
Results { lhs, rhs, value }
}
}
#[derive(Debug, PartialEq)]
pub enum ComparisonOp {
GT(SuccGenerator),
GE(SuccGenerator),
LT(SuccGenerator),
LE(SuccGenerator),
EQ(SuccGenerator),
CMP(SuccGenerator),
}
impl fmt::Display for ComparisonOp {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
ComparisonOp::GT(succ) => write!(f, "> {}", succ),
ComparisonOp::GE(succ) => write!(f, ">= {}", succ),
ComparisonOp::LT(succ) => write!(f, "< {}", succ),
ComparisonOp::LE(succ) => write!(f, "<= {}", succ),
ComparisonOp::EQ(succ) => write!(f, "= {}", succ),
ComparisonOp::CMP(succ) => write!(f, "<=> {}", succ),
}
}
}
#[derive(Debug, PartialEq, Clone)]
pub enum MulDivOp {
Mul(i32),
Div(i32),
}
impl fmt::Display for MulDivOp {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
MulDivOp::Mul(n) => write!(f, " x {}", n),
MulDivOp::Div(n) => write!(f, " / {}", n),
}
}
}
#[derive(Debug, PartialEq)]
pub struct MulDivGenerator {
pub succ: SuccGenerator,
pub op: Option<MulDivOp>,
}
impl fmt::Display for MulDivGenerator {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.succ)?;
if let Some(op) = &self.op {
write!(f, "{}", op)?;
}
write!(f, "")
}
}
impl MulDivGenerator {
pub fn generate<R: Rng + ?Sized>(&self, rng: &mut R) -> Pool {
let mut pool = self.succ.generate(rng);
match &self.op {
Some(op) => match op {
MulDivOp::Mul(n) => {
let total = pool.sum();
pool.set_total(total * n);
pool
}
MulDivOp::Div(n) => {
let total = pool.sum();
pool.set_total(total / n);
pool
}
},
None => pool,
}
}
}
#[derive(Debug, PartialEq)]
pub struct SuccGenerator {
pub hits: HitsGenerator,
pub op: Option<SuccessOp>,
}
impl fmt::Display for SuccGenerator {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.hits)?;
if let Some(op) = &self.op {
write!(f, "{}", op)?;
}
write!(f, "")
}
}
impl SuccGenerator {
pub fn generate<R: Rng + ?Sized>(&self, rng: &mut R) -> Pool {
let mut pool = self.hits.generate(rng);
match &self.op {
Some(op) => match op {
SuccessOp::TargetSucc(n) => {
if pool.sum() >= *n {
pool.set_total(pool.sum() - n + 1);
} else {
pool.set_total(0);
}
pool
}
SuccessOp::TargetSuccNext(n, m) => {
if pool.sum() >= *n {
pool.set_total(((pool.sum() - n) / m) + 1);
} else {
pool.set_total(0);
}
pool
}
},
None => pool,
}
}
}
#[derive(Debug, PartialEq)]
pub enum SuccessOp {
TargetSucc(i32),
TargetSuccNext(i32, i32),
}
impl fmt::Display for SuccessOp {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
SuccessOp::TargetSucc(n) => write!(f, "{{{}}}", n),
SuccessOp::TargetSuccNext(n, m) => write!(f, "{{{}, {}}}", n, m),
}
}
}
#[derive(Debug, PartialEq)]
pub struct HitsGenerator {
pub expr: ExprGenerator,
pub op: Option<TargetOp>,
}
impl fmt::Display for HitsGenerator {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.expr)?;
if let Some(op) = &self.op {
write!(f, "{}", op)?;
}
write!(f, "")
}
}
impl HitsGenerator {
pub fn generate<R: Rng + ?Sized>(&self, rng: &mut R) -> Pool {
let mut pool = self.expr.generate(rng);
match &self.op {
Some(op) => match op {
TargetOp::TargetHigh(n) => {
for idx in 0..pool.count() {
let b = pool.values[idx].sum().abs() >= *n;
pool.values[idx].set_hit(b);
}
pool.set_total(pool.sum());
pool
}
TargetOp::TargetLow(n) => {
for idx in 0..pool.count() {
let b = pool.values[idx].sum().abs() <= *n;
pool.values[idx].set_hit(b);
}
pool.set_total(pool.sum());
pool
}
},
None => pool,
}
}
}
#[derive(Debug, PartialEq)]
pub enum TargetOp {
TargetHigh(i32),
TargetLow(i32),
}
impl fmt::Display for TargetOp {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
TargetOp::TargetHigh(n) => write!(f, "[{}]", n),
TargetOp::TargetLow(n) => write!(f, "({})", n),
}
}
}
#[derive(Debug, PartialEq)]
pub struct ExprGenerator {
pub terms: Vec<ArithTermGenerator>,
}
impl fmt::Display for ExprGenerator {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
for t in self.terms.iter() {
write!(f, "{}", t)?;
}
write!(f, "")
}
}
impl ExprGenerator {
pub fn generate<R: Rng + ?Sized>(&self, rng: &mut R) -> Pool {
let mut pool = Pool::new();
for t in self.terms.iter() {
pool.values.append(&mut t.generate(rng).values);
}
pool.set_total(pool.sum());
pool
}
}
#[derive(Debug, PartialEq, Clone)]
pub enum ArithOp {
ImplicitAdd,
Add,
Sub,
}
impl fmt::Display for ArithOp {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
ArithOp::ImplicitAdd => write!(f, ""),
ArithOp::Add => write!(f, " + "),
ArithOp::Sub => write!(f, " - "),
}
}
}
#[derive(Debug, PartialEq, Clone)]
pub struct ArithTermGenerator {
pub op: ArithOp,
pub term: TermGenerator,
}
impl fmt::Display for ArithTermGenerator {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}{}", self.op, self.term)
}
}
impl ArithTermGenerator {
pub fn generate<R: Rng + ?Sized>(&self, rng: &mut R) -> Pool {
let mut pool = self.term.generate(rng);
match &self.op {
ArithOp::Sub => {
for idx in 0..pool.count() {
pool.values[idx].mark_penalty();
}
pool
}
_ => pool,
}
}
}
#[derive(Debug, PartialEq, Clone)]
pub enum TermGenerator {
Pool(PoolGenerator),
Constant(i32),
}
impl fmt::Display for TermGenerator {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
TermGenerator::Pool(pg) => write!(f, "{}", pg),
TermGenerator::Constant(n) => write!(f, "{}", n),
}
}
}
impl TermGenerator {
pub fn generate<R: Rng + ?Sized>(&self, rng: &mut R) -> Pool {
match self {
TermGenerator::Pool(pg) => pg.generate(rng),
TermGenerator::Constant(n) => Pool::new_with_values(vec![Value::constant(*n)]),
}
}
}
#[derive(Debug, PartialEq, Clone)]
pub struct PoolGenerator {
pub count: i32,
pub range: i32,
pub op: Option<PoolOp>,
}
impl fmt::Display for PoolGenerator {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}d{}", self.count, self.range)?;
if let Some(op) = &self.op {
write!(f, "{}", op)?;
}
write!(f, "")
}
}
impl PoolGenerator {
pub fn generate<R: Rng + ?Sized>(&self, rng: &mut R) -> Pool {
let mut pool = Pool::new();
for _ in 0..self.count {
let val = Value::random(self.range, false, rng);
pool.values.push(val);
if let Some(op) = &self.op {
op.apply_last(&mut pool, rng);
}
}
if let Some(op) = &self.op {
op.apply_all(&mut pool, rng);
}
pool
}
}
#[derive(Debug, PartialEq, Clone)]
pub enum PoolOp {
Explode(Option<i32>),
ExplodeUntil(Option<i32>),
ExplodeEach(Option<i32>),
ExplodeEachUntil(Option<i32>),
AddEach(Option<i32>),
SubEach(Option<i32>),
TakeMid(i32),
TakeLow(i32),
TakeHigh(i32),
Disadvantage,
Advantage,
BestGroup,
}
impl fmt::Display for PoolOp {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match self {
PoolOp::Explode(n) => {
if let Some(n) = *n {
if n > 1 {
write!(f, "!{}", n)
} else {
write!(f, "!")
}
} else {
write!(f, "!")
}
}
PoolOp::ExplodeUntil(n) => {
if let Some(n) = *n {
if n > 1 {
write!(f, "!!{}", n)
} else {
write!(f, "!!")
}
} else {
write!(f, "!!")
}
}
PoolOp::ExplodeEach(n) => {
if let Some(n) = *n {
if n > 1 {
write!(f, "*{}", n)
} else {
write!(f, "*")
}
} else {
write!(f, "*")
}
}
PoolOp::ExplodeEachUntil(n) => {
if let Some(n) = *n {
if n > 1 {
write!(f, "**{}", n)
} else {
write!(f, "**")
}
} else {
write!(f, "**")
}
}
PoolOp::AddEach(n) => {
if let Some(n) = n {
write!(f, "++{}", n)
} else {
write!(f, "++")
}
}
PoolOp::SubEach(n) => {
if let Some(n) = n {
write!(f, "--{}", n)
} else {
write!(f, "--")
}
}
PoolOp::TakeMid(n) => write!(f, "~{}", n),
PoolOp::TakeLow(n) => write!(f, "`{}", n),
PoolOp::TakeHigh(n) => write!(f, "^{}", n),
PoolOp::Disadvantage => write!(f, " DIS"),
PoolOp::Advantage => write!(f, " ADV"),
PoolOp::BestGroup => write!(f, "Y"),
}
}
}
impl PoolOp {
pub fn apply_last<R: Rng + ?Sized>(&self, pool: &mut Pool, rng: &mut R) {
if pool.count() == 0 {
return;
}
match self {
PoolOp::ExplodeEach(n) => {
let n = self.safe_n(n, pool.last_range());
if pool.last_value() >= n {
let new_roll = Value::random(pool.last_range(), true, rng);
pool.values.push(new_roll);
}
}
PoolOp::ExplodeEachUntil(n) => loop {
let n = self.safe_n(n, pool.last_range());
if pool.last_value() >= n {
let new_roll = Value::random(pool.last_range(), true, rng);
pool.values.push(new_roll);
} else {
break;
}
},
PoolOp::AddEach(n) => {
let mut last = pool.values.pop().unwrap();
let n = n.unwrap_or(1);
last.set_modifier(n);
pool.values.push(last);
}
PoolOp::SubEach(n) => {
let mut last = pool.values.pop().unwrap();
let n = -n.unwrap_or(1);
last.set_modifier(n);
pool.values.push(last);
}
_ => (),
}
}
fn safe_n(&self, n: &Option<i32>, range: i32) -> i32 {
match *n {
Some(n) => {
if n <= 1 || n > range {
range
} else {
n
}
}
None => range,
}
}
pub fn apply_all<R: Rng + ?Sized>(&self, pool: &mut Pool, rng: &mut R) {
let cnt = pool.count();
if cnt == 0 {
return;
}
match self {
PoolOp::Explode(n) => {
let range = pool.range();
let n = self.safe_n(n, range);
let explode = pool.values.iter().all(|v| v.value >= n);
if explode {
for _ in 0..cnt {
let roll = Value::random(range, true, rng);
pool.values.push(roll);
}
}
}
PoolOp::ExplodeUntil(n) => {
let range = pool.range();
let n = self.safe_n(n, range);
let mut explode = pool.values.iter().all(|v| v.value >= n);
while explode {
for _ in 0..cnt {
pool.values.push(Value::random(range, true, rng));
if pool.last_value() < n {
explode = false;
}
}
}
}
PoolOp::TakeLow(take) => {
let take = *take as usize;
if cnt <= take {
return;
}
pool.values.sort_by(|a, b| a.value.cmp(&b.value));
for idx in 0..cnt {
if idx >= take {
pool.values[idx].mark_discarded();
}
}
}
PoolOp::TakeMid(take) => {
let take = *take as usize;
if cnt <= take {
return;
}
pool.values.sort_by(|a, b| b.value.cmp(&a.value));
let skip_start = (cnt - take) / 2;
let skip_end = skip_start + take;
for idx in 0..cnt {
if idx < skip_start || idx >= skip_end {
pool.values[idx].mark_discarded();
}
}
}
PoolOp::TakeHigh(take) => {
let take = *take as usize;
if cnt <= take {
return;
}
pool.values.sort_by(|a, b| b.value.cmp(&a.value));
for idx in 0..cnt {
if idx >= take {
pool.values[idx].mark_discarded();
}
}
}
PoolOp::Advantage => {
let old = pool.sum();
let range = pool.range();
for _ in 0..cnt {
let roll = Value::random(range, false, rng);
pool.values.push(roll);
}
if pool.sum() > old * 2 {
for idx in 0..cnt {
pool.values[idx].mark_discarded();
}
} else {
for idx in cnt..cnt * 2 {
pool.values[idx].mark_discarded();
}
}
}
PoolOp::Disadvantage => {
let old = pool.sum();
let range = pool.range();
for _ in 0..cnt {
let roll = Value::random(range, false, rng);
pool.values.push(roll);
}
if pool.sum() > old * 2 {
for idx in cnt..cnt * 2 {
pool.values[idx].mark_discarded();
}
} else {
for idx in 0..cnt {
pool.values[idx].mark_discarded();
}
}
}
PoolOp::BestGroup => {
pool.values.sort_by(|a, b| b.value.cmp(&a.value));
let mut last_val = 0;
let mut max_val = 0;
let mut max_run = 0;
let mut curr_run = 0;
let values = pool.values();
for val in values.into_iter() {
if let Some(n) = val {
if last_val == n {
curr_run += 1;
if curr_run > max_run {
max_run = curr_run;
max_val = last_val;
}
} else {
last_val = n;
curr_run = 0;
}
}
}
for v in &mut pool.values {
if v.value != max_val {
v.mark_discarded();
}
}
}
_ => (),
}
}
}