-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathExpertSystem.cs
More file actions
134 lines (119 loc) · 5.01 KB
/
ExpertSystem.cs
File metadata and controls
134 lines (119 loc) · 5.01 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
using System.Collections.Generic;
using System.Linq;
namespace PuzzleSolver
{
////////////////////////////////////////////////////////////////////////////////////////////////////
/// <summary>
/// The ExpertSystem is composed of a set of rules which act on IPartialSolution objects. It
/// applies those rules until none apply or until some rule flags the partial solution as
/// unsolvable.
/// </summary>
///
/// <remarks> Darrellp, 2/14/2011. </remarks>
///
/// ### <typeparam name="TPs"> The type of the partial solution we're applied to. </typeparam>
////////////////////////////////////////////////////////////////////////////////////////////////////
public class ExpertSystem<TPs> where TPs : IPartialSolution
{
///<summary>True if we're gathering reasons during the backtracking process. </summary>
public bool IsKeepingReasons {get; set;}
readonly List<IRule> _lstIRule;
private readonly List<IRule> _lstOneTimeRules;
////////////////////////////////////////////////////////////////////////////////////////////////////
/// <summary> Constructor. </summary>
///
/// <remarks> Darrellp, 2/14/2011. </remarks>
///
/// <param name="lstRules"> The list of rules. </param>
/// <param name="lstOneTimeRules"> Rules run only once at the start </param>
/// <param name="fKeepReasons"> true if we want to gather reasons. </param>
////////////////////////////////////////////////////////////////////////////////////////////////////
public ExpertSystem(List<IRule> lstRules, List<IRule> lstOneTimeRules = null, bool fKeepReasons = false)
{
_lstIRule = lstRules;
IsKeepingReasons = fKeepReasons;
_lstOneTimeRules = lstOneTimeRules;
}
////////////////////////////////////////////////////////////////////////////////////////////////////
/// <summary> Apply the rules of this expert system without keeping reasons. </summary>
///
/// <remarks> Darrellp, 2/14/2011. </remarks>
///
/// <param name="ps"> The partial solution we're being applied to. </param>
/// <param name="firstTime"> Being called for the first time </param>
/// <returns> Returns false if an impossible state is detected, else true. </returns>
////////////////////////////////////////////////////////////////////////////////////////////////////
public bool FApply(TPs ps, bool firstTime)
{
List<ReasonRulePair> lstrrp;
return FApply(ps, firstTime, out lstrrp);
}
////////////////////////////////////////////////////////////////////////////////////////////////////
/// <summary> Apply the rules of this expert system. </summary>
///
/// <remarks> Darrellp, 2/14/2011. </remarks>
/// <param name="ps"> The partial solution we're being applied to. </param>
/// <param name="firstTime"> True if this is the first time the expert system is being called</param>
/// <param name="lstrrp"> [out] The list of reasons for rule applications. </param>
/// <returns> Returns false if an impossible state is detected, else true. </returns>
////////////////////////////////////////////////////////////////////////////////////////////////////
public bool FApply(TPs ps, bool firstTime, out List<ReasonRulePair> lstrrp)
{
// Set up
var fApplied = true;
lstrrp = IsKeepingReasons ? new List<ReasonRulePair>() : null;
// As long as some rule is applied, cycle through and try to apply them all
while (fApplied)
{
fApplied = false;
var ruleList = firstTime && _lstOneTimeRules != null ? _lstOneTimeRules : _lstIRule;
// For every applicable rule
foreach (var rl in ruleList.Where(rl => rl.FTrigger(ps)))
{
List<IReason> lstReason;
bool fImpossible;
// Apply the rule
fApplied = rl.FApply(ps, out lstReason, out fImpossible);
// Are we gathering reasons?
if (lstrrp != null && lstReason != null)
{
// Assume all rules applied
foreach (var reason in lstReason)
{
reason.Applied = true;
}
// Add the new reasons
lstrrp.AddRange(lstReason.Select(reason => new ReasonRulePair(reason, rl)));
}
// Are we at an impossible position?
if (fImpossible)
{
if (lstrrp != null && lstReason != null)
{
// We assume that any impossible situation was immediately returned from
// and so will be the last reason in the list...
lstrrp[lstrrp.Count - 1].Reason.Applied = false;
lstrrp[lstrrp.Count - 1].Reason.Impossible = true;
}
// The rule detected an unsolveable partial solution - return false
return false;
}
if (fApplied)
{
// If we find a rule that applied, then restart at the top
// since ones at the top (the most "important/useful" ones)
// may now apply
if (!firstTime)
{
break;
}
}
}
fApplied = fApplied || firstTime;
firstTime = false;
}
// We've applied as many rules as we can - start a full backtracking search.
return true;
}
}
}