基于HarmonyOS的五子棋对战游戏设计与实现

在这里插入图片描述

在这里插入图片描述在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
在这里插入图片描述

摘要

本文详细介绍了基于华为HarmonyOS操作系统开发的五子棋对战游戏的设计与实现过程。该应用采用ArkTS语言开发,充分利用HarmonyOS的声明式UI框架和路由管理机制,实现了包含主菜单、难度选择、游戏对战、规则说明等完整功能模块的五子棋游戏。本文重点阐述了游戏的核心算法设计、AI对战策略的实现、界面布局技术以及HarmonyOS应用的架构设计,为开发者提供HarmonyOS游戏开发的实践经验参考。

关键词:HarmonyOS;ArkTS;五子棋;AI对战;声明式UI


目录

  1. 引言
  2. 项目概述与技术选型
  3. 应用架构设计
  4. 核心功能模块实现
  5. 游戏算法设计与优化
  6. 用户界面设计与实现
  7. 页面路由与导航管理
  8. 常见问题与解决方案
  9. 性能优化与用户体验提升
  10. 总结与展望

1. 引言

1.1 研究背景

随着移动互联网的快速发展,移动端游戏一直是用户日常娱乐的重要组成部分。五子棋作为一种历史悠久、规则简单但又充满策略性的棋类游戏,在全球范围内拥有广泛的受众群体。传统的五子棋游戏多基于Java或Kotlin开发,主要运行在Android系统上。近年来,华为推出的HarmonyOS操作系统凭借其分布式能力和跨设备特性,逐渐成为移动端开发的新选择。

HarmonyOS采用了全新的应用开发语言ArkTS(Ark TypeScript),它是TypeScript的子集,专门为HarmonyOS应用开发而设计。ArkTS继承并扩展了TypeScript的类型系统,同时兼容JavaScript生态,为开发者提供了更加简洁、高效的开发体验。然而,目前基于HarmonyOS开发的棋类游戏应用相对较少,相关技术文档和实践经验也不够丰富。因此,开发一款基于HarmonyOS的五子棋对战游戏,不仅具有实际的应用价值,也能够为HarmonyOS游戏开发领域积累宝贵的实践经验。

1.2 研究意义

本研究的意义主要体现在以下几个方面:首先,通过实际项目的开发过程,深入探索HarmonyOS应用开发的技术特点和方法,总结ArkTS语言的开发规范和最佳实践;其次,设计并实现多种难度的AI对战功能,探讨如何在移动端有限的计算资源下实现高效的游戏AI;最后,通过详细的代码实现和算法分析,为后续的HarmonyOS游戏开发提供技术参考和借鉴。

1.3 论文结构

本文按照软件工程的开发流程,从项目需求分析出发,逐步完成系统架构设计、核心功能实现、算法优化和界面设计等工作。全文共分为十个章节,第一章为引言,介绍研究背景和意义;第二章为项目概述与技术选型;第三章为应用架构设计;第四章为核心功能模块实现;第五章为游戏算法设计与优化;第六章为用户界面设计与实现;第七章为页面路由与导航管理;第八章为常见问题与解决方案;第九章为性能优化与用户体验提升;第十章为总结与展望。


2. 项目概述与技术选型

2.1 项目需求分析

五子棋,又称连珠棋,是一种由两人对弈的策略型棋类游戏。游戏规则简单明了:黑白双方轮流在棋盘上放置棋子,首先在横、竖、斜任意方向上形成五子连珠的一方获胜。本项目需要实现一款功能完整的五子棋对战游戏,具体需求包括以下几个方面。

在游戏功能方面,应用需要提供人机对战模式,玩家执黑棋先行,AI执白棋后行。AI需要具备三种不同的难度级别,分别是简单模式下的随机落子策略、中等模式下的混合策略(50%概率使用最优策略)以及困难模式下的完整评估算法。同时,游戏需要实现完整的胜负判定逻辑,能够准确判断五子连珠、平局等情况,并在游戏结束时弹出胜负提示对话框。

