基于 Spring Boot 的井字棋游戏开发与实现

目录

引言

项目概述

项目搭建

1. 环境准备

2. 创建 Spring Boot 项目

3. 项目结构

代码实现

1. DemoApplication.java

2. TicTacToeController.java

3. pom.xml

电脑落子策略 - Minimax 算法

findBestMove 方法

minimax 方法

运行游戏

总结


引言

        在软件开发领域,通过构建简单的游戏项目来学习和实践新的技术框架是一种高效且有趣的方式。本文将详细介绍如何使用 Spring Boot 和 Maven 框架开发一个简单的井字棋(Tic - Tac - Toe)游戏。这个项目不仅能帮助你熟悉 Spring Boot 的基本使用,还能让你了解如何构建一个基于 RESTful 接口的交互式应用。

项目概述

        我们要开发的井字棋游戏是一个基于 Web 的应用,玩家可以通过浏览器或工具(如 Postman)发送请求来与电脑进行游戏。游戏规则遵循传统的井字棋规则,玩家和电脑轮流在 3x3 的棋盘上落子,先在横、竖、斜方向连成一线的一方获胜,如果棋盘填满且没有一方获胜,则为平局。

项目搭建

1. 环境准备

确保你已经安装了 Java 和 Maven。Java 是运行 Spring Boot 应用的基础,而 Maven 则用于管理项目的依赖和构建。

2. 创建 Spring Boot 项目

