
【引言】
"区字棋"是一款简单而有趣的棋类游戏,玩家通过移动棋子,连接棋子之间的线条,最终实现胜利条件。游戏规则简单明了,但需要一定的策略和思考。
【环境准备】
电脑系统:windows 10
开发工具:DevEco Studio NEXT Beta1 Build Version: 5.0.3.806
工程版本:API 12
真机:Mate 60 Pro
语言:ArkTS、ArkUI
【技术实现】
在本案例中,我们使用了鸿蒙系统提供的UI框架和特性,通过自定义棋子类Cell和连接关系类Connection,实现了棋子之间的移动和连接。同时,利用鸿蒙系统的动画功能,为棋子移动添加了流畅的动画效果,提升了用户体验。
【游戏流程】
游戏加载时,初始化棋盘上的棋子和连接关系。
玩家点击棋子进行移动,判断移动是否合法,若合法则执行移动动画。
移动完成后,切换玩家回合,检查游戏是否结束。
若游戏未结束,AI自动走法,选择最佳移动策略。
游戏结束时,显示胜利者信息,并提供重新开始按钮。
【结语】
【完整代码】
import { promptAction } from '@kit.ArkUI';
@ObservedV2
class Cell {
@Trace user: number = 0;
name: string = "";
x: number = 0;
y: number = 0;
width: number = 100;
height: number = 100;
constructor(name: string, x: number, y: number, user: number) {
this.user = user;
this.name = name;
this.x = x;
this.y = y;
}
@Trace animX: number = 0;
@Trace animY: number = 0;
getCenterX() {
return this.x - this.width / 2;
}
getCenterY() {
return this.y - this.height / 2;
}
moveAnimation(animationTime: number, toCell: Cell, callback?: () => void) {
animateToImmediately({
duration: animationTime,
iterations: 1,
curve: Curve.Linear,
onFinish: () => {
animateToImmediately({
duration: 0,
iterations: 1,
curve: Curve.Linear,
onFinish: () => {
if (callback) {
callback();
}
}
}, () => {
this.animX = 0;
this.animY = 0;
let temp = this.user;
this.user = toCell.user;
toCell.user = temp;
});
}
}, () => {
this.animX = toCell.x - this.x;
this.animY = toCell.y - this.y;
});
}
}
class Connection {
startName: string;
endName: string;
startX: number;
startY: number;
endX: number;
endY: number;
constructor(start: Cell, end: Cell) {
this.startName = start.name;
this.endName = end.name;
this.startX = start.x;
this.startY = start.y;
this.endX = end.x;
this.endY = end.y;
}
}
@Entry
@Component
struct TwoSonChessGame {
@State isAnimationRunning: boolean = false;
@State cells: Cell[] = [];
@State connections: Connection[] = [];
@State currentPlayer: number = 1;
aboutToAppear(): void {
const cellA = new Cell("A", 180, 180, 2);
const cellB = new Cell("B", 540, 180, 1);
const cellC = new Cell("C", 360, 360, 0);
const cellD = new Cell("D", 180, 540, 1);
const cellE = new Cell("E", 540, 540, 2);
this.cells.push(cellA, cellB, cellC, cellD, cellE);
this.connections.push(new Connection(cellA, cellB));
this.connections.push(new Connection(cellA, cellC));
this.connections.push(new Connection(cellA, cellD));
this.connections.push(new Connection(cellB, cellC));
this.connections.push(new Connection(cellC, cellD));
this.connections.push(new Connection(cellC, cellE));
this.connections.push(new Connection(cellD, cellE));
}
resetGame() {
this.currentPlayer = 1;
this.cells[0].user = 2;
this.cells[1].user = 1;
this.cells[2].user = 0;
this.cells[3].user = 1;
this.cells[4].user = 2;
}
move(cell: Cell) {
if (this.isCellValid(cell)) {
let targetIndex = this.checkValidMove(cell);
if (targetIndex !== -1) {
this.isAnimationRunning = true;
cell.moveAnimation(300, this.cells[targetIndex], () => {
this.isAnimationRunning = false;
this.moveCompleted();
});
} else {
console.info(`当前位置无法移动`);
}
}
}
moveCompleted() {
this.currentPlayer = this.currentPlayer === 1 ? 2 : 1;
if (this.isGameOver()) {
let winner = this.currentPlayer === 1 ? '白棋赢了' : '黑棋赢了';
console.info(`${winner}`);
promptAction.showDialog({
title: '游戏结束',
message: `${winner}`,
buttons: [{ text: '重新开始', color: '#ffa500' }]
}).then(() => {
this.resetGame();
});
} else {
if (this.currentPlayer === 2) {
this.aiMove();
}
}
}
aiMove() {
let whiteCells = this.cells.filter(cell => cell.user === 2 && this.checkValidMove(cell) !== -1);
if (whiteCells.length === 1) {
this.move(whiteCells[0]);
} else if (whiteCells.length === 2) {
let moveIndex = this.chooseBestMove(whiteCells);
this.move(whiteCells[moveIndex]);
}
}
chooseBestMove(whiteCells: Cell[]): number {
let emptyIndex = this.cells.findIndex(cell => cell.user === 0);
let bestMoveIndex = -1;
for (let i = 0; i < whiteCells.length; i++) {
let tempUser = whiteCells[i].user;
whiteCells[i].user = this.cells[emptyIndex].user;
this.cells[emptyIndex].user = tempUser;
this.currentPlayer = 1;
let isGameOver = this.isGameOver();
tempUser = whiteCells[i].user;
whiteCells[i].user = this.cells[emptyIndex].user;
this.cells[emptyIndex].user = tempUser;
this.currentPlayer = 2;
if (isGameOver) {
bestMoveIndex = i;
break;
}
}
if (bestMoveIndex === -1) {
bestMoveIndex = Math.floor(Math.random() * 2);
}
return bestMoveIndex;
}
isCellValid(cell: Cell): boolean {
return (cell.user === 1 && this.currentPlayer === 1) || (cell.user === 2 && this.currentPlayer === 2);
}
isGameOver(): boolean {
for (let i = 0; i < this.cells.length; i++) {
if (this.currentPlayer == this.cells[i].user && this.checkValidMove(this.cells[i]) != -1) {
return false;
}
}
return true;
}
checkValidMove(cell: Cell): number {
for (let i = 0; i < this.connections.length; i++) {
if (cell.name === this.connections[i].startName) {
for (let j = 0; j < this.cells.length; j++) {
if (this.cells[j].name === this.connections[i].endName && this.cells[j].user === 0) {
return j;
}
}
} else if (cell.name === this.connections[i].endName) {
for (let j = 0; j < this.cells.length; j++) {
if (this.cells[j].name === this.connections[i].startName && this.cells[j].user === 0) {
return j;
}
}
}
}
return -1;
}
build() {
Column({ space: 10 }) {
Stack() {
ForEach(this.connections, (connection: Connection, _index) => {
Line()
.width(5)
.height(5)
.startPoint([`${connection.startX}lpx`, `${connection.startY}lpx`])
.endPoint([`${connection.endX}lpx`, `${connection.endY}lpx`])
.stroke(Color.Black)
.fill(Color.Green);
});
ForEach(this.cells, (cell: Cell, _index) => {
Text()
.width(`${cell.width}lpx`)
.height(`${cell.height}lpx`)
.margin({ left: `${cell.getCenterX()}lpx`, top: `${cell.getCenterY()}lpx` })
.translate({ x: `${cell.animX}lpx`, y: `${cell.animY}lpx` })
.backgroundColor(cell.user === 0 ? Color.Transparent :
(cell.user === 1 ? Color.Black : Color.White))
.borderRadius('50%')
.onClick(() => {
if (this.isAnimationRunning) {
console.info(`动画执行中`);
return;
}
this.move(cell);
});
});
}
.align(Alignment.TopStart)
.width('720lpx')
.height('720lpx')
.backgroundColor(Color.Orange);
Button('重新开始').onClick(() => {
if (this.isAnimationRunning) {
console.info(`动画执行中`);
return;
}
this.resetGame();
});
}
}
}
- 1.
- 2.
- 3.
- 4.
- 5.
- 6.
- 7.
- 8.
- 9.
- 10.
- 11.
- 12.
- 13.
- 14.
- 15.
- 16.
- 17.
- 18.
- 19.
- 20.
- 21.
- 22.
- 23.
- 24.
- 25.
- 26.
- 27.
- 28.
- 29.
- 30.
- 31.
- 32.
- 33.
- 34.
- 35.
- 36.
- 37.
- 38.
- 39.
- 40.
- 41.
- 42.
- 43.
- 44.
- 45.
- 46.
- 47.
- 48.
- 49.
- 50.
- 51.
- 52.
- 53.
- 54.
- 55.
- 56.
- 57.
- 58.
- 59.
- 60.
- 61.
- 62.
- 63.
- 64.
- 65.
- 66.
- 67.
- 68.
- 69.
- 70.
- 71.
- 72.
- 73.
- 74.
- 75.
- 76.
- 77.
- 78.
- 79.
- 80.
- 81.
- 82.
- 83.
- 84.
- 85.
- 86.
- 87.
- 88.
- 89.
- 90.
- 91.
- 92.
- 93.
- 94.
- 95.
- 96.
- 97.
- 98.
- 99.
- 100.
- 101.
- 102.
- 103.
- 104.
- 105.
- 106.
- 107.
- 108.
- 109.
- 110.
- 111.
- 112.
- 113.
- 114.
- 115.
- 116.
- 117.
- 118.
- 119.
- 120.
- 121.
- 122.
- 123.
- 124.
- 125.
- 126.
- 127.
- 128.
- 129.
- 130.
- 131.
- 132.
- 133.
- 134.
- 135.
- 136.
- 137.
- 138.
- 139.
- 140.
- 141.
- 142.
- 143.
- 144.
- 145.
- 146.
- 147.
- 148.
- 149.
- 150.
- 151.
- 152.
- 153.
- 154.
- 155.
- 156.
- 157.
- 158.
- 159.
- 160.
- 161.
- 162.
- 163.
- 164.
- 165.
- 166.
- 167.
- 168.
- 169.
- 170.
- 171.
- 172.
- 173.
- 174.
- 175.
- 176.
- 177.
- 178.
- 179.
- 180.
- 181.
- 182.
- 183.
- 184.
- 185.
- 186.
- 187.
- 188.
- 189.
- 190.
- 191.
- 192.
- 193.
- 194.
- 195.
- 196.
- 197.
- 198.
- 199.
- 200.
- 201.
- 202.
- 203.
- 204.
- 205.
- 206.
- 207.
- 208.
- 209.
- 210.
- 211.
- 212.
- 213.
- 214.
- 215.
- 216.
- 217.
- 218.
- 219.
- 220.
- 221.
- 222.
- 223.
- 224.
- 225.
- 226.
- 227.
- 228.
- 229.
- 230.
- 231.
- 232.
- 233.
- 234.
- 235.
- 236.
- 237.
- 238.
- 239.
- 240.
- 241.
- 242.
- 243.
- 244.
- 245.
- 246.
- 247.
- 248.
- 249.
- 250.
- 251.
- 252.
- 253.
- 254.
- 255.
- 256.
- 257.
- 258.
- 259.
- 260.
- 261.
- 262.
- 263.
- 264.
- 265.
- 266.
- 267.
- 268.
- 269.
- 270.
- 271.
- 272.
- 273.
- 274.
- 275.
- 276.
- 277.
- 278.
- 279.
- 280.
- 281.
- 282.
- 283.
- 284.
- 285.
- 286.
- 287.
- 288.
- 289.
- 290.
- 291.
- 292.
- 293.
- 294.
- 295.
- 296.
- 297.
- 298.
- 299.
- 300.
- 301.
- 302.
- 303.