-
-
Notifications
You must be signed in to change notification settings - Fork 10
/
app.js
130 lines (109 loc) · 4.52 KB
/
app.js
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
const readline = require('readline-sync');
const kb = require('./data/plants.js');
const forwardChain = function(assertions) {
// Select the first rule.
let ruleIndex = 0;
let rule = kb[ruleIndex];
// While there are more rules.
while (ruleIndex < kb.length) {
// If all premises in the rule are in assertions
const allPremisesExist = rule.premises.every(premise =>
assertions.some(assertion => assertion.attribute === premise.attribute && assertion.value === premise.value)
);
// If all premises from the rule are in the assertions but not the conclusion.
if (allPremisesExist && !assertions.some(assertion => assertion.attribute === rule.conclusion.attribute && assertion.value === rule.conclusion.value)) {
// Add the conclusion to assertions.
assertions.push(rule.conclusion);
// Go back to the first rule, since the new assertion might exist as a premise in another rule.
// This can be optimized by sorting rules based on if A's conclusion is a premise in B's, then place A before B.
ruleIndex = 0;
}
else {
// Select the next rule.
rule = kb[++ruleIndex];
}
}
return assertions;
};
const backChain = function(goal, assertions) {
// Select the first rule.
let ruleIndex = 0;
// Start with a single assertion of the goal.
assertions = assertions || [ goal ];
// If there is an assertion with the current goal as its attribute, select it.
let assertion = assertions.filter(assertion => assertion.attribute === goal.attribute && assertion.value);
assertion = assertion.length ? assertion[0] : null;
if (!assertion) {
while (!assertion && ruleIndex < kb.length) {
const rule = kb[ruleIndex];
if (rule.conclusion.attribute === goal.attribute) {
let premiseIndex = 0;
let allPremisesTrue = false;
let premise = rule.premises[premiseIndex];
let isPremiseAssertionTrue = true;
while (!allPremisesTrue && isPremiseAssertionTrue) {
// Satisfy the current premise as the next goal.
trueAssertion = backChain(premise, assertions);
// Add the assertion to the assertion list.
trueAssertion && !assertions.some(assertion => assertion.attribute === trueAssertion.attribute && assertion.value === trueAssertion.value) && assertions.push(trueAssertion);
// Is the trueAssertion equal to the premise?
isPremiseAssertionTrue = JSON.stringify(premise) === JSON.stringify(trueAssertion);
if (isPremiseAssertionTrue) {
// If there are more premises to satisfy, continue to the next one.
if (++premiseIndex < rule.premises.length) {
premise = rule.premises[premiseIndex];
}
else {
// All premises are satisfied, the current rule triggers.
allPremisesTrue = true;
}
}
}
if (allPremisesTrue) {
// Trigger the current rule and include its conclusion as a true assertion.
assertion = rule.conclusion;
}
}
// Select the next rule.
ruleIndex++;
}
if (!assertion) {
// We can't deduce a value for this goal. If the goal is not the original query, prompt the user for a value.
if (!assertions.filter(a => a.attribute === goal.attribute && !a.value).length) {
// Prompt user for goal.
const value = readline.question(`What is the value for ${goal.attribute}? `);
assertion = { attribute: goal.attribute, value }; // Enter new assertion for the type and value specified by the user.
}
}
}
return assertion;
};
//
// Test forward-chaining.
// User inputs: stem woody, position upright, one main trunk yes, broad and flat no
// Output includes: type is tree (because of original inputs), then using type is tree we can now deduce class gymnosperm
//
let assertions = [];
let attribute = 1;
while (attribute) {
attribute = readline.question('Enter an attribute? ');
if (attribute) {
let value = readline.question('Enter a value? ');
value && assertions.push({ attribute, value });
}
}
assertions = forwardChain(assertions);
console.log(assertions);
//
// Test backward-chaining.
// User inputs: class, stem woody, position upright, one main trunk yes, broad and flat yes
// Output: angiosperm
//
let goal = { attribute: readline.question('What attribute type do you want to know? ') };
assertion = backChain(goal);
if (assertion) {
console.log(assertion.value);
}
else {
console.log(`You cannot answer enough questions to determine ${goal.attribute}.`);
}