可以使用 Spring Initializr(https://start.spring.io/)来快速创建一个 Spring Boot 项目,选择以下配置:

  • Project: Maven Project
  • Language: Java
  • Spring Boot: 2.7.5
  • Dependencies: Spring Web

3. 项目结构

项目的主要结构如下:

src
├── main
│   ├── java
│   │   └── com
│   │       └── example
│   │           └── demo
│   │               ├── DemoApplication.java
│   │               └── controller
│   │                   └── TicTacToeController.java
│   └── resources
│       └── application.properties
└── pom.xml

代码实现

1. DemoApplication.java

package com.example.demo;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;@SpringBootApplication
public class DemoApplication {public static void main(String[] args) {SpringApplication.run(DemoApplication.class, args);}}

这是 Spring Boot 的主应用类,包含 main 方法,用于启动 Spring Boot 应用。@SpringBootApplication 注解是一个组合注解,它包含了 @Configuration@EnableAutoConfiguration 和 @ComponentScan 注解,用于自动配置 Spring Boot 应用。

2. TicTacToeController.java

package com.example.demo.controller;import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;import java.util.ArrayList;
import java.util.List;
import java.util.Random;@RestController
public class TicTacToeController {private static final int BOARD_SIZE = 3;private char[][] board = new char[BOARD_SIZE][BOARD_SIZE];private boolean isPlayerTurn = true;private boolean gameOver = false;private char winner = ' ';public TicTacToeController() {initializeBoard();}private void initializeBoard() {for (int i = 0; i < BOARD_SIZE; i++) {for (int j = 0; j < BOARD_SIZE; j++) {board[i][j] = ' ';}}isPlayerTurn = true;gameOver = false;winner = ' ';}@GetMapping("/start")public String startGame() {initializeBoard();return "新游戏开始!你先手,使用 /move?row=x&col=y 落子(x 和 y 范围 0 - 2)。";}@GetMapping("/move")public String makeMove(@RequestParam int row, @RequestParam int col) {if (gameOver) {return "游戏已结束,请使用 /start 开始新游戏。";}if (!isPlayerTurn) {return "现在是电脑回合,请等待。";}if (!isValidMove(row, col)) {return "无效的落子位置,请重新选择。";}board[row][col] = 'X';checkGameStatus();if (!gameOver) {computerMove();checkGameStatus();}return getBoardStatus();}private boolean isValidMove(int row, int col) {return row >= 0 && row < BOARD_SIZE && col >= 0 && col < BOARD_SIZE && board[row][col] == ' ';}private void computerMove() {List<int[]> availableMoves = getAvailableMoves();if (availableMoves.isEmpty()) {return;}int[] bestMove = findBestMove();board[bestMove[0]][bestMove[1]] = 'O';isPlayerTurn = true;}private List<int[]> getAvailableMoves() {List<int[]> moves = new ArrayList<>();for (int i = 0; i < BOARD_SIZE; i++) {for (int j = 0; j < BOARD_SIZE; j++) {if (board[i][j] == ' ') {moves.add(new int[]{i, j});}}}return moves;}private int[] findBestMove() {int bestScore = Integer.MIN_VALUE;int[] bestMove = null;for (int[] move : getAvailableMoves()) {board[move[0]][move[1]] = 'O';int score = minimax(board, 0, false);board[move[0]][move[1]] = ' ';if (score > bestScore) {bestScore = score;bestMove = move;}}return bestMove;}private int minimax(char[][] board, int depth, boolean isMaximizing) {if (hasWon('O')) {return 1;} else if (hasWon('X')) {return -1;} else if (getAvailableMoves().isEmpty()) {return 0;}if (isMaximizing) {int bestScore = Integer.MIN_VALUE;for (int[] move : getAvailableMoves()) {board[move[0]][move[1]] = 'O';int score = minimax(board, depth + 1, false);board[move[0]][move[1]] = ' ';bestScore = Math.max(score, bestScore);}return bestScore;} else {int bestScore = Integer.MAX_VALUE;for (int[] move : getAvailableMoves()) {board[move[0]][move[1]] = 'X';int score = minimax(board, depth + 1, true);board[move[0]][move[1]] = ' ';bestScore = Math.min(score, bestScore);}return bestScore;}}private boolean hasWon(char player) {// 检查行for (int i = 0; i < BOARD_SIZE; i++) {if (board[i][0] == player && board[i][0] == board[i][1] && board[i][1] == board[i][2]) {return true;}}// 检查列for (int j = 0; j < BOARD_SIZE; j++) {if (board[0][j] == player && board[0][j] == board[1][j] && board[1][j] == board[2][j]) {return true;}}// 检查对角线if (board[0][0] == player && board[0][0] == board[1][1] && board[1][1] == board[2][2]) {return true;}if (board[0][2] == player && board[0][2] == board[1][1] && board[1][1] == board[2][0]) {return true;}return false;}private void checkGameStatus() {if (hasWon('X')) {winner = 'X';gameOver = true;return;}if (hasWon('O')) {winner = 'O';gameOver = true;return;}// 检查平局if (getAvailableMoves().isEmpty()) {gameOver = true;winner = ' ';}}private String getBoardStatus() {StringBuilder sb = new StringBuilder();for (int i = 0; i < BOARD_SIZE; i++) {for (int j = 0; j < BOARD_SIZE; j++) {sb.append(board[i][j]);if (j < BOARD_SIZE - 1) {sb.append(" | ");}}sb.append("\n");if (i < BOARD_SIZE - 1) {sb.append("---------\n");}}if (gameOver) {if (winner == 'X') {sb.append("你赢了!使用 /start 开始新游戏。");} else if (winner == 'O') {sb.append("电脑赢了!使用 /start 开始新游戏。");} else {sb.append("平局!使用 /start 开始新游戏。");}} else {sb.append("轮到你落子,使用 /move?row=x&col=y。");}return sb.toString();}
}

这是一个 RESTful 控制器,包含两个主要的端点:

  • /start:用于开始新游戏,调用 initializeBoard 方法初始化棋盘,并返回提示信息。
  • /move:用于玩家落子,接收玩家的落子位置,验证其有效性,更新棋盘状态,检查游戏是否结束,如果未结束则调用 computerMove 方法让电脑落子,最后返回当前棋盘状态。

3. pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>com.example</groupId><artifactId>demo</artifactId><version>0.0.1-SNAPSHOT</version><packaging>jar</packaging><name>demo</name><description>Demo project for Spring Boot</description><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.7.5</version><relativePath/> <!-- lookup parent from repository --></parent><properties><java.version>1.8</java.version></properties><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build></project>

        这是 Maven 项目的配置文件,包含了项目的依赖和构建配置。spring-boot-starter-web 依赖用于支持 Spring Web 开发,spring-boot-starter-test 依赖用于测试。

电脑落子策略 - Minimax 算法

        电脑落子使用了 Minimax 算法来寻找最优落子位置。Minimax 算法是一种递归算法,用于在零和博弈中寻找最优策略。在井字棋游戏中,电脑会模拟所有可能的落子情况,通过递归调用 minimax 方法计算每个落子的得分,然后选择得分最高的落子位置。

findBestMove 方法

        该方法遍历所有可用的落子位置,对每个位置调用 minimax 方法计算得分,最终选择得分最高的位置作为最佳落子位置。

minimax 方法

  • 如果电脑获胜,返回 1。
  • 如果玩家获胜,返回 -1。
  • 如果平局,返回 0。
  • 如果是电脑回合(isMaximizing 为 true),选择得分最高的落子。
  • 如果是玩家回合(isMaximizing 为 false),选择得分最低的落子。

运行游戏

  1. 打开终端,进入项目根目录,运行以下命令启动应用:
mvn spring-boot:run
  1. 打开浏览器或使用工具(如 Postman)访问以下 URL 来玩游戏:
  2. 开始游戏:http://localhost:8080/start
  3. 落子:http://localhost:8080/move?row=0&col=0(将 0 替换为你想要的落子位置)

总结

通过这个项目,我们学习了如何使用 Spring Boot 和 Maven 框架开发一个简单的井字棋游戏。同时,我们还了解了 Minimax 算法在游戏开发中的应用,通过该算法可以让电脑做出更智能的决策。这个项目不仅可以作为学习 Spring Boot 的入门示例,还可以进一步扩展,例如添加用户界面、支持多人游戏等。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/80912.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【算法笔记】贪心算法

一、什么是贪心算法&#xff1f; 贪心算法是一种在每一步选择中都采取当前看起来最优&#xff08;最“贪心”&#xff09;的策略&#xff0c;从而希望得到全局最优解的算法设计思想。 核心思想&#xff1a;每一步都做出局部最优选择&#xff0c;不回退。适用场景&#xff1a;…

现代c++获取linux所有的网络接口名称

现代c获取linux所有的网络接口名称 前言一、在linux中查看网络接口名称二、使用c代码获取三、验证四、完整代码如下五、总结 前言 本文介绍一种使用c获取本地所有网络接口名称的方法。 一、在linux中查看网络接口名称 在linux系统中可以使用ifconfig -a命令列举出本机所有网络…

打印及判断回文数组、打印N阶数组、蛇形矩阵

打印回文数组 1 1 1 1 1 1 2 2 2 1 1 2 3 2 1 1 2 2 2 1 1 1 1 1 1方法1&#xff1a; 对角线对称 左上和右下是对称的。 所以先考虑左上打印&#xff0c; m i n ( i 1 , j 1 ) \text min(i1,j1) min(i1,j1)&#xff0c;打印出来&#xff1a; 1 1 1 1 1 2 2 2 1 2 3 3 1 2 …

详解UnityWebRequest类

什么是UnityWebRequest类 UnityWebRequest 是 Unity 引擎中用于处理网络请求的一个强大类&#xff0c;它可以让你在 Unity 项目里方便地与网络资源进行交互&#xff0c;像发送 HTTP 请求、下载文件等操作都能实现。下面会详细介绍 UnityWebRequest 的相关内容。 UnityWebRequ…

UE5 在旋转A的基础上执行旋转B

用径向slider实现模型旋转时&#xff0c;得到的结果与ue编辑器里面的结果有很大出入。 问题应该是 两个FRotator&#xff08;0&#xff0c;10&#xff0c;0&#xff09;和&#xff08;10&#xff0c;20&#xff0c;30&#xff09;&#xff0c; 两个FRotator的加法结果为&…

4.2 Prompt工程与任务建模:高效提示词设计与任务拆解方法

提示词工程&#xff08;Prompt Engineering&#xff09;和任务建模&#xff08;Task Modeling&#xff09;已成为构建高效智能代理&#xff08;Agent&#xff09;系统的核心技术。提示词工程通过精心设计的自然语言提示词&#xff08;Prompts&#xff09;&#xff0c;引导大型语…

MySQL 索引的最左前缀匹配原则是什么?

MySQL 索引的最左前缀匹配原则详解 最左前缀匹配原则&#xff08;Leftmost Prefix Principle&#xff09;是 MySQL 复合索引&#xff08;联合索引&#xff09;查询优化中的核心规则&#xff0c;理解这一原则对于高效使用索引至关重要。 核心概念 定义&#xff1a;当查询条件…

SQL命令

一、控制台中查询命令 默认端口号&#xff1a;3306 查看服务器版本: mysql –version 启动MySQL服务&#xff1a;net start mysql 登录数据库&#xff1a;mysql -u root -p 查看当前系统下的数据库&#xff1a;show databases&#xff1b; 创建数据库&#xff1a;create…

新增 29 个专业,科技成为关键赛道!

近日&#xff0c;教育部正式发布《普通高等学校本科专业目录&#xff08;2025年&#xff09;》&#xff0c;新增 29 个本科专业&#xff0c;包括区域国别学、碳中和科学与工程、海洋科学与技术、健康与医疗保障、智能分子工程、医疗器械与装备工程、时空信息工程、国际邮轮管理…

零基础上手Python数据分析 (23):NumPy 数值计算基础 - 数据分析的加速“引擎”

写在前面 —— 超越原生 Python 列表,解锁高性能数值计算,深入理解 Pandas 的底层依赖 在前面一系列关于 Pandas 的学习中,我们已经领略了其在数据处理和分析方面的强大威力。我们学会了使用 DataFrame 和 Series 来高效地操作表格数据。但是,你是否好奇,Pandas 为何能够…

Android 13.0 MTK Camera2 设置默认拍照尺寸功能实现

Android 13.0 MTK Camera2 设置默认拍照尺寸功能实现 文章目录 需求&#xff1a;参考资料架构图了解Camera相关专栏零散知识了解部分相机源码参考&#xff0c;学习API使用&#xff0c;梳理流程&#xff0c;偏应用层Camera2 系统相关 修改文件-修改方案修改文件&#xff1a;修改…

HarmonyOS 框架基础知识

参考文档&#xff1a;HarmonyOS开发者文档 第三方库&#xff1a;OpenHarmony三方库中心仓 基础特性 Entry&#xff1a;关键装饰器 Components&#xff1a;组件 特性EntryComponent​​作用范围仅用于页面入口可定义任意可复用组件​​数量限制​​每个页面有且仅有一个无数量…

前端分页与瀑布流最佳实践笔记 - React Antd 版

前端分页与瀑布流最佳实践笔记 - React Antd 版 1. 分页与瀑布流对比 分页&#xff08;Pagination&#xff09;瀑布流&#xff08;Infinite Scroll&#xff09;展示方式按页分批加载&#xff0c;有明确页码控件滚动到底部时自动加载更多内容&#xff0c;无明显分页用户控制用…

Linux网络编程:TCP多进程/多线程并发服务器详解

Linux网络编程&#xff1a;TCP多进程/多线程并发服务器详解 TCP并发服务器概述 在Linux网络编程中&#xff0c;TCP服务器主要有三种并发模型&#xff1a; 多进程模型&#xff1a;为每个客户端连接创建新进程多线程模型&#xff1a;为每个客户端连接创建新线程I/O多路复用&am…

详解springcloudalibaba采用prometheus+grafana实现服务监控

文章目录 1.官网下载安装 prometheus和grafana1.promethus2.grafana 2. 搭建springcloudalibaba集成prometheus、grafana1. 引入依赖,springboot3.2之后引入如下2. 在yml文件配置监控端点暴露配置3. 在当前启动的应用代码中添加&#xff0c;在prometheus显示的时候附加当前应用…

数据分析1

一、常用数据处理模块Numpy Numpy常用于高性能计算&#xff0c;在机器学习常常作为传递数据的容器。提供了两种基本对象&#xff1a;ndarray、ufunc。 ndarray具有矢量算术运算和复杂广播能力的快速且节省空间的多维数组。 ufunc提供了对数组快速运算的标准数学函数。 ndar…

DeepSeek智能时空数据分析(六):大模型NL2SQL绘制城市之间连线

序言&#xff1a;时空数据分析很有用&#xff0c;但是GIS/时空数据库技术门槛太高 时空数据分析在优化业务运营中至关重要&#xff0c;然而&#xff0c;三大挑战仍制约其发展&#xff1a;技术门槛高&#xff0c;需融合GIS理论、SQL开发与时空数据库等多领域知识&#xff1b;空…

2023ICPC合肥题解

文章目录 F. Colorful Balloons(签到)E. Matrix Distances(思维小结论)J. Takeout Delivering(最短路)G. Streak Manipulation(二分dp)C. Cyclic Substrings(回文自动机) 题目链接 F. Colorful Balloons(签到) int n;cin>>n;for(int i1;i<n;i) cin>>s[i];map<…

数字技术驱动下教育生态重构:从信息化整合到数字化转型的路径探究

一、引言 &#xff08;一&#xff09;研究背景与问题提出 在当今时代&#xff0c;数字技术正以前所未有的速度和深度渗透到社会的各个领域&#xff0c;教育领域也不例外。从早期的教育信息化整合到如今的数字化转型&#xff0c;教育系统正经历着一场深刻的范式变革。 回顾教…

terraform 动态块(Dynamic Blocks)详解与实践

在 Terraform 中&#xff0c;动态块&#xff08;Dynamic Blocks&#xff09; 是一种强大的机制&#xff0c;允许你根据变量或表达式动态生成配置块&#xff0c;避免重复编写相似的代码。这在处理需要重复定义的结构&#xff08;如资源参数、嵌套配置&#xff09;时特别有用。以…