24点游戏(穷举算法+JavaFX界面/回溯算法)--注释详细

题目介绍: 

 

 

版本一:

 

package data_structure_curriculum_design.experiment3_24point_poker_game.version1;

import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Random;
import java.util.Stack;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class Check extends Application {
    static final int cardNum = 52;//将扑克牌数量设为一个变量,便于扩展
    static Image[] images = new Image[cardNum];
    static ArrayList<ImageView> imageViews = new ArrayList<>();
    static ArrayList<ImageView> arrImgView = new ArrayList<>();//存放随机产生的4个ImageView;
    static int[] cardnums = new int[4];//存放随机产生的扑克牌上的数字;
    // 使用数组排序后与用户输入的数字存储在数组中排序后逐个比较来判断用户的输入是否与显示的扑克牌的数字一致
    
    static {//初始化扑克牌图片
        for (int i = 0; i < cardNum; i++) {//图片命名有规律,使用循环简化代码;image路径问题:网络图像文件前面加“http:”,而本地文件则要加“file:”;
            images[i] = new Image("file:src/data_structure_curriculum_design/experiment3_24point_poker_game/images/" + (i + 1) + ".png");
            imageViews.add(new ImageView(images[i]));
        }
        generatingCards();//随机产生并且显示4张扑克牌图片
    }

    @Override
    public void start(Stage primaryStage) {

        Button refreshBt = new Button("Refresh");
        HBox tPane = new HBox(refreshBt);
        tPane.setPadding(new Insets(10));
        tPane.setAlignment(Pos.BASELINE_RIGHT);

        HBox cardPane = new HBox(10);
        cardPane.setPadding(new Insets(0, 60, 10, 60));
        for (ImageView e : arrImgView) cardPane.getChildren().add(e);

        Label expressionLabel = new Label("Enter an expression:");
        TextField expressionTf = new TextField();
        Button verifyBt = new Button("Verify");
        HBox bPane = new HBox(10);
        bPane.setPadding(new Insets(5));
        bPane.getChildren().addAll(expressionLabel, expressionTf, verifyBt);

        BorderPane pane = new BorderPane(cardPane);
        pane.setTop(tPane);
        pane.setBottom(bPane);

        refreshBt.setOnAction(e -> {//更新card;
            cardPane.getChildren().clear();//先清除原来面板内的结点,再进行新结点的添加
            arrImgView.clear();
            generatingCards();
            for (ImageView i : arrImgView) cardPane.getChildren().add(i);
        });

        verifyBt.setOnAction(e -> {//检查TextField的输入
            String expression = expressionTf.getText();
            if (!Pattern.matches("[+*/()0-9-]*", expression)) {//对输入的数学表达式进行检查,若输入中含有+-*/()0-9以外的字符,直接提示错误
                enterErrorAlert();
                return;
            }
            Pattern pattern = Pattern.compile("[+*/()-]");
            String[] numStr = pattern.split(expression);
            int[] nums = new int[4];
            int numsIndex = 0;
            try {//输入的表达式含有的数值的数目大于5时,将抛出的异常转换为输入错误警告;
                for (String i : numStr)
                    if (i.length() != 0)
                        nums[numsIndex++] = Integer.parseInt(i);
            } catch (Exception exc) {
                enterErrorAlert();
                return;
            }
            Arrays.sort(nums);
            for (int i = 0; i < nums.length; i++) {
                if (nums[i] != cardnums[i]) {
                    enterMismatchingWarning();
                    return;
                }
            }

            double result = evaluate(expression);
            if (result == 24) {
                enterCorrect();
            } else {
                enterErrorAlert();
            }
        });

        Scene scene = new Scene(pane, 430, 200);
        primaryStage.setScene(scene);
        primaryStage.setTitle("24-Point Card Game");
        primaryStage.setResizable(false);
        primaryStage.show();
    }

    /**
     * 随机产生4张扑克牌
     * 使用ArrayList是为了使用它的contains方法来避免产生重复的扑克牌
     */
    public static void generatingCards() {
        Random random = new Random();
        while (arrImgView.size() != 4) {
            ImageView imageView = imageViews.get(random.nextInt(cardNum));
            if (!arrImgView.contains(imageView)) {//避免参数的图片重复
                cardnums[arrImgView.size()] = imageViews.indexOf(imageView) % 13 + 1;/*巧妙利用arrImgView.size()作为数组下标,
                                                                     使用取余运算,将imageViews内存放扑克牌的下标转换为扑克排上显示的数字*/
                arrImgView.add(imageView);
            }
        }
        Arrays.sort(cardnums);
    }

    public static void enterErrorAlert() {//输入错误警告弹窗
        Alert alert = new Alert(Alert.AlertType.ERROR);
        alert.setTitle("Error Dialog");
        alert.setHeaderText("Entered error");
        alert.setContentText("You have entered the wrong expression");
        alert.showAndWait();
    }

    public static void enterMismatchingWarning() {//输入不匹配警告弹窗
        Alert alert = new Alert(Alert.AlertType.WARNING);
        alert.setTitle("Warning Dialog");
        alert.setHeaderText("Entered Mismatching");
        alert.setContentText("The numbers in the expression don't match the numbers in set");
        alert.showAndWait();
    }

    public static void enterCorrect() {//输入正确弹窗
        Alert alert = new Alert(Alert.AlertType.INFORMATION);
        alert.setTitle("Correct Dialog");
        alert.setHeaderText("Entered Correct");
        alert.setContentText("You have entered the correct expression");
        alert.showAndWait();
    }


    public static int getOperatorIndex(String operator) {//获得操作符的下标
        String[] operators = {"+", "-", "*", "/", "(", ")", "#"};
        for (int i = 0; i < operators.length; i++) {
            if (operator.equals(operators[i]))
                return i;
        }
        return -1;
    }

    public static boolean isOperator(String c) {//判断是否为操作符
        if (getOperatorIndex(c) != -1) return true;
        else return false;
    }

    public static String[] splitExpression(String expression) {//对字符串表达式按表达式内的次序分割为相应数目的操作数和操作符存放在String类型的数组中
        ArrayList<String> units = new ArrayList<>();
        String unit;
        units.add("#");//#作为起始和结束标志
        Pattern pattern = Pattern.compile("\\d+(\\.\\d+)*");//这里兼容了double类型数值,可以替换为Pattern.compile("\\d+");
        Matcher matcher = pattern.matcher(expression);//Matcher类的构造方法也是私有的,不能随意创建,只能通过Pattern.matcher(CharSequence input)方法得到该类的实例
        int i = 0;/*Matcher类提供了对正则表达式的分组支持,以及对正则表达式的多次匹配支持. */
        while (i < expression.length()) {
            if (isOperator(String.valueOf(expression.charAt(i)))) {//下标处是操作符的情况
                units.add(String.valueOf(expression.charAt(i)));
                i++;
            } else if (matcher.find()) {//find()对字符串进行匹配,匹配到的字符串可以在任何位置
                unit = matcher.group();//group()返回匹配到的子字符串
                units.add(unit);
                i += unit.length();
            }
        }
        units.add("#");
        String[] unitsArray = new String[units.size()];
        units.toArray(unitsArray);
        return unitsArray;
    }

    public static String priority(String operator1, String operator2) {/*用于比较操作符优先级该二维数组数据存放
                                                                 与getOperatorIndex()方法内存放操作符的数组具有关系*/
        String[][] priorityMatrix = {
                {">", ">", "<", "<", "<", ">", ">"},
                {">", ">", "<", "<", "<", ">", ">"},
                {">", ">", ">", ">", "<", ">", ">"},
                {">", ">", ">", ">", "<", ">", ">"},
                {"<", "<", "<", "<", "<", "=", ""},
                {">", ">", ">", ">", "", ">", ">"},
                {"<", "<", "<", "<", "<", "", "="}
        };
        return priorityMatrix[getOperatorIndex(operator1)][getOperatorIndex(operator2)];
    }

    public static String calculate(String leftOperand, String operator, String rightOperand) {//根据传入的两个操作数以及操作符进行数学计算,返回字符串类型的计算结果
        double left = Double.parseDouble(leftOperand);  //这里兼容了double类型数据,可以写为Integer.parseInt(***);
        double right = Double.parseDouble(rightOperand);
        double result = 0;
        switch (operator) {
            case "+":
                result = left + right;
                break;
            case "-":
                result = left - right;
                break;
            case "*":
                result = left * right;
                break;
            case "/":
                result = left / right;
        }
        return String.valueOf(result);//将计算结果以字符串形式返回
    }

    public static double evaluate(String expression) {//这里的返回值"兼容"double类型,可以设为int类型
        String result = null;
        Stack<String> operatorStack = new Stack<>();
        Stack<String> operandStack = new Stack<>();
        String[] units = splitExpression(expression);
        int unitIndex = 1;
        operatorStack.push(units[0]);
        String unit = units[unitIndex];/*当读取到结束标志"#"或操作符栈剩余开始标志"#"时,结束循环输出结果*/
        while (!unit.equalsIgnoreCase("#") || !operatorStack.peek().equalsIgnoreCase("#")) {
            if (isOperator(unit)) {//读取到的为操作符
                String priority = priority(operatorStack.peek(), unit);
                switch (priority) {
                    case "<"://若读取到的操作符优先级大于栈顶操作符,则直接将读取的操作符压入操作符栈
                        operatorStack.push(unit);
                        unitIndex++;//需要注意指针移动,下同
                        break;
                    case "="://若读取到的操作符优先级等于栈顶操作符说明读取到了")",将操作符栈弹栈,"("弹出
                        operatorStack.pop();
                        unitIndex++;
                        break;
                    case ">"://若读取到的操作符优先级小于栈顶操作符,则操作数栈弹出两个操作数,操作符栈弹出一个操作符进行运算后将运算结果压栈
                        String rightOperand = operandStack.pop();
                        String leftOperand = operandStack.pop();
                        String operator = operatorStack.pop();
                        result = calculate(leftOperand, operator, rightOperand);
                        operandStack.push(result);
                        break;
                }
            } else {//读取到操作数,直接将读取到的操作数压入操作数栈
                operandStack.push(unit);
                unitIndex++;
            }
            unit = units[unitIndex];
        }
        return Double.parseDouble(result);
    }

    public static void main(String[] args) {
        Application.launch(args);
    }
}

 版本二(1):

