Reputation:
I need to allow the users choose their own path by picking between two simple choices displayed on their screen in order to progress to the next set of choices, until they get to one of the endings, i.e something like this should be achieved:
I have tried the following code, but only the left side is evaluated each time. I am wondering how can I achieve a results like the above image (covering all the branches)? For instance, if the user selects "No" the application shouldn't ask any further question from the user and just simply shows the "Maybe you want a Pizza" message. I have done this with decision tree algorithm and need to fix it, so that it covers both the left and right sides just like the above image.
namespace ConsoleApp3
{
class Program
{
static void Main(string[] args)
{
var decisionTree = MainDecisionTree();
var client = new Client();
Console.WriteLine("Do you want a book? true/false");
client.Answer[0] = bool.Parse(Console.ReadLine());
Console.WriteLine("Do you like it? true/false");
client.Answer[1] = bool.Parse(Console.ReadLine());
Console.WriteLine("Are you sure? true/false");
client.Answer[2] = bool.Parse(Console.ReadLine());
decisionTree.Evaluate(client);
Console.WriteLine("Press any key...");
Console.ReadKey();
}
private static DecisionQuery MainDecisionTree()
{
//Decision 2
var wantBranch = new DecisionQuery
{
Title = "Do you want a book?",
Test = (client) => client.Answer[0],
Positive = new DecisionResult { Result = true },
Negative = new DecisionResult { Result = false }
};
//Decision 1
var deserveBranch = new DecisionQuery
{
Title = "Do you like it?",
Test = (client) => client.Answer[1],
Positive = wantBranch,
Negative = new DecisionResult { Result = false }
};
//Decision 0
var sureBranch = new DecisionQuery
{
Title = "Are you sure?",
Test = (client) => client.Answer[2],
Positive = deserveBranch,
Negative = new DecisionResult { Result = false }
};
return sureBranch;
}
}
public class DecisionResult : Decision
{
public bool Result { get; set; }
public override void Evaluate(Client client)
{
Console.WriteLine("\r\nThe result: {0}", Result ? "Buy it" : "You need to wait");
}
}
public class DecisionQuery : Decision
{
public string Title { get; set; }
public Decision Positive { get; set; }
public Decision Negative { get; set; }
public Func<Client, bool> Test { get; set; }
public override void Evaluate(Client client)
{
bool result = this.Test(client);
string resultAsString = result ? "yes" : "no";
Console.WriteLine($"\t- {this.Title}? {resultAsString}");
if (result) this.Positive.Evaluate(client);
else this.Negative.Evaluate(client);
}
}
public abstract class Decision
{
public abstract void Evaluate(Client client);
}
public class Client
{
public bool[] Answer { get; set; } = new bool[3];
}
}
Upvotes: 3
Views: 2709
Reputation:
If I understand your issue, here is your code corrected.
I renamed some things.
I called MakeDecisionTree
the method that initializes the expert system with the conditions tree and it returns the root condition.
Each condition
contains a sentence
to evaluate
and it can be a query
or a result
.
For a result
, the evaluate
display the sentence
.
For a query
, the evaluate
method asks the user to answer the question by yes
or no
. And using this answer, it calls the corresponding evaluate
of the next child condition
.
Sorry for my english here, it is not my native language, and I'm not involved in AI.
static private void DecisionTreeTest()
{
Console.WriteLine("Please, answer a few questions with yes or no.");
Console.WriteLine();
MakeDecisionTree().Evaluate();
}
static private bool GetUserAnswer(string question)
{
Console.WriteLine(question);
string userInput;
while ( true )
{
userInput = Console.ReadLine().ToLower();
if ( userInput == "yes" )
return true;
else
if ( userInput == "no" )
return false;
else
Console.WriteLine("Your answer is not supported, retry please." +
Environment.NewLine + Environment.NewLine +
question);
}
}
static private DecisionTreeQuery MakeDecisionTree()
{
var queryAreYouSure
= new DecisionTreeQuery("Are you sure?",
new DecisionTreeResult("Buy it."),
new DecisionTreeResult("You need to wait."),
GetUserAnswer);
var queryIsItAGoodBook
= new DecisionTreeQuery("Is it a good book?",
new DecisionTreeResult("What are you waiting for? Just buy it."),
new DecisionTreeResult("Find another one."),
GetUserAnswer);
var queryDoYouLikeIt
= new DecisionTreeQuery("Do you like it?",
queryAreYouSure,
queryIsItAGoodBook,
GetUserAnswer);
var queryDoYouWantABook
= new DecisionTreeQuery("Do you want a book?",
queryDoYouLikeIt,
new DecisionTreeResult("Maybe you want a pizza."),
GetUserAnswer);
return queryDoYouWantABook;
}
abstract public class DecisionTreeCondition
{
protected string Sentence { get; private set; }
abstract public void Evaluate();
public DecisionTreeCondition(string sentence)
{
Sentence = sentence;
}
}
public class DecisionTreeQuery : DecisionTreeCondition
{
private DecisionTreeCondition Positive;
private DecisionTreeCondition Negative;
private Func<string, bool> UserAnswerProvider;
public override void Evaluate()
{
if ( UserAnswerProvider(Sentence) )
Positive.Evaluate();
else
Negative.Evaluate();
}
public DecisionTreeQuery(string sentence,
DecisionTreeCondition positive,
DecisionTreeCondition negative,
Func<string, bool> userAnswerProvider)
: base(sentence)
{
Positive = positive;
Negative = negative;
UserAnswerProvider = userAnswerProvider;
}
}
public class DecisionTreeResult : DecisionTreeCondition
{
public override void Evaluate()
{
Console.WriteLine(Sentence);
}
public DecisionTreeResult(string sentence)
: base(sentence)
{
}
}
Upvotes: 2
Reputation: 2652
You decision tree design is very good, just the way you ask the user is not. You ask all the questions in advance, and go to the decision tree later on. Of course the tree cannot make asked questions unasked. It's not a decision tree problem.
you would have to put these pairs of lines:
Console.WriteLine("Are you sure? true/false");
client.Answer[2] = bool.Parse(Console.ReadLine());
into the Evaluate method, or you do a callback or whatever architectural design to separate logic from user-interface. The decision-tree has to ask, or at least know the question. The answer does not need to be stored, you can evaluate it right after input.
Maybe you pass something to Ask the user to the evaluate Method
public interface IAsk
{
bool Question(string message);
}
And for your sample you instantiate that with
public class ConsoleAsk : IAsk
{
bool Question(string message)
{
Console.WriteLine(message);
return bool.Parse(Console.ReadLine());
}
}
and you modify your Evaluate like this:
public override void Evaluate(IAsk ask)
{
bool result = ask.Question("do you feel good or bad");
string resultAsString = result ? "yes" : "no";
Console.WriteLine($"\t- {this.Title}? {resultAsString}");
if (result) this.Positive.Evaluate(ask);
else this.Negative.Evaluate(ask);
}
of course you replace the question with your title property.
Upvotes: -1