昆仑山:眼中无形心中有穴之穴人合一

夫君子之行,静以修身,俭以养德;非澹泊无以明志,非宁静无以致远。夫学须静也,才须学也;非学无以广才,非志无以成学。怠慢则不能励精,险躁则不能冶性。年与时驰,意与岁去,遂成枯落,多不接世。悲守穷庐,将复何及!

 

基于SpringBoot2048游戏+加强版

项目 总体结构

项目需要的依赖=Maven依赖配置

<?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>
	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>3.3.12</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>com.jd</groupId>
	<artifactId>Handle2048Game</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>Handle2048Game</name>
	<description>Handle2048Game</description>
	<url/>
	<licenses>
		<license/>
	</licenses>
	<developers>
		<developer/>
	</developers>
	<scm>
		<connection/>
		<developerConnection/>
		<tag/>
		<url/>
	</scm>
	<properties>
		<java.version>21</java.version>
	</properties>
	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-actuator</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-thymeleaf</artifactId>
		</dependency>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-devtools</artifactId>
			<scope>runtime</scope>
			<optional>true</optional>
		</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>

项目应用配置信息

# 应用名称
# 用于在分布式系统中唯一标识应用
spring.application.name=Handle2048Game

# Spring Boot 应用配置
# src/main/resources/application.properties

# 服务器端口
# 设置应用运行的端口号
server.port=8080

# Thymeleaf 模板配置
# 设置Thymeleaf模板文件的位置和格式
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.html
spring.thymeleaf.mode=HTML
spring.thymeleaf.encoding=UTF-8
# 开发环境下禁用模板缓存,以便实时看到模板更改的效果
spring.thymeleaf.cache=false

# 静态资源配置
# 设置静态资源文件的位置,如CSS、JavaScript文件
spring.web.resources.static-locations=classpath:/static/

# 日志配置
# 设置日志级别,以便捕获不同级别的日志信息
logging.level.com.example.game2048=DEBUG
logging.level.org.springframework.web=DEBUG

# JSON 格式化
# 设置JSON输出格式,便于阅读和调试
spring.jackson.serialization.indent-output=true

主应用类

package com.jd.handle2048game;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
// 1.主应用程序
@SpringBootApplication
public class Handle2048GameApplication {

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

}

游戏状态枚举

package com.jd.handle2048game.dao;
// 2. 游戏状态枚举
/**
 * 游戏状态枚举类
 * 用于定义游戏的三种状态:进行中、胜利、失败
 */
public enum GameStatus {
    // 游戏进行中状态
    PLAYING,
    // 游戏胜利状态
    WIN,
    // 游戏失败状态
    LOSE
}


游戏板逻辑

package com.jd.handle2048game.dao;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Random;

/** 游戏板的逻辑2 
 * GameBoard 类代表 2048 游戏的游戏板。
 * 它包含了游戏板的状态、分数、游戏状态以及相关操作。
 */
public class GameBoard {

    private int[][] board;
    private int score;
    private GameStatus status;
    private static final int SIZE = 4;
    private static final int TARGET = 2048;

    /**
     * 构造一个 GameBoard 对象。
     * 初始化游戏板,设置初始分数为 0,游戏状态为 PLAYING,并调用 initBoard 方法。
     */
    public GameBoard() {
        this.board = new int[SIZE][SIZE];
        this.score = 0;
        this.status = GameStatus.PLAYING;
        initBoard();
    }

    /**
     * 初始化游戏板,添加两个随机方块。
     */
    private void initBoard() {
        addRandomTile();
        addRandomTile();
    }

    /**
     * 添加一个随机方块到游戏板上。
     * 方块的值为 2 或 4,位置随机。
     */
    private void addRandomTile() {
        List<int[]> emptyCells = getEmptyCells();
        if (!emptyCells.isEmpty()) {
            Random random = new Random();
            int[] cell = emptyCells.get(random.nextInt(emptyCells.size()));
            board[cell[0]][cell[1]] = random.nextDouble() < 0.9 ? 2 : 4;
        }
    }

    /**
     * 获取游戏板上所有空单元格的列表。
     *
     * @return 包含所有空单元格坐标的列表。
     */
    private List<int[]> getEmptyCells() {
        List<int[]> emptyCells = new ArrayList<>();
        for (int i = 0; i < SIZE; i++) {
            for (int j = 0; j < SIZE; j++) {
                if (board[i][j] == 0) {
                    emptyCells.add(new int[]{i, j});
                }
            }
        }
        return emptyCells;
    }