package data_structure_curriculum_design.experiment3_24point_poker_game.version2;


import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Random;
import java.util.Stack;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class Presentation extends Application {
    static final int cardNum = 52;//将扑克牌数量设为一个变量,便于扩展
    static Image[] images = new Image[cardNum];
    static ArrayList<ImageView> imageViews = new ArrayList<>();
    static ArrayList<ImageView> arrImgView = new ArrayList<>();//存放随机产生的4个ImageView;
    static int[] cardnums = new int[4];//存放随机产生的扑克牌上的数字;
    // 使用数组排序后与用户输入的数字存储在数组中排序后逐个比较来判断用户的输入是否与显示的扑克牌的数字一致
    static char[] operators = {'+', '-', '*', '/'};//存放所有的操作符
    static ArrayList<String> arrSolution = new ArrayList<>();//存放当前扑克牌数字可以得到24点的所有解;
    static int indexOfArrSolution = 0;//当前扑克牌解的下标

    static {//初始化扑克牌图片
        for (int i = 0; i < cardNum; i++) {
            images[i] = new Image("file:src/data_structure_curriculum_design/experiment3_24point_poker_game/images/" + (i + 1) + ".png");
            imageViews.add(new ImageView(images[i]));
        }
        generatingCards();
    }

    @Override
    public void start(Stage primaryStage) {

        Button findBt = new Button("Find a solution");
        TextField solutionTf = new TextField();
        solutionTf.setEditable(false);
        Button refreshBt = new Button("Refresh");
        HBox tPane = new HBox(10);
        tPane.setPadding(new Insets(10));
        tPane.getChildren().addAll(findBt, solutionTf, refreshBt);

        HBox cardPane = new HBox(10);
        cardPane.setPadding(new Insets(0, 60, 10, 60));
        for (ImageView e : arrImgView) cardPane.getChildren().add(e);

        Label expressionLabel = new Label("Enter an expression:");
        TextField expressionTf = new TextField();
        Button verifyBt = new Button("Verify");
        HBox bPane = new HBox(10);
        bPane.setPadding(new Insets(5));
        bPane.getChildren().addAll(expressionLabel, expressionTf, verifyBt);

        BorderPane pane = new BorderPane(cardPane);
        pane.setTop(tPane);
        pane.setBottom(bPane);

        findBt.setOnAction(e -> {//穷举解决方案
            if (arrSolution.size() == 0) {
                solutionTf.setText("No solution");
                return;
            }
            if (indexOfArrSolution == arrSolution.size()) indexOfArrSolution = 0;//显示到最后一条解后,回显第一条
            solutionTf.setText(arrSolution.get(indexOfArrSolution));
            indexOfArrSolution++;
        });

        refreshBt.setOnAction(e -> {//更新card;每次更新扑克牌后都要把所有可能存在的解存放在arrSolution里面;
            cardPane.getChildren().clear();
            arrImgView.clear();
            generatingCards();
            for (ImageView i : arrImgView) cardPane.getChildren().add(i);

            indexOfArrSolution = 0;
            arrSolution.clear();//更新扑克牌后将之前扑克牌的解清空,并且下标指向arrSolution的首元素
            getSolutions();//放在方法最后以不影响之前的FX界面展示
        });

        verifyBt.setOnAction(e -> {//检查TextField的输入
            String expression = expressionTf.getText();
            if (!Pattern.matches("[+*/()0-9-]*", expression)) {//对输入的数学表达式进行检查,若输入中含有+-*/()0-9以外的字符,直接提示错误
                enterErrorAlert();
                return;
            }
            Pattern pattern = Pattern.compile("[+*/()-]");
            String[] numStr = pattern.split(expression);
            int[] nums = new int[4];
            int numsIndex = 0;
            try {//输入的表达式含有的数值的数目大于5时,将抛出的异常转换为输入错误警告;
                for (String i : numStr)
                    if (i.length() != 0)
                        nums[numsIndex++] = Integer.parseInt(i);
            } catch (Exception exc) {
                enterErrorAlert();
                return;
            }
            Arrays.sort(cardnums);
            Arrays.sort(nums);
            for (int i = 0; i < nums.length; i++) {
                if (nums[i] != cardnums[i]) {
                    enterMismatchingWarning();
                    return;
                }
            }

            double result = evaluate(expression);
            if (result == 24) {
                enterCorrect();
            } else {
                enterErrorAlert();
            }
        });

        Scene scene = new Scene(pane, 430, 200);
        primaryStage.setScene(scene);
        primaryStage.setTitle("24-Point Card Game");
        primaryStage.setResizable(false);
        primaryStage.show();
    }

    /**
     * 随机产生4张扑克牌
     * 使用ArrayList是为了使用它的contains方法来避免产生重复的扑克牌
     */
    public static void generatingCards() {
        Random random = new Random();
        while (arrImgView.size() != 4) {
            ImageView imageView = imageViews.get(random.nextInt(cardNum));
            if (!arrImgView.contains(imageView)) {
                cardnums[arrImgView.size()] = imageViews.indexOf(imageView) % 13 + 1;//巧妙利用arrImgView.size()作为数组下标
                arrImgView.add(imageView);
            }
        }
    }

    public static void enterErrorAlert() {//输入错误警告弹窗
        Alert alert = new Alert(Alert.AlertType.ERROR);
        alert.setTitle("Error Dialog");
        alert.setHeaderText("Entered error");
        alert.setContentText("You have entered the wrong expression");
        alert.showAndWait();
    }

    public static void enterMismatchingWarning() {//输入不匹配警告弹窗
        Alert alert = new Alert(Alert.AlertType.WARNING);
        alert.setTitle("Warning Dialog");
        alert.setHeaderText("Entered Mismatching");
        alert.setContentText("The numbers in the expression don't match the numbers in set");
        alert.showAndWait();
    }

    public static void enterCorrect() {//输入正确弹窗
        Alert alert = new Alert(Alert.AlertType.INFORMATION);
        alert.setTitle("Correct Dialog");
        alert.setHeaderText("Entered Correct");
        alert.setContentText("You have entered the correct expression");
        alert.showAndWait();
    }

    public static void getSolutions() {//获得所有解
        String[] expressions = new String[5];//放在方法最后以不影响之前的FX界面展示
        for (int i = 0; i < cardnums.length; i++) {
            for (int j = 0; j < cardnums.length; j++) {
                if (j == i) continue;
                for (int k = 0; k < cardnums.length; k++) {
                    if (k == j || k == i) continue;
                    for (int l = 0; l < cardnums.length; l++) {
                        if (l == k || l == j || l == i) continue;
                        for (int m = 0; m < operators.length; m++) {
                            for (int n = 0; n < operators.length; n++) {
                                for (int o = 0; o < operators.length; o++) {
                                    expressions[0] = "((" + cardnums[i] + "" + operators[m] + "" + cardnums[j] + ")" + operators[n] + "" + cardnums[k] + ")" + operators[o] + "" + cardnums[l];
                                    expressions[1] = "(" + cardnums[i] + "" + operators[m] + "(" + cardnums[j] + "" + operators[n] + "" + cardnums[k] + "))" + operators[o] + "" + cardnums[l];
                                    expressions[2] = "(" + cardnums[i] + "" + operators[m] + "" + cardnums[j] + ")" + operators[n] + "(" + cardnums[k] + "" + operators[o] + "" + cardnums[l] + ")";
                                    expressions[3] = cardnums[i] + "" + operators[m] + "((" + cardnums[j] + "" + operators[n] + "" + cardnums[k] + ")" + operators[o] + "" + cardnums[l] + ")";
                                    expressions[4] = cardnums[i] + "" + operators[m] + "(" + cardnums[j] + "" + operators[n] + "(" + cardnums[k] + "" + operators[o] + "" + cardnums[l] + "))";
                                    for (String expression : expressions)
                                        if (evaluate(expression) == 24)
                                            arrSolution.add(expression);
                                }
                            }
                        }
                    }
                }
            }
        }
    }

    public static int getOperatorIndex(String operator) {
        String[] operators = {"+", "-", "*", "/", "(", ")", "#"};
        for (int i = 0; i < operators.length; i++) {
            if (operator.equals(operators[i]))
                return i;
        }
        return -1;
    }

    public static boolean isOperator(String c) {
        if (getOperatorIndex(c) != -1) return true;
        else return false;
    }

    public static String[] splitExpression(String expression) {
        ArrayList<String> units = new ArrayList<>();
        String unit;
        units.add("#");
        Pattern pattern = Pattern.compile("\\d+(\\.\\d+)*");//这里兼容了double类型数值,可以替换为Pattern.compile("\\d+");
        Matcher matcher = pattern.matcher(expression);
        int i = 0;
        while (i < expression.length()) {
            if (isOperator(String.valueOf(expression.charAt(i)))) {
                units.add(String.valueOf(expression.charAt(i)));
                i++;
            } else if (matcher.find()) {
                unit = matcher.group();
                units.add(unit);
                i += unit.length();
            }
        }
        units.add("#");
        String[] unitsArray = new String[units.size()];
        units.toArray(unitsArray);
        return unitsArray;
    }

    public static String priority(String operator1, String operator2) {
        String[][] priorityMatrix = {
                {">", ">", "<", "<", "<", ">", ">"},
                {">", ">", "<", "<", "<", ">", ">"},
                {">", ">", ">", ">", "<", ">", ">"},
                {">", ">", ">", ">", "<", ">", ">"},
                {"<", "<", "<", "<", "<", "=", ""},
                {">", ">", ">", ">", "", ">", ">"},
                {"<", "<", "<", "<", "<", "", "="}
        };
        return priorityMatrix[getOperatorIndex(operator1)][getOperatorIndex(operator2)];
    }

    public static String calculate(String leftOperand, String operator, String rightOperand) {
        double left = Double.parseDouble(leftOperand);  //这里兼容了double类型数据,可以写为Integer.parseInt(***);
        double right = Double.parseDouble(rightOperand);
        double result = 0;
        switch (operator) {
            case "+":
                result = left + right;
                break;
            case "-":
                result = left - right;
                break;
            case "*":
                result = left * right;
                break;
            case "/":
                result = left / right;
        }
        return String.valueOf(result);
    }

    public static double evaluate(String expression) {//这里的返回值"兼容"double类型,可以设为int类型
        String result = null;
        Stack<String> operatorStack = new Stack<>();
        Stack<String> operandStack = new Stack<>();
        String[] units = splitExpression(expression);
        int unitIndex = 1;
        operatorStack.push(units[0]);
        String unit = units[unitIndex];
        while (!unit.equalsIgnoreCase("#") || !operatorStack.peek().equalsIgnoreCase("#")) {
            if (isOperator(unit)) {
                String priority = priority(operatorStack.peek(), unit);
                switch (priority) {
                    case "<":
                        operatorStack.push(unit);
                        unitIndex++;
                        break;
                    case "=":
                        operatorStack.pop();
                        unitIndex++;
                        break;
                    case ">":
                        String rightOperand = operandStack.pop();
                        String leftOperand = operandStack.pop();
                        String operator = operatorStack.pop();
                        result = calculate(leftOperand, operator, rightOperand);
                        operandStack.push(result);
                        break;
                }
            } else {
                operandStack.push(unit);
                unitIndex++;
            }
            unit = units[unitIndex];
        }
        return Double.parseDouble(result);
    }

    public static void main(String[] args) {
        Application.launch(args);
    }
}

