完成情况

(1):随机生成四个随机数,程序可以自动的计算出所有等于24的表达式且无重复
(2):随机生成四个随机数,使用定时器,限时一分钟内接受用户输入的表达式,判断用户输入的表达式是否合法,比如是否是题目中要求的四位数,并且计算用户输入的表达式是否为24
(3):用户具有id属性,生命,得分属性。初始有三条命,答对一题的10分,答错一题不扣分但失去一次机会。当生命为0或者用户退出,程序结束。
(4):用户的相关信息,输出到文件中E:/in.txt

有关自动计算四个随机数所有等于24的表达式,这一功能的核心算法说明。

主要思路:采用循环嵌套,穷举法。

首先随机生成四位数,放入数组中,
第一重:
从四个数中按顺序选取两个(所有可能都遍历到),将这两位元素进行加减乘除(加减带上括号),每一种都得到一个算式,
第二重:
将得到的算式和上一次数组中剩下的两个数,组成新的数组(大小为三),在一次按顺序取出两个,(遍历所有可能),将这两位元素依然进行加减乘除(加减带上括号),每一种得到一个表达式,
第三重:
将得到的算式和上一轮数组中剩下的一个组成新的数组(大小为二),再一次按顺序取出两个(遍历所有可能),将这两个元素进行加减乘除(加减带上括号),每一种得到一个算式,此时为最内层循环也得到这一情况下的表达式,计算这一表达式是否等于24,如果是放入set集合中进一步去重。
三重九层循环,将每种可能性都遍历到

程序流程图

24点纸牌游戏-LMLPHP

计算表达式值,利用javascript引擎实现