在用户界面方面,应用需要设计简洁美观的操作界面,包括清晰的游戏标题栏、直观的棋盘显示、实时的游戏状态提示(如“你的回合”、“AI思考中”等),以及人性化的按钮设计(如“重新开始”、“返回菜单”等)。界面风格采用现代扁平化设计,以浅灰色为背景色,木色(#DEB887)为棋盘底色,营造出传统棋类游戏的视觉感受。

在导航功能方面,应用需要实现完整的页面路由系统,包括主菜单页面、难度选择页面、游戏主页面、游戏规则说明页面以及关于页面。各页面之间能够平滑切换,使用户获得流畅的操作体验。同时,需要处理好页面参数传递,确保难度选择能够正确传递到游戏页面。

2.2 技术栈介绍

本项目采用的技术栈主要包括HarmonyOS操作系统、ArkTS应用开发语言以及HarmonyOS应用框架。

HarmonyOS是华为公司自主研发的分布式操作系统,旨在为各种智能设备提供统一的操作系统解决方案。HarmonyOS采用微内核架构设计,具有模块化、可伸缩、高安全性的特点。该系统支持一次开发、多端部署,能够运行在手机、平板、智能穿戴、智能家居等多种设备上。HarmonyOS的应用采用FA(Feature Ability)和PA(Particle Ability)两种应用形式,本项目采用的是FA模式进行开发。

ArkTS是HarmonyOS应用开发的核心语言,它是TypeScript的子集,在TypeScript的基础上进行了适配和扩展。ArkTS保留了TypeScript的基本语法特性,如强类型检查、接口、泛型等,同时针对HarmonyOS的声明式UI框架进行了优化。使用ArkTS开发的应用具有类型安全、代码简洁、易于维护等优点。在 ArkTS 中,开发者可以通过装饰器(如 @Entry、@Component、@State 等)来声明UI组件和状态管理逻辑,使代码结构更加清晰。

HarmonyOS应用框架提供了丰富的系统能力和开发接口,包括UI框架、网络通信、数据存储、媒体处理等。本项目主要使用了HarmonyOS的UI框架和路由管理功能。UI框架采用声明式设计,开发者只需声明UI的结构和状态,框架会自动完成UI的渲染和更新工作。路由管理功能则提供了页面跳转、参数传递、页面返回等操作的支持。

2.3 开发环境配置

进行HarmonyOS应用开发需要配置相应的开发环境。首先,需要安装华为DevEco Studio集成开发环境,这是官方提供的HarmonyOS应用开发工具。DevEco Studio基于IntelliJ IDEA社区版开发,提供了代码编辑、调试、构建、签名等完整的开发功能。安装完成后,需要配置HarmonyOS SDK,包括系统镜像、编译工具链、开发文档等资源。

项目采用模块化组织结构,主要包含app模块和entry模块。app模块用于存放应用级别的配置信息和资源文件,entry模块则是应用的主模块,包含应用的入口代码和主要功能实现。在entry模块中,又细分为ets目录(存放ArkTS源代码)、resources目录(存放资源文件)和profile目录(存放配置文件)。

项目的构建工具采用hvigor,这是HarmonyOS自带的构建系统。hvigor基于Node.js运行,支持增量编译、并行构建等功能,能够显著提升开发效率。在项目根目录下,hvigorfile.ts文件定义了构建任务和插件配置,build-profile.json5文件则配置了编译选项和产物信息。

2.4 项目文件结构

本项目的文件结构组织如下所示:

demo03/
├── entry/
│   └── src/
│       └── main/
│           ├── ets/
│           │   ├── entryability/
│           │   │   └── EntryAbility.ets      # 应用入口能力
│           │   └── pages/
│           │       ├── MainMenu.ets           # 主菜单页面
│           │       ├── DifficultySelect.ets   # 难度选择页面
│           │       ├── GamePage.ets           # 游戏主页面
│           │       ├── Rules.ets              # 游戏规则页面
│           │       └── About.ets              # 关于页面
│           └── resources/
│               └── base/
│                   ├── profile/
│                   │   └── main_pages.json     # 页面路由配置
│                   └── element/               # 资源元素
├── hvigor/                                 # 构建配置
└── build-profile.json5                     # 项目构建配置

这种文件结构遵循了HarmonyOS应用的标准开发规范,将页面代码、配置文件、资源文件等分类存放,便于管理和维护。各页面之间通过路由机制进行跳转,实现了良好的模块解耦。


3. 应用架构设计

3.1 整体架构概述

本应用采用分层架构设计,将用户界面、业务逻辑和数据处理分离,以提高代码的可维护性和可扩展性。整体架构分为四个层次:表现层、业务逻辑层、数据层和系统层。

表现层负责用户界面的呈现和交互响应,主要使用ArkTS的声明式UI组件构建。本层的核心任务是根据应用状态渲染对应的UI界面,并处理用户的各种交互事件。在HarmonyOS的声明式UI框架中,UI界面是通过组件树的形式组织的,开发者只需声明组件的属性和状态,框架会自动完成组件的创建、布局和渲染工作。

业务逻辑层承担着游戏规则的实现和AI算法的运算任务。本层包括棋盘状态管理、胜负判定逻辑、AI落子策略等核心功能。业务逻辑层的设计充分考虑了可复用性和可测试性,各功能模块之间通过接口进行通信,便于后续的功能扩展和算法优化。

数据层负责管理游戏过程中的数据状态,包括棋盘数据、当前玩家、游戏状态等。在ArkTS中,使用@State装饰器来声明响应式状态,当状态发生变化时,UI框架会自动触发界面更新。本项目采用二维数组来存储棋盘状态,数组的每个元素代表一个交叉点的棋子状态。

系统层提供了HarmonyOS系统级别的功能支持,包括路由管理、页面跳转、系统配置等。本项目主要使用了@ohos.router模块进行页面导航,该模块提供了pushUrl、back等方法实现页面的前进和返回功能。

3.2 模块划分与职责

根据功能特点,本应用划分为以下几个主要模块:

主菜单模块(MainMenu)是应用的入口页面,负责显示应用标题和功能按钮。该模块的职责相对简单,主要包括三个导航按钮的实现:开始游戏按钮跳转到难度选择页面,游戏规则按钮跳转到规则说明页面,关于按钮跳转到关于页面。主菜单模块的设计追求简洁明了,让用户能够快速找到所需功能。

难度选择模块(DifficultySelect)提供三种难度级别的选择界面。用户选择相应难度后,系统会将难度参数传递给游戏模块,然后启动游戏。该模块采用卡片式按钮设计,每个难度按钮下方配有简要说明,帮助用户理解各难度的特点。

游戏核心模块(GamePage)是应用的核心部分,实现了五子棋的全部游戏逻辑。该模块包含棋盘渲染、棋子管理、事件处理、AI运算、胜负判定等功能。游戏模块的设计需要考虑性能优化,确保在复杂局面下AI运算不会导致界面卡顿。

规则说明模块(Rules)以滚动列表的形式展示游戏规则。内容组织分为四个部分:游戏简介、基本规则、胜负判定和难度说明。规则页面采用分段式设计,每段内容独立成块,便于用户阅读和理解。

关于模块(About)提供应用的版本信息和功能特性介绍。该模块的设计与主菜单保持一致的风格,包含应用名称、版本号、功能列表等内容。

3.3 状态管理设计

在HarmonyOS的声明式UI框架中,状态管理是核心概念之一。本应用使用@State装饰器来管理组件的私有状态,使用@Link装饰器来实现父子组件之间的状态双向绑定。

游戏状态主要包括以下几个变量:board表示15×15的二维棋盘数据,使用0表示空位、1表示玩家棋子、2表示AI棋子;currentPlayer表示当前回合的玩家,取值为PLAYER或AI;gameStatus表示游戏状态,取值包括playing(进行中)、player_win(玩家获胜)、ai_win(AI获胜)和draw(平局);message用于显示给用户的提示信息;difficulty存储当前游戏难度级别;showWinDialog控制胜负对话框的显示与隐藏。

状态更新的触发机制采用了观察者模式。当用户点击棋盘落子时,系统会更新board数组中对应位置的棋子状态,同时检查是否形成五子连珠。如果游戏结束,则更新gameStatus并显示胜负对话框。状态的变化会触发UI框架的重新渲染,界面会在下一刻反映出最新的游戏状态。

3.4 页面生命周期管理

HarmonyOS应用中的每个页面都有自己的生命周期,开发者需要在适当的生命周期回调中执行相应的操作。本项目主要使用了aboutToAppear生命周期回调来进行页面初始化工作。

aboutToAppear回调在页面即将显示时触发,适合进行数据的初始化和状态的重置操作。在游戏页面中,每次进入游戏时都会调用initBoard方法,该方法会重置棋盘为空、重置游戏状态为进行中、重置提示信息为“你的回合”。同时,从路由参数中获取难度设置,确保每次进入游戏都能正确获取用户选择的难度级别。

页面的路由跳转通过@ohos.router模块实现。pushUrl方法用于跳转到新页面,可以同时传递参数;back方法用于返回上一页。正确管理页面跳转和参数传递,是保证用户体验流畅的关键。


4. 核心功能模块实现

4.1 棋盘数据结构的定义

棋盘是五子棋游戏的基础数据结构,其设计直接影响游戏逻辑的实现效率。本项目采用15×15的标准五子棋棋盘,使用二维数组来存储每个交叉点的状态。

const EMPTY = 0  // 空位
const PLAYER = 1 // 玩家棋子(黑棋)
const AI = 2     // AI棋子(白棋)
const BOARD_SIZE = 15 // 棋盘大小

@State board: number[][] = [] // 棋盘状态数组

初始化棋盘的代码逻辑如下:遍历15×15的二维数组,将每个元素设置为EMPTY(0),表示该位置为空。对于五子棋游戏而言,棋盘的大小直接影响了游戏的复杂度。15×15的棋盘共有225个交叉点,提供了足够大的搜索空间,使得AI算法需要考虑更多的局面变化。

棋盘数据结构的优势在于其访问模式的时间复杂度为O(1),能够快速获取任意位置的状态信息。这对于胜负判定算法尤为重要,因为在每次落子后都需要检查四个方向上是否形成五子连珠。

4.2 棋子放置逻辑

棋子放置是玩家交互的核心功能,需要处理多种边界情况和游戏状态检查。当用户点击棋盘上的某个位置时,系统首先检查游戏是否正在进行、该位置是否已有棋子,然后才能执行放置操作。

placePiece(row: number, col: number) {
    // 检查游戏状态和位置有效性
    if (this.gameStatus !== 'playing' || this.board[row][col] !== EMPTY) {
        return
    }

    // 放置玩家棋子
    this.board[row][col] = PLAYER

    // 检查是否获胜
    if (this.checkWin(row, col, PLAYER)) {
        this.gameStatus = 'player_win'
        this.message = '恭喜你获胜!'
        this.showWinDialog = true
        return
    }

    // 检查是否平局
    if (this.isBoardFull()) {
        this.gameStatus = 'draw'
        this.message = '平局!'
        this.showWinDialog = true
        return
    }

    // 切换到AI回合
    this.currentPlayer = AI
    this.message = 'AI思考中...'

    // 延迟执行AI落子,提供更好的用户体验
    setTimeout(() => {
        this.aiMove()
    }, 500)
}

这段代码展示了棋子放置的完整流程。首先进行合法性检查,确保游戏正在进行且目标位置为空。然后放置玩家棋子并检查是否获胜。如果游戏未结束,则切换到AI回合,并显示“AI思考中”的提示信息。为了提供更好的用户体验,AI落子使用了500毫秒的延迟,给玩家留下心理准备的时间。

4.3 胜负判定算法

胜负判定是五子棋游戏的核心逻辑之一,需要在每次落子后检查是否形成五子连珠。本项目采用了方向检查的方法,分别对水平、垂直、对角线和反对角线四个方向进行统计。

checkWin(row: number, col: number, piece: number): boolean {
    return this.checkDirection(row, col, 0, 1, piece) ||  // 水平方向
           this.checkDirection(row, col, 1, 0, piece) ||  // 垂直方向
           this.checkDirection(row, col, 1, 1, piece) ||  // 对角线方向
           this.checkDirection(row, col, 1, -1, piece)    // 反对角线方向
}

checkDirection(row: number, col: number, dRow: number, dCol: number, piece: number): boolean {
    let count = 1 // 计数起点为1(包含落子点本身)

    // 正向检查
    let r = row + dRow
    let c = col + dCol
    while (r >= 0 && r < BOARD_SIZE && c >= 0 && c < BOARD_SIZE && this.board[r][c] === piece) {
        count++
        r += dRow
        c += dCol
    }

    // 反向检查
    r = row - dRow
    c = col - dCol
    while (r >= 0 && r < BOARD_SIZE && c >= 0 && c < BOARD_SIZE && this.board[r][c] === piece) {
        count++
        r -= dRow
        c -= dCol
    }

    return count >= 5
}

胜负判定算法的核心思想是:以落子位置为起点,分别向两个方向延伸,统计与落子颜色相同的连续棋子数量。如果在任意方向上连续棋子数达到5个或以上,则判定为获胜。这种算法的时间复杂度为O(5),即最多检查5个位置,具有较高的执行效率。

4.4 平局判定

平局判定相对简单,只需检查棋盘是否已填满即可。如果所有225个位置都有棋子且无人获胜,则判定为平局。

isBoardFull(): boolean {
    for (let i = 0; i < BOARD_SIZE; i++) {
        for (let j = 0; j < BOARD_SIZE; j++) {
            if (this.board[i][j] === EMPTY) {
                return false
            }
        }
    }
    return true
}

在实际游戏中,平局情况较为罕见,因为五子棋的先手优势较为明显。但是,平局判定的实现仍然是完整游戏逻辑的必要组成部分。


5. 游戏算法设计与优化

5.1 AI算法概述

人工智能是五子棋游戏的核心组成部分,决定了游戏的挑战性和趣味性。本项目实现了三种不同难度的AI策略,分别适用于不同水平的玩家。

简单难度的AI采用完全随机策略,在所有空位中随机选择一个位置落子。这种策略虽然简单,但有时也能给玩家带来意外的“惊喜”,增加游戏的趣味性。

中等难度的AI采用混合策略,以50%的概率选择最优位置,50%的概率选择随机位置。这种设计使得AI的行为具有一定的不确定性,不会总是选择最佳走法,给玩家留下获胜的机会。

困难难度的AI采用完整的评估算法,会主动进攻(尝试形成自己的连子)和防守(阻止对手形成连子)。这是本项目AI算法的核心,下面将详细阐述。

5.2 评分函数设计

评分函数是AI决策的基础,用于评估每个空位的价值。本项目采用多因素评分方法,综合考虑位置的攻击价值和防御价值。

evaluatePosition(row: number, col: number, piece: number): number {
    let score = 0
    // 评估四个方向的得分
    score += this.evaluateDirection(row, col, 0, 1, piece)
    score += this.evaluateDirection(row, col, 1, 0, piece)
    score += this.evaluateDirection(row, col, 1, 1, piece)
    score += this.evaluateDirection(row, col, 1, -1, piece)
    return score
}

evaluateDirection(row: number, col: number, dRow: number, dCol: number, piece: number): number {
    let count = 1      // 连续棋子数
    let openEnds = 0   // 开放端数

    // 正向检查
    let r = row + dRow
    let c = col + dCol
    while (r >= 0 && r < BOARD_SIZE && c >= 0 && c < BOARD_SIZE && this.board[r][c] === piece) {
        count++
        r += dRow
        c += dCol
    }
    if (r >= 0 && r < BOARD_SIZE && c >= 0 && c < BOARD_SIZE && this.board[r][c] === EMPTY) {
        openEnds++
    }

    // 反向检查
    r = row - dRow
    c = col - dCol
    while (r >= 0 && r < BOARD_SIZE && c >= 0 && c < BOARD_SIZE && this.board[r][c] === piece) {
        count++
        r -= dRow
        c -= dCol
    }
    if (r >= 0 && r < BOARD_SIZE && c >= 0 && c < BOARD_SIZE && this.board[r][c] === EMPTY) {
        openEnds++
    }

    // 根据连子数和开放端数评分
    if (count >= 5) return 100000  // 五连
    if (count === 4 && openEnds === 2) return 10000  // 活四
    if (count === 4 && openEnds === 1) return 1000   // 冲四
    if (count === 3 && openEnds === 2) return 1000   // 活三
    if (count === 3 && openEnds === 1) return 100    // 眠三
    if (count === 2 && openEnds === 2) return 100    // 活二
    if (count === 2 && openEnds === 1) return 10     // 眠二
    return count
}

评分函数的设计遵循了五子棋的专业术语和策略知识:

  • 活四(两面都有开放端):得分10000,这是即将获胜的必杀棋型。
  • 冲四(只有一面有开放端):得分1000,虽然不是必杀,但对手难以防守。
  • 活三(两面都有开放端的三连):得分1000,可以发展为活四。
  • 眠三(只有一面有开放端的三连):得分100,需要后续配合。
  • 活二(两面都有开放端的二连):得分100,发展的基础棋型。

这种评分体系能够准确反映每个位置的战略价值,为AI的决策提供可靠的依据。

5.3 最优落子选择

在困难难度下,AI需要从所有空位中选择最优位置。本项目采用“知己知彼”的策略,首先检查AI自身是否能获胜,然后检查是否需要防守玩家的必杀棋,最后选择综合得分最高的位置。

getBestMove(): Move | null {
    // 第一步:检查AI是否能赢
    for (let i = 0; i < BOARD_SIZE; i++) {
        for (let j = 0; j < BOARD_SIZE; j++) {
            if (this.board[i][j] === EMPTY) {
                this.board[i][j] = AI
                if (this.checkWin(i, j, AI)) {
                    this.board[i][j] = EMPTY
                    let move: Move = { row: i, col: j }
                    return move
                }
                this.board[i][j] = EMPTY
            }
        }
    }

    // 第二步:检查是否需要防守玩家
    for (let i = 0; i < BOARD_SIZE; i++) {
        for (let j = 0; j < BOARD_SIZE; j++) {
            if (this.board[i][j] === EMPTY) {
                this.board[i][j] = PLAYER
                if (this.checkWin(i, j, PLAYER)) {
                    this.board[i][j] = EMPTY
                    let move: Move = { row: i, col: j }
                    return move
                }
                this.board[i][j] = EMPTY
            }
        }
    }

    // 第三步:选择综合得分最高的位置
    let bestScore = -Infinity
    let bestMove: Move | null = null

    for (let i = 0; i < BOARD_SIZE; i++) {
        for (let j = 0; j < BOARD_SIZE; j++) {
            if (this.board[i][j] === EMPTY) {
                // 进攻得分减去0.9倍的防守得分(稍偏进攻)
                let score = this.evaluatePosition(i, j, AI) + this.evaluatePosition(i, j, PLAYER) * 0.9
                if (score > bestScore) {
                    bestScore = score
                    let move: Move = { row: i, col: j }
                    bestMove = move
                }
            }
        }
    }

    return bestMove
}

这种三层检查策略确保了AI不会错失获胜机会,同时也会积极防守对手的进攻。最后选择综合得分最高的位置,使得AI既有一定的进攻性,又不失稳健。

5.4 算法复杂度分析

本项目的AI算法采用了遍历评分的方法,其时间复杂度为O(n²),其中n为棋盘边长(15)。对于每个空位,需要评估其在四个方向上的得分,每个方向的评估最多检查5个位置。

在最坏情况下,需要评估的空位数约为200个(15×15-25,已落子约25个),每个空位需要评估4个方向,每个方向最多检查5个位置。因此,总的操作次数约为200×4×5=4000次,这在移动设备上是完全可接受的时间复杂度。

相比之下,如果采用更高级的搜索算法如极大极小搜索配合Alpha-Beta剪枝,理论上可以获得更强的AI,但实现复杂度也会显著增加。本项目的评估算法在简单性和有效性之间取得了良好的平衡。


6. 用户界面设计与实现

6.1 界面布局设计

本应用的界面设计遵循简洁美观的原则,采用分层布局结构。整体界面分为三个区域:顶部状态栏、中间游戏区和底部按钮区。

顶部状态栏用于显示当前游戏状态,包括回合提示信息(如“你的回合”、“AI思考中”、“恭喜你获胜!”等)。状态栏采用浅灰色背景(#F0F0F0),高度固定为60vp,内容水平垂直居中显示。

中间游戏区是界面核心,用于展示15×15的棋盘。棋盘采用木色背景(#DEB887),格子之间用棕色边框(#8B4513)分隔。棋盘尺寸根据格子大小和数量计算,每个格子大小为28vp×28vp,总尺寸为420vp×420vp。棋盘上方叠加一层透明的点击响应区域,用户点击任意位置都能触发落子逻辑。

底部按钮区包含两个操作按钮:“重新开始”按钮用于重置游戏状态,“返回菜单”按钮用于退出游戏返回主菜单。按钮采用现代扁平化设计,宽度140vp、高度50vp、字号18fp,排列方式为水平均匀分布。

6.2 棋盘渲染实现

棋盘的渲染采用嵌套循环结构,外层循环遍历行、内层循环遍历列。每个格子使用Stack组件实现,支持同时显示棋盘底色和棋子。

@Builder
buildCell(rowIndex: number, colIndex: number) {
    Stack({ alignContent: Alignment.Center }) {
        // 棋盘格子底层
        Column()
            .width(CELL_SIZE)
            .height(CELL_SIZE)
            .backgroundColor('#DEB887')
            .border({
                width: { left: colIndex === 0 ? 1 : 0, right: 1, top: rowIndex === 0 ? 1 : 0, bottom: 1 },
                color: { left: '#8B4513', right: '#8B4513', top: '#8B4513', bottom: '#8B4513' }
            })

        // 棋子层
        if (this.board[rowIndex][colIndex] !== EMPTY) {
            Column()
                .width(22)
                .height(22)
                .borderRadius(11)
                .backgroundColor(this.board[rowIndex][colIndex] === PLAYER ? '#000000' : '#FFFFFF')
                .border({
                    width: 1,
                    color: this.board[rowIndex][colIndex] === AI ? '#333333' : 'transparent'
                })
        }
    }
    .width(CELL_SIZE)
    .height(CELL_SIZE)
    .onClick(() => {
        if (this.currentPlayer === PLAYER && this.gameStatus === 'playing') {
            this.placePiece(rowIndex, colIndex)
        }
    })
}

这段代码使用@Builder装饰器定义了一个可复用的格子构建器。每个格子由Stack组件包裹,包含底层棋盘格子和上层的棋子(如果有的话)。玩家棋子使用黑色背景(#000000),AI棋子使用白色背景(#FFFFFF)并添加黑色边框以增强可见性。点击事件通过onClick回调处理,调用placePiece方法执行落子逻辑。

6.3 胜负对话框设计

游戏结束后,需要向用户展示胜负结果并提供后续操作选项。本项目采用模态对话框的形式实现胜负提示。

if (this.showWinDialog) {
    Stack() {
        Column() {
            Text(this.gameStatus === 'player_win' ? '恭喜获胜!' :
                 (this.gameStatus === 'ai_win' ? 'AI获胜!' : '平局!'))
                .fontSize(32)
                .fontWeight(FontWeight.Bold)
                .margin({ bottom: 30 })

            Row({ space: 20 }) {
                Button('再来一局')
                    .width(140)
                    .height(50)
                    .fontSize(18)
                    .onClick(() => {
                        this.restartGame()
                    })

                Button('返回菜单')
                    .width(140)
                    .height(50)
                    .fontSize(18)
                    .onClick(() => {
                        router.back()
                    })
            }
        }
        .width(320)
        .height(200)
        .backgroundColor('#FFFFFF')
        .borderRadius(20)
        .justifyContent(FlexAlign.Center)
        .alignItems(HorizontalAlign.Center)
        .shadow({
            radius: 20,
            color: 'rgba(0,0,0,0.3)',
            offsetX: 0,
            offsetY: 0
        })
    }
    .width('100%')
    .height('100%')
    .backgroundColor('rgba(0,0,0,0.5)')
    .justifyContent(FlexAlign.Center)
    .alignItems(HorizontalAlign.Center)
}

对话框使用半透明黑色遮罩覆盖整个界面,突出显示中间的白色对话框内容。对话框内部包含结果文本和两个操作按钮。文本根据游戏状态显示不同的内容,按钮分别提供“再来一局”和“返回菜单”两个选项。阴影效果使用shadow属性配置,增强了界面的层次感和立体感。

6.4 响应式设计考虑

虽然本应用主要针对手机屏幕设计,但在界面布局上仍考虑了不同屏幕尺寸的适应性。主要措施包括使用固定尺寸的棋盘(420vp×420vp)确保游戏区域不会因屏幕大小而变形,同时使用FlexLayout的justifyContent和alignItems属性确保按钮等元素在水平方向上居中排列。

对于不同难度选择页面和规则说明页面,采用了Scroll组件包裹长内容,确保在较小屏幕上用户仍能查看全部内容。这种设计符合HarmonyOS的响应式开发理念,能够在多种设备上提供一致的用户体验。


7. 页面路由与导航管理

7.1 路由配置

HarmonyOS应用采用集中式的路由配置管理,所有页面的路由信息都定义在main_pages.json配置文件中。这种设计使得路由管理更加规范和可控。

{
  "src": [
    "pages/MainMenu",
    "pages/DifficultySelect",
    "pages/GamePage",
    "pages/Rules",
    "pages/About"
  ]
}

配置文件位于entry/src/main/resources/base/profile/目录下,src数组列出了所有需要注册页面的路径。这种配置方式的好处是:所有页面的注册信息集中在一处,便于管理和维护;同时,框架可以在应用启动时预加载页面信息,提升页面跳转的响应速度。

7.2 页面跳转实现

页面跳转通过@ohos.router模块提供的pushUrl方法实现。在主菜单页面中,点击“开始游戏”按钮会跳转到难度选择页面。

import router from '@ohos.router'

Button('开始游戏')
    .onClick(() => {
        router.pushUrl({ url: 'pages/DifficultySelect' })
    })

参数url指定了目标页面的路径,路径相对于src目录计算。router模块会自动根据main_pages.json中的配置查找对应的页面文件并加载显示。

7.3 参数传递机制

页面之间不仅需要跳转,有时还需要传递参数。例如,难度选择页面需要将用户选择的难度级别传递给游戏页面。这通过pushUrl方法的params参数实现。

Button('困难')
    .onClick(() => {
        router.pushUrl({ url: 'pages/GamePage', params: { difficulty: 'hard' } })
    })

在目标页面中,可以通过router.getParams方法获取传递的参数。

aboutToAppear() {
    this.initBoard()
    let params = router.getParams() as Record<string, string>
    if (params && params.difficulty) {
        this.difficulty = params.difficulty
    }
}

参数的类型为Record<string, string>,使用时需要进行类型断言。这种参数传递机制简单有效,满足了本应用的参数传递需求。

7.4 页面返回与状态管理

返回上一页通过router.back方法实现。在游戏页面的“返回菜单”按钮中,调用back方法即可返回主菜单。

Button('返回菜单')
    .onClick(() => {
        router.back()
    })

需要注意的是,使用back方法返回时,目标页面的状态会保留。例如,如果用户从主菜单进入难度选择页面、选择困难难度、进入游戏页面、开始对局后点击返回菜单,系统会返回到主菜单页面,而不是难度选择页面。这是因为router维护了一个页面栈,back方法会弹出栈顶页面。

7.5 应用入口配置

除了页面级路由配置,应用的入口页面还需要在EntryAbility中进行设置。EntryAbility是HarmonyOS应用的生命周期管理单元,负责应用窗口的创建和销毁。

onWindowStageCreate(windowStage: window.WindowStage): void {
    windowStage.loadContent('pages/MainMenu', (err) => {
        if (err.code) {
            hilog.error(DOMAIN, 'testTag', 'Failed to load the content. Cause: %{public}s', JSON.stringify(err))
            return
        }
        hilog.info(DOMAIN, 'testTag', 'Succeeded in loading the content.')
    })
}

loadContent方法的第一个参数指定了入口页面的路径,这里设置为pages/MainMenu,即应用启动后首先显示主菜单页面。这是应用级别的页面配置,优先级高于main_pages.json中的配置。


8. 常见问题与解决方案

8.1 ArkTS类型声明问题

在开发过程中,遇到的最常见问题是ArkTS的严格类型检查。与TypeScript不同,ArkTS对类型的使用有更严格的限制,特别是在对象字面量的使用方面。

错误信息示例:

Object literals cannot be used as type declarations (arkts-no-obj-literals-as-types)

错误代码:

// 错误写法
getAIMove(): { row: number, col: number } | null {
    // ...
}

解决方案是使用interface或type关键字提前定义类型:

interface Move {
    row: number
    col: number
}

getAIMove(): Move | null {
    // ...
}

同样,在创建对象时也需要显式声明类型:

// 错误写法
emptySpots.push({ row: i, col: j })

// 正确写法
let spot: Move = { row: i, col: j }
emptySpots.push(spot)

8.2 组件属性使用错误

另一个常见错误是组件不支持某些属性或方法。例如,Stack组件不支持justifyContent属性,而应该使用alignContent。

错误代码:

// 错误写法
Stack() {
    // ...
}
.justifyContent(FlexAlign.Center)

解决方案:

// 正确写法
Stack({ alignContent: Alignment.Center }) {
    // ...
}

同样,某些组件的子组件排列属性也可能不同。在使用组件前,建议查阅官方文档确认正确的属性名称和使用方法。

8.3 @Entry组件单根节点限制

ArkTS要求@Entry装饰的组件的build方法只能有一个根节点。这意味着不能同时返回两个并列的组件。

错误代码:

build() {
    Column() { /* ... */ }  // 第一个根节点
    if (this.showWinDialog) {  // 第二个根节点
        Column() { /* ... */ }
    }
}

解决方案是将所有内容包裹在一个父容器中:

build() {
    Stack({ alignContent: Alignment.Center }) {
        Column() { /* 主界面内容 */ }
        if (this.showWinDialog) {
            Column() { /* 对话框内容 */ }
        }
    }
    .width('100%')
    .height('100%')
}

8.4 预览器页面替换问题

在使用DevEco Studio的预览功能时,可能会遇到预览器强制显示某个特定页面的问题。这是因为预览器默认使用当前打开的文件作为预览对象。

解决方案包括:

  1. 关闭不需要预览的文件标签页
  2. 打开希望预览的文件
  3. 点击预览器刷新按钮重新加载

如果问题仍然存在,可以尝试清理构建缓存。在项目目录下删除.hvigor/cache目录下的文件,然后重新构建项目。

8.5 路由跳转失败问题

如果点击按钮后页面没有跳转,可能的原因包括:

  1. 目标页面未在main_pages.json中注册
  2. 路由路径拼写错误
  3. 目标页面文件不存在

排查步骤:

  1. 检查main_pages.json配置,确保目标页面已注册
  2. 检查路由路径是否与配置文件中的路径完全一致
  3. 检查目标页面文件是否存在于指定目录下

9. 性能优化与用户体验提升

9.1 UI渲染性能优化

五子棋游戏需要频繁更新棋盘界面,优化UI渲染性能尤为重要。本项目采用了以下优化措施:

第一,使用@State装饰器实现精准更新。只有标记为@State的状态变化才会触发UI重新渲染,这避免了不必要的渲染开销。在本项目中,board数组的更新只会影响棋盘局部的显示,不会触发整个页面的重绘。

第二,使用ForEach组件进行列表渲染。相比手动创建大量组件,ForEach能够更高效地管理组件的生命周期,减少内存占用和渲染时间。

第三,避免在build方法中执行复杂计算。build方法在每次状态变化时都会被调用,如果其中包含复杂计算,会影响渲染性能。本项目将AI算法逻辑独立到专门的方法中,只有在需要时才调用。

9.2 AI运算性能优化

AI运算是性能消耗的主要来源,特别是在困难难度下需要评估所有空位的情况下。本项目采用了“早停”策略进行优化。

第一,进攻和防守检查优先。在评估所有位置之前,AI会先检查是否存在必杀机会或紧急威胁。这些检查的时间复杂度较低,如果能提前找到合适的落点,就可以跳过耗时的完整评估过程。

第二,减少评估范围。AI主要关注已有棋子周围的空位,而不会评估远离现有棋子的边缘位置。这符合五子棋的棋理——有价值的落点通常在已有棋子附近。

9.3 用户体验优化

除了技术层面的优化,用户体验的提升也是开发的重要目标。

第一,提供即时的视觉反馈。当玩家落子时,棋子立即显示在棋盘上;当AI思考时,显示“AI思考中”的提示,让玩家了解当前游戏状态。

第二,AI落子延迟设计。AI的落子操作有500毫秒的延迟,这不仅是为了避免界面卡顿,更重要的是给玩家留下心理准备的时间。如果AI秒速落子,会让玩家感到困惑和不真实。

第三,胜负对话框的情感化设计。对话框使用表情符号和祝贺文字,让胜利更有成就感,让失败也显得不那么沮丧。“恭喜获胜!”、“AI获胜!”、“平局!”等不同文案适应不同的游戏结果。

9.4 异常处理与容错设计

健壮的应用程序需要具备完善的异常处理能力。虽然五子棋游戏的逻辑相对简单,但仍需要考虑各种异常情况。

第一,输入合法性检查。在落子前检查位置是否为空、游戏是否在进行中,避免无效操作修改游戏状态。

第二,边界条件处理。在胜负判定等算法中,检查数组访问是否越界,确保在边界情况下也能正常工作。

第三,状态一致性维护。游戏状态(playing、player_win、ai_win、draw)在各种转换中保持一致性,避免出现矛盾的游戏状态。


10. 总结与展望

10.1 项目总结

本文详细介绍了基于HarmonyOS的五子棋对战游戏的设计与实现过程。项目采用ArkTS语言开发,充分利用HarmonyOS的声明式UI框架和路由管理机制,实现了包含主菜单、难度选择、游戏对战、规则说明、关于页面等完整功能模块的五子棋游戏。

在技术实现方面,项目完成了以下核心工作:

第一,设计并实现了15×15标准五子棋棋盘的数据结构和渲染逻辑,使用二维数组存储棋盘状态,并通过Stack组件实现棋盘和棋子的叠加显示。

第二,实现了三种不同难度的AI对战策略。简单难度采用随机落子策略,中等难度采用混合策略(50%概率最优+50%概率随机),困难难度采用完整的评分评估算法,能够主动进攻和防守。

第三,设计了简洁美观的用户界面,包括主菜单、游戏页面、胜负对话框等,采用现代扁平化设计风格,提供流畅的用户体验。

第四,实现了完整的页面路由系统,包括页面注册、跳转、参数传递、返回等功能,确保各页面之间能够平滑切换。

10.2 技术收获

通过本项目的开发,获得了以下技术收获:

首先,深入理解了HarmonyOS应用开发的技术体系,包括ArkTS语言特点、声明式UI框架、状态管理机制、路由管理系统等。这些经验对于后续的HarmonyOS应用开发具有重要的参考价值。

其次,掌握了游戏AI算法设计的基本方法。虽然本项目的AI算法相对简单,但其中的评分函数设计、攻防优先级处理等思想可以推广到更复杂的游戏AI开发中。

第三,积累了ArkTS开发中的常见问题解决经验。ArkTS的严格类型检查、组件属性限制、单根节点要求等都需要开发者特别注意,这些经验教训对于提高开发效率具有重要意义。

10.3 未来展望

本项目虽然实现了五子棋游戏的基本功能,但仍有许多可以改进和扩展的方向。

在AI算法方面,可以引入更高级的人工智能算法,如极大极小搜索配合Alpha-Beta剪枝、蒙特卡洛树搜索等,使AI具有更强的棋力。同时,可以考虑引入开局库和残局库,减少AI在常见局面下的思考时间。

在游戏功能方面,可以增加双人对战模式,允许两个玩家在同一设备上对弈。还可以增加悔棋功能、复盘功能、积分系统等,增强游戏的可玩性和社交性。

在界面设计方面,可以添加棋盘动画效果、落子音效、背景音乐等,提升游戏的视听体验。还可以支持自定义皮肤和主题,满足用户的个性化需求。

在跨设备方面,可以利用HarmonyOS的分布式能力,实现多设备协同对战。例如,玩家可以在手机上操控,在平板上显示棋盘,获得更好的游戏体验。

总之,基于HarmonyOS的五子棋游戏开发是一个既有技术挑战又有实际价值的项目,希望本文的介绍能够为HarmonyOS应用开发感兴趣的开发者提供一些参考和帮助。


参考文献

[1] 华为技术有限公司。 HarmonyOS应用开发文档。 https://developer.huawei.com/consumer/cn/doc/

[2] ArkTS语言介绍。 https://developer.huawei.com/consumer/cn/doc/architecture/

[3] HarmonyOS路由管理。 https://developer.huawei.com/consumer/cn/doc/guides/

[4] 五子棋AI算法分析。 人工智能技术与应用研究。

[5] 声明式UI框架设计原理。 软件工程杂志。


附录

附录A:项目源代码文件清单

文件名 说明
MainMenu.ets 主菜单页面
DifficultySelect.ets 难度选择页面
GamePage.ets 游戏主页面(含AI算法)
Rules.ets 游戏规则页面
About.ets 关于页面
EntryAbility.ets 应用入口能力
main_pages.json 页面路由配置

附录B:关键常量定义

常量名 说明
EMPTY 0 空位
PLAYER 1 玩家棋子(黑棋)
AI 2 AI棋子(白棋)
BOARD_SIZE 15 棋盘边长
CELL_SIZE 28 单个格子尺寸(vp)

附录C:游戏状态说明

状态值 说明
playing 游戏进行中
player_win 玩家获胜
ai_win AI获胜
draw 平局

作者: HarmonyOS Development Team

版本: 1.0.0

日期: 2026年6月

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