版本二(2):

package data_structure_curriculum_design.experiment3_24point_poker_game.version2;

import javafx.application.Application;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Alert;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.stage.Stage;

import java.util.ArrayList;
import java.util.Stack;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class EnterPresentation extends Application {

    static int[] prevCardnums = new int[4];//存放用户上一次输入的数字;
    static int[] cardnums = new int[4];//存放用户输入的数字;
    static boolean isEnterChange = false;//标志用户点击find a solution 按钮前是否修改了numTf里的数字;
    //若没有修改且存在解,则显示下一个解;若修改则更新arrSolution,若存在解,这显示第一个解,否则显示No solution
    static char[] operators = {'+', '-', '*', '/'};
    static ArrayList<String> arrSolution = new ArrayList<>();//存放当前输入的数字可以得到24点的所有解;
    static int indexOfArrSolution = 0;//当前输入数字解的下标

    static {
        prevCardnums[0] = prevCardnums[1] = prevCardnums[2] = prevCardnums[3] = 0;
    }

    @Override
    public void start(Stage primaryStage) throws Exception {
        Label reminder = new Label(" Input four numbers between 1 to 13");
        TextField numTf0 = new TextField();
        TextField numTf1 = new TextField();
        TextField numTf2 = new TextField();
        TextField numTf3 = new TextField();
        numTf0.setPrefSize(100, 100);
        numTf1.setPrefSize(100, 100);
        numTf2.setPrefSize(100, 100);
        numTf3.setPrefSize(100, 100);
        numTf0.setStyle("-fx-font-weight: bold;-fx-font-size: 30");
        numTf1.setStyle("-fx-font-weight: bold;-fx-font-size: 30");
        numTf2.setStyle("-fx-font-weight: bold;-fx-font-size: 30");
        numTf3.setStyle("-fx-font-weight: bold;-fx-font-size: 30");
        HBox enterPane = new HBox(10);
        enterPane.setPadding(new Insets(20));
        enterPane.getChildren().addAll(numTf0, numTf1, numTf2, numTf3);
        HBox findSolutionPane = new HBox(10);
        findSolutionPane.setAlignment(Pos.CENTER);
        findSolutionPane.setPadding(new Insets(20));
        TextField solutionTf = new TextField();
        solutionTf.setEditable(false);
        Button findSolutionBt = new Button("Find a solution");
        findSolutionPane.getChildren().addAll(solutionTf, findSolutionBt);
        BorderPane pane = new BorderPane(enterPane);
        pane.setTop(reminder);
        pane.setBottom(findSolutionPane);


        findSolutionBt.setOnAction(e -> {
            String[] nums = new String[4];
            nums[0] = numTf0.getText();
            nums[1] = numTf1.getText();
            nums[2] = numTf2.getText();
            nums[3] = numTf3.getText();

            for (String num : nums)
                if (!Pattern.matches("[0-9]{1,2}", num)) {
                    enterErrorAlert();
                    return;
                }

            isEnterChange = false;
            for (int i = 0; i < nums.length; i++) {
                cardnums[i] = Integer.parseInt(nums[i]);
                if (cardnums[i] < 1 || cardnums[i] > 13) {
                    enterErrorAlert();
                    return;
                }
                if (cardnums[i] != prevCardnums[i]) {
                    isEnterChange = true;//表示用户这次点击前修改了numTf里的内容;
                    prevCardnums[i] = cardnums[i];
                }
            }

            if (isEnterChange) {/*若用户修改了四个文本输入框的内容(或修改了任一个的内容)后又点击了findSolutionBt,
                                            则清空存放上一次数值的解的arrSolution重新获取当前输入数值组的解*/
                arrSolution.clear();
                indexOfArrSolution = 0;
                getSolutions();
            }

            if (arrSolution.size() == 0) {//若该数值组无解,则提示用户无解
                solutionTf.setText("No solution");
                return;
            } else {
                if (indexOfArrSolution == arrSolution.size()) indexOfArrSolution = 0;//显示到最后一条解后,回显第一条
                solutionTf.setText(arrSolution.get(indexOfArrSolution));
                indexOfArrSolution++;
            }


        });
        Scene scene = new Scene(pane, 400, 200);
        primaryStage.setScene(scene);
        primaryStage.setTitle("24-Point Card Game");
        primaryStage.setResizable(false);
        primaryStage.show();
    }

    public static void enterErrorAlert() {//输入错误警告弹窗
        Alert alert = new Alert(Alert.AlertType.ERROR);
        alert.setTitle("Error Dialog");
        alert.setHeaderText("Entered error");
        alert.setContentText("You should input four numbers between 1 to 13");
        alert.showAndWait();
    }

    public static void getSolutions() {//获得所有解
        String[] expressions = new String[5];//放在方法最后以不影响之前的FX界面展示
        for (int i = 0; i < cardnums.length; i++) {
            for (int j = 0; j < cardnums.length; j++) {
                if (j == i) continue;
                for (int k = 0; k < cardnums.length; k++) {
                    if (k == j || k == i) continue;
                    for (int l = 0; l < cardnums.length; l++) {
                        if (l == k || l == j || l == i) continue;
                        for (int m = 0; m < operators.length; m++) {
                            for (int n = 0; n < operators.length; n++) {
                                for (int o = 0; o < operators.length; o++) {
                                    expressions[0] = "((" + cardnums[i] + "" + operators[m] + "" + cardnums[j] + ")" + operators[n] + "" + cardnums[k] + ")" + operators[o] + "" + cardnums[l];
                                    expressions[1] = "(" + cardnums[i] + "" + operators[m] + "(" + cardnums[j] + "" + operators[n] + "" + cardnums[k] + "))" + operators[o] + "" + cardnums[l];
                                    expressions[2] = "(" + cardnums[i] + "" + operators[m] + "" + cardnums[j] + ")" + operators[n] + "(" + cardnums[k] + "" + operators[o] + "" + cardnums[l] + ")";
                                    expressions[3] = cardnums[i] + "" + operators[m] + "((" + cardnums[j] + "" + operators[n] + "" + cardnums[k] + ")" + operators[o] + "" + cardnums[l] + ")";
                                    expressions[4] = cardnums[i] + "" + operators[m] + "(" + cardnums[j] + "" + operators[n] + "(" + cardnums[k] + "" + operators[o] + "" + cardnums[l] + "))";
                                    for (String expression : expressions)
                                        if (evaluate(expression) == 24)
                                            arrSolution.add(expression);
                                }
                            }
                        }
                    }
                }
            }
        }
    }

    public static int getOperatorIndex(String operator) {
        String[] operators = {"+", "-", "*", "/", "(", ")", "#"};
        for (int i = 0; i < operators.length; i++) {
            if (operator.equals(operators[i]))
                return i;
        }
        return -1;
    }

    public static boolean isOperator(String c) {
        if (getOperatorIndex(c) != -1) return true;
        else return false;
    }

    public static String[] splitExpression(String expression) {
        ArrayList<String> units = new ArrayList<>();
        String unit;
        units.add("#");
        Pattern pattern = Pattern.compile("\\d+(\\.\\d+)*");//这里兼容了double类型数值,可以替换为Pattern.compile("\\d+");
        Matcher matcher = pattern.matcher(expression);
        int i = 0;
        while (i < expression.length()) {
            if (isOperator(String.valueOf(expression.charAt(i)))) {
                units.add(String.valueOf(expression.charAt(i)));
                i++;
            } else if (matcher.find()) {
                unit = matcher.group();
                units.add(unit);
                i += unit.length();
            }
        }
        units.add("#");
        String[] unitsArray = new String[units.size()];
        units.toArray(unitsArray);
        return unitsArray;
    }

    public static String priority(String operator1, String operator2) {
        String[][] priorityMatrix = {
                {">", ">", "<", "<", "<", ">", ">"},
                {">", ">", "<", "<", "<", ">", ">"},
                {">", ">", ">", ">", "<", ">", ">"},
                {">", ">", ">", ">", "<", ">", ">"},
                {"<", "<", "<", "<", "<", "=", ""},
                {">", ">", ">", ">", "", ">", ">"},
                {"<", "<", "<", "<", "<", "", "="}
        };
        return priorityMatrix[getOperatorIndex(operator1)][getOperatorIndex(operator2)];
    }

    public static String calculate(String leftOperand, String operator, String rightOperand) {
        double left = Double.parseDouble(leftOperand);  //这里兼容了double类型数据,可以写为Integer.parseInt(***);
        double right = Double.parseDouble(rightOperand);
        double result = 0;
        switch (operator) {
            case "+":
                result = left + right;
                break;
            case "-":
                result = left - right;
                break;
            case "*":
                result = left * right;
                break;
            case "/":
                result = left / right;
        }
        return String.valueOf(result);
    }

    public static double evaluate(String expression) {//这里的返回值"兼容"double类型,可以设为int类型
        String result = null;
        Stack<String> operatorStack = new Stack<>();
        Stack<String> operandStack = new Stack<>();
        String[] units = splitExpression(expression);
        int unitIndex = 1;
        operatorStack.push(units[0]);
        String unit = units[unitIndex];
        while (!unit.equalsIgnoreCase("#") || !operatorStack.peek().equalsIgnoreCase("#")) {
            if (isOperator(unit)) {
                String priority = priority(operatorStack.peek(), unit);
                switch (priority) {
                    case "<":
                        operatorStack.push(unit);
                        unitIndex++;
                        break;
                    case "=":
                        operatorStack.pop();
                        unitIndex++;
                        break;
                    case ">":
                        String rightOperand = operandStack.pop();
                        String leftOperand = operandStack.pop();
                        String operator = operatorStack.pop();
                        result = calculate(leftOperand, operator, rightOperand);
                        operandStack.push(result);
                        break;
                }
            } else {
                operandStack.push(unit);
                unitIndex++;
            }
            unit = units[unitIndex];
        }
        return Double.parseDouble(result);
    }

    public static void main(String[] args) {
        Application.launch(args);
    }
}