    /**
     * 根据指定方向移动方块。
     *
     * @param direction 移动方向,可以是 "up"、"down"、"left" 或 "right"。
     * @return 如果方块移动了,则返回 true;否则返回 false。
     */
    public boolean move(String direction) {
        int[][] oldBoard = copyBoard();
        boolean moved = false;

        switch (direction.toLowerCase()) {
            case "up":
                moved = moveUp();
                break;
            case "down":
                moved = moveDown();
                break;
            case "left":
                moved = moveLeft();
                break;
            case "right":
                moved = moveRight();
                break;
        }

        if (moved) {
            addRandomTile();
            updateGameStatus();
        }

        return moved;
    }

    /**
     * 向左移动方块并合并。
     *
     * @return 如果方块移动了,则返回 true;否则返回 false。
     */
    private boolean moveLeft() {
        boolean moved = false;
        for (int i = 0; i < SIZE; i++) {
            int[] row = board[i].clone();
            int[] newRow = moveAndMergeRow(row);
            if (!Arrays.equals(row, newRow)) {
                moved = true;
                board[i] = newRow;
            }
        }
        return moved;
    }

    /**
     * 向右移动方块并合并。
     *
     * @return 如果方块移动了,则返回 true;否则返回 false。
     */
    private boolean moveRight() {
        boolean moved = false;
        for (int i = 0; i < SIZE; i++) {
            int[] row = board[i].clone();
            reverseArray(row);
            int[] newRow = moveAndMergeRow(row);
            reverseArray(newRow);
            if (!Arrays.equals(board[i], newRow)) {
                moved = true;
                board[i] = newRow;
            }
        }
        return moved;
    }

    /**
     * 向上移动方块并合并。
     *
     * @return 如果方块移动了,则返回 true;否则返回 false。
     */
    private boolean moveUp() {
        transpose();
        boolean moved = moveLeft();
        transpose();
        return moved;
    }

    /**
     * 向下移动方块并合并。
     *
     * @return 如果方块移动了,则返回 true;否则返回 false。
     */
    private boolean moveDown() {
        transpose();
        boolean moved = moveRight();
        transpose();
        return moved;
    }

    /**
     * 移动并合并一行中的方块。
     *
     * @param row 要移动并合并的行。
     * @return 移动并合并后的行。
     */
    private int[] moveAndMergeRow(int[] row) {
        int[] newRow = new int[SIZE];
        int index = 0;

        // 移动非零元素
        for (int value : row) {
            if (value != 0) {
                newRow[index++] = value;
            }
        }

        // 合并相同元素
        for (int i = 0; i < SIZE - 1; i++) {
            if (newRow[i] != 0 && newRow[i] == newRow[i + 1]) {
                newRow[i] *= 2;
                newRow[i + 1] = 0;
                score += newRow[i];
                if (newRow[i] == TARGET) {
                    status = GameStatus.WIN;
                }
            }
        }

        // 再次移动非零元素
        int[] finalRow = new int[SIZE];
        index = 0;
        for (int value : newRow) {
            if (value != 0) {
                finalRow[index++] = value;
            }
        }

        return finalRow;
    }

    /**
     * 转置游戏板,即行列互换。
     */
    private void transpose() {
        for (int i = 0; i < SIZE; i++) {
            for (int j = i + 1; j < SIZE; j++) {
                int temp = board[i][j];
                board[i][j] = board[j][i];
                board[j][i] = temp;
            }
        }
    }

    /**
     * 反转数组,即数组中的元素顺序颠倒。
     *
     * @param arr 要反转的数组。
     */
    private void reverseArray(int[] arr) {
        for (int i = 0; i < arr.length / 2; i++) {
            int temp = arr[i];
            arr[i] = arr[arr.length - 1 - i];
            arr[arr.length - 1 - i] = temp;
        }
    }

    /**
     * 复制游戏板。
     *
     * @return 复制的游戏板。
     */
    private int[][] copyBoard() {
        int[][] copy = new int[SIZE][SIZE];
        for (int i = 0; i < SIZE; i++) {
            System.arraycopy(board[i], 0, copy[i], 0, SIZE);
        }
        return copy;
    }

