1use super::goal::{Goal, GoalStatus};
4use super::rule_executor::RuleExecutor;
5use std::sync::{Arc, Mutex};
6use crate::rete::propagation::IncrementalEngine;
7use crate::rete::working_memory::FactHandle;
8use crate::Facts;
9use crate::types::Value;
10use crate::KnowledgeBase;
11use crate::engine::rule::Rule;
12use std::collections::VecDeque;
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq)]
16pub enum SearchStrategy {
17 DepthFirst,
20
21 BreadthFirst,
24
25 Iterative,
28}
29
30#[derive(Debug)]
32pub struct SearchResult {
33 pub success: bool,
35
36 pub path: Vec<String>,
38
39 pub goals_explored: usize,
41
42 pub max_depth_reached: usize,
44
45 pub bindings: std::collections::HashMap<String, Value>,
47}
48
49impl SearchResult {
50 pub fn success(path: Vec<String>, goals_explored: usize, max_depth: usize) -> Self {
52 Self {
53 success: true,
54 path,
55 goals_explored,
56 max_depth_reached: max_depth,
57 bindings: std::collections::HashMap::new(),
58 }
59 }
60
61 pub fn failure(goals_explored: usize, max_depth: usize) -> Self {
63 Self {
64 success: false,
65 path: Vec::new(),
66 goals_explored,
67 max_depth_reached: max_depth,
68 bindings: std::collections::HashMap::new(),
69 }
70 }
71}
72
73pub struct DepthFirstSearch {
75 max_depth: usize,
76 goals_explored: usize,
77 path: Vec<String>,
78 executor: RuleExecutor,
79}
80
81impl DepthFirstSearch {
82 pub fn new(max_depth: usize, kb: KnowledgeBase) -> Self {
84 Self {
85 max_depth,
86 goals_explored: 0,
87 path: Vec::new(),
88 executor: RuleExecutor::new_with_inserter(kb, None),
89 }
90 }
91
92 pub fn new_with_engine(
96 max_depth: usize,
97 kb: KnowledgeBase,
98 engine: Option<Arc<Mutex<IncrementalEngine>>>,
99 ) -> Self {
100 let inserter = engine.map(|eng| {
102 let eng = eng.clone();
103 std::sync::Arc::new(move |fact_type: String, data: crate::rete::TypedFacts, rule_name: String, premises: Vec<String>| {
104 if let Ok(mut e) = eng.lock() {
105 let handles = e.resolve_premise_keys(premises);
107 let _ = e.insert_logical(fact_type, data, rule_name, handles);
108 }
109 }) as std::sync::Arc<dyn Fn(String, crate::rete::TypedFacts, String, Vec<String>) + Send + Sync>
110 });
111
112 Self {
113 max_depth,
114 goals_explored: 0,
115 path: Vec::new(),
116 executor: RuleExecutor::new_with_inserter(kb, inserter),
117 }
118 }
119
120 pub fn search_with_execution(&mut self, goal: &mut Goal, facts: &mut Facts, kb: &KnowledgeBase) -> SearchResult {
122 self.goals_explored = 0;
123 self.path.clear();
124
125 let success = self.search_recursive_with_execution(goal, facts, kb, 0);
126
127 SearchResult {
128 success,
129 path: self.path.clone(),
130 goals_explored: self.goals_explored,
131 max_depth_reached: goal.depth,
132 bindings: goal.bindings.to_map(),
133 }
134 }
135
136 pub fn search(&mut self, goal: &mut Goal, _facts: &Facts) -> SearchResult {
138 self.goals_explored = 0;
139 self.path.clear();
140
141 let success = self.search_recursive(goal, 0);
142
143 SearchResult {
144 success,
145 path: self.path.clone(),
146 goals_explored: self.goals_explored,
147 max_depth_reached: goal.depth,
148 bindings: goal.bindings.to_map(),
149 }
150 }
151
152 fn search_recursive_with_execution(
154 &mut self,
155 goal: &mut Goal,
156 facts: &mut Facts, kb: &KnowledgeBase,
158 depth: usize
159 ) -> bool {
160 self.goals_explored += 1;
161
162 if depth > self.max_depth {
164 goal.status = GoalStatus::Unprovable;
165 return false;
166 }
167
168 if self.check_goal_in_facts(goal, facts) {
170 goal.status = GoalStatus::Proven;
171 return true;
172 }
173
174 if goal.status == GoalStatus::InProgress {
176 goal.status = GoalStatus::Unprovable;
177 return false;
178 }
179
180 goal.status = GoalStatus::InProgress;
181 goal.depth = depth;
182
183 for rule_name in goal.candidate_rules.clone() {
185 self.path.push(rule_name.clone());
186
187 facts.begin_undo_frame();
190
191 if let Some(rule) = kb.get_rule(&rule_name) {
193 match self.executor.try_execute_rule(&rule, facts) {
195 Ok(true) => {
196 if self.check_goal_in_facts(goal, facts) {
199 goal.status = GoalStatus::Proven;
200 return true; }
202 }
204 Ok(false) => {
205 if self.try_prove_rule_conditions(&rule, facts, kb, depth + 1) {
207 match self.executor.try_execute_rule(&rule, facts) {
209 Ok(true) => {
210 if self.check_goal_in_facts(goal, facts) {
211 goal.status = GoalStatus::Proven;
212 return true; }
214 }
215 _ => {
216 }
218 }
219 }
220 }
221 Err(_) => {
222 }
224 }
225 }
226
227 facts.rollback_undo_frame();
229 self.path.pop();
230 }
231
232 let mut all_subgoals_proven = true;
235 if !goal.sub_goals.is_empty() {
236 facts.begin_undo_frame();
237 for sub_goal in &mut goal.sub_goals {
238 if !self.search_recursive_with_execution(sub_goal, facts, kb, depth + 1) {
239 all_subgoals_proven = false;
240 break;
241 }
242 }
243
244 if all_subgoals_proven {
245 facts.commit_undo_frame();
247 goal.status = GoalStatus::Proven;
248 return true;
249 }
250
251 facts.rollback_undo_frame();
253 }
254
255 goal.status = GoalStatus::Unprovable;
257 false
258 }
259
260 fn check_goal_in_facts(&self, goal: &Goal, facts: &Facts) -> bool {
264 if let Some(condition) = self.parse_goal_pattern(&goal.pattern) {
266 self.executor.evaluate_condition(&condition, facts).unwrap_or(false)
268 } else {
269 false
270 }
271 }
272
273 fn parse_goal_pattern(&self, pattern: &str) -> Option<crate::engine::rule::Condition> {
279 use crate::engine::rule::{Condition, ConditionExpression};
280 use crate::types::Operator;
281
282 let operators = [
284 (">=", Operator::GreaterThanOrEqual),
285 ("<=", Operator::LessThanOrEqual),
286 ("==", Operator::Equal),
287 ("!=", Operator::NotEqual),
288 (" > ", Operator::GreaterThan),
289 (" < ", Operator::LessThan),
290 (" contains ", Operator::Contains),
291 (" not_contains ", Operator::NotContains),
292 (" starts_with ", Operator::StartsWith),
293 (" startsWith ", Operator::StartsWith),
294 (" ends_with ", Operator::EndsWith),
295 (" endsWith ", Operator::EndsWith),
296 (" matches ", Operator::Matches),
297 ];
298
299 for (op_str, operator) in operators {
300 if let Some(pos) = pattern.find(op_str) {
301 let field = pattern[..pos].trim().to_string();
302 let value_str = pattern[pos + op_str.len()..].trim();
303
304 let value = self.parse_value_string(value_str);
306
307 return Some(Condition {
308 field: field.clone(),
309 expression: ConditionExpression::Field(field),
310 operator,
311 value,
312 });
313 }
314 }
315
316 None
317 }
318
319 fn parse_value_string(&self, s: &str) -> Value {
321 let s = s.trim();
322
323 if s == "true" {
325 return Value::Boolean(true);
326 }
327 if s == "false" {
328 return Value::Boolean(false);
329 }
330
331 if s == "null" {
333 return Value::Null;
334 }
335
336 if (s.starts_with('"') && s.ends_with('"')) || (s.starts_with('\'') && s.ends_with('\'')) {
338 return Value::String(s[1..s.len()-1].to_string());
339 }
340
341 if let Ok(n) = s.parse::<f64>() {
343 return Value::Number(n);
344 }
345
346 if let Ok(i) = s.parse::<i64>() {
348 return Value::Integer(i);
349 }
350
351 Value::String(s.to_string())
353 }
354
355 fn try_prove_rule_conditions(
358 &mut self,
359 rule: &Rule,
360 facts: &mut Facts,
361 kb: &KnowledgeBase,
362 depth: usize,
363 ) -> bool {
364 self.try_prove_condition_group(&rule.conditions, facts, kb, depth)
366 }
367
368 fn try_prove_condition_group(
370 &mut self,
371 group: &crate::engine::rule::ConditionGroup,
372 facts: &mut Facts,
373 kb: &KnowledgeBase,
374 depth: usize,
375 ) -> bool {
376 use crate::engine::rule::ConditionGroup;
377
378 match group {
379 ConditionGroup::Single(condition) => {
380 self.try_prove_single_condition(condition, facts, kb, depth)
382 }
383 ConditionGroup::Compound { left, operator, right } => {
384 use crate::types::LogicalOperator;
386 match operator {
387 LogicalOperator::And => {
388 self.try_prove_condition_group(left, facts, kb, depth)
390 && self.try_prove_condition_group(right, facts, kb, depth)
391 }
392 LogicalOperator::Or => {
393 self.try_prove_condition_group(left, facts, kb, depth)
395 || self.try_prove_condition_group(right, facts, kb, depth)
396 }
397 LogicalOperator::Not => {
398 !self.try_prove_condition_group(left, facts, kb, depth)
400 }
401 }
402 }
403 ConditionGroup::Not(_) | ConditionGroup::Exists(_) | ConditionGroup::Forall(_) | ConditionGroup::Accumulate { .. } => {
404 self.executor.evaluate_conditions(group, facts).unwrap_or(false)
408 }
409 }
410 }
411
412 fn try_prove_single_condition(
414 &mut self,
415 condition: &crate::engine::rule::Condition,
416 facts: &mut Facts,
417 kb: &KnowledgeBase,
418 depth: usize,
419 ) -> bool {
420 if let Ok(satisfied) = self.executor.evaluate_condition(condition, facts) {
422 if satisfied {
423 return true;
424 }
425 }
426
427 let goal_pattern = self.condition_to_goal_pattern(condition);
429
430 let mut sub_goal = Goal::new(goal_pattern.clone());
432 sub_goal.depth = depth;
433
434 for candidate_rule in kb.get_rules() {
436 if self.rule_could_prove_pattern(&candidate_rule, &goal_pattern) {
437 sub_goal.add_candidate_rule(candidate_rule.name.clone());
438 }
439 }
440
441 self.search_recursive_with_execution(&mut sub_goal, facts, kb, depth)
443 }
444
445 fn condition_to_goal_pattern(&self, condition: &crate::engine::rule::Condition) -> String {
447 use crate::engine::rule::ConditionExpression;
448
449 let field = match &condition.expression {
450 ConditionExpression::Field(f) => f.clone(),
451 ConditionExpression::FunctionCall { name, .. } => name.clone(),
452 ConditionExpression::Test { name, .. } => format!("test({})", name),
453 ConditionExpression::MultiField { field, .. } => field.clone(),
454 };
455
456 let op_str = match condition.operator {
457 crate::types::Operator::Equal => "==",
458 crate::types::Operator::NotEqual => "!=",
459 crate::types::Operator::GreaterThan => ">",
460 crate::types::Operator::LessThan => "<",
461 crate::types::Operator::GreaterThanOrEqual => ">=",
462 crate::types::Operator::LessThanOrEqual => "<=",
463 crate::types::Operator::Contains => "contains",
464 crate::types::Operator::NotContains => "not_contains",
465 crate::types::Operator::StartsWith => "starts_with",
466 crate::types::Operator::EndsWith => "ends_with",
467 crate::types::Operator::Matches => "matches",
468 };
469
470 let value_str = match &condition.value {
472 Value::Boolean(b) => b.to_string(),
473 Value::Number(n) => n.to_string(),
474 Value::Integer(i) => i.to_string(),
475 Value::String(s) => format!("\"{}\"", s),
476 Value::Null => "null".to_string(),
477 _ => format!("{:?}", condition.value),
478 };
479
480 format!("{} {} {}", field, op_str, value_str)
481 }
482
483 fn rule_could_prove_pattern(&self, rule: &Rule, pattern: &str) -> bool {
485 for action in &rule.actions {
487 match action {
488 crate::types::ActionType::Set { field, .. } => {
489 if pattern.contains(field) {
490 return true;
491 }
492 }
493 crate::types::ActionType::MethodCall { object, method, .. } => {
494 if pattern.contains(object) || pattern.contains(method) {
495 return true;
496 }
497 }
498 _ => {}
499 }
500 }
501 false
502 }
503
504 fn search_recursive(&mut self, goal: &mut Goal, depth: usize) -> bool {
506 self.goals_explored += 1;
507
508 if depth > self.max_depth {
510 goal.status = GoalStatus::Unprovable;
511 return false;
512 }
513
514 if goal.status == GoalStatus::InProgress {
516 goal.status = GoalStatus::Unprovable;
517 return false;
518 }
519
520 goal.status = GoalStatus::InProgress;
522 goal.depth = depth;
523
524 for rule_name in goal.candidate_rules.clone() {
526 self.path.push(rule_name.clone());
527
528 goal.status = GoalStatus::Proven;
542 return true;
543 }
544
545 for sub_goal in &mut goal.sub_goals {
547 if !self.search_recursive(sub_goal, depth + 1) {
548 goal.status = GoalStatus::Unprovable;
549 return false;
550 }
551 }
552
553 if goal.sub_goals.is_empty() && goal.candidate_rules.is_empty() {
555 goal.status = GoalStatus::Unprovable;
556 return false;
557 }
558
559 goal.status = GoalStatus::Proven;
560 true
561 }
562}
563
564pub struct BreadthFirstSearch {
566 max_depth: usize,
567 goals_explored: usize,
568 executor: RuleExecutor,
569}
570
571pub struct IterativeDeepeningSearch {
573 max_depth: usize,
574 goals_explored: usize,
575 kb: KnowledgeBase,
576 engine: Option<Arc<Mutex<IncrementalEngine>>>,
577}
578
579impl IterativeDeepeningSearch {
580 pub fn new(max_depth: usize, kb: KnowledgeBase) -> Self {
582 Self { max_depth, goals_explored: 0, kb, engine: None }
583 }
584
585 pub fn new_with_engine(max_depth: usize, kb: KnowledgeBase, engine: Option<Arc<Mutex<IncrementalEngine>>>) -> Self {
587 Self { max_depth, goals_explored: 0, kb, engine }
589 }
590
591 pub fn search_with_execution(&mut self, root_goal: &mut Goal, facts: &mut Facts, kb: &KnowledgeBase) -> SearchResult {
594 self.goals_explored = 0;
595 let mut cumulative_goals = 0usize;
596
597 for depth_limit in 0..=self.max_depth {
599 let mut probe_goal = root_goal.clone();
601 let probe_kb = self.kb.clone();
602 let mut probe_dfs = DepthFirstSearch::new(depth_limit, probe_kb);
603 let probe_result = probe_dfs.search(&mut probe_goal, facts);
604 cumulative_goals += probe_result.goals_explored;
605
606 if probe_result.success {
607 let exec_kb = self.kb.clone();
609 let mut exec_dfs = DepthFirstSearch::new_with_engine(depth_limit, exec_kb, self.engine.clone());
610 let exec_result = exec_dfs.search_with_execution(root_goal, facts, kb);
611 let mut final_result = exec_result;
613 final_result.goals_explored += cumulative_goals - final_result.goals_explored;
614 return final_result;
615 }
616 }
617
618 SearchResult::failure(cumulative_goals, self.max_depth)
620 }
621
622 pub fn search(&mut self, root_goal: &mut Goal, facts: &Facts) -> SearchResult {
624 self.goals_explored = 0;
625 let mut cumulative_goals = 0usize;
626
627 for depth_limit in 0..=self.max_depth {
628 let mut probe_goal = root_goal.clone();
629 let probe_kb = self.kb.clone();
630 let mut probe_dfs = DepthFirstSearch::new(depth_limit, probe_kb);
631 let probe_result = probe_dfs.search(&mut probe_goal, facts);
632 cumulative_goals += probe_result.goals_explored;
633 if probe_result.success {
634 let mut res = probe_result;
636 res.goals_explored = cumulative_goals;
637 return res;
638 }
639 }
640
641 SearchResult::failure(cumulative_goals, self.max_depth)
642 }
643}
644
645impl BreadthFirstSearch {
646 pub fn new(max_depth: usize, kb: KnowledgeBase) -> Self {
648 Self {
649 max_depth,
650 goals_explored: 0,
651 executor: RuleExecutor::new_with_inserter(kb, None),
652 }
653 }
654
655 pub fn new_with_engine(max_depth: usize, kb: KnowledgeBase, engine: Option<Arc<Mutex<IncrementalEngine>>>) -> Self {
657 let inserter = engine.map(|eng| {
659 let eng = eng.clone();
660 std::sync::Arc::new(move |fact_type: String, data: crate::rete::TypedFacts, rule_name: String, _premises: Vec<String>| {
661 if let Ok(mut e) = eng.lock() {
662 let _ = e.insert_logical(fact_type, data, rule_name, Vec::new());
663 }
664 }) as std::sync::Arc<dyn Fn(String, crate::rete::TypedFacts, String, Vec<String>) + Send + Sync>
665 });
666
667 Self {
668 max_depth,
669 goals_explored: 0,
670 executor: RuleExecutor::new_with_inserter(kb, inserter),
671 }
672 }
673
674 pub fn search_with_execution(&mut self, root_goal: &mut Goal, facts: &mut Facts, kb: &KnowledgeBase) -> SearchResult {
676 self.goals_explored = 0;
677 let mut queue = VecDeque::new();
678 let mut path = Vec::new();
679 let mut max_depth = 0;
680
681 queue.push_back((root_goal as *mut Goal, 0));
682
683 while let Some((goal_ptr, depth)) = queue.pop_front() {
684 let goal = unsafe { &mut *goal_ptr };
686
687 self.goals_explored += 1;
688 max_depth = max_depth.max(depth);
689
690 if depth > self.max_depth {
691 continue;
692 }
693
694 goal.depth = depth;
695
696 if self.check_goal_in_facts(goal, facts) {
698 goal.status = GoalStatus::Proven;
699 continue;
700 }
701
702 for rule_name in goal.candidate_rules.clone() {
704 path.push(rule_name.clone());
705
706 if let Some(rule) = kb.get_rule(&rule_name) {
708 match self.executor.try_execute_rule(&rule, facts) {
710 Ok(true) => {
711 if self.check_goal_in_facts(goal, facts) {
714 goal.status = GoalStatus::Proven;
715 break;
716 }
717 }
718 Ok(false) => {
719 }
721 Err(_) => {
722 }
724 }
725 }
726 }
727
728 for sub_goal in &mut goal.sub_goals {
730 queue.push_back((sub_goal as *mut Goal, depth + 1));
731 }
732 }
733
734 let success = root_goal.is_proven();
735
736 SearchResult {
737 success,
738 path,
739 goals_explored: self.goals_explored,
740 max_depth_reached: max_depth,
741 bindings: root_goal.bindings.to_map(),
742 }
743 }
744
745 fn check_goal_in_facts(&self, goal: &Goal, facts: &Facts) -> bool {
749 if let Some(condition) = self.parse_goal_pattern(&goal.pattern) {
751 self.executor.evaluate_condition(&condition, facts).unwrap_or(false)
753 } else {
754 false
755 }
756 }
757
758 fn parse_goal_pattern(&self, pattern: &str) -> Option<crate::engine::rule::Condition> {
764 use crate::engine::rule::{Condition, ConditionExpression};
765 use crate::types::Operator;
766
767 let operators = [
769 (">=", Operator::GreaterThanOrEqual),
770 ("<=", Operator::LessThanOrEqual),
771 ("==", Operator::Equal),
772 ("!=", Operator::NotEqual),
773 (" > ", Operator::GreaterThan),
774 (" < ", Operator::LessThan),
775 (" contains ", Operator::Contains),
776 (" not_contains ", Operator::NotContains),
777 (" starts_with ", Operator::StartsWith),
778 (" startsWith ", Operator::StartsWith),
779 (" ends_with ", Operator::EndsWith),
780 (" endsWith ", Operator::EndsWith),
781 (" matches ", Operator::Matches),
782 ];
783
784 for (op_str, operator) in operators {
785 if let Some(pos) = pattern.find(op_str) {
786 let field = pattern[..pos].trim().to_string();
787 let value_str = pattern[pos + op_str.len()..].trim();
788
789 let value = self.parse_value_string(value_str);
791
792 return Some(Condition {
793 field: field.clone(),
794 expression: ConditionExpression::Field(field),
795 operator,
796 value,
797 });
798 }
799 }
800
801 None
802 }
803
804 fn parse_value_string(&self, s: &str) -> Value {
806 let s = s.trim();
807
808 if s == "true" {
810 return Value::Boolean(true);
811 }
812 if s == "false" {
813 return Value::Boolean(false);
814 }
815
816 if s == "null" {
818 return Value::Null;
819 }
820
821 if (s.starts_with('"') && s.ends_with('"')) || (s.starts_with('\'') && s.ends_with('\'')) {
823 return Value::String(s[1..s.len()-1].to_string());
824 }
825
826 if let Ok(n) = s.parse::<f64>() {
828 return Value::Number(n);
829 }
830
831 if let Ok(i) = s.parse::<i64>() {
833 return Value::Integer(i);
834 }
835
836 Value::String(s.to_string())
838 }
839
840 pub fn search(&mut self, root_goal: &mut Goal, _facts: &Facts) -> SearchResult {
842 self.goals_explored = 0;
843 let mut queue = VecDeque::new();
844 let mut path = Vec::new();
845 let mut max_depth = 0;
846
847 queue.push_back((root_goal as *mut Goal, 0));
848
849 while let Some((goal_ptr, depth)) = queue.pop_front() {
850 let goal = unsafe { &mut *goal_ptr };
852
853 self.goals_explored += 1;
854 max_depth = max_depth.max(depth);
855
856 if depth > self.max_depth {
857 continue;
858 }
859
860 goal.depth = depth;
861
862 for rule_name in &goal.candidate_rules {
864 path.push(rule_name.clone());
865 }
866
867 for sub_goal in &mut goal.sub_goals {
869 queue.push_back((sub_goal as *mut Goal, depth + 1));
870 }
871
872 if !goal.candidate_rules.is_empty() || goal.all_subgoals_proven() {
874 goal.status = GoalStatus::Proven;
875 }
876 }
877
878 let success = root_goal.is_proven();
879
880 SearchResult {
881 success,
882 path,
883 goals_explored: self.goals_explored,
884 max_depth_reached: max_depth,
885 bindings: root_goal.bindings.to_map(),
886 }
887 }
888}
889
890#[cfg(test)]
891mod tests {
892 use super::*;
893
894 #[test]
895 fn test_search_strategies() {
896 assert_eq!(SearchStrategy::DepthFirst, SearchStrategy::DepthFirst);
897 assert_ne!(SearchStrategy::DepthFirst, SearchStrategy::BreadthFirst);
898 }
899
900 #[test]
901 fn test_search_result_creation() {
902 let success = SearchResult::success(vec!["Rule1".to_string()], 5, 3);
903 assert!(success.success);
904 assert_eq!(success.path.len(), 1);
905 assert_eq!(success.goals_explored, 5);
906
907 let failure = SearchResult::failure(10, 5);
908 assert!(!failure.success);
909 assert!(failure.path.is_empty());
910 }
911
912 #[test]
913 fn test_depth_first_search_creation() {
914 let kb = KnowledgeBase::new("test");
915 let dfs = DepthFirstSearch::new(10, kb);
916 assert_eq!(dfs.max_depth, 10);
917 assert_eq!(dfs.goals_explored, 0);
918 }
919
920 #[test]
921 fn test_depth_first_search_simple() {
922 let kb = KnowledgeBase::new("test");
923 let mut dfs = DepthFirstSearch::new(10, kb);
924 let facts = Facts::new();
925
926 let mut goal = Goal::new("test".to_string());
927 goal.add_candidate_rule("TestRule".to_string());
928
929 let result = dfs.search(&mut goal, &facts);
930
931 assert!(result.success);
932 assert!(goal.is_proven());
933 assert!(result.goals_explored > 0);
934 }
935
936 #[test]
937 fn test_breadth_first_search() {
938 let kb = KnowledgeBase::new("test");
939 let mut bfs = BreadthFirstSearch::new(10, kb);
940 let facts = Facts::new();
941
942 let mut goal = Goal::new("test".to_string());
943 goal.add_candidate_rule("TestRule".to_string());
944
945 let result = bfs.search(&mut goal, &facts);
946
947 assert!(result.success);
948 assert_eq!(result.goals_explored, 1);
949 }
950
951 #[test]
952 fn test_iterative_deepening_search_success() {
953 let kb = KnowledgeBase::new("test");
954 let mut ids = IterativeDeepeningSearch::new(5, kb.clone());
955 let mut root = Goal::new("test".to_string());
956 root.add_candidate_rule("TestRule".to_string());
957
958 let mut facts = Facts::new();
960 let res = ids.search(&mut root, &facts);
961 assert!(res.success);
962 }
963
964 #[test]
965 fn test_iterative_deepening_search_depth_limit() {
966 let kb = KnowledgeBase::new("test");
967 let mut ids = IterativeDeepeningSearch::new(0, kb.clone());
969 let mut root = Goal::new("test".to_string());
970 let mut sub = Goal::new("sub".to_string());
972 sub.add_candidate_rule("SubRule".to_string());
973 root.sub_goals.push(sub);
974
975 let facts = Facts::new();
976 let res = ids.search(&mut root, &facts);
977 assert!(!res.success);
978 }
979}