Spring Boot 24-Point Calculator

Spring Boot —— 从一个 24点 计算器开始。

先从一个 Spring Boot 小项目开始写,24点的计算实现就是炒现饭了,具体见这篇24点运算器

重点在于熟悉项目框架,掌握前后端交互方式。

1. 项目概述

  • 项目名称:24 点计算器
  • 项目目标:给定 4 个数字,通过加、减、乘、除运算,找到一种组合方式,使结果为 24。
  • 技术栈
    • 后端:Spring Boot(Java)
    • 前端:HTML + JavaScript
    • 算法:递归 + 回溯

2. 实现思路

前端逻辑

  1. 页面结构
    • 使用 HTML 创建输入框和按钮。
    • 使用 CSS 美化页面布局。
  2. 交互逻辑
    • 使用 JavaScript 监听按钮点击事件。
    • 通过 fetch API 调用后端接口,获取计算结果并显示。

后端逻辑

  1. 算法核心
    • 使用递归和回溯的方法,枚举所有可能的运算组合。
    • 支持加、减、乘、除四种运算,并处理运算顺序(通过括号)。
    • 通过浮点数精度判断结果是否接近 24(使用 Math.abs(result - 24) < 1e-6)。
  2. 服务层
    • 实现 TwentyFourPointService 类,封装 24 点计算的逻辑。
    • 提供 solve(double[] numbers) 方法,接收 4 个数字并返回运算表达式。
  3. 控制器层
    • 实现 TwentyFourPointController 类,提供 REST API。
    • 接收前端传递的 4 个数字,调用服务层方法并返回结果。

3. 项目结构

1
2
3
4
5
6
7
8
9
10
src/main/java/
com/demo/calcu/
controller/
TwentyFourPointController.java // 控制器层
service/
TwentyFourPointService.java // 服务层
CalcuApplication.java // 启动类
src/main/resources/
static/
index.html // 前端页面

4. 代码实现

启动类

CalcuApplication.java 实现 Spring Boot 应用的启动类,是整个项目的入口。

1
2
3
4
5
6
7
8
9
10
11
package com.demo.calcu;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class CalcuApplication {
public static void main(String[] args) {
SpringApplication.run(CalcuApplication.class, args);
}
}

控制类

TwentyFourPointController.java REST 控制器,负责处理 HTTP 请求并调用服务层的逻辑。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package com.demo.calcu.controller;

import com.demo.calcu.service.TwentyFourPointService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/api")
public class TwentyFourPointController {

@Autowired
private TwentyFourPointService twentyFourPointService;

@GetMapping("/solve")
public String solve(@RequestParam double num1, @RequestParam double num2,
@RequestParam double num3, @RequestParam double num4) {
double[] numbers = {num1, num2, num3, num4};
return twentyFourPointService.solve(numbers);
}
}

服务类

TwentyFourPointService.java 实现 24 点求解逻辑。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
package com.demo.calcu.service;

import org.springframework.stereotype.Service;

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