    /**
     * 更新游戏状态。
     */
    private void updateGameStatus() {
        if (status == GameStatus.WIN) {
            return;
        }

        if (getEmptyCells().isEmpty() && !canMove()) {
            status = GameStatus.LOSE;
        }
    }

    /**
     * 检查游戏板上是否还能移动。
     *
     * @return 如果还能移动,则返回 true;否则返回 false。
     */
    private boolean canMove() {
        // 检查是否还能移动(有相邻相同数字)
        for (int i = 0; i < SIZE; i++) {
            for (int j = 0; j < SIZE; j++) {
                if (j < SIZE - 1 && board[i][j] == board[i][j + 1]) return true;
                if (i < SIZE - 1 && board[i][j] == board[i + 1][j]) return true;
            }
        }
        return false;
    }

    /**
     * 重置游戏。
     */
    public void reset() {
        this.board = new int[SIZE][SIZE];
        this.score = 0;
        this.status = GameStatus.PLAYING;
        initBoard();
    }

    // Getters
    public int[][] getBoard() { return board; }
    public int getScore() { return score; }
    public GameStatus getStatus() { return status; }
}

响应数据传输对象

package com.jd.handle2048game.dao;

/**
 * 游戏响应类,用于封装游戏的状态和结果
 * 提供游戏板状态、得分、游戏状态和是否移动的封装
 */
public class GameResponse {
    // 游戏板,二维数组表示
    private int[][] board;
    // 当前得分
    private int score;
    // 游戏状态
    private GameStatus status;
    // 是否移动,用于指示最近一次操作是否导致游戏板变化
    private boolean moved;

    // 默认构造函数
    public GameResponse() {
    }

    /**
     * 带参数的构造函数,用于初始化游戏响应对象
     *
     * @param board 游戏板状态,二维数组表示
     * @param score 当前得分
     * @param status 游戏状态
     * @param moved 是否移动,指示最近一次操作是否导致游戏板变化
     */
    public GameResponse(int[][] board, int score, GameStatus status, boolean moved) {
        this.board = board;
        this.score = score;
        this.status = status;
        this.moved = moved;
    }

    // Getters and Setters
    /**
     * 获取游戏板状态
     *
     * @return 游戏板状态,二维数组表示
     */
    public int[][] getBoard() {
        return board;
    }

    /**
     * 设置游戏板状态
     *
     * @param board 游戏板状态,二维数组表示
     */
    public void setBoard(int[][] board) {
        this.board = board;
    }

    /**
     * 获取当前得分
     *
     * @return 当前得分
     */
    public int getScore() {
        return score;
    }

    /**
     * 设置当前得分
     *
     * @param score 当前得分
     */
    public void setScore(int score) {
        this.score = score;
    }

    /**
     * 获取游戏状态
     *
     * @return 游戏状态
     */
    public GameStatus getStatus() {
        return status;
    }

    /**
     * 设置游戏状态
     *
     * @param status 游戏状态
     */
    public void setStatus(GameStatus status) {
        this.status = status;
    }

    /**
     * 获取是否移动的状态
     *
     * @return 是否移动,true表示最近一次操作导致游戏板变化,false表示未变化
     */
    public boolean isMoved() {
        return moved;
    }

    /**
     * 设置是否移动的状态
     *
     * @param moved 是否移动,true表示最近一次操作导致游戏板变化,false表示未变化
     */
    public void setMoved(boolean moved) {
        this.moved = moved;
    }

}


游戏业务逻辑

package com.jd.handle2048game.services;

import com.jd.handle2048game.dao.GameBoard;
import com.jd.handle2048game.dao.GameResponse;
import org.springframework.stereotype.Service;

/**
 * 游戏服务类,提供2048游戏的基本操作接口
 */
@Service
public class GameService {
    // 游戏棋盘实例
    private GameBoard gameBoard;

    // 构造方法,初始化游戏棋盘
    public GameService() {
        this.gameBoard = new GameBoard();
    }

    /**
     * 获取当前游戏状态
     *
     * @return 返回包含当前游戏棋盘、分数、状态和是否可以移动的响应对象
     */
    public GameResponse getGameState() {
        return new GameResponse(
                gameBoard.getBoard(),
                gameBoard.getScore(),
                gameBoard.getStatus(),
                true
        );
    }

