结对小组信息:
13070027 郑亦然
13070038 李芳达
基于之前的个人项目(http://www.cnblogs.com/yuki8819/p/5305107.html),本项目将主体功能封装,并加入了新的控制模块,并且制作了GUI
效果如图
程序主要分为三个模块:Controller, Generator, Calculator
Controller负责控制GUI,与后台交互
Generator封装了生成算式的功能
Calculator封装了计算算式的功能
关于Calculator和Generator的设计可见http://www.cnblogs.com/yuki8819/p/5305107.html
Generator支持参数修改:本轮生成多少算式?算式中运算数的最大数值?算式长度?
使用主界面中的Option按钮可以进入修改参数的界面
按下Next按钮可以生成一条算式,在输入框中输入答案后,按OK按钮可以得到结果(作对或做错,以及正确答案)
做完规定的题数后,会得到最后的得分(即作对的题数)
按Restart键可以重新开始一轮答题
测试方面,由于这个程序本身在输入上不会出现非法输入(输入框有格式限制),所以我新建了一个控制台程序来测试Calculator模块,代码如下:
1 static void Main(string[] args) 2 { 3 while (true) 4 { 5 string line = Console.ReadLine(); 6 Console.WriteLine(Calculate(line)); 7 } 8 }
输入算式,由CMD显示运算结果或错误提示
输入错误测试:
+12-34(以符号开始)
12-34+(以符号结束)
12++34*56(连续的运算符)
12+34/0-56*78(0作被除数)
12x45+56*78/23(非法字符)
(空算式)
12+34/56*78(正确算式)
结果:
这次的结对项目是我和同学李芳达一起完成的,在设计过程中,我们一起讨论了几种可行的解决方案,最后选择了上述方案。对于GUI的选择,我们使用了Unity3D的GUI系统。虽然我们都对这个引擎不很熟悉,但是我们对它很感兴趣,所以决定一起学习使用这个引擎。过程还是比较顺利的,最终结果也比较令人满意。
全部源代码:
PS: 源代码需要在Unity3D工程下才可以运行
Controller
1 using UnityEngine; 2 using System.Collections; 3 using UnityEngine.UI; 4 using System; 5 6 public class Controller : MonoBehaviour 7 { 8 public GameObject field_Answer; 9 public GameObject btn_Next; 10 public GameObject btn_Restart; 11 public GameObject btn_Options; 12 public GameObject btn_OK; 13 public GameObject txt_Quiz; 14 public GameObject txt_Result; 15 16 public GameObject OptionText; 17 18 public GameObject field_Amount; 19 public GameObject field_Max; 20 public GameObject field_Operators; 21 22 public Generator generator; 23 24 public string result; 25 public int count; 26 public int correct; 27 28 // Use this for initialization 29 void Start() 30 { 31 generator = new Generator(); 32 field_Answer = new GameObject(); 33 btn_Next = new GameObject(); 34 btn_Restart = new GameObject(); 35 btn_Options = new GameObject(); 36 btn_OK = new GameObject(); 37 OptionText = new GameObject(); 38 txt_Quiz = new GameObject(); 39 txt_Result = new GameObject(); 40 field_Amount = new GameObject(); 41 field_Max = new GameObject("field_Max"); 42 field_Operators = new GameObject("field_Operators"); 43 44 field_Answer = GameObject.Find("field_Answer"); 45 btn_Next = GameObject.Find("btn_Next"); 46 btn_Restart = GameObject.Find("btn_Restart"); 47 btn_Options = GameObject.Find("btn_Options"); 48 btn_OK = GameObject.Find("btn_OK"); 49 txt_Quiz = GameObject.Find("txt_Quiz"); 50 txt_Result = GameObject.Find("txt_Result"); 51 52 OptionText = GameObject.Find("OptionText"); 53 54 field_Amount = GameObject.Find("field_Amount"); 55 field_Max = GameObject.Find("field_Max"); 56 field_Operators = GameObject.Find("field_Operators"); 57 58 59 60 field_Amount.SetActive(false); 61 field_Max.SetActive(false); 62 field_Operators.SetActive(false); 63 txt_Result.SetActive(false); 64 65 count = 0; 66 correct = 0; 67 } 68 69 // Update is called once per frame 70 void Update() 71 { 72 73 } 74 75 public void OnNext() 76 { 77 generator.GenerateLines(); 78 Text txt = txt_Quiz.GetComponent<Text>(); 79 txt.text = generator.lines; 80 result = Calculator.Calculate(generator.lines_final, generator.tf); 81 Debug.Log(result); 82 btn_OK.SetActive(true); 83 InputField inf = field_Answer.GetComponent<InputField>(); 84 inf.text = ""; 85 } 86 87 public void OnOK() 88 { 89 count++; 90 btn_OK.SetActive(false); 91 InputField inf = field_Answer.GetComponent<InputField>(); 92 if (inf.text == result) 93 { 94 correct++; 95 Text txt = txt_Quiz.GetComponent<Text>(); 96 txt.text = "Correct!"; 97 } 98 else 99 { 100 Text txt = txt_Quiz.GetComponent<Text>(); 101 txt.text = "Wrong answer! The answer should be: " + result; 102 } 103 if (count >= generator.rows) 104 { 105 txt_Result.SetActive(true); 106 Text txt = txt_Result.GetComponent<Text>(); 107 txt.text = "Finished! Your Score is: " + correct + " out of " + count; 108 btn_Next.SetActive(false); 109 } 110 } 111 112 public void OnRestart() 113 { 114 btn_OK.SetActive(false); 115 count = 0; 116 correct = 0; 117 Text txt = txt_Quiz.GetComponent<Text>(); 118 txt.text = ""; 119 txt_Result.SetActive(false); 120 btn_Next.SetActive(true); 121 } 122 123 public void OnOptions() 124 { 125 if (!field_Amount.active) 126 { 127 field_Amount.SetActive(true); 128 InputField infa = field_Amount.GetComponent<InputField>(); 129 infa.text = "" + generator.rows; 130 field_Max.SetActive(true); 131 InputField infm = field_Max.GetComponent<InputField>(); 132 infm.text = "" + generator.max; 133 field_Operators.SetActive(true); 134 InputField info = field_Operators.GetComponent<InputField>(); 135 info.text = "" + generator.operators; 136 137 field_Answer.SetActive(false); 138 btn_Next.SetActive(false); 139 txt_Quiz.SetActive(false); 140 btn_OK.SetActive(false); 141 txt_Result.SetActive(false); 142 143 Text txt = OptionText.GetComponent<Text>(); 144 txt.text = "Confirm"; 145 } 146 else 147 { 148 field_Amount.SetActive(false); 149 InputField infa = field_Amount.GetComponent<InputField>(); 150 generator.rows = Int32.Parse(infa.text); 151 field_Max.SetActive(false); 152 InputField infm = field_Max.GetComponent<InputField>(); 153 generator.max = Int32.Parse(infm.text); 154 field_Operators.SetActive(false); 155 InputField info = field_Operators.GetComponent<InputField>(); 156 generator.operators = Int32.Parse(info.text); 157 158 field_Answer.SetActive(true); 159 btn_Next.SetActive(true); 160 txt_Quiz.SetActive(true); 161 btn_OK.SetActive(false); 162 txt_Result.SetActive(false); 163 164 Text txt = OptionText.GetComponent<Text>(); 165 txt.text = "Options"; 166 OnRestart(); 167 } 168 169 } 170 }
Generator
1 using System; 2 using System.Reflection; 3 using System.Globalization; 4 using Microsoft.CSharp; 5 using System.CodeDom; 6 using System.CodeDom.Compiler; 7 using System.Text; 8 using System.Collections.Generic; 9 using System.IO; 10 11 using UnityEngine; 12 using System.Collections; 13 14 15 public class Generator 16 { 17 public int max;//最大数值 18 public int rows;//生成算式个数 19 public int operators;//每行运算符个数 20 public List<int> nums; 21 public List<int> add_n_minus; 22 public List<int> div; 23 public List<int> div_nums; 24 public int tf;//通分分母 25 public int amount; 26 public string lines; 27 public string lines_tf;//通分分母字符串 28 public string lines_final;//通分分子字符串 29 public int count; 30 31 32 public Generator() 33 { 34 max = 50; 35 rows = 10; 36 operators = 10; 37 } 38 39 40 public void GenerateLines() 41 { 42 tf = 1; 43 nums = new List<int>(); 44 add_n_minus = new List<int>(); 45 div = new List<int>(); 46 div_nums = new List<int>(); 47 System.Random rnd = new System.Random(GetRandomSeed()); 48 amount = rnd.Next(1, operators + 1); 49 lines = ""; 50 lines_tf = ""; 51 lines_final = ""; 52 53 int x = 0; 54 int d = 0; 55 for (int i = 0; i < amount; i++) 56 { 57 //A new number 58 rnd = new System.Random(GetRandomSeed()); 59 nums.Add(rnd.Next(1, max)); 60 lines += nums[i]; 61 //A new operator 62 System.Random rnd0 = new System.Random(GetRandomSeed()); 63 int key = rnd0.Next(0, 4); 64 switch (key) 65 { 66 case 0: 67 x = 0; 68 lines += "+"; 69 add_n_minus.Add(lines.Length); 70 break; 71 case 1: 72 x = 0; 73 lines += "-"; 74 add_n_minus.Add(lines.Length); 75 break; 76 case 2: 77 x++; 78 if (x <= 3) 79 { 80 lines += "*"; 81 break; 82 } 83 else 84 { 85 goto case 1; 86 } 87 case 3: 88 x = 0; 89 d++; 90 if (d <= 3) 91 { 92 lines += "/"; 93 div.Add(i); 94 break; 95 } 96 else 97 { 98 goto case 1; 99 } 100 default: 101 x = 0; 102 break; 103 } 104 } 105 106 rnd = new System.Random(GetRandomSeed()); 107 nums.Add(rnd.Next(1, max)); 108 lines += nums[amount]; 109 110 for (int i = 0; i < div.Count; i++) 111 { 112 div_nums.Add(nums[div[i] + 1]); 113 lines_tf += div_nums[i]; 114 lines_tf += "*"; 115 tf *= div_nums[i]; 116 } 117 118 lines_final += lines_tf; 119 lines_final += lines; 120 121 if (div.Count > 0) 122 { 123 for (int i = 1; i <= add_n_minus.Count; i++) 124 { 125 lines_final = lines_final.Insert(i * lines_tf.Length + add_n_minus[i - 1], lines_tf); 126 } 127 } 128 } 129 130 public int GetRandomSeed() 131 { 132 byte[] bytes = new byte[4]; 133 System.Security.Cryptography.RNGCryptoServiceProvider rng = new System.Security.Cryptography.RNGCryptoServiceProvider(); 134 rng.GetBytes(bytes); 135 return BitConverter.ToInt32(bytes, 0); 136 } 137 }
Calculator
1 using UnityEngine; 2 using System; 3 using System.Reflection; 4 using System.Globalization; 5 using Microsoft.CSharp; 6 using System.CodeDom; 7 using System.CodeDom.Compiler; 8 using System.Text; 9 using System.Collections.Generic; 10 using System.IO; 11 using System.Collections; 12 13 static class Calculator 14 { 15 public static string Calculate(string lines) 16 { 17 if (lines[0] < '0' || lines[0] > '9') 18 { 19 return "Error: the formula must be begun with a number"; 20 } 21 if (lines[lines.Length - 1] < '0' || lines[lines.Length - 1] > '9') 22 { 23 return "Error: the formula must be ended with a number"; 24 } 25 for (int i = 0; i < lines.Length; i++) 26 { 27 if ((lines[i] >= '0' && lines[i] <= '9') || lines[i] == '+' || lines[i] == '-' || lines[i] == '*' || lines[i] == '/') 28 { 29 if (lines[i] < '0' || lines[i] > '9') 30 { 31 if (lines[i + 1] < '0' || lines[i + 1] > '9') 32 { 33 return "Error: continuous operators are not allowed"; 34 } 35 if (lines[i] == '/') 36 { 37 if (lines[i + 1] == '0') 38 { 39 return "Error: 0 cannot be divided"; 40 } 41 } 42 } 43 } 44 else 45 { 46 return "Error: invalid characters included"; 47 } 48 } 49 50 string temp = lines; 51 List<int> nums = new List<int>(); 52 List<int> add_n_minus = new List<int>(); 53 List<int> div = new List<int>(); 54 List<int> div_nums = new List<int>(); 55 string lines_tf = ""; 56 string lines_final = ""; 57 int tf = 1; 58 for (int i = temp.Length; i >= 0; i--) 59 { 60 if (temp[i] == '+' || temp[i] == '-' || temp[i] == '*' || temp[i] == '/') 61 { 62 string n = temp.Substring(i + 1, temp.Length - i); 63 temp = temp.Remove(i); 64 nums.Add(Int32.Parse(n)); 65 if (temp[i] == '+' || temp[i] == '-') 66 { 67 add_n_minus.Add(lines.Length - i); 68 } 69 else if (temp[i] == '/') 70 { 71 div.Add(nums.Count); 72 } 73 } 74 } 75 nums.Add(Int32.Parse(temp)); 76 nums.Reverse(0,nums.Count - 1); 77 div.Reverse(); 78 for (int i = 0; i < div.Count; i++) 79 { 80 div[i] = nums.Count - div[i]; 81 } 82 for (int i = 0; i < div.Count; i++) 83 { 84 div_nums.Add(nums[div[i] + 1]); 85 lines_tf += div_nums[i]; 86 lines_tf += "*"; 87 tf *= div_nums[i]; 88 } 89 90 lines_final += lines_tf; 91 lines_final += lines; 92 93 if (div.Count > 0) 94 { 95 for (int i = 1; i <= add_n_minus.Count; i++) 96 { 97 lines_final = lines_final.Insert(i * lines_tf.Length + add_n_minus[i - 1], lines_tf); 98 } 99 } 100 101 // 1.CSharpCodePrivoder 102 CSharpCodeProvider objCSharpCodePrivoder = new CSharpCodeProvider(); 103 104 // 2.ICodeComplier 105 ICodeCompiler objICodeCompiler = objCSharpCodePrivoder.CreateCompiler(); 106 107 // 3.CompilerParameters 108 CompilerParameters objCompilerParameters = new CompilerParameters(); 109 objCompilerParameters.ReferencedAssemblies.Add("System.dll"); 110 objCompilerParameters.GenerateExecutable = false; 111 objCompilerParameters.GenerateInMemory = true; 112 113 // 4.CompilerResults 114 115 116 CompilerResults cr = objICodeCompiler.CompileAssemblyFromSource(objCompilerParameters, GenerateCode(lines_final, tf)); 117 118 if (cr.Errors.HasErrors) 119 { 120 Debug.Log("编译错误:"); 121 foreach (CompilerError err in cr.Errors) 122 { 123 Debug.Log(err.ErrorText); 124 } 125 return "Error: compiler error."; 126 } 127 else 128 { 129 //通过反射,调用HelloWorld的实例 130 Assembly objAssembly = cr.CompiledAssembly; 131 object objHelloWorld = objAssembly.CreateInstance("DynamicCodeGenerate.HelloWorld"); 132 MethodInfo objMI = objHelloWorld.GetType().GetMethod("OutPut"); 133 return objMI.Invoke(objHelloWorld, null).ToString(); 134 } 135 } 136 137 public static string Calculate(string lines_final, int tf) 138 { 139 if (tf == 0) 140 { 141 return "Error: 0 cannot be divided"; 142 } 143 if (lines_final[0] < '0' || lines_final[0] > '9') 144 { 145 return "Error: the formula must be begun with a number"; 146 } 147 if (lines_final[lines_final.Length - 1] < '0' || lines_final[lines_final.Length - 1] > '9') 148 { 149 return "Error: the formula must be ended with a number"; 150 } 151 for (int i = 0; i < lines_final.Length; i++) 152 { 153 if ((lines_final[i] >= '0' && lines_final[i] <= '9') || lines_final[i] == '+' || lines_final[i] == '-' || lines_final[i] == '*' || lines_final[i] == '/') 154 { 155 if (lines_final[i] < '0' || lines_final[i] > '9') 156 { 157 if (lines_final[i + 1] < '0' || lines_final[i + 1] > '9') 158 { 159 return "Error: continuous operators are not allowed"; 160 } 161 if (lines_final[i] == '/') 162 { 163 if (lines_final[i + 1] == '0') 164 { 165 return "Error: 0 cannot be divided"; 166 } 167 } 168 } 169 } 170 else 171 { 172 return "Error: invalid characters included"; 173 } 174 } 175 176 // 1.CSharpCodePrivoder 177 CSharpCodeProvider objCSharpCodePrivoder = new CSharpCodeProvider(); 178 179 // 2.ICodeComplier 180 ICodeCompiler objICodeCompiler = objCSharpCodePrivoder.CreateCompiler(); 181 182 // 3.CompilerParameters 183 CompilerParameters objCompilerParameters = new CompilerParameters(); 184 objCompilerParameters.ReferencedAssemblies.Add("System.dll"); 185 objCompilerParameters.GenerateExecutable = false; 186 objCompilerParameters.GenerateInMemory = true; 187 188 // 4.CompilerResults 189 190 191 CompilerResults cr = objICodeCompiler.CompileAssemblyFromSource(objCompilerParameters, GenerateCode(lines_final, tf)); 192 193 if (cr.Errors.HasErrors) 194 { 195 Debug.Log("编译错误:"); 196 foreach (CompilerError err in cr.Errors) 197 { 198 Debug.Log(err.ErrorText); 199 } 200 return "Error: compiler error."; 201 } 202 else 203 { 204 //通过反射,调用HelloWorld的实例 205 Assembly objAssembly = cr.CompiledAssembly; 206 object objHelloWorld = objAssembly.CreateInstance("DynamicCodeGenerate.HelloWorld"); 207 MethodInfo objMI = objHelloWorld.GetType().GetMethod("OutPut"); 208 return objMI.Invoke(objHelloWorld, null).ToString(); 209 } 210 } 211 212 public static string GenerateCode(string lines_final, int tf) 213 { 214 StringBuilder sb = new StringBuilder(); 215 sb.Append("using System;using System.IO;using System.Collections.Generic;using System.Text;"); 216 sb.Append(Environment.NewLine); 217 sb.Append("namespace DynamicCodeGenerate"); 218 sb.Append(Environment.NewLine); 219 sb.Append("{"); 220 sb.Append(Environment.NewLine); 221 sb.Append(" public class HelloWorld"); 222 sb.Append(Environment.NewLine); 223 sb.Append(" {"); 224 sb.Append(Environment.NewLine); 225 sb.Append(" public string OutPut()"); 226 sb.Append(Environment.NewLine); 227 sb.Append(" {"); 228 sb.Append(Environment.NewLine); 229 sb.Append("long tf = " + tf + ";"); 230 sb.Append(Environment.NewLine); 231 sb.Append("long re = " + lines_final + ";"); 232 sb.Append(Environment.NewLine); 233 sb.Append("for (int i = 2; i < re || i < tf; i++){while (re % i == 0 && tf % i == 0){re = re / i;tf = tf / i;}}"); 234 sb.Append(Environment.NewLine); 235 //sb.Append("Console.Write("Result = ");"); 236 sb.Append(" if (tf != 1){return re.ToString() + "/" + tf.ToString();} else{return re.ToString();}"); 237 sb.Append(Environment.NewLine); 238 sb.Append(Environment.NewLine); 239 sb.Append(" }"); 240 sb.Append(Environment.NewLine); 241 sb.Append(" }"); 242 sb.Append(Environment.NewLine); 243 sb.Append("}"); 244 string code = sb.ToString(); 245 return code; 246 } 247 }