@Service
public class TwentyFourPointService {

public String solve(double[] a) {
for (int i = 0; i < 4; i++) {
for (int j = i + 1; j < 4; j++) {
List<Double> res1 = new ArrayList<>();
res1.add(a[i] + a[j]);
res1.add(a[i] - a[j]);
res1.add(a[j] - a[i]);
res1.add(a[i] * a[j]);
res1.add(a[i] / a[j]);
res1.add(a[j] / a[i]);

// res1 与剩下的其中一个数先运算
for (int u = 0; u < 4; u++) {
if (u == i || u == j) continue;
for (int k = 0; k < 6; k++) {
List<Double> res2 = new ArrayList<>();
res2.add(res1.get(k) + a[u]);
res2.add(res1.get(k) - a[u]);
res2.add(a[u] - res1.get(k));
res2.add(res1.get(k) * a[u]);
res2.add(res1.get(k) / a[u]);
res2.add(a[u] / res1.get(k));

for (int v = 0; v < 4; v++) {
if (v == i || v == j || v == u) continue;
for (int h = 0; h < 6; h++) {
List<Double> res3 = new ArrayList<>();
res3.add(res2.get(h) + a[v]);
res3.add(res2.get(h) - a[v]);
res3.add(a[v] - res2.get(h));
res3.add(res2.get(h) * a[v]);
res3.add(res2.get(h) / a[v]);
res3.add(a[v] / res2.get(h));

for (int g = 0; g < 6; g++) {
if (Math.abs(res3.get(g) - 24) < 1e-6) {
return buildExpression(a[i], a[j], a[u], a[v], k, h, g);
}
}
}
}
}
}

// 剩下的两个数先运算
for (int u = 0; u < 4; u++) {
if (u == i || u == j) continue;
for (int v = 0; v < 4; v++) {
if (v == i || v == j || v == u) continue;
List<Double> res2 = new ArrayList<>();
res2.add(a[u] + a[v]);
res2.add(a[u] - a[v]);
res2.add(a[v] - a[u]);
res2.add(a[u] * a[v]);
res2.add(a[u] / a[v]);
res2.add(a[v] / a[u]);

for (int k = 0; k < 6; k++) {
for (int h = 0; h < 6; h++) {
List<Double> res3 = new ArrayList<>();
res3.add(res1.get(k) + res2.get(h));
res3.add(res1.get(k) - res2.get(h));
res3.add(res2.get(h) - res1.get(k));
res3.add(res1.get(k) * res2.get(h));
res3.add(res1.get(k) / res2.get(h));
res3.add(res2.get(h) / res1.get(k));

for (int g = 0; g < 6; g++) {
if (Math.abs(res3.get(g) - 24) < 1e-6) {
return buildExpression(a[i], a[j], a[u], a[v], k, h, g);
}
}
}
}
}
}
}
}
return "No solution found.";
}

private String buildExpression(double a, double b, double c, double d, int op1, int op2, int op3) {
String o1 = String.valueOf((int) a);
String o2 = String.valueOf((int) b);
String o3 = String.valueOf((int) c);
String o4 = String.valueOf((int) d);

String exp1 = applyOperation(o1, o2, op1);
String exp2 = applyOperation(exp1, o3, op2);
return applyOperation(exp2, o4, op3);
}

private String applyOperation(String x, String y, int op) {
switch (op) {
case 0: return "(" + x + " + " + y + ")";
case 1: return "(" + x + " - " + y + ")";
case 2: return "(" + y + " - " + x + ")";
case 3: return "(" + x + " * " + y + ")";
case 4: return "(" + x + " / " + y + ")";
case 5: return "(" + y + " / " + x + ")";
default: return "";
}
}
}

前端页面

index.html 实现页面展示,通过 fetch API 调用后端接口,获取计算结果并显示。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>24-Point Calculator</title>
<style>
body {
font-family: Arial, sans-serif;
display: flex;
justify-content: center;
align-items: center;
height: 100vh;
margin: 0;
background-color: #f0f0f0;
}
.calculator {
background: #fff;
padding: 20px;
border-radius: 10px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
text-align: center;
}
input {
padding: 10px;
margin: 5px;
width: 100px;
border: 1px solid #ccc;
border-radius: 5px;
}
button {
padding: 10px 20px;
margin-top: 10px;
border: none;
background-color: #28a745;
color: white;
border-radius: 5px;
cursor: pointer;
}
button:hover {
background-color: #218838;
}
.result {
margin-top: 20px;
font-size: 1.5em;
color: #333;
}
</style>
</head>
<body>
<div class="calculator">
<h1>24-Point Calculator</h1>
<input type="number" id="num1" placeholder="Number 1">
<input type="number" id="num2" placeholder="Number 2">
<input type="number" id="num3" placeholder="Number 3">
<input type="number" id="num4" placeholder="Number 4">
<button onclick="calculate()">Calculate</button>
<div class="result" id="result"></div>
</div>

<script>
function calculate() {
const num1 = document.getElementById('num1').value;
const num2 = document.getElementById('num2').value;
const num3 = document.getElementById('num3').value;
const num4 = document.getElementById('num4').value;

fetch(`/api/solve?num1=${num1}&num2=${num2}&num3=${num3}&num4=${num4}`)
.then(response => response.text())
.then(result => {
document.getElementById('result').innerText = `Result: ${result}`;
})
.catch(error => {
console.error('Error:', error);
});
}
</script>
</body>
</html>

5. 实现结果