    /**
     * 根据指定方向移动棋盘
     *
     * @param direction 移动方向,包括上、下、左、右
     * @return 返回更新后的游戏状态和是否成功移动的响应对象
     */
    public GameResponse move(String direction) {
        // 尝试按指定方向移动棋盘,并记录是否成功移动
        boolean moved = gameBoard.move(direction);
        return new GameResponse(
                gameBoard.getBoard(),
                gameBoard.getScore(),
                gameBoard.getStatus(),
                moved
        );
    }

    /**
     * 重置游戏,用于开始新游戏
     *
     * @return 返回新游戏的初始状态响应对象
     */
    public GameResponse newGame() {
        // 重置游戏棋盘
        gameBoard.reset();
        // 返回新游戏的初始状态
        return getGameState();
    }

}

游戏API控制器

package com.jd.handle2048game.controller;

import com.jd.handle2048game.dao.GameResponse;
import com.jd.handle2048game.services.GameService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

/**
 * 6. 游戏控制器
 * 该控制器用于处理与2048游戏相关的HTTP请求,提供游戏状态获取、移动操作和新游戏初始化的接口
 */
@RestController
@RequestMapping("/api/game")
@CrossOrigin(origins = "*")
public class GameController {

    @Autowired
    private GameService gameService;

    /**
     * 获取当前游戏状态
     *
     * @return 当前游戏状态的响应对象,包含游戏矩阵、分数等信息
     */
    @GetMapping("/state")
    public GameResponse getGameState() {
        return gameService.getGameState();
    }

    /**
     * 执行游戏中的移动操作
     *
     * @param direction 移动方向,可以是上、下、左、右
     * @return 执行移动操作后的游戏状态响应对象
     */
    @PostMapping("/move/{direction}")
    public GameResponse move(@PathVariable String direction) {
        return gameService.move(direction);
    }

    /**
     * 新建一个游戏
     *
     * @return 新建游戏的初始状态响应对象
     */
    @PostMapping("/new")
    public GameResponse newGame() {
        return gameService.newGame();
    }
}


页面控制器

package com.jd.handle2048game.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

/**
 * WebController类负责处理与2048游戏相关的网络请求
 * 它使用Spring框架的@Controller注解来标记其作为一个控制器类
 */
@Controller
public class WebController {

    /**
     * 处理根路径的GET请求,返回游戏的主页视图
     *
     * @return 返回"index"字符串,指示视图解析器解析并返回对应的主页视图
     */
    @GetMapping("/")
    public String index() {
        return "index";
    }
}

游戏前端页面

