2

ユーザーに質問するボットを作成しようとしています。起動時に、ボットはユーザーがどちらを選択するかを尋ねます。

  1. クイズを開始します (後でクイズのテーマと難易度を選択する必要があります)。
  2. 彼が最後のクイズで得たスコアを見てください。
  3. スコアをリセットします。

クイズの内容は XML ファイルです。構造体に関連付けられた質問と回答を既に保存しています。

FormBuilder は次のとおりです。

[Serializable]
public class QuizQuestionsLoader
{
    public QuizQuestion Question { get; set; }

    public static IForm<QuizQuestionsLoader> QuestionLoaderForm(QuizQuestion question)
    {
        return new FormBuilder<QuizQuestionsLoader>()
            .Field(new FieldReflector<QuizQuestionsLoader>(nameof(Question))
                .SetType(null)
                .SetDefine(async (state, field) =>
                {
                    field
                        .AddDescription(state.Question.QuestionText, state.Question.QuestionText)
                        .AddTerms(state.Question.QuestionText, state.Question.QuestionText);

                    return true;
                }))
            .AddRemainingFields()
            .Build();
    }
}

そこで、ユーザーがどちらを選択したかを決定するスイッチを使用して IDialog を実行しました。ユーザーがクイズの開始を選択した場合、DefaultCase が有効になります。

new DefaultCase<QuizChoices?, IDialog<string>>((context, value) =>
                {
                    return Chain.From(() => FormDialog.FromForm(QuizStart.QuizForm, FormOptions.PromptInStart))
                        .Select(c => c.category)
                        .ContinueWith(async (ctx, res) =>
                        {
                            CategoryOptions? category = await res;
                            IList<QuizQuestion> questions = QuestionsLoader.LoadQuestions(category.Value.ToString().ToLowerInvariant()).ToList();

                            QuizQuestion currentQuestion = questions[0];
                            var questionsDialogs = Chain.From(() => FormDialog.FromForm(() => { return QuizQuestionsLoader.QuestionLoaderForm(currentQuestion); })).PostToUser();

                            for (int i = 1; i < questions.Count(); i++)
                            {
                                currentQuestion = questions[i];

                                questionsDialogs.ContinueWith(async (forctx, fores) =>
                                {
                                    await fores;
                                    return Chain.From(() => FormDialog.FromForm(() => { return QuizQuestionsLoader.QuestionLoaderForm(currentQuestion); }));
                                }).PostToUser();
                            }

                            return Chain.Return(questionsDialogs).Unwrap();
                    })
                    .ContinueWith(async (ctx, res) =>
                    {
                        await res;
                        return Chain.Return("Quiz fini !");
                    });
                })

ユーザーに 10 個の質問を表示したいので、別の方法でそれを行う方法がわからないため、FormBuilder を思い出すのは良い考えだと思いました。これをビルドして実行すると、難易度を選択した後、Bot Framework エミュレーターが 500 内部サーバー エラーを送信します。

そのため、for ループを使用して FormDialog を呼び出せるかどうかを確認するために、単純なメッセージと 3 つの選択肢で構成される "テスト" FormBuilder を呼び出してみました。FormBuilder は次のとおりです。

public enum TestOptions
{
    A, B, C
}

[Serializable]
public class Test
{
    public TestOptions? choice;

    public static IForm<Test> TestForm()
    {
        return new FormBuilder<Test>()
            .Message("Test")
            .Field(nameof(choice))
            .Build();
    }
}

そして、ここに IDialog があります:

return Chain.From(() => FormDialog.FromForm(Test.TestForm, FormOptions.PromptInStart))
                        .ContinueWith(async(ctx, res) =>
                        {
                            await res;
                            var testDialog = Chain.From(() => FormDialog.FromForm(() => { return Test.TestForm(); })).PostToUser();

                            for (int i = 0; i < 2; i++)
                            {
                                testDialog.ContinueWith<Test, Test>(async (forctx, fores) =>
                                {
                                    await fores;
                                    return Chain.From(() => FormDialog.FromForm(Test.TestForm, FormOptions.PromptInStart));
                                });
                            }

                            return Chain.Return(testDialog);
                        })

これで一度 FormDialog が表示されますが、for ループが実行されていることがわかりました。ただし、testDialog 変数は null です。

では、Bot Framework Emulator について 10 個の質問をするために FormBuilder を正しく呼び出す方法を知っていますか?

どうもありがとう !

4

2 に答える 2

2

このcommitを使用して、クイズで質問を繰り返す方法を示すサンプルを追加しました。FoldDialog という連鎖可能なダイアログを使用して、一連のダイアログを順番に呼び出し、応答を集約します。