版本三:

package data_structure_curriculum_design.experiment3_24point_poker_game.version3;

import java.util.ArrayList;
import java.util.List;

public class Backtracking {
    static final int TARGET = 24;
    static final double EPSILON = 1e-14;
    static final int ADD = 0, MULTIPLY = 1, SUBTRACT = 2, DIVIDE = 3;
    static int[] nums = new int[4];//存放获取的4张扑克牌的数值
    static int numberOfCombinations = 0;
    static int numberOfSolutions = 0;

    public static void main(String[] args) {
        for (int i = 1; i <= 13; i++) {//4张牌属于4种不同的花色
            for (int j = 1; j <= 13; j++) {
                for (int k = 1; k <= 13; k++) {
                    for (int l = 1; l <= 13; l++) {
                        nums[0] = i;
                        nums[1] = j;
                        nums[2] = k;
                        nums[3] = l;
                        numberOfCombinations++;
                        if (judgePoint24(nums)) numberOfSolutions++;
                    }
                }
            }
        }

        for (int i = 1; i <= 13; i++) {//4张牌属于3种不同的花色   3种花色1、1、2分
            for (int j = 1; j <= 13; j++) {
                for (int k = 1; k <= 13; k++) {
                    for (int l = 1; l <= 13; l++) {
                        if (k >= l) continue;
                        nums[0] = i;
                        nums[1] = j;
                        nums[2] = k;
                        nums[3] = l;
                        numberOfCombinations += 12;//4种花色中选择3种花色,共有4种选法
                        if (judgePoint24(nums)) numberOfSolutions += 12;
                    }
                }
            }
        }

        for (int i = 1; i <= 13; i++) {//4张牌属于2种不同的花色   两种花色2、2分
            for (int j = 1; j <= 13; j++) {
                if (i >= j) continue;
                for (int k = 1; k <= 13; k++) {
                    for (int l = 1; l <= 13; l++) {
                        if (k >= l) continue;
                        nums[0] = i;
                        nums[1] = j;
                        nums[2] = k;
                        nums[3] = l;
                        numberOfCombinations += 6;//4种花色种选择2种花色,共有6种选法
                        if (judgePoint24(nums)) numberOfSolutions += 6;
                    }
                }
            }
        }
        for (int i = 1; i <= 13; i++) {//4张牌属于2种不同的花色   两种花色1、3分
            for (int j = 1; j <= 13; j++) {
                for (int k = 1; k <= 13; k++) {
                    if (j >= k) continue;
                    for (int l = 1; l <= 13; l++) {
                        if (k >= l) continue;
                        nums[0] = i;
                        nums[1] = j;
                        nums[2] = k;
                        nums[3] = l;
                        numberOfCombinations += 12;//4种花色种选择2种花色,共有6种选法
                        if (judgePoint24(nums)) numberOfSolutions += 12;
                    }
                }
            }
        }

        for (int i = 1; i <= 13; i++) {//4张牌属于1种花色
            for (int j = 1; j <= 13; j++) {
                if (i >= j) continue;
                for (int k = 1; k <= 13; k++) {
                    if (j >= k) continue;
                    for (int l = 1; l <= 13; l++) {
                        if (k >= l) continue;
                        nums[0] = i;
                        nums[1] = j;
                        nums[2] = k;
                        nums[3] = l;
                        numberOfCombinations += 4;//4种花色种选择1种花色,共有4种选法
                        if (judgePoint24(nums)) numberOfSolutions += 4;
                    }
                }
            }
        }

        System.out.println("Total number of combinations is " + numberOfCombinations);
        System.out.println("Total number of solutions is " + numberOfSolutions);
        System.out.println("The solution ratio is " + (double) numberOfSolutions / numberOfCombinations);
    }