<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>2048游戏</title>
    <style>
        * {
            margin: 0;
            padding: 0;
            box-sizing: border-box;
        }

        body {
            font-family: 'Arial', sans-serif;
            background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
            min-height: 100vh;
            display: flex;
            justify-content: center;
            align-items: center;
            color: #333;
        }

        .game-container {
            background: white;
            border-radius: 20px;
            padding: 30px;
            box-shadow: 0 20px 40px rgba(0, 0, 0, 0.1);
            text-align: center;
            max-width: 500px;
            width: 100%;
        }

        .game-title {
            font-size: 48px;
            font-weight: bold;
            color: #776e65;
            margin-bottom: 10px;
            text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.1);
        }

        .game-description {
            color: #776e65;
            margin-bottom: 20px;
            font-size: 14px;
        }

        .score-container {
            display: flex;
            justify-content: space-between;
            margin-bottom: 20px;
        }

        .score-box {
            background: #bbada0;
            padding: 10px 20px;
            border-radius: 10px;
            color: white;
            font-weight: bold;
            min-width: 80px;
        }

        .score-label {
            font-size: 12px;
        }

        .score-value {
            font-size: 20px;
        }

        .game-board {
            background: #bbada0;
            border-radius: 10px;
            padding: 10px;
            margin: 20px 0;
            display: grid;
            grid-template-columns: repeat(4, 1fr);
            grid-gap: 10px;
            max-width: 400px;
            margin-left: auto;
            margin-right: auto;
        }

        .tile {
            background: #cdc1b4;
            border-radius: 6px;
            width: 80px;
            height: 80px;
            display: flex;
            align-items: center;
            justify-content: center;
            font-weight: bold;
            font-size: 24px;
            transition: all 0.3s ease;
            position: relative;
            overflow: hidden;
        }

        .tile.empty {
            background: #cdc1b4;
        }

        .tile.tile-2 { background: #eee4da; color: #776e65; }
        .tile.tile-4 { background: #ede0c8; color: #776e65; }
        .tile.tile-8 { background: #f2b179; color: #f9f6f2; }
        .tile.tile-16 { background: #f59563; color: #f9f6f2; }
        .tile.tile-32 { background: #f67c5f; color: #f9f6f2; }
        .tile.tile-64 { background: #f65e3b; color: #f9f6f2; }
        .tile.tile-128 { background: #edcf72; color: #f9f6f2; font-size: 20px; }
        .tile.tile-256 { background: #edcc61; color: #f9f6f2; font-size: 20px; }
        .tile.tile-512 { background: #edc850; color: #f9f6f2; font-size: 20px; }
        .tile.tile-1024 { background: #edc53f; color: #f9f6f2; font-size: 18px; }
        .tile.tile-2048 { background: #edc22e; color: #f9f6f2; font-size: 18px; box-shadow: 0 0 20px rgba(237, 194, 46, 0.5); }

        .controls {
            margin: 20px 0;
        }

        .new-game-btn {
            background: #8f7a66;
            color: white;
            border: none;
            padding: 12px 24px;
            border-radius: 8px;
            font-size: 16px;
            font-weight: bold;
            cursor: pointer;
            transition: all 0.3s ease;
            margin: 0 10px;
        }

        .new-game-btn:hover {
            background: #9f8a76;
            transform: translateY(-2px);
        }

        .game-instructions {
            margin-top: 20px;
            font-size: 14px;
            color: #776e65;
            line-height: 1.4;
        }

        .game-over {
            position: fixed;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background: rgba(0, 0, 0, 0.8);
            display: none;
            align-items: center;
            justify-content: center;
            z-index: 1000;
        }

        .game-over-content {
            background: white;
            padding: 40px;
            border-radius: 20px;
            text-align: center;
            max-width: 400px;
        }

        .game-over-title {
            font-size: 32px;
            margin-bottom: 20px;
            color: #776e65;
        }

        .game-over.win .game-over-title {
            color: #f2b179;
        }

        .game-over.lose .game-over-title {
            color: #f67c5f;
        }

        .arrow-keys {
            display: grid;
            grid-template-columns: repeat(3, 1fr);
            grid-gap: 10px;
            max-width: 150px;
            margin: 20px auto;
        }

        .arrow-key {
            background: #bbada0;
            color: white;
            border: none;
            padding: 15px;
            border-radius: 8px;
            font-size: 18px;
            cursor: pointer;
            transition: all 0.2s ease;
        }

        .arrow-key:hover {
            background: #a39489;
        }

        .arrow-key:active {
            transform: scale(0.95);
        }

        .arrow-key.up { grid-column: 2; }
        .arrow-key.left { grid-column: 1; grid-row: 2; }
        .arrow-key.right { grid-column: 3; grid-row: 2; }
        .arrow-key.down { grid-column: 2; grid-row: 3; }

        @media (max-width: 600px) {
            .game-container {
                margin: 20px;
                padding: 20px;
            }

            .tile {
                width: 60px;
                height: 60px;
                font-size: 18px;
            }

            .tile.tile-128, .tile.tile-256, .tile.tile-512 {
                font-size: 16px;
            }

            .tile.tile-1024, .tile.tile-2048 {
                font-size: 14px;
            }
        }
    </style>
</head>
<body>
<div class="game-container">
    <h1 class="game-title">2048</h1>
    <p class="game-description">合并数字,达到2048!</p>

    <div class="score-container">
        <div class="score-box">
            <div class="score-label">得分</div>
            <div class="score-value" id="score">0</div>
        </div>
        <div class="score-box">
            <div class="score-label">状态</div>
            <div class="score-value" id="status">游戏中</div>
        </div>
    </div>

    <div class="controls">
        <button class="new-game-btn" onclick="newGame()">新游戏</button>
    </div>

    <div class="game-board" id="gameBoard">
        <!-- 游戏板将在这里动态生成 -->
    </div>

    <div class="arrow-keys">
        <button class="arrow-key up" onclick="move('up')">↑</button>
        <button class="arrow-key left" onclick="move('left')">←</button>
        <button class="arrow-key right" onclick="move('right')">→</button>
        <button class="arrow-key down" onclick="move('down')">↓</button>
    </div>

    <div class="game-instructions">
        <p><strong>操作说明:</strong></p>
        <p>使用方向键或点击箭头按钮移动数字</p>
        <p>相同数字相撞时会合并成一个数字</p>
        <p>目标是创建2048这个数字!</p>
    </div>
</div>

<div class="game-over" id="gameOver">
    <div class="game-over-content">
        <h2 class="game-over-title" id="gameOverTitle">游戏结束</h2>
        <p id="gameOverMessage">你的得分:<span id="finalScore">0</span></p>
        <button class="new-game-btn" onclick="newGame()">再来一局</button>
    </div>
</div>

<script>
    const API_BASE = '/api/game';
    let gameState = null;

    // 初始化游戏
    async function initGame() {
        try {
            const response = await fetch(`${API_BASE}/state`);
            gameState = await response.json();
            updateDisplay();
        } catch (error) {
            console.error('初始化游戏失败:', error);
        }
    }

    // 移动
    async function move(direction) {
        if (!gameState || gameState.status !== 'PLAYING') {
            return;
        }

        try {
            const response = await fetch(`${API_BASE}/move/${direction}`, {
                method: 'POST'
            });
            const newState = await response.json();

            if (newState.moved) {
                gameState = newState;
                updateDisplay();

                // 检查游戏状态
                if (gameState.status === 'WIN' || gameState.status === 'LOSE') {
                    setTimeout(showGameOver, 500);
                }
            }
        } catch (error) {
            console.error('移动失败:', error);
        }
    }

    // 新游戏
    async function newGame() {
        try {
            const response = await fetch(`${API_BASE}/new`, {
                method: 'POST'
            });
            gameState = await response.json();
            updateDisplay();
            hideGameOver();
        } catch (error) {
            console.error('开始新游戏失败:', error);
        }
    }

    // 更新显示
    function updateDisplay() {
        if (!gameState) return;

        // 更新得分
        document.getElementById('score').textContent = gameState.score;

        // 更新状态
        const statusText = {
            'PLAYING': '游戏中',
            'WIN': '胜利!',
            'LOSE': '失败'
        };
        document.getElementById('status').textContent = statusText[gameState.status] || '游戏中';

        // 更新游戏板
        const gameBoard = document.getElementById('gameBoard');
        gameBoard.innerHTML = '';

        for (let i = 0; i < 4; i++) {
            for (let j = 0; j < 4; j++) {
                const tile = document.createElement('div');
                tile.className = 'tile';

                const value = gameState.board[i][j];
                if (value === 0) {
                    tile.classList.add('empty');
                } else {
                    tile.classList.add(`tile-${value}`);
                    tile.textContent = value;
                }

                gameBoard.appendChild(tile);
            }
        }
    }

    // 显示游戏结束界面
    function showGameOver() {
        const gameOver = document.getElementById('gameOver');
        const title = document.getElementById('gameOverTitle');
        const finalScore = document.getElementById('finalScore');

        if (gameState.status === 'WIN') {
            gameOver.className = 'game-over win';
            title.textContent = '恭喜你!';
            document.getElementById('gameOverMessage').innerHTML =
                `你达到了2048!<br>最终得分:<span id="finalScore">${gameState.score}</span>`;
        } else {
            gameOver.className = 'game-over lose';
            title.textContent = '游戏结束';
            document.getElementById('gameOverMessage').innerHTML =
                `没有可移动的空间了!<br>最终得分:<span id="finalScore">${gameState.score}</span>`;
        }

        gameOver.style.display = 'flex';
    }

    // 隐藏游戏结束界面
    function hideGameOver() {
        document.getElementById('gameOver').style.display = 'none';
    }

    // 键盘事件监听
    document.addEventListener('keydown', function(e) {
        const keyMap = {
            'ArrowUp': 'up',
            'ArrowDown': 'down',
            'ArrowLeft': 'left',
            'ArrowRight': 'right'
        };

        if (keyMap[e.key]) {
            e.preventDefault();
            move(keyMap[e.key]);
        }
    });

    // 页面加载时初始化游戏
    window.addEventListener('load', initGame);
</script>
</body>
</html>

posted on 2025-06-09 10:01  Indian_Mysore  阅读(148)  评论(0)    收藏  举报

导航