结对项目
一、Github
Githu:https://github.com/tryanddie/teamwork_2
项目成员:陈创智,3118005041;蔡双浩 3118005040。
二、PSP
Planning |
计划 |
|
|
|
||
· Estimate |
· 估计这个任务需要多少时间 |
40 |
30 |
|
||
Development |
开发 |
|
|
|
||
· Analysis |
· 需求分析 (包括学习新技术) |
200 |
240 |
|
||
· Design Spec |
· 生成设计文档 |
60 |
50 |
|
||
· Design Review |
· 设计复审 (和同事审核设计文档) |
80 |
80 |
|
|
|
· Coding Standard |
· 代码规范 (为目前的开发制定合适的规范) |
10 |
10 |
|
||
· Design |
· 具体设计 |
60 |
55 |
|
||
· Coding |
· 具体编码 |
420 |
500 |
|
||
· Code Review |
· 代码复审 |
120 |
180 |
|
||
· Test |
· 测试(自我测试,修改代码,提交修改) |
100 |
150 |
|
||
Reporting |
报告 |
|
|
|
||
· Test Report |
· 测试报告 |
30 |
45 |
|
||
· Size Measurement |
· 计算工作量 |
5 |
5 |
|
||
· Postmortem & Process Improvement Plan |
· 事后总结, 并提出过程改进计划 |
120 |
100 |
|
||
合计 |
|
1245 | 1445 |
|
二、题目
设计一个自动生成四则运算算术题的控制台程序,或者其他类似功能的程序。
- 支持加减乘除算符,支持括号
- 支持整数和分数,其中,当分数的分子的绝对值大于分母时,采用带分数形式,如11/4应显示为2`3/4;
- 生成的题目中计算过程不能产生负数,也就是说算术表达式中如果存在形如e1− e2的子表达式,那么e1≥ e2。
- 每道题目中出现的运算符个数不超过3个。
- 程序一次运行生成的题目不能重复,即任何两道题目不能通过有限次交换+和×左右的算术表达式变换为同一道题目。例如,23 + 45 = 和45 + 23 = 是重复的题目,6 × 8 = 和8 × 6 = 也是重复的题目。3+(2+1)和1+2+3这两个题目是重复的,由于+是左结合的,1+2+3等价于(1+2)+3,也就是3+(1+2),也就是3+(2+1)。但是1+2+3和3+2+1是不重复的两道题,因为1+2+3等价于(1+2)+3,而3+2+1等价于(3+2)+1,它们之间不能通过有限次交换变成同一个题目。
- 在生成题目的同时,计算出所有题目的答案,并存入执行程序的当前目录下的Answers.txt文件。
- 程序应能支持一万道题目的生成。
- 程序支持对给定的题目文件和答案文件,判定答案中的对错并进行数量统计。
- 统计结果输出到文件Grade.txt
三、困难与解决方案
控制生成题目的个数,控制题目中数值的范围
用循环次数来控制生成的个数,用ran来控制随机生成的范围
每道题目中出现的运算符个数不超过3个
用随机数来生成决定运算符个数
其他各种问题等
四、代码说明
主函数代码
public class index { static String wenTi = "Exercises.txt"; //对照答案时问题的路径,未选取的时候默认为Exercises.txt文件 static String daAn = "Answers.txt"; //对照答案时问题的路径,未选取的时候默认为Exercises.txt文件 static String sizi = new String(); static char[] fuhao = {'+','-','*','÷'}; static String str[][] = new String[10000][7]; public static void main(String[] args) { sizi=""; //设置主界面 JFrame mainFrame= new JFrame("题目自动生成器"); mainFrame.setSize(410,400); mainFrame.setLocation(200, 200); mainFrame.setLayout(new GridLayout(3, 1)); mainFrame.setLayout(null); mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // 创建输入框 JPanel Input = new JPanel(); Input.setBounds(10, 10, 375, 120); Input.setLayout(new GridLayout(4,3,10,10)); JLabel geshu = new JLabel("生成题目个数:"); final JTextField geshuText = new JTextField(); JLabel fanwei= new JLabel("生成数值范围:"); final JTextField fanweiText = new JTextField(); JButton b = new JButton("生成随机题目"); final JButton wenti = new JButton("选择题目文件"); final JButton daan= new JButton("选择答案文件"); JButton jieguo = new JButton("判断对错"); wenti.setLayout(null); daan.setLayout(null); b.setLayout(null); jieguo.setLayout(null); Input.add(geshu); Input.add(geshuText); Input.add(fanwei); Input.add(fanweiText); //文本域 b.setBounds(120, 120 + 30, 160, 30); wenti.setBounds(10, 220, 160, 30); daan.setBounds(210, 220 , 160, 30); jieguo.setBounds(120,250, 160, 30); //主界面加入文本域,按钮和输入框 mainFrame.add(Input); mainFrame.add(b); mainFrame.add(wenti); mainFrame.add(daan); mainFrame.add(jieguo); mainFrame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); wenti.addActionListener(new ActionListener() { //选择问题的文件 public void actionPerformed(ActionEvent e) { JFileChooser chooser = new JFileChooser(); //设置选择器 chooser.setMultiSelectionEnabled(true); //设为多选 int returnVal = chooser.showOpenDialog(wenti); //是否打开文件选择框 System.out.println("returnVal="+returnVal); if (returnVal == JFileChooser.APPROVE_OPTION) { //如果符合文件类型 wenTi= chooser.getSelectedFile().getAbsolutePath(); } } }); daan.addActionListener(new ActionListener() { //选择问题的文件 public void actionPerformed(ActionEvent e) { JFileChooser chooser = new JFileChooser(); //设置选择器 chooser.setMultiSelectionEnabled(true); //设为多选 int returnVal = chooser.showOpenDialog(daan); //是否打开文件选择框 System.out.println("returnVal="+returnVal); if (returnVal == JFileChooser.APPROVE_OPTION) { //如果符合文件类型 daAn= chooser.getSelectedFile().getAbsolutePath(); } }}); jieguo.addActionListener(new ActionListener() { //选择问题的文件 public void actionPerformed(ActionEvent e) { try{ String daan=result(wenTi); saveAsFileWriter(daan,"Answer1.txt"); //将问题的答案放入答案文件 } catch(IOException h) {h.printStackTrace();} try{judge("Answer1.txt",daAn);} catch(IOException k) {k.printStackTrace();} } }); //点击按钮,开始生成题目 b.addActionListener(new ActionListener() { boolean checkedpass = true; public void actionPerformed(ActionEvent e) { checkedpass = true; //检验是否有数值没有填 checkNumber(geshuText,"题目个数"); checkNumber(fanweiText,"题目范围"); String Strnum = geshuText.getText(); String Strscope = fanweiText.getText(); //将输入的值转化为整型 int num = Integer.parseInt(Strnum); int scope = Integer.parseInt(Strscope); create(scope,num); saveAsFileWriter(sizi,"Exercises.txt"); //生成问题存进文件 try{ String daan=result("Exercises.txt"); saveAsFileWriter(daan,"Answers.txt"); //将问题的答案放入答案文件 } catch(IOException h) {h.printStackTrace();} JOptionPane.showMessageDialog(null,"随机生成题目成功\n题目与答案已经保存"); } //检验输入数值是否为整数 private void checkNumber(JTextField tf, String msg){ if(!checkedpass) return; String value = tf.getText(); try { Integer.parseInt(value); } catch (NumberFormatException e) { JOptionPane.showMessageDialog(null, msg + "必须是整数"); tf.grabFocus(); checkedpass = false; } } });//按钮结束位置 mainFrame.setVisible(true); }
将字符串放入文件
private static void saveAsFileWriter(String content,String filePath) { //content为写入的字符串,filepath为写入文件的路径 FileWriter fwriter = null; try { fwriter = new FileWriter(filePath); fwriter.write(content); } catch (IOException ex) { ex.printStackTrace(); } finally { try { fwriter.flush(); fwriter.close(); } catch (IOException ex) { ex.printStackTrace(); } } }
随机生成题目
static void create(int r,int p) { //随机生成题目,r为生成范围,p为生成个数 for( int n=0 ; n<p ;){ //开始循环 Random ran = new Random(); int flag=ran.nextInt(3)+1; String [] array=new String[2*flag+1]; char[] a = new char[flag]; //char[] c = new char[flag]; int[] number = new int[flag+1]; // int[] paixu=new int[flag+1]; for(int i=0;i<flag+1;i++) { number[i]=ran.nextInt(r)+1;//创建三个随机数 } for(int j=0;j<flag;j++) { a[j]=fuhao[ran.nextInt(4)]; switch(a[j]) { // '-'对数有特殊要求 case '-': if(number[j]>number[j+1]) break; else {j--;break;} case '÷': if(number[j]%number[j+1]==0) break; else {j--;break;} case '+': case '*':break; } }//该括号为生成字符结束 for(int i=0; i < flag ; i++) array[i] = String.valueOf(a[i]); for(int i=0; i < flag+1; i++) array[flag+i]=String.valueOf(number[i]); //将字符都放进array中 int biaoji=0; for(int i=0 ; i< p; i++) {if(Arrays.equals(str[i],array)) {biaoji =1; //如果与已有式子一样,重新生成题目 break;} } String shizi = new String(); //对运算数和运算符进行连接 shizi=String.valueOf(number[0]); for(int k=0;k<flag;k++) { shizi=shizi+a[k]+String.valueOf(number[k+1]); } if(count(shizi)==false) biaoji = 1; if(biaoji == 0){ str[n] = array; n++; for(int k=0;k<flag;k++) { sizi=sizi+String.valueOf(number[k])+a[k]; } sizi =sizi + String.valueOf(number[flag])+'=' + '\n'; } } }
对答案进行判断
static //对答案进行判断 void judge(String exe,String answer) throws IOException { String correct=" ";//用于存储正确的题目的编号,即(2,3)这样的 int correct_=0; //用于存储正确的题目总数 String wrong=" "; //用于存储错误题目的编号 int wrong_=0; //用于存储错误的题目总数 //读取回答文件,若不存在则抛出异常 File myexe=new File(exe); String exe2=null; //用于存储读取的回答 if(!myexe.exists()||myexe.isDirectory()) throw new FileNotFoundException(); //读取答案文件,若不存在则抛出异常 File answer_1=new File(answer); String answer_2=null; //用于存储读取的答案 if(!answer_1.exists()||answer_1.isDirectory()) throw new FileNotFoundException(); BufferedReader exe1=new BufferedReader(new FileReader(myexe)); BufferedReader answer_3=new BufferedReader(new FileReader(answer_1)); //exe2和answer_3用于存储读取的行 for(int i=1;((exe2=exe1.readLine())!=null && (answer_2=answer_3.readLine())!=null);i++) { //如果文件没读完,循环继续 if(exe2.equals(answer_2)) { //字符相同,说明答案一样 if(correct==" ") correct=correct+String.valueOf(i); ////纯粹为了保持 “,”的数目正常 else correct=correct+','+String.valueOf(i); //将正确的编号接入字符串 correct_++; //正确的题目数+1 } else { if(wrong==" ") wrong=wrong+String.valueOf(i); //纯粹为了保持 “,”的数目正常 else wrong=wrong+','+String.valueOf(i); //将错误的编号接入字符串 wrong_++; } } //如果你要测试的话,main函数中的路径记得改 JOptionPane.showMessageDialog(null,"correct:"+String.valueOf(correct_)+'('+correct+" )"+"\nwrong:"+String.valueOf(wrong_)+'('+wrong+" )"); // System.out.println("wrong:"+String.valueOf(wrong_)+'('+wrong+" )"); exe1.close(); answer_3.close(); } static boolean count(String str) { //单个判断正负 str=remove(str, '='); str=str.replace('÷','/'); ScriptEngineManager manager = new ScriptEngineManager(); ScriptEngine se = manager.getEngineByName("js"); Double result = null; try { result = Double.valueOf(se.eval(str).toString()); if (result == null) { result = new Double(0); } } catch (ScriptException e) { e.printStackTrace(); } if(result>0) return true; else return false; }
计算生成题目答案
static String result(String Lujin) throws IOException { File answer_1=new File(Lujin); String str=null; //用于存储读取的答案 if(!answer_1.exists()||answer_1.isDirectory()) throw new FileNotFoundException(); String result_0=""; BufferedReader answer_3=new BufferedReader(new FileReader(answer_1)); while((str=answer_3.readLine())!=null) { str=remove(str, '='); str=str.replace('÷','/'); ScriptEngineManager manager = new ScriptEngineManager(); ScriptEngine se = manager.getEngineByName("js"); Double result = null; try { result = Double.valueOf(se.eval(str).toString()); if (result == null) { result = new Double(0); } } catch (ScriptException e) { e.printStackTrace(); } if(result.equals(Math.floor(result))) { double result1=result; int i= (int)result1; result_0=result_0+String.valueOf(i)+"\n"; } } answer_3.close(); return result_0; }
五、测试运行
生成10000道题目
修改几个答案并比较结果
与预期效果一致
六、两人总结
这次项目做起来比起上一次更加有规划性了,通过两人合作,各自互补,学到了许多新的知识,也学会了如何合作才能更快的完成项目。但是,
在提前准备方面,我们做的还有些不足,对于项目的一些“坑”没有提前谈论清楚,导致项目进展缓慢,以及在协调方面有了一些问题,将一部分时间花费在了解同伴需求的身上,下次应该能更进一步。