目錄
引言
項目概述
項目搭建
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
),選擇得分最低的落子。
運行游戲
- 打開終端,進入項目根目錄,運行以下命令啟動應用:
mvn spring-boot:run
- 打開瀏覽器或使用工具(如 Postman)訪問以下 URL 來玩游戲:
- 開始游戲:
http://localhost:8080/start
- 落子:
http://localhost:8080/move?row=0&col=0
(將?0
?替換為你想要的落子位置)
總結
通過這個項目,我們學習了如何使用 Spring Boot 和 Maven 框架開發一個簡單的井字棋游戲。同時,我們還了解了 Minimax 算法在游戲開發中的應用,通過該算法可以讓電腦做出更智能的決策。這個項目不僅可以作為學習 Spring Boot 的入門示例,還可以進一步擴展,例如添加用戶界面、支持多人游戲等。