✨家人们记得点个账号关注,会持续发布大前端领域技术文章💕 🍃
引言
五子棋是一款经典的策略游戏,规则简单但乐趣无穷。本文将带你使用 HarmonyOS 的 ArkUI 框架,以组件化的思想快速实现一个双人对战的五子棋游戏。我们将逻辑与 UI 分离,打造一个结构清晰、易于维护的应用。

仓库地址(小游戏/五子棋目录)
https://gitee.com/harmonyos-projects/codelabs
1. 棋盘

ets/pages/gobang/01-棋盘.ets
less
@Entry
@Component
struct Study {
// 一个15x15的二维数组,表示棋盘,初始值为0表格空位,1表示黑棋,2表示白棋。
@State board: number[][] = Array(15).fill(null).map(() => Array(15).fill(0))
@State currentPlayer: number = 1 // 1表示黑棋,2表示白棋。
@State gameOver: boolean = false
build() {
Column() {
// 游戏标题和状态显示
Row() {
Text('五子棋').fontSize(24).fontWeight(FontWeight.Bold)
Text(`当前:${this.currentPlayer==1?'黑':'白'}棋`).fontSize(16).margin({ left: 20 })
}
.width('100%')
.margin({ bottom: 10 })
.justifyContent(FlexAlign.Center)
// 棋盘
Column() {
// 行
ForEach(this.board, (row: number[], rowIndex:number) => {
Row() {
ForEach(row, (cell: number, colIndex:number) => {
// 列
Column() {
if (cell) {
Text().width(18).height(18).backgroundColor(cell === 1 ? '#000' : '#fff').borderRadius(9)
}
}
.width(20).height(20).backgroundColor('#DEB887')
.border({ width: 1, color: '#999' })
})
}
})
}.margin(10)
// 操作按钮
Row() {
Button('悔棋').width(120)
Button('重新开始').width(120).margin({ left: 10 })
}
.width('100%')
.justifyContent(FlexAlign.Center)
}.padding(20)
}
}
2. 棋子

ets/pages/gobang/02-棋子.ets
less
@Entry
@Component
struct Study {
// 一个15x15的二维数组,表示棋盘,初始值为0表格空位,1表示黑棋,2表示白棋。
@State board: number[][] = Array(15).fill(null).map(() => Array(15).fill(0))
@State currentPlayer: number = 1 // 1表示黑棋,2表示白棋。
@State gameOver: boolean = false
// 下棋
playChess(row: number, col: number) {
// 1. 过滤数据
if (this.gameOver || this.board[row][col] !== 0) return
// 2. 修改状态
// - 棋盘
this.board[row][col] = this.currentPlayer // 修改二维没响应式
this.board = [...this.board] // 直接覆盖原数据响应式生效
// - next 棋子
this.currentPlayer = this.currentPlayer === 1 ? 2 : 1
}
build() {
Column() {
// 游戏标题和状态显示
Row() {
Text('五子棋').fontSize(24).fontWeight(FontWeight.Bold)
Text(`当前:${this.currentPlayer==1?'黑':'白'}棋`).fontSize(16).margin({ left: 20 })
}
.width('100%')
.margin({ bottom: 10 })
.justifyContent(FlexAlign.Center)
// 棋盘
Column() {
// 行
ForEach(this.board, (row: number[], rowIndex:number) => {
Row() {
ForEach(row, (cell: number, colIndex:number) => {
// 列
Column() {
if (cell) {
Text().width(18).height(18).backgroundColor(cell === 1 ? '#000' : '#fff').borderRadius(9)
}
}
.width(20).height(20).backgroundColor('#DEB887')
.border({ width: 1, color: '#999' })
.onClick(() => this.playChess(rowIndex, colIndex))
})
}
})
}.margin(10)
// 操作按钮
Row() {
Button('悔棋').width(120)
Button('重新开始').width(120).margin({ left: 10 })
}
.width('100%')
.justifyContent(FlexAlign.Center)
}.padding(20)
}
}
3. 检查胜负

