use crate::builtin::{Group, Id, Item};
use crate::levelstring::ObjParam;
use crate::optimize::*;
use cached::proc_macro::cached;
use std::cmp::{max, min, Ordering};
use std::collections::{HashMap, HashSet};
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub enum IcExpr {
Or(Box<IcExpr>, Box<IcExpr>),
And(Box<IcExpr>, Box<IcExpr>),
True,
False,
Equals(Item, i32),
MoreThan(Item, i32),
LessThan(Item, i32),
}
fn create_instant_count_trigger(
reference_trigger: Trigger,
target_group: Group,
group: Group,
operation: u8,
num: i32,
item: Item,
objects: &mut Triggerlist,
network: &mut TriggerNetwork,
settings: (bool, bool),
) {
let mut new_obj_map = HashMap::new();
new_obj_map.insert(1, ObjParam::Number(1811.0));
new_obj_map.insert(51, ObjParam::Group(target_group));
new_obj_map.insert(80, ObjParam::Item(item));
new_obj_map.insert(77, ObjParam::Number(num.into()));
new_obj_map.insert(88, ObjParam::Number(operation.into()));
new_obj_map.insert(56, ObjParam::Bool(true));
new_obj_map.insert(57, ObjParam::Group(group));
let new_obj = GdObj {
params: new_obj_map,
func_id: reference_trigger.obj.0,
mode: ObjectMode::Trigger,
unique_id: objects[reference_trigger.obj].0.unique_id,
sync_group: 0,
sync_part: 0,
};
(*objects.list)[reference_trigger.obj.0]
.obj_list
.push((new_obj.clone(), reference_trigger.order));
let obj_index = (
reference_trigger.obj.0,
objects.list[reference_trigger.obj.0].obj_list.len() - 1,
);
let new_trigger = Trigger {
obj: obj_index,
optimized: settings.0,
deleted: settings.1,
role: TriggerRole::Func,
..reference_trigger
};
if let Some(ObjParam::Group(group)) = new_obj.params.get(&57) {
match network.get_mut(group) {
Some(gang) => (*gang).triggers.push(new_trigger),
None => {
network.insert(*group, TriggerGang::new(vec![new_trigger]));
}
}
} else {
match network.get_mut(&NO_GROUP) {
Some(gang) => (*gang).triggers.push(new_trigger),
None => {
network.insert(NO_GROUP, TriggerGang::new(vec![new_trigger]));
}
}
}
}
impl IcExpr {
fn get_variables(&self) -> HashSet<Item> {
let mut variables = HashSet::new();
match self {
Self::Equals(item, _) | Self::MoreThan(item, _) | Self::LessThan(item, _) => {
variables.insert(*item);
}
Self::And(expr1, expr2) | Self::Or(expr1, expr2) => {
variables.extend(expr1.get_variables());
variables.extend(expr2.get_variables());
}
_ => (),
};
variables
}
fn flatten_and(&self) -> Vec<Self> {
match &self {
&Self::And(a, b) => a.flatten_and().into_iter().chain(b.flatten_and()).collect(),
a => {
vec![(*a).clone()]
}
}
}
fn flatten_or(&self) -> Vec<Self> {
match &self {
&Self::Or(a, b) => a.flatten_or().into_iter().chain(b.flatten_or()).collect(),
a => {
vec![(*a).clone()]
}
}
}
fn stack_and(mut iter: impl Iterator<Item = Self>) -> Self {
let mut out = iter.next().unwrap();
for expr in iter {
out = Self::And(out.into(), expr.into());
}
out
}
fn stack_or(mut iter: impl Iterator<Item = Self>) -> Self {
let mut out = iter.next().unwrap();
for expr in iter {
out = Self::Or(out.into(), expr.into());
}
out
}
fn remove_duplicates(&self) -> Self {
match self {
Self::And(_, _) => {
let list = self.flatten_and();
let set: HashSet<_> = list.iter().collect();
let mut set_iter = set.iter().cloned();
let mut out = (*set_iter.next().unwrap()).remove_duplicates();
for expr in set_iter {
out = Self::And(out.into(), expr.remove_duplicates().into());
}
out
}
Self::Or(_, _) => {
let list = self.flatten_or();
let set: HashSet<_> = list.iter().collect();
let mut set_iter = set.iter().cloned();
let mut out = (*set_iter.next().unwrap()).remove_duplicates();
for expr in set_iter {
out = Self::Or(out.into(), expr.remove_duplicates().into());
}
out
}
a => a.clone(),
}
}
fn decrease_and(&self) -> Self {
match self {
Self::Or(a, b) => {
if let Self::And(a1, b1) = *a.clone() {
if let Self::And(a2, b2) = *b.clone() {
let a1 = a1.decrease_and();
let b1 = b1.decrease_and();
let a2 = a2.decrease_and();
let b2 = b2.decrease_and();
if a1 == a2 {
Self::And(a1.into(), Self::Or(b1.into(), b2.into()).into())
} else if a1 == b2 {
Self::And(a1.into(), Self::Or(b1.into(), a2.into()).into())
} else if b1 == a2 {
Self::And(b1.into(), Self::Or(a1.into(), b2.into()).into())
} else if b1 == b2 {
Self::And(b1.into(), Self::Or(a1.into(), a2.into()).into())
} else {
Self::Or(a.decrease_and().into(), b.decrease_and().into())
}
} else {
Self::Or(a.decrease_and().into(), b.decrease_and().into())
}
} else {
Self::Or(a.decrease_and().into(), b.decrease_and().into())
}
}
Self::And(a, b) => Self::And(a.decrease_and().into(), b.decrease_and().into()),
a => a.clone(),
}
}
}
#[cached]
fn equal_behaviour(first: IcExpr, other: IcExpr) -> bool {
if first == other {
return true;
};
let mut critical_value_sets = HashMap::new();
get_critical_value_sets(&first, &mut critical_value_sets);
get_critical_value_sets(&other, &mut critical_value_sets);
let inputs = enumerate_truth_table_inputs(&critical_value_sets);
let truth_table1 = get_truth_table(&first, &inputs);
let truth_table2 = get_truth_table(&other, &inputs);
truth_table1 == truth_table2
}
#[derive(Debug, Clone, Eq)]
struct HeapItem {
complexity: u16,
formula: IcExpr,
}
impl PartialEq for HeapItem {
fn eq(&self, other: &Self) -> bool {
self.complexity == other.complexity
}
}
impl PartialOrd for HeapItem {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.complexity.cmp(&other.complexity))
}
}
impl Ord for HeapItem {
fn cmp(&self, other: &Self) -> Ordering {
self.complexity.cmp(&other.complexity)
}
}
#[derive(Debug, Clone, Eq)]
struct FullHeapItem {
item: HeapItem,
count: u32,
iter: Combinations,
}
impl PartialEq for FullHeapItem {
fn eq(&self, other: &Self) -> bool {
self.item == other.item
}
}
impl PartialOrd for FullHeapItem {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(&other))
}
}
impl Ord for FullHeapItem {
fn cmp(&self, other: &Self) -> Ordering {
match self.item.cmp(&other.item) {
Ordering::Equal => self.count.cmp(&other.count),
a => a,
}
}
}
impl HeapItem {
fn new(formula: IcExpr) -> Self {
Self {
complexity: get_complexity(&formula),
formula,
}
}
}
use std::cmp::Reverse;
use std::collections::BinaryHeap;
type CriticalValueSets = HashMap<Item, HashSet<i32>>;
fn get_critical_value_sets(formula: &IcExpr, result: &mut CriticalValueSets) {
let mut insert_to_result = |item, num: &i32| {
if let Some(set) = result.get_mut(item) {
set.insert(*num);
} else {
let mut new_set = HashSet::new();
new_set.insert(*num);
result.insert(*item, new_set);
}
};
match formula {
IcExpr::True | IcExpr::False => (),
IcExpr::LessThan(item, num) => insert_to_result(item, num),
IcExpr::Equals(item, num) => {
insert_to_result(item, num);
insert_to_result(item, &(*num + 1));
}
IcExpr::MoreThan(item, num) => {
insert_to_result(item, &(*num + 1));
}
IcExpr::And(lhs, rhs) | IcExpr::Or(lhs, rhs) => {
get_critical_value_sets(&**lhs, result); get_critical_value_sets(&**rhs, result);
}
};
}
fn get_solve_complexity(formula: &IcExpr) -> u16 {
match formula {
IcExpr::True | IcExpr::False => 0,
IcExpr::LessThan(_, _) => 1,
IcExpr::Equals(_, _) => 2,
IcExpr::MoreThan(_, _) => 1,
IcExpr::And(lhs, rhs) | IcExpr::Or(lhs, rhs) => {
get_solve_complexity(&**lhs) + get_solve_complexity(&**rhs)
}
}
}
fn enumerate_truth_table_inputs(
critical_value_sets: &CriticalValueSets,
) -> Vec<HashMap<Item, i32>> {
use itertools::Itertools;
let value_sets = critical_value_sets.values();
let product = value_sets
.map(|value_set| {
let mut new_set = value_set.clone();
new_set.insert(i32::MIN);
new_set.iter().copied().collect::<Vec<i32>>()
})
.multi_cartesian_product();
product
.map(|values| {
let mut dict = HashMap::new();
let mut values_iter = values.iter();
for variable in critical_value_sets.keys() {
dict.insert(*variable, *values_iter.next().unwrap());
}
dict
})
.collect()
}
fn enumerate_useful_primitives(critical_value_sets: &CriticalValueSets) -> Vec<IcExpr> {
let mut out = vec![IcExpr::True, IcExpr::False];
for (variable, value_set) in critical_value_sets.iter() {
for value in value_set {
out.push(IcExpr::LessThan(*variable, *value));
if value_set.get(&(value + 1)).is_some() {
out.push(IcExpr::Equals(*variable, *value));
}
out.push(IcExpr::MoreThan(*variable, *value - 1));
}
}
out
}
fn evaluate(formula: &IcExpr, input: &HashMap<Item, i32>) -> bool {
match formula {
IcExpr::True => true,
IcExpr::False => false,
IcExpr::LessThan(item, num) => input[item] < *num,
IcExpr::Equals(item, num) => input[item] == *num,
IcExpr::MoreThan(item, num) => input[item] > *num,
IcExpr::And(e1, e2) => evaluate(&**e1, input) && evaluate(&**e2, input),
IcExpr::Or(e1, e2) => evaluate(&**e1, input) || evaluate(&**e2, input),
}
}
fn get_truth_table(formula: &IcExpr, inputs: &[HashMap<Item, i32>]) -> u64 {
let mut truth_table = 0;
for input in inputs {
truth_table = (truth_table << 1) + evaluate(formula, input) as u64;
}
truth_table
}
pub fn get_complexity(formula: &IcExpr) -> u16 {
match formula {
IcExpr::True | IcExpr::False => 0,
IcExpr::LessThan(_, _) | IcExpr::MoreThan(_, _) | IcExpr::Equals(_, _) => 0,
IcExpr::And(lhs, rhs) => {
let ands_lhs = get_complexity(&**lhs);
let ands_rhs = get_complexity(&**rhs);
ands_lhs + 1 + ands_rhs
}
IcExpr::Or(lhs, rhs) => {
let ands_lhs = get_complexity(&**lhs);
let ands_rhs = get_complexity(&**rhs);
ands_lhs + ands_rhs
}
}
}
struct Merge {
heap: BinaryHeap<Reverse<FullHeapItem>>,
iter_count: u32,
}
impl Merge {
fn update(&mut self, mut iter: Combinations) {
if let Some(value) = iter.next() {
self.heap.push(Reverse(FullHeapItem {
item: value,
count: self.iter_count,
iter,
}));
self.iter_count += 1;
}
}
fn push(&mut self, item: HeapItem) {
self.heap.push(Reverse(FullHeapItem {
item,
count: self.iter_count,
iter: Combinations {
or: false,
formula: IcExpr::False,
best_formulas: Vec::new(),
i: 0,
},
}));
self.iter_count += 1;
}
fn next(&mut self) -> Option<HeapItem> {
if self.heap.is_empty() {
return None;
}
let mut item = self.heap.pop().unwrap().0;
if let Some(next_value) = item.iter.next() {
self.heap.push(Reverse(FullHeapItem {
item: next_value,
..item.clone()
}))
}
Some(item.item)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
struct Combinations {
or: bool, formula: IcExpr,
best_formulas: Vec<IcExpr>,
i: usize,
}
impl Combinations {
fn next(&mut self) -> Option<HeapItem> {
if self.i >= self.best_formulas.len() {
return None;
}
let formula = if self.or {
IcExpr::Or(
self.formula.clone().into(),
self.best_formulas[self.i].clone().into(),
)
} else {
IcExpr::And(
self.formula.clone().into(),
self.best_formulas[self.i].clone().into(),
)
};
self.i += 1;
Some(HeapItem::new(formula))
}
}
pub fn simplify_ic_expr_full(target_formula: IcExpr) -> IcExpr {
let mut critical_value_sets = HashMap::new();
get_critical_value_sets(&target_formula, &mut critical_value_sets);
let inputs = enumerate_truth_table_inputs(&critical_value_sets);
let target_truth_table = get_truth_table(&target_formula, &inputs);
let mut best = HashMap::<u64, IcExpr>::new();
let mut merge = Merge {
heap: BinaryHeap::new(),
iter_count: 0,
};
for formula in enumerate_useful_primitives(&critical_value_sets) {
merge.push(HeapItem::new(formula));
}
let mut best_formulas = Vec::new();
loop {
if let Some(out) = best.get(&target_truth_table) {
return out.clone();
}
let item = match merge.next() {
Some(item) => item,
None => unreachable!(),
};
let formula = item.formula;
let truth_table = get_truth_table(&formula, &inputs);
if best.get(&truth_table).is_some() {
continue;
}
merge.update(Combinations {
or: false,
formula: formula.clone(),
best_formulas: best_formulas.clone(),
i: 0,
});
merge.update(Combinations {
or: true,
formula: formula.clone(),
best_formulas: best_formulas.clone(),
i: 0,
});
best.insert(truth_table, formula.clone());
best_formulas.push(formula);
}
}
pub fn build_ic_connections(
network: &mut TriggerNetwork,
objects: &mut Triggerlist,
closed_group: &mut u16,
mut connections: Vec<(Group, Group, IcExpr, Trigger)>,
) -> HashMap<Group, Group> {
if connections.is_empty() {
return HashMap::new();
}
let mut swaps = HashMap::new();
connections = {
let mut new_connections = Vec::new();
for (start, end, expr, trigger) in connections {
for new_expr in expr.flatten_or() {
new_connections.push((start, end, new_expr, trigger))
}
}
new_connections
};
let connections = connections
.iter()
.map(|(s, e, expr, t)| (*s, *e, expr.flatten_and(), *t))
.collect::<Vec<_>>();
let mut nodes = Vec::<(IcExpr, u16)>::new();
let mut costs = HashMap::new();
use connection_combiner::{reduce, IoNode, Sets};
let mut sets = Sets::new();
for (start, end, list, trigger) in connections {
let mut new_list = HashSet::new();
for node in list {
let compl = get_complexity(&node) + 1;
let mut found = false;
for (i, (other, other_compl)) in nodes.iter_mut().enumerate() {
if equal_behaviour(node.clone(), other.clone()) {
if compl < *other_compl {
*other = node.clone();
costs.insert(i, compl);
}
new_list.insert(i);
found = true;
break;
}
}
if !found {
new_list.insert(nodes.len());
costs.insert(nodes.len(), compl);
nodes.push((node, compl));
}
}
sets.push((
[IoNode::Input(start)].iter().copied().collect(),
new_list,
[IoNode::Output(end)].iter().copied().collect(),
trigger,
))
}
let mut edges = Vec::new();
let mut index = 0;
let mut ref_triggers = HashMap::new();
while !sets.is_empty() {
reduce(&mut sets, &mut index, &mut ref_triggers, &costs, |a, b| {
edges.push((a, b));
});
}
let mut trigger_groups: HashMap<IoNode, Group> = HashMap::new(); let mut get_group = |node: &IoNode, closed_group: &mut u16| match trigger_groups.get(node) {
Some(g) => *g,
None => {
(*closed_group) += 1;
let new_group = Group {
id: Id::Arbitrary(*closed_group),
};
trigger_groups.insert(*node, new_group);
new_group
}
};
for (start, end) in edges {
match start {
IoNode::Output(_) => unreachable!(),
IoNode::Input(input) => match end {
IoNode::Color(col, i) => {
let node_group = get_group(&IoNode::Color(col, i), closed_group);
create_spawn_trigger(
ref_triggers[&(col, i)],
node_group,
input,
0.0,
objects,
network,
(false, false),
);
}
_ => unreachable!(),
},
IoNode::Color(col, i) => {
let start_group = get_group(&IoNode::Color(col, i), closed_group);
let end_group = match end {
node @ IoNode::Color(_, _) => get_group(&node, closed_group),
IoNode::Output(output) => output,
_ => unreachable!(),
};
let ref_trigger = ref_triggers[&(col, i)];
match &nodes[col].0 {
expr @ IcExpr::And(_, _) | expr @ IcExpr::Or(_, _) => {
let new_swaps = build_ic_connections(
network,
objects,
closed_group,
vec![(start_group, end_group, expr.clone(), ref_trigger)],
);
for swap in new_swaps {
assert!(swaps.insert(swap.0, swap.1).is_none());
}
}
IcExpr::True => {
assert!(swaps.insert(start_group, end_group).is_none());
}
expr => {
build_instant_count_network(
network,
objects,
start_group,
end_group,
expr.clone(),
ref_trigger,
closed_group,
);
}
}
}
}
}
swaps
}
pub fn build_instant_count_network<'a>(
network: &'a mut TriggerNetwork,
objects: &'a mut Triggerlist,
start_group: Group,
target: Group,
expr: IcExpr,
reference_trigger: Trigger,
closed_group: &mut u16,
) -> bool {
match expr {
IcExpr::Equals(item, num) | IcExpr::MoreThan(item, num) | IcExpr::LessThan(item, num) => {
create_instant_count_trigger(
reference_trigger,
target,
start_group,
match expr {
IcExpr::Equals(_, _) => 0,
IcExpr::MoreThan(_, _) => 1,
IcExpr::LessThan(_, _) => 2,
_ => unreachable!(),
},
num,
item,
objects,
network,
(false, false),
);
true
}
IcExpr::True => {
create_spawn_trigger(
reference_trigger,
target,
start_group,
0.0,
objects,
network,
(false, false),
);
true
}
IcExpr::And(expr1, expr2) => {
unreachable!()
}
IcExpr::Or(expr1, expr2) => {
unreachable!()
}
IcExpr::False => {
false
}
}
}
mod connection_combiner {
use std::collections::{BTreeSet, HashMap, HashSet};
use crate::{builtin::Group, optimize::Trigger};
type Color = usize;
#[derive(PartialEq, Eq, Hash, Clone, Copy, PartialOrd, Ord)]
pub enum IoNode {
Input(Group),
Output(Group),
Color(Color, u16),
}
impl std::fmt::Debug for IoNode {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Input(n) => f.write_str(&format!("input {:?}", n)),
Self::Output(n) => f.write_str(&format!("output {:?}", n)),
Self::Color(n, index) => f.write_str(&format!("{}_{}", n, index)),
}
}
}
pub type Sets = Vec<(BTreeSet<IoNode>, HashSet<Color>, BTreeSet<IoNode>, Trigger)>;
fn combine(sets: &mut Sets) {
let mut i = 0;
while i < sets.len() {
let mut j = i + 1;
while j < sets.len() {
if sets[j].1 == sets[i].1 && sets[j].2 == sets[i].2 {
for k in sets[j].0.clone() {
if !sets[i].0.contains(&k) {
sets[i].0.insert(k);
}
}
sets.remove(j);
j -= 1;
}
j += 1;
}
i += 1;
}
i = 0;
while i < sets.len() {
let mut j = i + 1;
while j < sets.len() {
if sets[j].1 == sets[i].1 && sets[j].0 == sets[i].0 {
for k in sets[j].2.clone() {
if !sets[i].2.contains(&k) {
sets[i].2.insert(k);
}
}
sets.remove(j);
j -= 1;
}
j += 1;
}
i += 1;
}
}
fn push_input<F>(
input: BTreeSet<IoNode>,
point: Color,
ref_trigger: Trigger,
sets: &mut Sets,
index: &mut u16,
ref_triggers: &mut HashMap<(Color, u16), Trigger>,
mut connect: F,
) where
F: FnMut(IoNode, IoNode),
{
(*index) += 1;
let current_index = *index;
for i in &input {
connect(*i, IoNode::Color(point, current_index));
}
for set in sets.iter_mut() {
if set.0 == input && set.1.contains(&point) {
(*set).1.remove(&point);
(*set).0 = [IoNode::Color(point, current_index)]
.iter()
.copied()
.collect();
ref_triggers.insert((point, current_index), ref_trigger);
}
}
erase_blank(sets, connect)
}
fn push_output<F>(
output: BTreeSet<IoNode>,
point: Color,
ref_trigger: Trigger,
sets: &mut Sets,
index: &mut u16,
ref_triggers: &mut HashMap<(Color, u16), Trigger>,
mut connect: F,
) where
F: FnMut(IoNode, IoNode),
{
(*index) += 1;
let current_index = *index;
for o in &output {
connect(IoNode::Color(point, current_index), *o);
}
for set in sets.iter_mut() {
if set.2 == output && set.1.contains(&point) {
(*set).1.remove(&point);
(*set).2 = [IoNode::Color(point, current_index)]
.iter()
.copied()
.collect();
ref_triggers.insert((point, current_index), ref_trigger);
}
}
erase_blank(sets, connect)
}
pub fn erase_blank<F>(sets: &mut Sets, mut connect: F)
where
F: FnMut(IoNode, IoNode),
{
for set in sets.iter() {
if set.1.is_empty() {
for a in &set.0 {
for b in &set.2 {
connect(*a, *b);
}
}
}
}
sets.retain(|a| !a.1.is_empty())
}
pub fn reduce<F>(
sets: &mut Sets,
index: &mut u16,
ref_triggers: &mut HashMap<(Color, u16), Trigger>,
costs: &HashMap<Color, u16>,
connect: F,
) where
F: FnMut(IoNode, IoNode),
{
combine(sets);
#[derive(Debug)]
struct Score {
inputs: HashMap<BTreeSet<IoNode>, u16>,
outputs: HashMap<BTreeSet<IoNode>, u16>,
ref_trigger: Trigger,
}
let mut scores = HashMap::<Color, Score>::new();
for (input, nodes, output, ref_trigger) in sets.iter_mut() {
for node in nodes.iter() {
let score = if let Some(score) = scores.get_mut(node) {
score
} else {
scores.insert(
*node,
Score {
inputs: HashMap::new(),
outputs: HashMap::new(),
ref_trigger: *ref_trigger,
},
);
scores.get_mut(node).unwrap()
};
if let Some(num) = score.inputs.get_mut(input) {
*num += 1;
} else {
score.inputs.insert(input.clone(), 1);
}
if let Some(num) = score.outputs.get_mut(output) {
*num += 1;
} else {
score.outputs.insert(output.clone(), 1);
}
}
}
let mut i_max = 0;
let mut o_max = 0;
let mut i_max_point = 0;
let mut o_max_point = 0;
let mut max_input = BTreeSet::new();
let mut max_output = BTreeSet::new();
let mut max_trigger = sets[0].3;
for (node, score) in scores {
let cost = costs[&node];
for (input, mut num) in score.inputs {
num *= cost;
if num > i_max {
i_max = num;
i_max_point = node;
max_input = input;
max_trigger = score.ref_trigger;
}
}
for (output, mut num) in score.outputs {
num *= cost;
if num > o_max {
o_max = num;
o_max_point = node;
max_output = output;
max_trigger = score.ref_trigger;
}
}
}
if o_max < i_max {
push_input(
max_input,
i_max_point,
max_trigger,
sets,
index,
ref_triggers,
connect,
);
} else {
push_output(
max_output,
o_max_point,
max_trigger,
sets,
index,
ref_triggers,
connect,
);
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn ic_expr_simplify() {
use crate::builtin::Id::*;
let _a = Item {
id: Id::Specific(1),
};
let _b = Item {
id: Id::Specific(2),
};
use IcExpr::{Equals, LessThan, MoreThan};
fn Or(e1: IcExpr, e2: IcExpr) -> IcExpr {
IcExpr::Or(e1.into(), e2.into())
}
fn And(e1: IcExpr, e2: IcExpr) -> IcExpr {
IcExpr::And(e1.into(), e2.into())
}
let expr = Or(
And(
And(
And(
And(
Equals(Item { id: Arbitrary(29) }, 0),
LessThan(Item { id: Specific(56) }, 3),
),
MoreThan(Item { id: Arbitrary(27) }, 0),
),
Equals(Item { id: Arbitrary(20) }, 1),
),
Equals(Item { id: Specific(54) }, 0),
),
And(
And(
And(
And(
Equals(Item { id: Arbitrary(29) }, 0),
LessThan(Item { id: Specific(56) }, 3),
),
LessThan(Item { id: Arbitrary(27) }, 0),
),
Equals(Item { id: Arbitrary(20) }, 1),
),
Equals(Item { id: Specific(54) }, 0),
),
);
println!("len: {:?}\n", get_solve_complexity(&expr));
}
}
pub fn get_all_ic_connections(
triggers: &mut TriggerNetwork,
objects: &Triggerlist,
) -> Vec<(Group, Group, IcExpr, Trigger)> {
let mut ictriggers = HashMap::<Group, Vec<(Group, IcExpr, Trigger)>>::new();
let mut inputs = HashSet::<Group>::new();
let mut outputs = HashSet::<Group>::new();
let mut to_be_subtracted_from = Vec::new();
for (group, gang) in triggers.iter_mut() {
let output_condition = gang
.triggers
.iter()
.any(|t| objects[t.obj].0.params.get(&1) != Some(&ObjParam::Number(1811.0)));
if output_condition {
outputs.insert(*group);
}
for trigger in &mut gang.triggers {
let obj = &objects[trigger.obj].0.params;
if let Some(ObjParam::Number(n)) = obj.get(&1) {
if *n as u16 == 1811 {
if obj.get(&56) == Some(&ObjParam::Bool(false)) {
continue;
}
let target = match obj.get(&51) {
Some(ObjParam::Group(g)) => *g,
_ => continue,
};
if gang.non_ic_triggers_in
|| *group
== (Group {
id: Id::Specific(0),
})
{
inputs.insert(*group);
}
let item = if let ObjParam::Item(i) =
obj.get(&80).unwrap_or(&ObjParam::Item(Item {
id: Id::Specific(0),
})) {
*i
} else {
Item {
id: Id::Specific(0),
}
};
let num = if let ObjParam::Number(a) =
obj.get(&77).unwrap_or(&ObjParam::Number(0.0))
{
*a as i32
} else {
0
};
let expr = match obj.get(&88) {
Some(ObjParam::Number(n)) => match *n as u8 {
1 => IcExpr::MoreThan(item, num),
2 => IcExpr::LessThan(item, num),
_ => IcExpr::Equals(item, num),
},
_ => IcExpr::Equals(item, num),
};
let group = match obj.get(&57) {
Some(ObjParam::Group(g)) => *g,
Some(ObjParam::GroupList(_)) => unimplemented!(),
_ => Group {
id: Id::Specific(0),
},
};
(*trigger).deleted = true;
(*trigger).optimized = true;
to_be_subtracted_from.push(target);
if let Some(l) = ictriggers.get_mut(&group) {
l.push((target, expr, *trigger))
} else {
ictriggers.insert(group, vec![(target, expr, *trigger)]);
}
}
}
}
}
for g in &to_be_subtracted_from {
(*triggers.get_mut(g).unwrap()).connections_in -= 1;
}
let mut all = Vec::new();
fn look_for_cycle(
current: Group,
ictriggers: &HashMap<Group, Vec<(Group, IcExpr, Trigger)>>,
visited: HashSet<Group>,
inputs: &mut HashSet<Group>,
outputs: &mut HashSet<Group>,
all: &mut Vec<(Group, Group, IcExpr, Trigger)>,
) {
if let Some(connections) = ictriggers.get(¤t) {
for (g, expr, trigger) in connections {
if visited.contains(&g) {
outputs.insert(current);
inputs.insert(*g);
all.push((current, *g, expr.clone(), *trigger));
return;
}
let mut new_visited = visited.clone();
new_visited.insert(current);
look_for_cycle(*g, ictriggers, new_visited, inputs, outputs, all);
}
}
}
for start in inputs.clone() {
look_for_cycle(
start,
&ictriggers,
HashSet::new(),
&mut inputs,
&mut outputs,
&mut all,
)
}
fn traverse(
current: Group,
origin: Group,
expr: IcExpr,
trigger: Option<Trigger>,
outputs: &HashSet<Group>,
ictriggers: &HashMap<Group, Vec<(Group, IcExpr, Trigger)>>,
visited: HashSet<Group>,
d: u16,
) -> Vec<(Group, Group, IcExpr, Trigger)> {
if visited.contains(¤t) {
unreachable!()
}
let mut out = Vec::new();
if let Some(connections) = ictriggers.get(¤t) {
for (g, e, trigger) in connections {
let new_expr = if expr == IcExpr::True {
e.clone()
} else {
IcExpr::And(expr.clone().into(), e.clone().into())
};
if outputs.contains(g) {
out.push((origin, *g, new_expr.clone(), *trigger));
out.extend(traverse(
*g,
*g,
IcExpr::True,
None,
&outputs,
&ictriggers,
HashSet::new(),
d + 1,
));
} else {
let mut new_visited = visited.clone(); new_visited.insert(current);
out.extend(traverse(
*g,
origin,
new_expr,
Some(*trigger),
outputs,
ictriggers,
new_visited,
d + 1,
))
}
}
} else if let Some(t) = trigger {
out.push((origin, current, expr, t)) } else {
assert!(outputs.contains(¤t));
}
if !out.is_empty() {
}
out
}
for start in inputs {
all.extend(traverse(
start,
start,
IcExpr::True, None,
&outputs,
&ictriggers,
HashSet::new(),
0,
));
}
let mut finished_expressions = HashMap::<(Group, Group), (IcExpr, Trigger)>::new();
for (start, end, expr, trigger) in all {
if let Some(e) = finished_expressions.get_mut(&(start, end)) {
*e = (IcExpr::Or(e.0.clone().into(), expr.clone().into()), e.1)
} else {
finished_expressions.insert((start, end), (expr.clone(), trigger));
}
}
let out = finished_expressions
.iter()
.map(|((start, end), (expr, trigger))| (*start, *end, expr.clone(), *trigger))
.collect();
out
}
fn overlap(mut expr1: IcExpr, mut expr2: IcExpr) -> IcExpr {
use IcExpr::*;
expr1 = simplify_ic_expr_fast(expr1);
expr2 = simplify_ic_expr_fast(expr2);
let base_expr = And(Box::from(expr1.clone()), Box::from(expr2.clone()));
match (expr1, expr2) {
(True, True) => True,
(False, _) | (_, False) => False,
(Equals(item1, num1), Equals(item2, num2)) => {
if item1 == item2 {
if num1 != num2 {
False
} else {
base_expr
}
} else {
base_expr
}
}
(MoreThan(item1, num1), MoreThan(item2, num2)) => {
if item1 == item2 {
MoreThan(item1, max(num1, num2))
} else {
base_expr
}
}
(LessThan(item1, num1), LessThan(item2, num2)) => {
if item1 == item2 {
LessThan(item1, min(num1, num2))
} else {
base_expr
}
}
(LessThan(item1, num1), MoreThan(item2, num2))
| (MoreThan(item2, num2), LessThan(item1, num1)) => {
if item1 == item2 && num1 <= num2 + 1 {
False
} else if item1 == item2 && num1 == num2 + 2 {
Equals(item1, num2 + 1)
} else {
base_expr
}
}
(Equals(item1, num1), MoreThan(item2, num2))
| (MoreThan(item2, num2), Equals(item1, num1)) => {
if item1 == item2 {
if num1 > num2 {
Equals(item1, num1)
} else {
False
}
} else {
base_expr
}
}
(Equals(item1, num1), LessThan(item2, num2))
| (LessThan(item2, num2), Equals(item1, num1)) => {
if item1 == item2 {
if num1 < num2 {
Equals(item1, num1)
} else {
False
}
} else {
base_expr
}
}
(Or(or1, or2), expr2) | (expr2, Or(or1, or2)) => {
let attempt = simplify_ic_expr_fast(Or(
And(or1, expr2.clone().into()).into(),
And(or2, expr2.into()).into(),
));
if get_complexity(&attempt) < get_complexity(&base_expr) {
attempt
} else {
base_expr
}
}
(And(and1, and2), expr2) | (expr2, And(and1, and2)) => {
let combinations = [
And(
simplify_ic_expr_fast(And(and1.clone(), expr2.clone().into())).into(),
and2.clone(),
),
And(simplify_ic_expr_fast(And(and2, expr2.into())).into(), and1),
base_expr,
];
combinations
.iter()
.min_by(|a, b| get_complexity(&a).cmp(&get_complexity(&b)))
.unwrap()
.clone()
}
(_, _) => base_expr,
}
}
fn union(mut expr1: IcExpr, mut expr2: IcExpr) -> IcExpr {
use IcExpr::*;
expr1 = simplify_ic_expr_fast(expr1);
expr2 = simplify_ic_expr_fast(expr2);
Or(Box::from(expr1), Box::from(expr2))
}
fn simplify_ic_expr_fast(mut expr: IcExpr) -> IcExpr {
expr = match expr {
IcExpr::And(e1, e2) => overlap(*e1, *e2),
IcExpr::Or(e1, e2) => union(*e1, *e2),
a => a,
};
expr
}