用户需求:
程序能接收用户输入的整数答案,并判断对错
程序结束时,统计出答对、答错的题目数量。
补充说明:0——10的整数是随机生成的
用户可以选择四则运算中的一种
用户可以结束程序的运行,并显示统计结果。
在此基础上,做增量开发。
增量内容: 1)处理用户的错误输入,比如输入字母或符号等,处理除法运算中分母为0的情况,处理结果为负数的情况,保证是小学水平不出现负数,比如不能出现5-8=-3这种情况;
2)用户可以设定倒计时;
3)用户可以设定随机整数的范围和题目数量;
4)用户可以选择哪种计算类型,比如加减乘除,或可选择软件随机生成四则运算中的一种;
5)用户可以选择随机生成的题目中是否带有小括号,比如(2+3)*5,如果是gui程序,添加这个功能可以用复选框实现。
6)保证生成过的题目不再重复出现。
设计思路:
因为是做增量啊,所以呢,我在看了5和6这两个增量之后,第一感觉是有点难度。这次和之前的运算不同的地方是:
对于5):加了小括号,这就要考虑优先级了,并且不能再用两个文本框来生成两个数字进行运算了。所以我考虑,把两个文本框合成一个文本框,这样的话就让它来存生成的一个式子,最后只对这个式子进行运算就OK了,这不但可以生成两个数的运算表达式,还可以生成三个数的运算表达式,就看你怎么定义出题方法了。当然了,想的是很简单,毕竟生成的是一个式子而并非两个数进行计算那么简单了。所以我继续分析,就对后面的这个式子进行研究啊。首先它是一个字符串表达式,而进行计算的话肯定不能用string类型的来进行计算。所以用队列把表达式里的每一个数取出来,并定义一个栈,让取出的元素放到栈里,这样的话只对栈里元素用逆波兰式进行计算就可以了。
1.对于实现 出题为一个字符串表达式,我用了如下这种方法:
n1 = ran.Next(int.Parse(textBox4.Text), int.Parse(textBox5.Text)); n2 = ran.Next(int.Parse(textBox4.Text), int.Parse(textBox5.Text)); int n3 = ran.Next(1, int.Parse(textBox5.Text)); textBox1.Text += "("; if (n1 < 0) textBox1.Text += "(" + n1 + ")"; else textBox1.Text += n1; if (ran.Next(0, 2) == 1) textBox1.Text += "+"; else textBox1.Text += "-"; if (n2 < 0) textBox1.Text += "(" + n2 + ")"; else textBox1.Text += n2 + ")"; if (ran.Next(0, 2) == 1) textBox1.Text += "*"+n3; else textBox1.Text += "/" + n3; break;
2.对于让它连续出一种运算的表达式,根据最小取值范围的字符长度,也就是截取第一个运算数后面的运算符来进行判断。如下:
if (s.Substring(textBox4.TextLength, 1) == "+") { RandomNumjia(); }
代码实现:
Form1.cs
1 using System; 2 using System.Collections.Generic; 3 using System.ComponentModel; 4 using System.Data; 5 using System.Drawing; 6 using System.Linq; 7 using System.Text; 8 using System.Windows.Forms; 9 10 namespace _Random 11 { 12 public partial class Form1 : Form 13 { 14 public Form1() 15 { 16 InitializeComponent(); 17 } 18 public static int select = 0; 19 public static int Count = 0; 20 private int t = 60; 21 public static int right = 0; 22 23 private void button1_Click(object sender, EventArgs e) 24 { 25 label2.Text = t.ToString(); 26 timer1.Enabled = true; 27 timer1.Interval = 1000; 28 timer1.Start(); 29 } 30 31 private void RDN() 32 { 33 Random ran=new Random(); 34 int n1,n2; 35 if (textBox4.Text==""&&textBox5.Text=="") 36 { 37 MessageBox.Show("请输入取值范围!"); 38 return; 39 } 40 if (checkBox1.Checked == true) 41 select = 1; 42 for (int i = 0; i < int.Parse(textBox6.Text); i++) 43 { 44 textBox1.Clear(); 45 switch (select) 46 { 47 case 1: 48 { 49 n1 = ran.Next(int.Parse(textBox4.Text), int.Parse(textBox5.Text)); 50 n2 = ran.Next(int.Parse(textBox4.Text), int.Parse(textBox5.Text)); 51 int n3 = ran.Next(1, int.Parse(textBox5.Text)); 52 textBox1.Text += "("; 53 if (n1 < 0) textBox1.Text += "(" + n1 + ")"; 54 else textBox1.Text += n1; 55 if (ran.Next(0, 2) == 1) textBox1.Text += "+"; 56 else textBox1.Text += "-"; 57 if (n2 < 0) textBox1.Text += "(" + n2 + ")"; 58 else textBox1.Text += n2 + ")"; 59 if (ran.Next(0, 2) == 1) textBox1.Text += "*"+n3; 60 else textBox1.Text += "/" + n3; 61 break; 62 } 63 } 64 65 textBox3.Text = ""; 66 } 67 } 68 69 private void RandomNumjia() 70 { 71 textBox1.Clear(); 72 textBox3.Clear(); 73 if (textBox4.Text == "" && textBox5.Text == "") 74 { 75 MessageBox.Show("请输入取值范围!"); 76 return; 77 } 78 79 Random ran = new Random(); 80 int n1 = ran.Next(int.Parse(textBox4.Text), int.Parse(textBox5.Text)); 81 int n2 = ran.Next(int.Parse(textBox4.Text), int.Parse(textBox5.Text)); 82 if (n1 < 0) textBox1.Text += "(" + n1 + ")"; 83 else textBox1.Text += n1; 84 textBox1.Text += "+"; 85 if (n2 < 0) textBox1.Text += "(" + n2 + ")"; 86 else textBox1.Text += n2; 87 } 88 89 private void RandomNumjian() 90 { 91 textBox1.Clear(); 92 textBox3.Clear(); 93 if (textBox4.Text == "" && textBox5.Text == "") 94 { 95 MessageBox.Show("请输入取值范围!"); 96 return; 97 } 98 99 Random ran = new Random(); 100 int n1 = ran.Next(int.Parse(textBox4.Text), int.Parse(textBox5.Text)); 101 int n2 = ran.Next(int.Parse(textBox4.Text), int.Parse(textBox5.Text)); 102 if (n1 < 0) textBox1.Text += "(" + n1 + ")"; 103 else textBox1.Text += n1; 104 textBox1.Text += "-"; 105 if (n2 < 0) textBox1.Text += "(" + n2 + ")"; 106 else textBox1.Text += n2; 107 } 108 109 private void RandomNumcheng() 110 { 111 textBox1.Clear(); 112 textBox3.Clear(); 113 if (textBox4.Text == "" && textBox5.Text == "") 114 { 115 MessageBox.Show("请输入取值范围!"); 116 return; 117 } 118 119 Random ran = new Random(); 120 int n1 = ran.Next(int.Parse(textBox4.Text), int.Parse(textBox5.Text)); 121 int n2 = ran.Next(int.Parse(textBox4.Text), int.Parse(textBox5.Text)); 122 if (n1 < 0) textBox1.Text += "(" + n1 + ")"; 123 else textBox1.Text += n1; 124 textBox1.Text += "*"; 125 if (n2 < 0) textBox1.Text += "(" + n2 + ")"; 126 else textBox1.Text += n2; 127 } 128 129 private void RandomNumchu() 130 { 131 textBox1.Clear(); 132 textBox3.Clear(); 133 if (textBox4.Text == "" && textBox5.Text == "") 134 { 135 MessageBox.Show("请输入取值范围!"); 136 return; 137 } 138 139 Random ran = new Random(); 140 int n1 = ran.Next(int.Parse(textBox4.Text), int.Parse(textBox5.Text)); 141 int n2 = ran.Next(int.Parse(textBox4.Text), int.Parse(textBox5.Text)); 142 if (n1 < 0) textBox1.Text += "(" + n1 + ")"; 143 else textBox1.Text += n1; 144 textBox1.Text += "/"; 145 if (n2 < 0) textBox1.Text += "(" + n2 + ")"; 146 else textBox1.Text += n2; 147 } 148 149 private void timer1_Tick(object sender, EventArgs e) 150 { 151 if (t <= 0) 152 { 153 timer1.Enabled = false; 154 textBox3.Enabled = false; 155 MessageBox.Show("时间到!"); 156 textBox3.Enabled = false; 157 Form2 frm2 = new Form2(); 158 frm2.ShowDialog(); 159 } 160 t = t - 1; 161 label2.Text = t.ToString(); 162 } 163 164 private void button2_Click(object sender, EventArgs e) 165 { 166 timer1.Stop(); 167 Form2 frm2 = new Form2(); 168 frm2.ShowDialog(); 169 } 170 171 private void button3_Click(object sender, EventArgs e) 172 { 173 RandomNumjia(); 174 } 175 176 private void button4_Click(object sender, EventArgs e) 177 { 178 RandomNumjian(); 179 } 180 181 private void button5_Click(object sender, EventArgs e) 182 { 183 RandomNumcheng(); 184 } 185 186 private void button6_Click(object sender, EventArgs e) 187 { 188 RandomNumchu(); 189 } 190 191 private void button7_Click(object sender, EventArgs e) 192 { 193 if (textBox4.Text == "" && textBox5.Text == "") 194 { 195 MessageBox.Show("请输入取值范围!"); 196 return; 197 } 198 else 199 { 200 for (int i = 0; i < int.Parse(textBox6.Text);i++) 201 { 202 RDN(); 203 } 204 } 205 } 206 207 private void textBox3_KeyDown(object sender, KeyEventArgs e) 208 { 209 string result = textBox1.Text; 210 211 if (Count == int.Parse(textBox6.Text)) 212 { 213 Form2 frm2 = new Form2(); 214 frm2.ShowDialog(); 215 } 216 217 if (e.KeyCode == Keys.Enter) 218 { 219 if (textBox3.Text == Calucate(result).ToString()) //直接调用Calucate这个方法计算result的值并与输入的值进行比较 220 { 221 right++; 222 Count++; 223 MessageBox.Show("回答正确!"); 224 } 225 226 else 227 { 228 MessageBox.Show("答题错误!"); 229 Count++; 230 string s = textBox1.Text; 231 if (s.Substring(textBox4.TextLength, 1) == "+") 232 { 233 RandomNumjia(); 234 } 235 else if (s.Substring(textBox4.TextLength, 1) == "-") 236 { 237 RandomNumjian(); 238 } 239 else if (s.Substring(textBox4.TextLength, 1) == "*") 240 { 241 RandomNumcheng(); 242 } 243 else if (s.Substring(textBox4.TextLength, 1) == "/") 244 { 245 RandomNumchu(); 246 } 247 else if (s.Length > 5) 248 { 249 RDN(); 250 } 251 } 252 253 string m = textBox1.Text; 254 if (m.Substring(textBox4.TextLength, 1) == "+") 255 { 256 RandomNumjia(); 257 } 258 else if (m.Substring(1, 1) == "-") 259 { 260 RandomNumjian(); 261 } 262 else if (m.Substring(1, 1) == "*") 263 { 264 RandomNumcheng(); 265 } 266 else if (m.Substring(1, 1) == "/") 267 { 268 RandomNumchu(); 269 } 270 else if (m.Length > 5) 271 { 272 RDN(); 273 } 274 } 275 } 276 277 const string operators = "+-*/"; //运算符 278 static Dictionary<char, int> priorities = null; //优先级 279 280 static void Calculator() //添加了四种运算符以及四种运算符的优先级 281 { 282 priorities = new Dictionary<char, int>(); 283 priorities.Add('#', -1); 284 priorities.Add('+', 0); 285 priorities.Add('-', 0); 286 priorities.Add('*', 1); 287 priorities.Add('/', 1); 288 } 289 290 static int Compute(int leftNum, int rightNum, char op) //这是一种方法,用来计算左右两个数的静态方法! 291 { 292 switch (op) 293 { 294 case '+': return leftNum + rightNum; 295 case '-': return leftNum - rightNum; 296 case '*': return leftNum * rightNum; 297 case '/': return leftNum / rightNum; 298 default: return 0; 299 } 300 } 301 302 static bool IsOperator(char op) //每次判断这个字符是否是运算符? 303 { 304 return operators.IndexOf(op) >= 0; 305 } 306 307 static bool IsAssoc(char op) //返回一个关联符号 308 { 309 return op == '+' || op == '-' || op == '*' || op == '/'; 310 } 311 312 static Queue<object> QueueSort (string expression) // 队列排序 313 { 314 Queue<object> result = new Queue<object>(); 315 Stack<char> operatorStack = new Stack<char>(); //运算符栈 316 operatorStack.Push('#'); 317 char top, cur, tempChar; //top栈顶,current最近的; 318 string tempNum; 319 for (int i = 0, j; i < expression.Length; ) //取出表达式 320 { 321 cur = expression[i++]; //取出表达式的每个字符赋给cur 322 top = operatorStack.Peek(); //栈顶元素赋给top此时为"#" 323 324 if (cur == '(') //将左括号压栈,此时栈顶元素为"(" 325 { 326 operatorStack.Push(cur); 327 } 328 else 329 { 330 if (IsOperator(cur)) //如果是运算符的话 331 { 332 while (IsOperator(top) && ((IsAssoc(cur) && priorities[cur] <= priorities[top])) || (!IsAssoc(cur) && priorities[cur] < priorities[top])) 333 { 334 result.Enqueue(operatorStack.Pop()); //如果元素为运算符并且优先级小于栈顶元素优先级,出栈 335 top = operatorStack.Peek(); //继续把栈顶元素赋给top 336 } 337 operatorStack.Push(cur); //把数字压栈 338 } 339 else if (cur == ')') //将右括号添加到结尾 340 { 341 while (operatorStack.Count > 0 && (tempChar = operatorStack.Pop()) != '(') 342 { 343 result.Enqueue(tempChar); 344 } 345 } 346 else 347 { 348 tempNum = "" + cur; 349 j = i; 350 while (j < expression.Length && (expression[j] == '.' || (expression[j] >= '0' && expression[j] <= '9'))) 351 { 352 tempNum += expression[j++]; 353 } 354 i = j; 355 result.Enqueue(tempNum); 356 } 357 } 358 } 359 while (operatorStack.Count > 0) 360 { 361 cur = operatorStack.Pop(); 362 if (cur == '#') continue; 363 if (operatorStack.Count > 0) 364 { 365 top = operatorStack.Peek(); 366 } 367 368 result.Enqueue(cur); 369 } 370 371 return result; 372 } 373 374 static int Calucate(string expression) 375 { 376 try 377 { 378 var rpn = QueueSort(expression); //rpn逆波兰表达式reverse polish notation 379 Stack<int> operandStack = new Stack<int>(); 380 int left, right; 381 object cur; 382 while (rpn.Count > 0) 383 { 384 cur = rpn.Dequeue(); //出列 385 if (cur is char) //如果cur为字符的话 386 { 387 right = operandStack.Pop(); //右边的数字出栈 388 left = operandStack.Pop(); //左边的数字出栈 389 operandStack.Push(Compute(left, right, (char)cur)); //此时调用compute方法 390 } 391 else 392 { 393 operandStack.Push(int.Parse(cur.ToString())); //是数字就压栈 394 } 395 } 396 return operandStack.Pop(); 397 } 398 catch 399 { 400 throw new Exception("表达式格式不正确!"); 401 } 402 } 403 404 } 405 }
代码编写过程:
Form2.cs
1 using System; 2 using System.Collections.Generic; 3 using System.ComponentModel; 4 using System.Data; 5 using System.Drawing; 6 using System.Linq; 7 using System.Text; 8 using System.Windows.Forms; 9 10 namespace _Random 11 { 12 public partial class Form2 : Form 13 { 14 public Form2() 15 { 16 InitializeComponent(); 17 } 18 19 private void Form2_Load(object sender, EventArgs e) 20 { 21 textBox1.Text = Form1.Count.ToString(); 22 textBox2.Text = Form1.right.ToString(); 23 textBox3.Text = (Form1.Count - Form1.right).ToString(); 24 } 25 26 } 27 }
运行过程:
答题的时候给出取值范围,和预想答题数目,然后点击随机,这时程序就会生成一个式子。
当我在输入答案并回车的时候会对生成的式子进行计算,并与输入的答案进行比较。
当时间用完,或者点击结束运算的时候,会弹出测试结果并提示时间到。
结对编程总结:
说明:我们一起做了1个增量