ets/pages/gobang/03-检查胜负.ets
less
@Entry
@Component
struct Study {
// 一个15x15的二维数组,表示棋盘,初始值为0表格空位,1表示黑棋,2表示白棋。
@State board: number[][] = Array(15).fill(null).map(() => Array(15).fill(0))
@State currentPlayer: number = 1 // 1表示黑棋,2表示白棋。
@State gameOver: boolean = false
// 下棋
playChess(row: number, col: number) {
// 1. 过滤数据
if (this.gameOver || this.board[row][col] !== 0) return
// 2. 修改状态
// - 棋盘
this.board[row][col] = this.currentPlayer // 修改二维没响应式
this.board = [...this.board] // 直接覆盖原数据响应式生效
// - next 棋子
// this.currentPlayer = this.currentPlayer === 1 ? 2 : 1
// 3. 判断下棋后结果
if (this.checkWin(row, col)) {
this.gameOver = true
AlertDialog.show({ message: `${this.currentPlayer === 1 ? '黑棋' : '白棋'}获胜!` })
} else {
this.currentPlayer = this.currentPlayer === 1 ? 2 : 1
}
}
// 检查胜负
checkWin(row: number, col: number): boolean {
const directions = [ [[-1, 0], [1, 0]], // 垂直
[[0, -1], [0, 1]], // 水平
[[-1, -1], [1, 1]], // 主对角线
[[-1, 1], [1, -1]] // 副对角线
]
for (let direction of directions) {
let count = 1
for (let i = 0; i < direction.length; i++) {
let dx = direction[i][0]
let dy = direction[i][1]
let x = row + dx
let y = col + dy
while (x >= 0 && x < 15 && y >= 0 && y < 15 &&
this.board[x][y] === this.currentPlayer) {
count++
x += dx
y += dy
}
}
if (count >= 5) return true
}
return false
}
build() {
Column() {
// 游戏标题和状态显示
Row() {
Text('五子棋').fontSize(24).fontWeight(FontWeight.Bold)
Text(`当前:${this.currentPlayer==1?'黑':'白'}棋`).fontSize(16).margin({ left: 20 })
}
.width('100%')
.margin({ bottom: 10 })
.justifyContent(FlexAlign.Center)
// 棋盘
Column() {
// 行
ForEach(this.board, (row: number[], rowIndex:number) => {
Row() {
ForEach(row, (cell: number, colIndex:number) => {
// 列
Column() {
if (cell) {
Text().width(18).height(18).backgroundColor(cell === 1 ? '#000' : '#fff').borderRadius(9)
}
}
.width(20).height(20).backgroundColor('#DEB887')
.border({ width: 1, color: '#999' })
.onClick(() => this.playChess(rowIndex, colIndex))
})
}
})
}.margin(10)
// 操作按钮
Row() {
Button('悔棋').width(120)
Button('重新开始').width(120).margin({ left: 10 })
}
.width('100%')
.justifyContent(FlexAlign.Center)
}.padding(20)
}
}
4. 重新开始
ets/pages/gobang/04-重新开始.ets
javascript
// 重新开发
resetGame() {
this.board = Array(15).fill(null).map(() => Array(15).fill(0))
this.currentPlayer = 1
this.gameOver = false
}
5. 悔棋 选写
- 下棋playChess时,保存row、col索引
- 点击悔棋修改改索引状态为空格
- 并且把currentPlayer修改
6. 面向对象封装

ets/gobang/Control.ets
typescript
export class Control {
private currentPlayer: number = 1 // 1表示黑棋,2表示白棋。
private gameOver: boolean = false
// 下棋
playChess(board:number[][], row: number, col: number) {
// 1. 过滤数据
if (this.gameOver || board[row][col] !== 0) return
// 2. 修改状态
// - 棋盘
board[row][col] = this.currentPlayer
// - next 棋子
// this.currentPlayer = this.currentPlayer === 1 ? 2 : 1
// 3. 判断下棋后结果
if (this.checkWin(board, row, col)) {
this.gameOver = true
AlertDialog.show({ message: `${this.currentPlayer === 1 ? '黑棋' : '白棋'}获胜!` })
} else {
this.currentPlayer = this.currentPlayer === 1 ? 2 : 1
}
return [...board]
}
// 检查胜负
checkWin(board:number[][], row: number, col: number): boolean {
const directions = [
[[-1, 0], [1, 0]], // 垂直
[[0, -1], [0, 1]], // 水平
[[-1, -1], [1, 1]], // 主对角线
[[-1, 1], [1, -1]] // 副对角线
]
for (let direction of directions) {
let count = 1
for (let i = 0; i < direction.length; i++) {
let dx = direction[i][0]
let dy = direction[i][1]
let x = row + dx
let y = col + dy
while (x >= 0 && x < 15 && y >= 0 && y < 15 &&
board[x][y] === this.currentPlayer) {
count++
x += dx
y += dy
}
}
if (count >= 5) return true
}
return false
}
// 重新开始
resetGame():number[][] {
this.currentPlayer = 1
this.gameOver = false
return this.sourceData()
}
// 数据源
sourceData():number[][] {
return Array(15).fill(null).map(() => Array(15).fill(0))
}
}
ets/pages/gobang/05-封装.ets
scss
import { Control } from './Control'
@Entry
@Component
struct Study {
private control1 = new Control()
@State board1: number[][] = this.control1.sourceData()
private control2 = new Control()
@State board2: number[][] = this.control2.sourceData()
build() {
Column() {
// 棋盘1
Column() {
// 行
ForEach(this.board1, (row: number[], rowIndex:number) => {
Row() {
ForEach(row, (cell: number, colIndex:number) => {
// 列
Column() {
if (cell) {
Text().width(18).height(18).backgroundColor(cell === 1 ? '#000' : '#fff').borderRadius(9)
}
}
.width(20).height(20).backgroundColor('#DEB887')
.border({ width: 1, color: '#999' })
.onClick(() => {
const data = this.control1.playChess(this.board1, rowIndex, colIndex)
if (data) this.board1 = data
})
})
}
})
Button('重新开始').onClick(() => this.board1 = this.control1.resetGame())
}.margin(10)
// 棋盘2
Column() {
// 行
ForEach(this.board2, (row: number[], rowIndex:number) => {
Row() {
ForEach(row, (cell: number, colIndex:number) => {
// 列
Column() {
if (cell) {
Text().width(18).height(18).backgroundColor(cell === 1 ? '#000' : '#fff').borderRadius(9)
}
}
.width(20).height(20).backgroundColor('#DEB887')
.border({ width: 1, color: '#999' })
.onClick(() => {
const data = this.control2.playChess(this.board2, rowIndex, colIndex)
if (data) this.board2 = data
})
})
}
})
Button('重新开始').onClick(() => this.board2 = this.control2.resetGame())
}.margin(10)
}.padding(20)
}
}
✨家人们点个账号关注,会持续发布大前端领域技术文章💕 🍃
✨家人们点个账号关注,会持续发布大前端领域技术文章💕 🍃
✨家人们点个账号关注,会持续发布大前端领域技术文章💕 🍃
^_^ 点关注、不迷路、主播带你学技术 (๑′ᴗ‵๑)I Lᵒᵛᵉᵧₒᵤ❤