var quiz = Chain
            .PostToChain()
            .Select(_ => "how many questions?")
            .PostToUser()
            .WaitToBot()
            .Select(m => int.Parse(m.Text))
            .Select(count => Enumerable.Range(0, count).Select(index => Chain.Return($"question {index + 1}?").PostToUser().WaitToBot().Select(m => m.Text)))
            .Fold((l, r) => l + "," + r)
            .Select(answers => "your answers were: " + answers)
            .PostToUser();

次のようなスクリプトを作成できます。

"hello",
"how many questions?",
"3",
"question 1?",
"A",
"question 2?",
"B",
"question 3?",
"C",
"your answers were: A,B,C"
于 2016-06-27T19:42:40.880 に答える
0

まず、Will Pornoy さん、ご回答ありがとうございます。

ただし、次のように、回答の前に問題を解決することに成功しています。

new DefaultCase<QuizChoices?, IDialog<string>>((context, value) =>
                {
                    return Chain.From(() => FormDialog.FromForm(QuizStart.QuizForm, FormOptions.PromptInStart))
                        .Select(c => new QuizParameters
                        {
                            CategoryParameter = c.category.Value.ToString(),
                            DifficultyParameter = c.difficulty.Value.ToString()
                        })
                        .ContinueWith<QuizParameters?, int>(async (ctx, res) =>
                        {
                            await res;

                            IList<QuizQuestion> questions = QuestionsLoader.LoadQuestions(QuizParameters.CategoryParameter, QuizParameters.DifficultyParameter).ToList();
                            return new QuizQuestionsLoader(questions);
                        })

カテゴリQuizParameterとユーザーの難易度の選択を含む構造体です。単純IListに、質問テキストとその回答を含む を取得します。

最後に、それを new object に渡しますQuizQuestionLoader。このクラスでは、いくつかのメソッドを作成します。

[Serializable]
public class QuizQuestionsLoader : IDialog<int>
{ 
    public static int Score { get; private set; }

    private IList<QuizQuestion> problems;
    private QuizQuestion theQuestion;

    private int index;      
    private int jokerCount = 2;
    private const string jokerAnswerText = "Utiliser un joker";

    public QuizQuestionsLoader(IList<QuizQuestion> problems)
    {
        this.problems = problems;
    }

Taskクイズが開始されるたびに呼び出されるメソッド:

public async Task StartAsync(IDialogContext context)
    {
        problems.Shuffle();

        DisplayQuestion(context);
    }

メソッドのDisplayQuestionオーバーロード (最初はジョーカーが残っていない場合) :

private void DisplayQuestion(IDialogContext context)
    {
        DisplayQuestion(context, false);
    }

    private void DisplayQuestion(IDialogContext context, bool useJoker)
    {
        theQuestion = problems[index];
        string questionText = theQuestion.QuestionText;
        IList<Answer> answers = theQuestion.Answers.ToList();

        if (useJoker)
        {
            IList<Answer> randomBadAnswers = answers.Where(a => !a.IsCorrect).ToList();
            randomBadAnswers.Shuffle();
            randomBadAnswers = randomBadAnswers.Take(2).ToList();

            answers = answers.Except(randomBadAnswers).ToList();
        }
        else if (jokerCount > 0)
        {
            Answer jokerAnswer = new Answer
            {
                AnswerText = $"{jokerAnswerText} ({jokerCount}) restant(s)"
            };

            answers.Add(jokerAnswer);  
        }

        PromptDialog.Choice(context, CheckResponseAsync, answers, questionText, null, 0, PromptStyle.Auto);
    }

そして最後に、10 個の質問が表示されるまでこのプロセスをリロードするループ:

public async Task CheckResponseAsync(IDialogContext context, IAwaitable<Answer> argument)
    {
        Answer answer = await argument;

        if (answer.AnswerText.StartsWith(jokerAnswerText))
        {
            jokerCount--;
            await context.PostAsync("Suppression de deux mauvaises réponses...");
            DisplayQuestion(context, true);
        }
        else
        { 
            await context.PostAsync(answer.IsCorrect ? "Bonne réponse !" : "Mauvaise réponse !");
            index++;

            Answer goodAnswer = theQuestion.Answers.First(a => a.IsCorrect);

            if (answer.AnswerText == goodAnswer.AnswerText)
            {
                Score++;
            }

            if (index < problems.Count)
            {
                DisplayQuestion(context);
            }
            else
            {
                await context.PostAsync($"Votre score est de {Score}");
                context.Done(Score);
            }
        }
    }

それが役立つことを願っています! :)

于 2016-06-30T21:05:12.717 に答える