    public static boolean judgePoint24(int[] nums) {//由int类型数组获取其对应的double类型的数组然后调用solve()方法进行判断
        List<Double> list = new ArrayList<>();
        for (int num : nums) {
            list.add((double) num);
        }
        return solve(list);
    }

    public static boolean solve(List<Double> list) {//判断这4个数值的组合组成的所有数学四则运算表达式的结果中是否含有24
        if (list.size() == 0) {
            return false;
        }
        /*递归结束的终点*/
        if (list.size() == 1) {//数组中只含有一个数值时,表明参数列表中的数组中4个数值已经组成数学四则运算表达式运算后得到最后的结果
            return Math.abs(list.get(0) - TARGET) < EPSILON;//判断结果是否为24
        }
        int size = list.size();
        for (int i = 0; i < size; i++) {
            for (int j = 0; j < size; j++) {
                if (i != j) {//将没有被选择到做运算的数值存放到List<Double>类型的变量中
                    List<Double> list2 = new ArrayList<>();
                    for (int k = 0; k < size; k++) {
                        if (k != i && k != j) {
                            list2.add(list.get(k));
                        }
                    }
                    for (int k = 0; k < 4; k++) {//对选择到的操作数进行4种运算符的运算
                        if (k < 2 && i > j) {
                            continue;
                        }
                        if (k == ADD) {
                            list2.add(list.get(i) + list.get(j));
                        } else if (k == MULTIPLY) {
                            list2.add(list.get(i) * list.get(j));
                        } else if (k == SUBTRACT) {
                            list2.add(list.get(i) - list.get(j));
                        } else if (k == DIVIDE) {
                            if (Math.abs(list.get(j)) < EPSILON) {//若除数为0,则直接跳过该操作
                                continue;
                            } else {
                                list2.add(list.get(i) / list.get(j));
                            }
                        }
                        if (solve(list2)) {//递归
                            return true;
                        }
                        list2.remove(list2.size() - 1);//移除当次种类运算的结果,一遍进行下一种类的运算
                    }
                }
            }
        }
        return false;
    }

}

运行效果图:

版本一:

  

   

  

版本二(1):

  

版本二(2):

    

版本三:

版本三核心代码借鉴LeetCode 24点游戏 https://leetcode-cn.com/problems/24-game/solution/24-dian-you-xi-by-leetcode-solution/

 

  • 2
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值