Ver Fonte

加入對 Condition 狀態的早期支援

0x0001F36D há 5 anos atrás
pai
commit
2a7ae68fbf

+ 19 - 8
src/Yuuna.Contracts.Test/Fake.cs

@@ -9,10 +9,17 @@ namespace Yuuna.Contracts.Test
     using Yuuna.Contracts.Patterns;
     using Yuuna.Contracts.Modules;
     using Yuuna.Contracts.Semantics;
-    using Yuuna.Common.Utilities; 
+    using Yuuna.Common.Utilities;
+    using System.Diagnostics;
 
     public sealed class Fake : ModuleBase
-    { 
+    {
+        private volatile bool _doorState;
+        public Fake()
+        {
+            this._doorState = new[] { true, false, true, false }.RandomTakeOne();
+        }
+
         protected override void BuildPatterns(IGroupManager g, IPatternBuilder p)
         {
             g.Define("open").AppendOrCreate(new[] { "打開", "開" });
@@ -22,11 +29,13 @@ namespace Yuuna.Contracts.Test
 
             p.Build(g["open"], g["door"]).OnInvoke(score =>
             {
-                var DOOR = new { IS_OPENED = new[] { true, false, true, false }.RandomTakeOne() };
-                Console.WriteLine("門的狀態是: " + (DOOR.IS_OPENED ? "開著的" : "關著的"));
+                Debug.WriteLine("門的狀態是: " + (this._doorState ? "開著的" : "關著的"));
                 // 開門
-                if (!DOOR.IS_OPENED)
+                if (!this._doorState)
+                {
+                    this._doorState = !this._doorState;
                     return (Moods.Happy, "已經開好門囉 <3");
+                }
                 else
                     return new Response[]
                     {
@@ -39,11 +48,13 @@ namespace Yuuna.Contracts.Test
 
             p.Build(g["close"], g["door"]).OnInvoke(score =>
             {
-                var DOOR = new { IS_OPENED = new[] { true, false, true, false }.RandomTakeOne() };
-                Console.WriteLine("門的狀態是: " + (DOOR.IS_OPENED ? "開著的" : "關著的"));
+                Debug.WriteLine("門的狀態是: " + (this._doorState ? "開著的" : "關著的"));
                 // 開門
-                if (DOOR.IS_OPENED)
+                if (this._doorState)
+                {
+                    this._doorState = !this._doorState;
                     return (Moods.Happy, "已經關好門囉 <3");
+                }
                 else
                     return new Response[]
                     {

+ 10 - 0
src/Yuuna.Contracts/Interaction/Response.cs

@@ -50,5 +50,15 @@ namespace Yuuna.Contracts.Interaction
         }
 
         public override string ToString() => this.Message;
+
+        public static Response operator +(Response r, string s)
+        {
+            return new Response(r.Mood, r.Message + s);
+        }
+
+        public static Response operator +(string s, Response r)
+        {
+            return new Response(r.Mood, s + r.Message);
+        }
     }
 }

+ 4 - 4
src/Yuuna.Contracts/Optimization/DefaultStrategy.cs

@@ -25,11 +25,11 @@ namespace Yuuna.Contracts.Optimization
         /// </summary>
         Optimal,
         /// <summary>
-        /// 近似解。表示缺少一個群組(Group)。
+        /// 條件。表示缺少一個群組(Group)。
         /// </summary>
-        Closest,
+        Condition,
         /// <summary>
-        /// 命題。表示具有兩個近似解(<see cref="Closest"/>)。
+        /// 命題。表示具有兩個近似解(<see cref="Condition"/>)。
         /// </summary>
         Proposition,
         /// <summary>
@@ -61,7 +61,7 @@ namespace Yuuna.Contracts.Optimization
                 }
                 else if (single.Equals(1))
                 {
-                    this.Status = Status.Closest;// 近似解(缺一組條件,詢問使用者) 
+                    this.Status = Status.Condition;// 近似解(缺一組條件,詢問使用者) 
                 }
             }
             else if (count.Equals(2))

+ 244 - 68
src/Yuuna/EntryPoint.cs

@@ -4,8 +4,7 @@
 namespace Yuuna
 {
     using System;
-    using System.Text;
-
+    using System.Text; 
 
     using Yuuna.Contracts.Optimization;
     using Yuuna.Contracts.Modules;
@@ -17,110 +16,287 @@ namespace Yuuna
     using System.Threading.Tasks;
     using System.Reflection;
     using System.Linq;
+    using Yuuna.Contracts.Interaction;
+    using System.Diagnostics;
 
-    internal class EntryPoint
+    public class Antonym
     {
-        public static void Main(string[] args)
+        public enum TypeKinds
         {
-            //var mc = ModuleManager.Instance;
-            //foreach (var m in mc.Modules)
-            //{
-            //    Console.WriteLine(m.Name);
-            //}
-            //Console.WriteLine("----");
-            //Console.ReadKey();
-            //mc.UnloadAll();
-            //foreach (var m in mc.Modules)
-            //{
-            //    Console.WriteLine(m.Name);
-            //} 
-            //Console.WriteLine("----");
-            //Console.ReadKey();
-            //mc.ReloadAll();  
-            //foreach (var m in mc.Modules)
-            //{
-            //    Console.WriteLine(m.Name);
-            //}
+            Unknown = default,
+            Positive,
+            Negative,
+            Question
+        }
 
-            Send("打開門");
-            return;
+        private struct Word
+        {
+            public Word(string value, TypeKinds condition)
+            {
+                this.Value = value;
+                this.Condition = condition;
+                this.Priority = value.Length;
+            }
+            internal string Value { get; }
+            internal TypeKinds Condition { get; }
+            internal int Priority { get; }
+        }
+
+        private readonly Word[] _words;
+
+        public Antonym(string positive, string negative, string question)
+        {
+            this._words = new[] { new Word(positive, TypeKinds.Positive), new Word(negative, TypeKinds.Negative), new Word(question, TypeKinds.Question) };
+            Array.Sort(this._words, new Comparison<Word>((x, y) => y.Priority - x.Priority)); 
+        }
 
-            Send("開燈");
-            try
+        /// <summary>
+        /// 
+        /// </summary>
+        /// <param name="positive"></param>
+        /// <param name="negative"></param>
+        public Antonym(string positive, string negative) : this(positive, negative, positive + negative)
+        {
+        }
+
+        public bool Judge(string sentence,out TypeKinds condition)
+        {
+            foreach (var w in this._words)
             {
-                var stt = SpeechRecognizer.Create("secret.json");
-                stt.RecognizeCompleted += a =>
+                if(sentence.Contains(w.Value))
                 {
-                    if (a.Count > 0)
-                    {
-                        Console.WriteLine("## Google 語音: " + a[0].Transcript);
-                        Send(a[0].Transcript);
-                    }
-                };
+                    condition = w.Condition;
+                    return true;
+                }
+            }
+            condition = default;
+            return false;
+        }
+    }
 
-                Console.WriteLine("## Proof of Concept Ver.");
-                Console.WriteLine("可以叫我開關燈或者開關門,其他的我還不會");
 
-                Console.WriteLine("按下 Enter 鍵開始錄音,任意鍵離開");
-                Loop:
-                if(Console.ReadKey().Key == ConsoleKey.Enter)
-                { 
-                    using (stt.Recognize())
-                    {
-                        Console.WriteLine("按下 Enter 停止錄音,任意鍵離開"); 
-                        if(Console.ReadKey().Key == ConsoleKey.Enter)
-                            goto Loop;
-                    }
-                } 
-            }
-            catch (Exception e)
-            {
-                Console.WriteLine(e.Message);
-                Console.ReadKey();
-            } 
+    public interface IInteractiveView
+    {
+        string Accept();
+        void OnReceive(Response response);
+    } 
+
+    public sealed class ConsoleBotInteract : IInteractiveView
+    {
+        public void OnReceive(Response response)
+        { 
+            Console.WriteLine("Bot: " + response.Message);
         }
 
-        public static void Send(string text)
+        public string Accept()
         {
-            var segmenter = new JiebaTextSegmenter() as ITextSegmenter;
+            Console.Write("我: ");
+            return Console.ReadLine();
+        }
+    }
+
+
+    internal class EntryPoint
+    {
+        public static void Main(string[] args)
+        {
+            var canResponses = new Response[]
+            {
+                (Moods.Sad, "我不清楚你想做什麼 OvQ"),
+                (Moods.Sad, "我不懂你想幹嘛 QAQ"),
+                (Moods.Sad, "我不知道你是什麼意思 OHQ"),
+            };
+
+            var antonyms = new[]
+            {
+                new Antonym("是", "不是"),
+                new Antonym("對", "不對"),
+                new Antonym("是", "否"),
+            };
+
+            IInteractiveView interactor = new ConsoleBotInteract();
+            ITextSegmenter segmenter = new JiebaTextSegmenter();
+            IStrategy plan = new DefaultStrategy();
+
             var allPlugins = ModuleManager.Instance.Modules;
             foreach (var item in allPlugins)
             {
                 item.Initialize(segmenter, new GroupManager());
-                Console.WriteLine("已載入模組: " + item.GetType().AssemblyQualifiedName);
+                Debug.WriteLine("已載入模組: " + item.Type.AssemblyQualifiedName);
             }
 
-            Console.WriteLine("我: " + text);
+            // prepare task completed.
+            LOOP:
+            var text = interactor.Accept();
             var cutted = segmenter.Cut(text);
-            Console.WriteLine($"來自分詞器 {segmenter.Name} 的分詞結果: [ {string.Join(", ", cutted)} ]");
+            Debug.WriteLine($"來自分詞器 {segmenter.Name} 的分詞結果: [ {string.Join(", ", cutted)} ]");
 
-            IStrategy plan = new DefaultStrategy();
-            var alternative = plan.FindBest(allPlugins.Select(x=>x.PatternSet).ToArray(), cutted);
+            var alternative = plan.FindBest(allPlugins.Select(x => x.PatternSet).ToArray(), cutted);
+            Debug.WriteLine(alternative.Status);
             switch (alternative.Status)
             {
+                // 罐頭回應
                 case Status.Invalid:
-                    Console.WriteLine("無效的模組");
+                    interactor.OnReceive(canResponses.RandomTakeOne());
                     break;
+
                 case Status.Optimal:
-                    alternative.Matches[0].Pattern.Owner.Patterns.TryGet(alternative.Matches[0].Pattern, out var bag);
-                    var r = bag.Invoke(alternative.Matches[0]);
-                    Console.WriteLine(r.Mood + " | " + r.Message);
+                    {
+                        var single = alternative.Matches[0];
+                        single.Pattern.Owner.Patterns.TryGet(single.Pattern, out var bag);
+                        var r = bag.Invoke(single);
+                        interactor.OnReceive(r);
+                    }
                     break;
-                case Status.Closest:
-                    Console.WriteLine("closest");
+
+                case Status.Condition:
+                    {
+                        var sb = new StringBuilder();
+                        sb.Append("你是想要");
+                        foreach (var matched in alternative.Matches[0].SequentialMatches)
+                        {
+                            sb.Append(matched.ToImmutable().RandomTakeOne());
+                        }
+                        foreach (var missing in alternative.Matches[0].Missing)
+                        {
+                            foreach (var unmatch in missing.ToImmutable())
+                                sb.Append(unmatch.ToImmutable().RandomTakeOne());
+                        }
+                        sb.Append("嗎?");
+                        interactor.OnReceive(sb.ToString());
+
+                        text = interactor.Accept();
+
+                        foreach (var antonym in antonyms)
+                        {
+                            if(antonym.Judge(text, out var type) && type.Equals(Antonym.TypeKinds.Positive))
+                            {
+                                var single = alternative.Matches[0];
+                                single.Pattern.Owner.Patterns.TryGet(single.Pattern, out var bag);
+                                var r = bag.Invoke(single);
+                                interactor.OnReceive(r);
+                                goto Leave;
+                            }
+                        }
+
+                        interactor.OnReceive(canResponses.RandomTakeOne());
+                    }
+                    Leave: 
                     break;
+
                 case Status.Proposition:
                     Console.WriteLine("proposition");
                     break;
+
                 case Status.Paradox:
                     Console.WriteLine("paradox");
                     break;
+
                 case Status.NoModuleInstalled:
                     Console.WriteLine("noModuleInstalled");
+                    Console.WriteLine("無效的模組");
                     break;
+
                 default:
-                    break;
+                    throw new InvalidOperationException();
             }
+            goto LOOP;
+
+            //try
+            //{Send("開燈");
+            //    var stt = SpeechRecognizer.Create("secret.json");
+            //    stt.RecognizeCompleted += a =>
+            //    {
+            //        if (a.Count > 0)
+            //        {
+            //            Console.WriteLine("## Google 語音: " + a[0].Transcript);
+            //            Send(a[0].Transcript);
+            //        }
+            //    };
+
+            //    Console.WriteLine("## Proof of Concept Ver.");
+            //    Console.WriteLine("可以叫我開關燈或者開關門,其他的我還不會");
+
+            //    Console.WriteLine("按下 Enter 鍵開始錄音,任意鍵離開");
+            //    Loop:
+            //    if(Console.ReadKey().Key == ConsoleKey.Enter)
+            //    { 
+            //        using (stt.Recognize())
+            //        {
+            //            Console.WriteLine("按下 Enter 停止錄音,任意鍵離開"); 
+            //            if(Console.ReadKey().Key == ConsoleKey.Enter)
+            //                goto Loop;
+            //        }
+            //    } 
+            //}
+            //catch (Exception e)
+            //{
+            //    Console.WriteLine(e.Message);
+            //    Console.ReadKey();
+            //} 
+        }
+
+        public static void Send(string text)
+        {
+            
+
+            //Console.WriteLine("我: " + text);
+            //var cutted = segmenter.Cut(text);
+            //Console.WriteLine($"來自分詞器 {segmenter.Name} 的分詞結果: [ {string.Join(", ", cutted)} ]");
+
+            //var alternative = plan.FindBest(allPlugins.Select(x=>x.PatternSet).ToArray(), cutted);
+            //switch (alternative.Status)
+            //{
+            //    // 罐頭回應
+            //    case Status.Invalid:
+            //        view.OnResponsed(s_canResponses.RandomTakeOne());
+            //        break;
+
+            //    case Status.Optimal:
+            //        {
+            //            var single = alternative.Matches[0];
+            //            single.Pattern.Owner.Patterns.TryGet(single.Pattern, out var bag);
+            //            var r = bag.Invoke(single);
+            //            view.OnResponsed(r);
+            //        }
+            //        break;
+
+            //    case Status.Condition:
+            //        {
+            //            Console.WriteLine("proposition");
+            //            Console.Write("你是想要");
+            //            foreach (var matched in alternative.Matches[0].SequentialMatches)
+            //            {
+            //                Console.Write(matched.ToImmutable().RandomTakeOne());
+            //            }
+            //            foreach (var missing in alternative.Matches[0].Missing)
+            //            {
+            //                foreach (var unmatch in missing.ToImmutable())
+            //                {
+            //                    Console.Write(unmatch.ToImmutable().RandomTakeOne());
+            //                }
+            //            }
+            //            Console.WriteLine("嗎?");
+            //            Console.ReadLine()
+            //        }
+            //        break;
+
+            //    case Status.Proposition:
+            //        Console.WriteLine("proposition");
+            //        break;
+
+            //    case Status.Paradox:
+            //        Console.WriteLine("paradox");
+            //        break;
+
+            //    case Status.NoModuleInstalled:
+            //        Console.WriteLine("noModuleInstalled");
+            //        Console.WriteLine("無效的模組");
+            //        break;
+
+            //    default:
+            //        throw new InvalidOperationException();
+            //}
 
 
 

+ 3 - 0
src/Yuuna/Plugins/ModuleProxy.cs

@@ -23,6 +23,9 @@ namespace Yuuna
         public bool Proxied => this._inst is null;
 
         public string Name => this._inst?.Name;
+
+        public Type Type => this._inst?.GetType();
+
         private ModuleProxy(DirectoryInfo moduleFolder, AssemblyLoadContext loader, ModuleBase inst)
         {
             this._moduleFolder = moduleFolder;

+ 2 - 1
src/Yuuna/TextSegmention/JiebaSegmenter.cs

@@ -7,6 +7,7 @@ namespace Yuuna.TextSegmention
 
     using System;
     using System.Collections.Immutable;
+    using System.Diagnostics;
     using System.Globalization;
 
     using Yuuna.Contracts.Semantics;
@@ -68,7 +69,7 @@ namespace Yuuna.TextSegmention
 
         private void InternalLog(string v)
         { 
-            Console.WriteLine(v);
+            Debug.WriteLine(v);
         }
     }
 }