一、项目需求:
用户:
小学、初中和高中数学老师。
功能:
1、命令行输入用户名和密码,两者之间用空格隔开(程序预设小学、初中和高中各三个账号,具体见附表),如果用户名和密码都正确,将根据账户类型显示“当前选择为XX出题”,XX为小学、初中和高中三个选项中的一个。否则提示“请输入正确的用户名、密码”,重新输入用户名、密码;
2、登录后,系统提示“准备生成XX数学题目,请输入生成题目数量(输入-1将退出当前用户,重新登录):”,XX为小学、初中和高中三个选项中的一个,用户输入所需出的卷子的题目数量,系统默认将根据账号类型进行出题。每道题目的操作数在1-5个之间,操作数取值范围为1-100;
3、题目数量的有效输入范围是“10-30”(含10,30,或-1退出登录),程序根据输入的题目数量生成符合小学、初中和高中难度的题目的卷子(具体要求见附表)。同一个老师的卷子中的题目不能与以前的已生成的卷子中的题目重复(以指定文件夹下存在的文件为准,见5);
4、在登录状态下,如果用户需要切换类型选项,命令行输入“切换为XX”,XX为小学、初中和高中三个选项中的一个,输入项不符合要求时,程序控制台提示“请输入小学、初中和高中三个选项中的一个”;输入正确后,显示“”系统提示“准备生成XX数学题目,请输入生成题目数量”,用户输入所需出的卷子的题目数量,系统新设置的类型进行出题;
5、生成的题目将以“年-月-日-时-分-秒.txt”的形式保存,每个账号一个文件夹。每道题目有题号,每题之间空一行;
6、个人项目9月17日晚上10点以前提交至创新课程管理系统。提交方式:工程文件打包,压缩包名为“几班+姓名.rar”。迟交2天及以内者扣分,每天扣20%。迟交2天及以上者0分。
附表-1:账户、密码
账户类型 |
账户 |
密码 |
备注 |
小学 |
张三1 |
123 |
|
张三2 |
123 |
|
|
张三3 |
123 |
|
|
初中 |
李四1 |
123 |
|
李四2 |
123 |
|
|
李四3 |
123 |
|
|
高中 |
王五1 |
123 |
|
王五2 |
123 |
|
|
王五3 |
123 |
|
附表-2:小学、初中、高中题目难度要求
|
小学 |
初中 |
高中 |
|
难度要求 |
+,-,*./ |
平方,开根号 |
sin,cos,tan |
|
备注 |
只能有+,-,*./和() |
题目中至少有一个平方或开根号的运算符 |
题目中至少有一个sin,cos或tan的运算符 |
|
二、程序运行测试:
运行程序进入登陆界面:
输入账号密码并生成题目:
成功生成,题目也符合要求。
切换难度并生成题目:
也成功生成。
三、队友代码分析:
结构:
使用了User类,Test类,PaperMaker类,Login类以及一个MakeNewProblem接口来完成该项目。
Test类:
1 public class Test { 2 public static void main(String[] args) { 3 Login.userInitiail(); //登录系统 4 } 5 }
运行程序的主类。
User类:
1 public class User { 2 private String name; //名称 3 private String password; //密码 4 private String type; //类型 5 6 public User(String name, String password, String type) { 7 this.name = name; 8 this.password = password; 9 this.type = type; 10 } 11 12 public String getType() { 13 return type; 14 } 15 16 public void setType(String type) { 17 this.type = type; 18 } 19 20 public String getName() { 21 return name; 22 } 23 24 public void setName(String name) { 25 this.name = name; 26 } 27 28 public String getPassword() { 29 return password; 30 } 31 32 public void setPassword(String password) { 33 this.password = password; 34 } 35 36 }
User十分简洁,包含三个属性:账号名称,账号密码,账号类别。方法包括构造方法和获取这三个类别的方法。
Login类:
1 package mathpaper; 2 3 import java.util.HashMap; 4 import java.util.Scanner; 5 6 /** 7 * This class is used for user login 8 * 9 * @author jason-st 10 * @date 2023/9/13 11 * @project Work_Of_Junior 12 */ 13 public class Login { 14 static HashMap<String, User> hmap = new HashMap<>(); 15 16 /* 17 * Initialization of Hash Table 18 * */ 19 public static void hmapInitial() { 20 hmap.put("张三1", new User("张三1", "123", "小学")); 21 hmap.put("张三2", new User("张三2", "123", "小学")); 22 hmap.put("张三3", new User("张三3", "123", "小学")); 23 hmap.put("李四1", new User("李四1", "123", "初中")); 24 hmap.put("李四2", new User("李四2", "123", "初中")); 25 hmap.put("李四3", new User("李四3", "123", "初中")); 26 hmap.put("王五1", new User("王五1", "123", "高中")); 27 hmap.put("王五2", new User("王五2", "123", "高中")); 28 hmap.put("王五3", new User("王五3", "123", "高中")); 29 } 30 31 /* 32 * Implement of user login 33 * */ 34 public static void userInitiail() { 35 hmapInitial(); 36 Scanner sc = new Scanner(System.in); 37 while (true) { 38 System.out.println("请输入用户名和密码(两者之间用空格隔开)"); 39 String tempname = sc.next(); 40 String temppassword = sc.next(); 41 User tempuser = hmap.get(tempname); //名字对不上则tempuser为null,名字对密码不对则要重新登录 42 if (tempuser != null && tempuser.getPassword().equals(temppassword)) { 43 while (true) { 44 System.out.println("准备生成" + tempuser.getType() + "数学题目"); 45 System.out.println("请输入生成题目数量(输入-1将退出当前用户,重新登录)"); 46 int num = sc.nextInt(); 47 if (num >= 10 && num <= 30) { 48 PaperMaker.makePaper(tempuser, num, tempuser.getType()); //生成试卷 49 System.out.println("已为您生成完毕,是否继续出题,出题请输入1,切换难度请输入2,退出请输入3"); 50 int picknum = sc.nextInt(); 51 dealPickNum(picknum, tempuser); 52 } else if (num == -1) { 53 return; //退出了当前用户,重新登录意味着整个系统要重新开始,直接return; 54 } else { 55 System.out.println("请输入10-30之间的自然数"); 56 } 57 } 58 } else { 59 System.out.println("请输入正确的用户名和密码"); 60 } 61 } 62 } 63 64 /* 65 * Change the type of user to create a question 66 * 67 * @param tempuser the given user 68 * */ 69 public static void changeTpye(User tempuser) { 70 Scanner sc = new Scanner(System.in); 71 while (true) { 72 System.out.println("请输入:切换为xx"); 73 System.out.println("xx为小学或初中或高中"); 74 String s = sc.next(); 75 if (s.substring(3, 5).equals("小学")) { 76 dealSpecificType("小学", tempuser); 77 break; 78 } else if (s.substring(3, 5).equals("初中")) { 79 dealSpecificType("初中", tempuser); 80 break; 81 } else if (s.substring(3, 5).equals("高中")) { 82 dealSpecificType("高中", tempuser); 83 break; 84 } else { 85 System.out.println("请输入小学、初中和高中三个选项中的一个"); 86 } 87 } 88 } 89 90 /* 91 * deal with a particular type 92 * 93 * @param type the given string 94 * @param tempuser the given user 95 * */ 96 public static void dealSpecificType(String type, User tempuser) { 97 Scanner sc = new Scanner(System.in); 98 System.out.println("准备生成" + type + "数学题目,请输入生成题目数量"); 99 int num = sc.nextInt(); 100 PaperMaker.makePaper(tempuser, num, type); 101 System.out.println("已为您生成完毕,谢谢您的使用"); 102 } 103 104 /* 105 * deal with a particular pick number 106 * 107 * @param picknum the given picked number 108 * @param tempuser the given user 109 * */ 110 public static void dealPickNum(int picknum, User tempuser) { 111 switch (picknum) { 112 case 1: 113 break; 114 case 2: 115 changeTpye(tempuser); 116 break; 117 case 3: 118 System.exit(0); 119 default: 120 break; 121 } 122 } 123 }
包含一个属性,一个存有所有账户信息的hashmap数据结构。
四个方法:
UserInitial()方法:
初始化登陆界面,账号和密码的输入和判断,并且进入下一级页面。
changeType()方法:
显示切换难度的界面,新的难度的输入以及通过调用dealSpecialType()方法来实现切换难度。
dealSpecialType()方法:
切换难度的具体实现。
dealPickNum()方法:
生产完题目后的下一步操作的选择和处理。
PaperMaker类:
1 package mathpaper; 2 3 import java.io.BufferedReader; 4 import java.io.File; 5 import java.io.FileReader; 6 import java.io.FileWriter; 7 import java.text.SimpleDateFormat; 8 import java.util.Date; 9 import java.util.Random; 10 11 /** 12 * This class is used for generating and storing test papers 13 * 14 * @author jason-st 15 * @date 2023/9/13 16 * @project Work_Of_Junior 17 */ 18 public class PaperMaker implements MakeNewProblem { 19 /* 20 * Generate some problem due to type 21 * 22 * @param type The type of the question 23 * @return the string of problem 24 * */ 25 public String makeProblem(String type) { 26 String s = ""; 27 if (type.equals("小学")) { 28 s = makePrimarySchoolProblem(); 29 } else if (type.equals("初中")) { 30 s = makeMiddleSchoolProblem(); 31 } else if (type.equals("高中")) { 32 s = makeHighSchoolProblem(); 33 } 34 return s; 35 } 36 37 /* 38 * Implementation of interfaces 39 * 40 * @param u a particular User 41 * @param n the num of the question 42 * @param type the type of the school 43 * @return a particular method due to the type. 44 * */ 45 public static void makePaper(User u, int n, String type) { 46 if (type.equals("小学")) { 47 primarySchool(u, n); 48 } else if (type.equals("初中")) { 49 middleSchool(u, n); 50 } else if (type.equals("高中")) { 51 highSchool(u, n); 52 } 53 } 54 55 /* 56 * get a randomnumber of a particular interval 57 * 58 * @param min the min number 59 * @param max the max number 60 * @return a random number between the min and the max 61 * */ 62 public static int getRandomNumber(int min, int max) { 63 Random r = new Random(); 64 return r.nextInt(max - min + 1) + min; 65 } 66 67 /* 68 * judge if the newformula is repetitive 69 * 70 * @param u the particular User 71 * @param newformula the new problem 72 * @return whether the formula is repetitive 73 * */ 74 public static boolean judge(User u, String newformula) { 75 //查重,需要对应的用户和新的公式 76 boolean flag = true; //默认不重复 77 String fatherpath = ".\\autopaper\\" + u.getName(); 78 79 File fp = new File(fatherpath); 80 //每个父亲目录下子目录名字的集合 81 String[] sonfiles = fp.list(); 82 //逐个目录进行查找 83 for (int i = 0; i < sonfiles.length; i++) { 84 try (FileReader fw1 = new FileReader(fatherpath + "\\" + sonfiles[i]); 85 BufferedReader bf = new BufferedReader(fw1);) { 86 String oldformula; 87 while ((oldformula = bf.readLine()) != null) { //旧的公式一定会包含公式后面的空格,所以用contains来进行判断比较合适 88 if (oldformula.contains(newformula)) { 89 return false; 90 } 91 } 92 93 } catch (Exception e) { 94 e.printStackTrace(); 95 } 96 } 97 98 return true; 99 } 100 101 /* 102 * input a problem into the given path 103 * 104 * @param path the saved path 105 * @param newformula the new problem 106 * */ 107 public static void input(String path, String newformula) { 108 //写入,需要对应路径,和新的公式 109 // 注意是追加写入,保证写的内容不会覆盖 110 try (FileWriter fw = new FileWriter(path, true);) { 111 fw.write(newformula); 112 fw.write("\r\n"); //第一次换行 113 fw.write("\r\n"); //空行 114 } catch (Exception e) { 115 e.printStackTrace(); 116 } 117 } 118 119 /* 120 * Write n questions into the corresponding user path 121 * 122 * @param u the particular user 123 * @param n the number of problems 124 * */ 125 public static void primarySchool(User u, int n) { 126 String path = "./autopaper\\" + u.getName(); 127 File f = new File(path); 128 //如果不存在对应的文件夹,先进行创建 129 if (!f.exists()) { 130 f.mkdir(); 131 } 132 //存在对应文件夹,则进行创建文件的操作 133 String rightnowtime = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss").format(new Date()); 134 path = path + "\\" + rightnowtime + ".txt"; //在该路径下去创建txt文件 135 try (FileWriter fw = new FileWriter(path);) { 136 //成功创建对应文件后。1.进行出题 2.进行查重判断3.将题目写入 137 for (int index = 1; index <= n; index++) { 138 String newformula = new PaperMaker().makeProblem("小学"); 139 //创建的不带序号,这样后面才好判断 140 if (judge(u, newformula)) { 141 //进行写入操作 142 newformula = index + ". " + newformula; 143 input(path, newformula); 144 } else { 145 index--; //之前写过就就重新写入 146 } 147 } 148 } catch (Exception e) { 149 e.printStackTrace(); 150 } 151 152 } 153 154 /* 155 * Write n questions into the corresponding user path 156 * 157 * @param u the particular user 158 * @param n the number of problems 159 * */ 160 public static void middleSchool(User u, int n) { 161 String path = "./autopaper\\" + u.getName(); 162 File f = new File(path); 163 //如果不存在对应的文件夹,先进行创建 164 if (!f.exists()) { 165 f.mkdir(); 166 } 167 //存在对应文件夹,则进行创建文件的操作 168 String rightnowtime = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss").format(new Date()); 169 path = path + "\\" + rightnowtime + ".txt"; //在该路径下去创建txt文件 170 try (FileWriter fw = new FileWriter(path);) { 171 //成功创建对应文件后。1.进行出题 2.进行查重判断3.将题目写入 172 for (int index = 1; index <= n; index++) { 173 String newformula = new PaperMaker().makeProblem("初中"); 174 //创建的不带序号,这样后面才好判断 175 if (judge(u, newformula)) { 176 //进行写入操作 177 newformula = index + ". " + newformula; 178 input(path, newformula); 179 } else { 180 index--; 181 } 182 } 183 } catch (Exception e) { 184 e.printStackTrace(); 185 } 186 } 187 188 /* 189 * Write n questions into the corresponding user path 190 * 191 * @param u the particular user 192 * @param n the number of problems 193 * */ 194 public static void highSchool(User u, int n) { 195 String path = "./autopaper\\" + u.getName(); 196 File f = new File(path); 197 //如果不存在对应的文件夹,先进行创建 198 if (!f.exists()) { 199 f.mkdir(); 200 } 201 //存在对应文件夹,则进行创建文件的操作 202 String rightnowtime = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss").format(new Date()); 203 path = path + "\\" + rightnowtime + ".txt"; //在该路径下去创建txt文件 204 try (FileWriter fw = new FileWriter(path);) { 205 //成功创建对应文件后。1.进行出题 2.进行查重判断3.将题目写入 206 for (int index = 1; index <= n; index++) { 207 String newformula = new PaperMaker().makeProblem("高中"); 208 //创建的不带序号,这样后面才好判断 209 if (judge(u, newformula)) { 210 //进行写入操作 211 newformula = index + ". " + newformula; 212 input(path, newformula); 213 } else { 214 index--; 215 } 216 } 217 } catch (Exception e) { 218 e.printStackTrace(); 219 } 220 } 221 222 /* 223 * Generate a primary school question 224 * 225 * @return a primary school question 226 * */ 227 public static String makePrimarySchoolProblem() { //完美的方法! 228 Random r = new Random(); 229 String[] symbol = {"+", "-", "*", "/"}; 230 int op_num = getRandomNumber(2, 5); 231 String[] ops = new String[op_num]; 232 for (int i = 0; i < op_num; i++) { 233 ops[i] = String.valueOf(getRandomNumber(1, 100)); 234 } 235 if (op_num >= 3 && r.nextBoolean()) { //如何加括号 236 int l = getRandomNumber(1, op_num - 2); 237 int left = getRandomNumber(0, op_num - l - 1); 238 int right = left + l; 239 ops[left] = "(" + ops[left]; 240 ops[right] = ops[right] + ")"; 241 } 242 String result = ""; 243 for (int i = 0; i < op_num - 1; i++) { 244 result += ops[i] + symbol[getRandomNumber(0, 3)]; 245 } 246 result += ops[op_num - 1] + "="; 247 return result; 248 } 249 250 /* 251 * Generate a middle school question 252 * 253 * @return a middle school question 254 * */ 255 256 public static String makeMiddleSchoolProblem() { 257 Random r = new Random(); 258 String[] symbol = {"+", "-", "*", "/", "^2", "√"}; 259 int op_num = getRandomNumber(2, 5); 260 String[] ops = new String[op_num]; 261 for (int i = 0; i < op_num; i++) { 262 ops[i] = String.valueOf(getRandomNumber(1, 100)); 263 } 264 //特殊处理的数字个数 265 int magicnum = getRandomNumber(1, op_num); 266 boolean[] modify_or_not = new boolean[op_num]; 267 //添加平方或者根号项 268 for (int i = 0; i < magicnum; i++) { 269 int modify_index = getRandomNumber(0, op_num - 1); 270 if (modify_or_not[modify_index] == false) { 271 if (r.nextBoolean()) { 272 ops[modify_index] += symbol[4]; 273 modify_or_not[modify_index] = true; 274 } else { 275 ops[modify_index] = symbol[5] + ops[modify_index]; 276 modify_or_not[modify_index] = true; 277 } 278 } 279 } 280 if (op_num >= 3 && r.nextBoolean()) { //太巧妙了,真的牛 281 int l = getRandomNumber(1, op_num - 2); 282 int left = getRandomNumber(0, op_num - l - 1); 283 int right = left + l; 284 ops[left] = "(" + ops[left]; 285 ops[right] = ops[right] + ")"; 286 } 287 String result = ""; 288 for (int i = 0; i < op_num - 1; i++) { 289 result += ops[i] + symbol[getRandomNumber(0, 3)]; 290 } 291 result += ops[op_num - 1] + "="; 292 return result; 293 } 294 295 /* 296 * Generate a high school question 297 * 298 * @return a high school question 299 * */ 300 301 public static String makeHighSchoolProblem() { 302 Random r = new Random(); 303 String[] symbol = {"+", "-", "*", "/", "^2", "√", "sin", "cos", "tan"}; 304 int op_num = getRandomNumber(2, 5); 305 String[] ops = new String[op_num]; 306 for (int i = 0; i < op_num; i++) { 307 ops[i] = String.valueOf(getRandomNumber(1, 100)); 308 } 309 int magicnum = getRandomNumber(1, op_num); //特殊处理的数字个数 310 boolean[] modify_or_not = new boolean[op_num]; //添加平方,根号项,sin,cos,tan 311 for (int i = 0; i < magicnum; i++) { 312 int modify_index = getRandomNumber(0, op_num - 1); 313 if (i == 0) { 314 ops[modify_index] = symbol[getRandomNumber(6, 8)] + ops[modify_index]; 315 modify_or_not[modify_index] = true; 316 } else { 317 if (modify_or_not[modify_index] == false) { 318 int picknum = getRandomNumber(4, 8); 319 dealPickNum(picknum, ops, symbol, modify_index); 320 modify_or_not[modify_index] = true; 321 } 322 } 323 } 324 if (op_num >= 3 && r.nextBoolean()) { //太巧妙了,真的牛 325 int l = getRandomNumber(1, op_num - 2); 326 int left = getRandomNumber(0, op_num - l - 1); 327 int right = left + l; 328 ops[left] = "(" + ops[left]; 329 ops[right] = ops[right] + ")"; 330 } 331 String result = ""; 332 for (int i = 0; i < op_num - 1; i++) { 333 result += ops[i] + symbol[getRandomNumber(0, 3)]; 334 } 335 result += ops[op_num - 1] + "="; 336 return result; 337 } 338 339 /* 340 * deal with the picknum 341 * 342 * @param picknum the num you input 343 * @param ops the Operand array 344 * @param symbol the symbol array 345 * @param modify_index the index of the modified number 346 * */ 347 public static void dealPickNum(int picknum, String[] ops, String[] symbol, int modify_index) { 348 switch (picknum) { 349 case 4: 350 ops[modify_index] += symbol[4]; 351 break; 352 case 5: 353 ops[modify_index] = symbol[5] + ops[modify_index]; 354 break; 355 case 6: 356 ops[modify_index] = symbol[6] + ops[modify_index]; 357 break; 358 case 7: 359 ops[modify_index] = symbol[7] + ops[modify_index]; 360 break; 361 case 8: 362 ops[modify_index] = symbol[8] + ops[modify_index]; 363 break; 364 default: 365 break; 366 } 367 } 368 369 }
功能1:根据输入的题目个数和用户类别生成相对应的题目.方法:makeHighSchoolProblem()、makeMiddleSchoolProblem()、makePrimarySchoolProblem()。
功能2:将生成的题目存入txt文件中并保存。方法:primarySchool()、middleSchool()、highSchool()。
功能3:将新生成的题目和该用户之前的题目对比,重复则不使用该题目。方法:jungle()。
其他一些方法主要是调用刚才这些方法。
四、优缺点分析:
优点:
1.代码编写符合规范,各种关键字的命名方式也很正确,每个方法和类都写了注释,通过检查没有问题。
2.合理得将代码分成多个类,让每个类专注于特定的功能,整个代码清晰易读。
3.代码可复用性高,同一个方法可以被重复利用来实现想要的功能,而且使用了接口,减少代码冗余。
4.在程序运行界面交互逻辑清晰,提供了数字选项供用户选择自己的功能。
5.在保存文件时使用相对路径,代码有较高的可移植性和灵活性。
缺点:
1.在第一次登陆成功后不能直接切换难度,要先出一次该账号类型的题目才可以切换难度。
2.在切换难度后只能出一次题,要再切换难度必须先出一次该账户类型的题目才行。
3.切换难度时不能识别其他的错误输入。
五、总结:
总的来说,整个项目还是基本完成了该完成的功能,作为我们在这门课的第一个项目已经很好了。在项目的代码编写过程中,我们也学到了很多东西,认识到了自己的不足,希望在之后的项目中我们能做的更好。