public String calculation(String expression) {//计算表达式值的方法,传入要计算表达式
        ScriptEngineManager scriptEngineManager = new ScriptEngineManager();//实例化ScriptEngineManager类
        ScriptEngine scriptEngine = scriptEngineManager.getEngineByName("nashorn");//获得javascrit的nashorn引擎

        try {
            String result = String.valueOf(scriptEngine.eval(expression));
            return result;
        } catch (ScriptException e) {
            e.printStackTrace();
        }

源代码

计算四个随机数等于24的所有表达式,并列举出来的核心算法类


public class PointList {
	/*
	 * 主要算法类,计算四个随机数等于24的所有表达式,并列举出来
	 */
	Stack sta=new Stack();//实例化Static类,用来调用计算方法,用来计算遍历生成的表达式的值是否为24
	TreeSet set  = new TreeSet();//实例化treeset类,将等于24的表达式放入去重

 void List1(Object[] num){//遍历生成所有符合条件的表达式的方法
	for(int i=0;i<num.length;i++){//第一重挑选,从四个里面取两个,并且循环遍历所有可能取到的值
		for(int j=0;j<num.length;j++){
			if(i!=j){//利用数组下标比较,取出的两个不能是同一个
				for(int op=0;op<4;op++){//循环所有的运算符号,加减乘除
					Object str=operation(num[i],num[j],op);//调用operation方法,返回第一重表达式
					Object[] num1=num.clone();//避免改变原数组,对下一次循环造成影响,将原数组克隆
					num1[i]=str;//将返回的表达式覆盖取出的num[i],存入数组
					num1 = remove(num1,j);//将num[j]移除,这样得到了第一次选取后的新数组,包含三个值

					for(int i1=0;i1<num1.length;i1++){//第二重挑选,从三个里面取两个,并且循环遍历所有可能取到的值
						for(int j1=0;j1<num1.length;j1++){
							if(i1!=j1){//利用数组下标比较,取出的两个不能是同一个
								for(int op1=0;op1<4;op1++){//循环所有的运算符号,加减乘除
									Object str1=operation(num1[i1],num1[j1],op1);//调用operation方法,返回第二重表达式
									Object[] num2=num1.clone();//避免改变原数组,对下一次循环造成影响,将原数组克隆
									num2[i1]=str1;//将返回的表达式覆盖取出的num[i1],存入数组
									num2 = remove(num2,j1);//将num[j1]移除,这样得到了第二次选取后的新数组,包含2个值

									for(int i2=0;i2<num2.length;i2++){//第三重挑选,从两个里面取两个,并且循环遍历所有可能取到的值
										for(int j2=0;j2<num2.length;j2++){
											if(i2!=j2){//利用数组下标比较,取出的两个不能是同一个
												for(int op2=0;op2<4;op2++){//循环所有的运算符号,加减乘除
													Object str2=operation(num2[i2],num2[j2],op2);//循环所有的运算符号,加减乘除
													if(sta.calculation(str2.toString()).equals(24+"")){//调用Stack类的calculation方法,计算表达式值
														set.add(str2);//如果表达式值为24,存入treeset集合中
													}
												}
											}
										}
									}
								}
							}
						}
					}
				}
			}
		}
	}
}

	Object operation(Object x, Object y, int operation) {// 选取运算符的方法,利用switch,选取加减乘除

		switch (operation) {
		case 0:
			return "(" + x + "+" + y + ")";//如果为+,返回(x+y)
		case 1:
			return "(" + x + "-" + y + ")";
		case 2:
			return x + "*" + y;
		case 3:
			return x + "/" + y;
		}
		return -1;

	}
	private Object[] remove(Object[] num,int i){ //移除数组元素的方法,并将数组元素减一
			for(int j=i;j<num.length-1;j++){
				num[j]=num[j+1];
			}
			Object[] num3 = new Object[num.length-1];
			for(int k=0;k<num.length-1;k++){
				num3[k] = num[k];
			}
				return num3;

	}

}

利用javascript引擎计算表达式的功能类

import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;

public class Stack {//计算表达式的值的类,利用script引擎

    public String calculation(String expression) {//计算表达式值的方法,传入要计算表达式
        ScriptEngineManager scriptEngineManager = new ScriptEngineManager();//实例化ScriptEngineManager类
        ScriptEngine scriptEngine = scriptEngineManager.getEngineByName("nashorn");//获得javascrit的nashorn引擎

        try {
            String result = String.valueOf(scriptEngine.eval(expression));//
            return result;
        } catch (ScriptException e) {
            e.printStackTrace();
        }
		return "";
    }
}

将用户信息输出到文件夹中的功能类

public class outputTxt {//文件输出类,将用户的id和得分在、输出到文件中
	public void outtxt(User use) {//文件输出方法
		try {
			BufferedWriter outsco = new BufferedWriter(new FileWriter(new File("E:/Score.txt")));// 文件输出流
			outsco.write("id="+use.id+"    "+"score="+use.sco);//调用writer方法,将用户信息写入文件中
			outsco.flush();//刷新输出流
			outsco.close();//关闭流
		} catch (Exception e) {
			System.out.println("文件输出错误");
		}
	}
}

用户类

public class User {
	/*
	 * 用户类,包括用户的一些相关属性,第生命值,成绩
	 */
	static String id;
	static int  life=3;
	static int  sco=0;

}

主页面功能类

public class MainFunc {
	/*
	 * 主页面功能类,包括随机生成四张牌,引导用户的操作,
	 * 判断用户输入是否合理,判断用户输入的时间是否超时,
	 *
	 */

	public static void main(String args[]) {
		Scanner sc = new Scanner(System.in);//实例化Scanner对象,
		Object[] num = new Object[4];//实例化一个数组,用来存放随机生成的四张牌
		Random r = new Random();//实例化random类产生随机数
		Stack st = new Stack();//实例化Stack类
		User use = new User();//实例化用户对象
		System.out.println("请输入您的id");
		use.id=sc.nextLine();//接收用户输入的id
		System.out.println("欢迎试玩24点小游戏");
		System.out.println("用户初始生命值3,初始分数为0。");
		System.out.println("随机生成4个代表扑克牌牌面的数字,由用户输入表达式,使结果为24");
		System.out.println("规定时间内输入表达式,运算正确则加10分,超时或运算错误则进入下一题并减少生命值(不扣分)");
		System.out.println("您可以查看提示");
		System.out.println("您的答题时间为一分钟");
		System.out.println("输入1开始游戏,输入2退出");
		int cir = sc.nextInt();//接收用户输入,
		while (cir == 1) {//当cir为1时,程序运行
			if (use.life > 0) {//当用户的生命值大于0时,继续执行
				for (int n = 0; n < num.length; n++) {
					num[n] = r.nextInt(13) + 1;//随机生成四个数,代表四张纸牌
				}
				System.out.println("四张牌分别是" +Arrays.toString(num) + "   请输入您的表达式");

				// 时间
				long startTime = System.currentTimeMillis();// 记录开始时间
				Scanner sc1 = new Scanner(System.in);//接收用户输入的表达式
				String scrin = sc1.nextLine();
				long endTime = System.currentTimeMillis();// 记录结束时间
				float excTime = (float) (endTime - startTime) / 1000;//计算用户耗时
				if(judgein(scrin,num)){//调用判断函数,计算用户输入是否正确,如果输入正确,则继续
					String re = st.calculation(scrin);//调用Stack的计算方法,
					if (re.equals(24 + "") && excTime < 60) {//判断用时是否在规定时间内,以及表达式是否等于24,如果正确则继续
						use.sco = use.sco + 10;//如果正确,用户+10分
						System.out.println("恭喜您输入正确");
						System.out.println("您的生命为"+use.life+"您的得分为"+use.sco);
						System.out.println("输入1继续游戏,输入2退出");
						String cir1 = sc1.next();//接收用户的输入
						if(cir1.equals(2+"")){
							cir=2 ;//如果用户输入2,退出
						}
					}
				}
				 else {//如果用户未能在规定时间内,输入正确的表达式,则执行下面的代码
					use.life = use.life - 1;//用户的生命值减一
					System.out.println("您未在规定时间内输入正确的表达式");
					System.out.println("您的生命为"+use.life+"您的得分为"+use.sco);
					System.out.println("提示表达式如下");
					PointList l = new PointList();//提示用户,调用主要算法,求出以上四张牌所有等于24的表达式
					l.List1(num);
					for (Object se : l.set) {
						System.out.println(se);//循环遍历并输出
					}
					System.out.println("输入1继续游戏,输入2退出");
					String cir2 = sc1.next();//判断用户输入
					if(cir2.equals(2+"")) cir=2 ;//如果输入2则退出
				}
			} else {//当用户生命值为0时,退出

				System.out.println("您的次数已用光" + "您的得分为" + use.sco);
				System.out.println("欢迎您下次再来");
				cir = 2;
			}
		}
		new outputTxt().outtxt(use);//关闭之前,调用outputtxt类的outtxt方法,将用户信息输出到文件夹中
		System.out.println("您的成绩在E:/score.txt文件夹下");

	}

	 static boolean judgein(String scrin,Object num[]){//判断函数,判断用户输入的是否是包含题目给出的四个数字的表达式
		 int count=0;
		boolean rightin=scrin.contains(num[0].toString())&&scrin.contains( num[1].toString())&&scrin.contains( num[2].toString())&&scrin.contains(num[3].toString());//判断是否包含题目给出的四个数字
		String reg = "((?<=\\(|-|\\+|/|\\*)-?\\d+)|(^-[0-9]+)";//利用正则表达式
        Pattern p = Pattern.compile(reg);
        Matcher m = p.matcher(scrin);
        while (m.find()) {//判断表达式中是否是四个数字
            count++;
        }
        if(count==4&&rightin)//如果两个都成立,则用户输入符合要求
		return true;
        return false;//负责用户输入错误
	}
}

代码测试

错误:数组越界异常,如下图
24点纸牌游戏-LMLPHP
改正:经测试发现,越界异常,发生在自己写的一个方法中,用来移除数组元素的,改正如下:
24点纸牌游戏-LMLPHP
错误:在运行中发现了如下错误
24点纸牌游戏-LMLPHP
提示给出:类型转换错误,查看源代码后发现,在用户表达式输入判断处出的问题,判断用户输入是否为四个数字,并且就是题目中给出的数字,用的String.contains()方法出现的类型转换错误,改正如下
24点纸牌游戏-LMLPHP
错误: 在求所有四个数所有等于24表达式的核心算法中,发现了如下错误,在电脑自动列出的表达式中,有包含三个目标数字的算式
24点纸牌游戏-LMLPHP
改正:由于此处我的代码采用的是多重循环,此处的Debug比较繁琐,在进行一番查错后,发现了由于粗心,数组下标写错造成的错误。如下图
24点纸牌游戏-LMLPHP
核心算法测试:测对随机生成的四个数,可否正确的列出所有等于24的表达式,结果如下:
24点纸牌游戏-LMLPHP
时间测试,我设置的时间为一分钟,如下图,输入正确的表达式,但却因为超时,而未能成功,时间检测正常
24点纸牌游戏-LMLPHP
退出检测:出现错误。如下图,明明已经选择了退出却依然继续进行游戏,测试后发现,是控制程序1继续运行的cir变量,主要是局部变量和全局变量的错误
24点纸牌游戏-LMLPHP
在进行了一系列的排错后,程序基本可以正常运行,部分运行截图如下
24点纸牌游戏-LMLPHP
24点纸牌游戏-LMLPHP
最后:文件输出是否正确,可以发现,用户的id和成绩都输出在了文件score.txt中
24点纸牌游戏-LMLPHP
最后,因为本人能力有限,文章中有错误的地方还望大家可以原谅,可以提出来好,如果关于题目中的算法有更好的思路,欢迎大家交流

10-05 19:38