/// <summary> /// Assesses the state of asserted facts and then decides what to do next. /// </summary> public static void ProcessRequest(string request) { Facts.Fact goalblob = ParseRequest(request); InitializeSession(); while (true) { // Get the response object from the interview engine Engine.Response response = Engine.Investigate(new List <Facts.Fact>() { goalblob }); // Ask the current question, or display the results if (!response.InvestigationComplete) { // Ask the next question DisplayQuestion(response); // Get and validate the answer GetAndParseAnswer(response); } else { DisplayResults(goalblob); break; } } // Display all facts that have been asserted (for diagnostic purposes) // Console.WriteLine("\nFacts: \n" + Facts.AssertedFacts()); }
/// <summary> /// Displays a question to the user. /// </summary> private static void DisplayQuestion(Engine.Response response) { // Get data about the next question Facts.Fact theFact = response.NextFact; Question theQ = Templates.GetQ(theFact.Relationship); // TODO: Display appropriate question control (using theQ.questionType) // White space Console.WriteLine(); // Display progress percentage Console.WriteLine("Percent complete: " + response.PercentComplete); // Display question text string qText = theFact.QuestionText(); // QuestionText(theFact, theQ); Console.WriteLine(qText); AkkTest.assertedRelationship = AkkTest.AddUnitTestAssertRel(theFact, theQ); // Display an explanation (if any) if (theQ.explanation != "") { Console.WriteLine("Note: " + theQ.explanation); } }
/// <summary> /// Adds a fact to the proof tree. /// </summary> public static void AddToProofTree(Facts.Fact f) { // Fact should be added if it is a regular rule that is // not already on the tree (to avoid repeats), or if it // is an input rule. bool addIt = true; foreach (ProofTreeNode n in ProofTree) { Facts.Fact f2 = n.TheFact; if (f.Relationship == f2.Relationship && Util.AreEqual(f.Arg1, f2.Arg1) && Util.AreEqual(f.Arg2, f2.Arg2) && Util.AreEqual(f.Arg3, f2.Arg3)) { addIt = false; } } // Approximates Akkadian function depth int depth = new StackTrace().FrameCount; // Sometimes the stack trace overestimates the function depth // (for example, in Switch() statements). The following code // compensates for this by de-indenting facts so they're not // more than one level deeper than their parent level. // int depthOfLastFact = 0; // if (ProofTree.Count > 0) depthOfLastFact = ProofTree[ProofTree.Count-1].Depth; // depth = Math.Min(depth, depthOfLastFact+1); if (addIt) { // TODO: Filter out repeats, etc. ProofTree.Add(new ProofTreeNode(f, depth)); } }
/// <summary> /// General response constructor. /// </summary> public Response(bool done, Facts.Fact next, int percent, List <Facts.Fact> goals) { InvestigationComplete = done; NextFact = next; PercentComplete = percent; Goals = goals; }
public Factoid(Facts.Fact f) { FactType = f.GetTvarType(); Relationship = f.Relationship; Arg1 = Util.ArgToString(f.Arg1); Arg2 = Util.ArgToString(f.Arg2); Arg3 = Util.ArgToString(f.Arg3); QuestionText = f.QuestionText(); }
/// <summary> /// Asks the next question or displays the interview results. /// </summary> public static Response Investigate(List <Facts.Fact> goals) { // Default outputs bool allDone = true; int percent = 0; Facts.Fact theNextFact = new Facts.Fact("", null, null, null); InitializeProofTree(); // Pre-evaluate each goal in order to cache the results of // each evaluted function in the FactBase. This will make // look-ahead short-circuiting work in the interview. // There will obviously be performance implications to // evaluating each goal twice. However, a four-line fix // to a vexing problem feels delightful. And the caching // may end up making things tolerable after all. // // But note that the look-ahead short-circuiting issue is not // completely solved: it will still fail in large rules where // the intermediate conditions are not checked/pre-evaluated. foreach (Facts.Fact g in goals) { g.Value(); } // Console.WriteLine(ShowProofTree()); // Prepare to look for unknown facts Facts.GetUnknowns = true; Facts.Unknowns.Clear(); // Iterate through each goal foreach (Facts.Fact g in goals) { if (!g.Value().HasBeenDetermined) { allDone = false; } } Facts.GetUnknowns = false; // Determine the next fact and the percent complete if (!allDone) { theNextFact = Facts.Unknowns[0]; percent = ProgressPercentage(Facts.Count(), Facts.Unknowns.Count); } return(new Engine.Response(allDone, theNextFact, percent, goals)); }
/// <summary> /// Adds the assertion relationship to the .akk unit test text. /// </summary> public static string AddUnitTestAssertRel(Facts.Fact theF, Question theQ) { string result = "- " + theQ.relationship + "(" + ((Thing)theF.Arg1).Id; if (theF.Arg2 == null) { // e.g. Rel(p) = result += ") = "; } else { // e.g. Rel(p,q) = result += "," + ((Thing)theF.Arg2).Id + ") = "; } return(result); }
/// <summary> /// Displays the engine's results of the interview session. /// </summary> private static void DisplayResults(Facts.Fact goal) { Console.WriteLine("\n"); // Indent and format results string tline = "\t" + goal.ValueAsString().Replace("\n", "\n\t"); // For eternal values, only show value if (goal.Value().IsEternal) { tline = tline.Replace("DawnOfTime ", ""); } // Concatenate question and answer string result = "\t" + goal.QuestionText() + "\n\n" + tline + "\n"; // Add result to test case Tvar testResult = goal.GetFunction().Invoke(); AkkTest.CloseUnitTest(testResult, goal.Relationship); Console.WriteLine(result); }
public ProofTreeNode(Facts.Fact fact, int depth) { TheFact = fact; Depth = depth; }
/// <summary> /// Takes an income packet and instantiates a Hammurabi session. /// </summary> public Packet Assess(Packet request) { // Start timer DateTime startTime = DateTime.Now; // Pre-evaluate each goal to enable look-ahead short circuiting. // See Hammurabi | Core | Engine.cs, line ~81, for an explanation. foreach (Factoid g in request.Goals) { Facts.Fact gb = new Facts.Fact(g.Relationship, g.Arg1, g.Arg2, g.Arg3); gb.Value(); } // Start a fresh session // Facts.Clear(); Facts.GetUnknowns = true; Facts.Unknowns.Clear(); bool allDone = true; request.PercentageComplete = 100; // Assert facts into a Hammurabi session foreach (Factoid f in request.AssertedFacts) { AssertFact(f); } // Iterate through each goal foreach (Factoid g in request.Goals) { Facts.Fact gb = new Facts.Fact(g.Relationship, g.Arg1, g.Arg2, g.Arg3); // Assign to a variable so it's only evaluated once Tvar gbVal = gb.Value(); // Convert Tvar to timeline object g.Timeline = TvarToTimeline(gbVal); // All goals resolved? if (!gbVal.HasBeenDetermined) { allDone = false; } } // Stop looking for unknown facts Facts.GetUnknowns = false; // Determine the next fact and the percent complete if (!allDone) { // Factoid neededFact = new Factoid("Tnum","USC.Tit26.Sec151.ThresholdAmount","Jim","",""); // Facts.Fact f = new Facts.Fact("USC.Tit26.Sec151.ThresholdAmount", null, null, null); // Factoid neededFact = new Factoid(f); Factoid neededFact = new Factoid(Facts.Unknowns[0]); request.NeededFacts = new List <Factoid>() { neededFact }; request.PercentageComplete = Interactive.Engine.ProgressPercentage(Facts.Count(), Facts.Unknowns.Count); } // Add elapsed time to response object request.ResponseTimeInMs = Convert.ToInt32((DateTime.Now - startTime).TotalMilliseconds); return(request); }